diff --git a/control/iosys.py b/control/iosys.py
index 29f5bfefb..42cf4094d 100644
--- a/control/iosys.py
+++ b/control/iosys.py
@@ -46,6 +46,24 @@ class NamedSignal(np.ndarray):
This class modifies the `numpy.ndarray` class and allows signals to
be accessed using the signal name in addition to indices and slices.
+ Signals can be either a 2D array, index by signal and time, or a 3D
+ array, indexed by signal, trace, and time.
+
+ Attributes
+ ----------
+ signal_labels : list of str
+ Label names for each of the signal elements in the signal.
+ trace_labels : list of str, optional
+ Label names for each of the traces in the signal (if multi-trace).
+
+ Examples
+ --------
+ >>> sys = ct.rss(
+ ... states=['p1', 'p2', 'p3'], inputs=['u1', 'u2'], outputs=['y'])
+ >>> resp = ct.step_response(sys)
+ >>> resp.states['p1', 'u1'] # Step response from u1 to p1
+ NamedSignal(...)
+
"""
def __new__(cls, input_array, signal_labels=None, trace_labels=None):
# See https://numpy.org/doc/stable/user/basics.subclassing.html
@@ -314,7 +332,7 @@ def _repr_latex_(self):
def _repr_html_(self):
# Defaults to using __repr__; override in subclasses
return None
-
+
def _repr_markdown_(self):
return self._repr_html_()
diff --git a/control/tests/phaseplot_test.py b/control/tests/phaseplot_test.py
index fc4edcbea..ac5249948 100644
--- a/control/tests/phaseplot_test.py
+++ b/control/tests/phaseplot_test.py
@@ -167,7 +167,7 @@ def invpend_ode(t, x, m=0, l=0, b=0, g=0):
ct.phase_plane_plot(
invpend_ode, [-5, 5, 2, 2], params={'stuff': (1, 1, 0.2, 1)},
plot_streamlines=True)
-
+
with pytest.raises(ValueError, match="gridtype must be 'meshgrid' when using streamplot"):
ct.phase_plane_plot(ct.rss(2, 1, 1), plot_streamlines=False,
plot_streamplot=True, gridtype='boxgrid')
@@ -190,7 +190,7 @@ def invpend_ode(t, x, m=0, l=0, b=0, g=0):
sys, [-12, 12, -10, 10], 15, gridspec=[2, 9],
plot_streamlines=True, plot_separatrices=False,
suppress_warnings=True)
-
+
@pytest.mark.usefixtures('mplcleanup')
def test_phase_plot_zorder():
# some of these tests are a bit akward since the streamlines and separatrices
@@ -211,7 +211,7 @@ def get_zorders(cplt):
assert cplt.lines[3] == None or isinstance(cplt.lines[3], mpl.streamplot.StreamplotSet)
streamplot = max(cplt.lines[3].lines.get_zorder(), cplt.lines[3].arrows.get_zorder()) if cplt.lines[3] else None
return streamlines, quiver, streamplot, separatrices, equilpoints
-
+
def assert_orders(streamlines, quiver, streamplot, separatrices, equilpoints):
print(streamlines, quiver, streamplot, separatrices, equilpoints)
if streamlines is not None:
@@ -261,8 +261,6 @@ def sys(t, x):
# make sure changing the norm at least doesn't throw an error
ct.phase_plane_plot(sys, plot_streamplot=dict(vary_color=True, norm=mpl.colors.LogNorm()))
-
-
@pytest.mark.usefixtures('mplcleanup')
def test_basic_phase_plots(savefigs=False):
diff --git a/control/timeresp.py b/control/timeresp.py
index bd549589a..3c49d213e 100644
--- a/control/timeresp.py
+++ b/control/timeresp.py
@@ -551,10 +551,10 @@ def outputs(self):
def states(self):
"""Time response state vector.
- Time evolution of the state vector, indexed indexed by either the
- state and time (if only a single trace is given) or the state, trace,
- and time (for multiple traces). See `TimeResponseData.squeeze`
- for a description of how this can be modified using the `squeeze`
+ Time evolution of the state vector, indexed by either the state and
+ time (if only a single trace is given) or the state, trace, and
+ time (for multiple traces). See `TimeResponseData.squeeze` for a
+ description of how this can be modified using the `squeeze`
keyword.
Input and output signal names can be used to index the data in
@@ -616,9 +616,9 @@ def inputs(self):
def _legacy_states(self):
"""Time response state vector (legacy version).
- Time evolution of the state vector, indexed indexed by either the
- state and time (if only a single trace is given) or the state,
- trace, and time (for multiple traces).
+ Time evolution of the state vector, indexed by either the state and
+ time (if only a single trace is given) or the state, trace, and
+ time (for multiple traces).
The `legacy_states` property is not affected by the `squeeze` keyword
and hence it will always have these dimensions.
diff --git a/doc/Makefile b/doc/Makefile
index 493fd7da5..4029dd70f 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,7 +1,7 @@
# Makefile for python-control Sphinx documentation
# RMM, 15 Jan 2025
-FIGS = figures/classes.pdf
+FIGS = figures/classes.svg
RST_FIGS = figures/flatsys-steering-compare.png \
figures/iosys-predprey-open.png \
figures/timeplot-servomech-combined.png \
diff --git a/doc/_templates/extended-class-template.rst b/doc/_templates/extended-class-template.rst
new file mode 100644
index 000000000..6e1e4ccd7
--- /dev/null
+++ b/doc/_templates/extended-class-template.rst
@@ -0,0 +1,9 @@
+{{ fullname | escape | underline}}
+
+.. currentmodule:: {{ module }}
+
+.. autoclass:: {{ objname }}
+ :no-members:
+ :show-inheritance:
+ :no-inherited-members:
+ :no-special-members:
diff --git a/doc/classes.rst b/doc/classes.rst
index 0ab508a3a..000761724 100644
--- a/doc/classes.rst
+++ b/doc/classes.rst
@@ -14,10 +14,10 @@ systems (both linear time-invariant and nonlinear). They are usually
created from factory functions such as :func:`tf` and :func:`ss`, so the
user should normally not need to instantiate these directly.
-The following figure illustrates the relationship between the classes.
+The following figure illustrates the relationship between the classes:
-.. image:: figures/classes.pdf
- :width: 800
+.. figure:: figures/classes.svg
+ :width: 640
:align: center
.. autosummary::
@@ -34,6 +34,17 @@ The following figure illustrates the relationship between the classes.
InterconnectedSystem
LinearICSystem
+The time response of an input/output system is represented using a
+special :class:`NamedSignal` class that allows the individual signal
+elements to be access using signal names in place of integer offsets:
+
+.. autosummary::
+ :toctree: generated/
+ :template: extended-class-template.rst
+ :nosignatures:
+
+ NamedSignal
+
Response and Plotting Classes
=============================
@@ -95,5 +106,5 @@ operations:
optimal.OptimalEstimationProblem
optimal.OptimalEstimationResult
-More informaton on the functions used to create these classes can be
+More information on the functions used to create these classes can be
found in the :ref:`nonlinear-systems` chapter.
diff --git a/doc/figures/Makefile b/doc/figures/Makefile
index 1ca54b372..26bdf22c2 100644
--- a/doc/figures/Makefile
+++ b/doc/figures/Makefile
@@ -2,7 +2,7 @@
# RMM, 26 Dec 2024
# List of figures that need to be created (first figure generated is OK)
-FIGS = classes.pdf
+FIGS = classes.svg
# Location of the control package
SRCDIR = ../..
@@ -12,5 +12,5 @@ all: $(FIGS)
clean:
/bin/rm -f $(FIGS)
-classes.pdf: classes.fig
- fig2dev -Lpdf $< $@
+classes.svg: classes.fig
+ fig2dev -Lsvg $< $@
diff --git a/doc/figures/classes.fig b/doc/figures/classes.fig
index 4e63b8bff..17c112cc7 100644
--- a/doc/figures/classes.fig
+++ b/doc/figures/classes.fig
@@ -1,4 +1,5 @@
-#FIG 3.2 Produced by xfig version 3.2.8b
+#FIG 3.2 Produced by xfig version 3.2.9
+#encoding: UTF-8
Landscape
Center
Inches
@@ -37,12 +38,13 @@ Single
2 1 0 2 1 7 50 -1 -1 0.000 0 0 7 0 1 2
1 0 1.00 60.00 90.00
6525 1950 7050 2550
-4 1 1 50 -1 16 12 0.0000 4 210 2115 4050 3675 InterconnectedSystem\001
-4 1 1 50 -1 16 12 0.0000 4 165 1605 7950 3675 TransferFunction\001
-4 1 1 50 -1 0 12 0.0000 4 150 345 7050 2775 LTI\001
-4 1 1 50 -1 16 12 0.0000 4 210 1830 5175 2775 NonlinearIOSystem\001
-4 1 1 50 -1 16 12 0.0000 4 210 1095 6150 3675 StateSpace\001
-4 1 1 50 -1 16 12 0.0000 4 210 1500 5175 4575 LinearICSystem\001
-4 2 1 50 -1 16 12 0.0000 4 210 1035 3375 3225 FlatSystem\001
-4 0 1 50 -1 16 12 0.0000 4 165 420 8400 3225 FRD\001
-4 1 1 50 -1 16 12 0.0000 4 210 1770 6300 1875 InputOutputSystem\001
+4 1 1 50 -1 16 12 0.0000 4 191 1958 4050 3675 InterconnectedSystem\001
+4 1 1 50 -1 16 12 0.0000 4 151 1496 7950 3675 TransferFunction\001
+4 1 1 50 -1 0 12 0.0000 4 133 305 7050 2775 LTI\001
+4 1 1 50 -1 16 12 0.0000 4 191 1705 5175 2775 NonlinearIOSystem\001
+4 1 1 50 -1 16 12 0.0000 4 190 1016 6150 3675 StateSpace\001
+4 1 1 50 -1 16 12 0.0000 4 191 1394 5175 4575 LinearICSystem\001
+4 2 1 50 -1 16 12 0.0000 4 191 970 3375 3225 FlatSystem\001
+4 0 1 50 -1 16 12 0.0000 4 145 384 8400 3225 FRD\001
+4 1 1 50 -1 16 12 0.0000 4 191 1681 6300 1875 InputOutputSystem\001
+4 1 7 50 -1 16 12 0.0000 4 22 21 5175 4800 .\001
diff --git a/doc/figures/classes.pdf b/doc/figures/classes.pdf
deleted file mode 100644
index 2c51b0193..000000000
Binary files a/doc/figures/classes.pdf and /dev/null differ
diff --git a/doc/figures/classes.svg b/doc/figures/classes.svg
new file mode 100644
index 000000000..98fedb596
--- /dev/null
+++ b/doc/figures/classes.svg
@@ -0,0 +1,151 @@
+
+
+
+
+
diff --git a/doc/linear.rst b/doc/linear.rst
index a9960feca..200260303 100644
--- a/doc/linear.rst
+++ b/doc/linear.rst
@@ -531,6 +531,8 @@ equilibrium values (thereby keeping the input/output gain unchanged at
zero frequency ["DC"]).
+.. _displaying-lti-system-information:
+
Displaying LTI System Information
=================================
diff --git a/doc/phaseplot.rst b/doc/phaseplot.rst
index d2a3e6353..8c014415f 100644
--- a/doc/phaseplot.rst
+++ b/doc/phaseplot.rst
@@ -43,7 +43,7 @@ on a grid, equilibrium points and separatrices if they exist. A variety
of options are available to modify the information that is plotted,
including plotting a grid of vectors instead of streamlines, plotting
streamlines from arbitrary starting points and turning on and off
-various features of the plot.
+various features of the plot.
To illustrate some of these possibilities, consider a phase plane plot for
an inverted pendulum system, which is created using a mesh grid:
diff --git a/doc/releases.rst b/doc/releases.rst
index 88a76775a..7bc5c0f46 100644
--- a/doc/releases.rst
+++ b/doc/releases.rst
@@ -27,6 +27,7 @@ the ability to index systems and signal using signal labels.
.. toctree::
:maxdepth: 1
+ releases/0.10.2-notes
releases/0.10.1-notes
releases/0.10.0-notes
diff --git a/doc/releases/0.10.1-notes.rst b/doc/releases/0.10.1-notes.rst
index dd0939021..8b99100f2 100644
--- a/doc/releases/0.10.1-notes.rst
+++ b/doc/releases/0.10.1-notes.rst
@@ -2,8 +2,8 @@
.. _version-0.10.1:
-Version 0.10.1 Release Notes (current)
---------------------------------------
+Version 0.10.1 Release Notes
+----------------------------
* Released: 17 Aug 2024
* `GitHub release page
diff --git a/doc/releases/0.10.2-notes.rst b/doc/releases/0.10.2-notes.rst
new file mode 100644
index 000000000..3e13239aa
--- /dev/null
+++ b/doc/releases/0.10.2-notes.rst
@@ -0,0 +1,239 @@
+.. currentmodule:: control
+
+.. _version-0.10.2:
+
+Version 0.10.2 Release Notes (current)
+--------------------------------------
+
+* Released: date of release
+* `GitHub release page
+ `_
+
+This release contains numerous bug fixes and improvements, including
+substantial updates to the documentation, including refactoring of the
+online manual into a User Guide and a Reference Manual, as well as
+more consistent and complete docstrings. In addition, signals and
+systems can now be referenced using signal labels in addition to
+offsets, and phase plane plots make use of the matplotlib
+`~matplotlib.pyplot.streamplot` function. Numerous other changes have
+been made to improve consistency of keyword arguments and function
+names, with legacy aliases available.
+
+This version of `python-control` requires Python 3.10 or higher, NumPy
+1.23 or higher (2.x recommended), and SciPy 1.8 or higher.
+
+
+New classes, functions, and methods
+...................................
+
+The following new classes, functions, and methods have been added in
+this release:
+
+* `find_operating_point`: this function replaces (with a legacy alias)
+ the `find_eqpt` function and now returns an `OperatingPoint` object
+ containing the information about the operating point.
+
+* `combine_tf` and `split_tf`: these two new functions allow you to
+ create an MIMO transfer function from SISO transfer functions and
+ vice versa.
+
+* `create_statefbk_iosystem` now allows the creation of state feedback
+ controllers using a "reference gain" pattern (:math:`u = k_\text{f}\,
+ r - K x`) in addition to the default "trajectory generation" pattern
+ (:math:`u = u_\text{d} - K(x - x_\text{d})`).
+
+* `disk_margins`: compute disk-based stability margins for SISO and
+ MIMO systems.
+
+* `model_reduction`: allow specific states, inputs, or outputs to be
+ either eliminated or retained.
+
+* `place_acker`: renamed version of `acker` (which is still accessible
+ via an alias).
+
+
+Bug fixes
+.........
+
+The following bugs have been fixed in this release:
+
+* `phase_plane_plot`: fixed a bug in which the return value was
+ returning a sublist of lines rather than just a list of lines in
+ `cplt.lines`.
+
+* Processing of the timebase parameter (`dt`) for I/O systems is now
+ handled uniformly across all I/O system factory functions. This
+ affected the `zpk` function, which was defaulting to a discrete time
+ system to have timebase None instead of 0.
+
+* Multiplying (*), adding (+), or subtracting (-) a constant from any
+ (MIMO) LTI object now acts element-wise (same as ndarray's). This
+ fixes a bug where multiplying a MIMO LTI system by a constant was
+ multiplying by a matrix filled with the constant rather than a
+ diagonal matrix (scaled identity).
+
+* Fixed a bug where specifying an FRD system with fewer than 4
+ frequency points was generating an error because the default
+ settings try to set up a smooth (interpolating) response and the
+ default degree of the fit was 3.
+
+* Fixed some bugs where computing poles and zeros of transfer
+ functions could generate spurious error messages about unsafe
+ casting of complex numbers to real numbers.
+
+* `TimeResponseData.to_pandas`: multi-trace data (e.g., the output
+ from a MIMO step response) was not being processed correctly. A new
+ column 'trace' is now generated for multi-trace responses.
+
+* Fixed a bug where where some arguments to `nyquist_plot` were not
+ being processed correctly and generated errors about unrecognized
+ keywords.
+
+* Updated `ctrb` and `obsv` to handle 1D `B` or `C` matrix correctly.
+
+* `bode_plot`: Fixed missing plot title when `display_margin` keyword
+ was used.
+
+* `singular_values_plot`: color cycling was not working correctly when
+ a list of systems or responses was provided.
+
+* `nyquist_plot`: The `lines` parameter of the `ControlPlot` object
+ now matches the documentation. A 2D array is returned with the
+ first index corresponding to the response (system) index and the
+ second index corresponding to the segment type (primary, mirror x
+ unscaled, scaled).
+
+* Fix some internal bugs that cropped up when using NumPy 2.3.1 but
+ were latent prior to that.
+
+
+Improvements
+............
+
+The following additional improvements and changes in functionality
+were implemented in this release:
+
+* User documentation is now divided into a User Guide that provides a
+ description of the main functionality of the python-control package,
+ along with a Reference Manual describing classes, functions, and
+ parameters in more detail.
+
+* Signal responses and I/O subsystem specifications can now use signal
+ names in addition to indices to get the desired inputs, outputs, and
+ states (e.g., `response.outputs['y0', 'y1']`). This is implemented
+ via a new `NamedSignal` object, which generalizes `numpy.ndarray`.
+
+* `find_operating_point` (legacy `find_eqpt`): accepts new parameters
+ `root_method` and `root_kwargs` to set the root finding algorithm
+ that is used.
+
+* `root_locus_map` now correctly handles the degenerate case of being
+ passed a single gain.
+
+* The `PoleZeroData` object now takes a `sort_loci` parameter when it
+ is created, with a default value of True. This is useful if you
+ create a `PoleZeroData` object by hand (e.g., for producing stability
+ diagrams).
+
+* Factory functions for I/O system creation are now consistent in
+ terms of copying signal/system names, overriding system/signal
+ names, and converting between classes.
+
+* The `tf` factory function to allow a 2D list of SISO transfer
+ functions to be given as a means of creating a MIMO transfer
+ function (use the new `combine_tf` function).
+
+* The `nlsys` factory function can now create a `NonlinearIOSystem`
+ representation of a `StateSpace` system (passed as the first
+ argument to `nlsys`).
+
+* LTI systems now have member functions for computing the various time
+ responses and generating frequency domain plots. See `LTI.to_ss`,
+ `LTI.to_tf`, `LTI.bode_plot`, `LTI.nyquist_plot`, `LTI.nichols_plot`
+ and `LTI.forced_response`, `LTI.impulse_response`,
+ `LTI.initial_response`, `LTI.step_response`.
+
+* String representations of I/O systems (accessed via `repr`, `print`,
+ and `str`) have been updated to create a more consistent form and
+ provide more useful information. See
+ :ref:`displaying-lti-system-information` for more information.
+
+* Binary operations between MIMO and SISO functions are now supported,
+ with the SISO system being converted to a MIMO system as if it were
+ a scalar.
+
+* `nyquist_response`: generates an error if you force the system to
+ evaluate the dynamics at a pole.
+
+* `phase_crossover_frequencies`: turned off spurious warning messages.
+
+* `ss2tf`: added new `method=scipy` capability, allowing `ss2tf` to
+ work on MIMO systems even if Slycot is not present.
+
+* `flatsys.solve_flat_optimal` (legacy `flatsys.solve_flat_ocp`):
+ allows scalar time vector.
+
+* Improved checking of matrix shapes and better error messages in
+ state space factory functions and other operations where matrices
+ are passed as arguments.
+
+* `FrequencyResponseData`: use `~FrequencyResponseData.complex` to
+ access the (squeeze processed) complex frequency response (instead
+ of the legacy `response` property) and
+ `~FrequencyResponseData.frdata ` to access
+ the 3D frequency response data array (instead of the legacy `fresp`
+ attribute).
+
+* Time response and optimization function keywords have been
+ regularized to allow consistent use of keywords across related
+ functions:
+
+ - Parameters specifying the inputs, outputs, and states are referred
+ to as `inputs`, `outputs`, and `states` consistently throughout the
+ functions.
+
+ - Variables associated with inputs, outputs, states and time use
+ those words plus an appropriate modifier: `initial_state`,
+ `final_output`, `input_indices`, etc.
+
+ - Aliases are used both to maintain backward compatibility and to
+ allow shorthand descriptions: e.g., `U`, `Y`, `X0`. Short form
+ aliases are documented in docstrings by listing the parameter as
+ ``long_form (or sf) : type``.
+
+ - Existing legacy keywords are allowed and generate a
+ `PendingDeprecationWarning`. Specifying a parameter value in two
+ different ways (e.g., via long form and an alias) generates a
+ `TypeError`.
+
+* `phase_plane_plot`: makes use of the matplotlib
+ `~matplotlib.pyplot.streamplot` function to provide better default
+ phase plane diagrams.
+
+* `root_locus_plot`: added by the ability to recompute the root locus
+ when zooming in on portions of the root locus diagram.
+
+* `nyquist_plot`: updated the rescaling algorithm to use a more
+ gradual change in the magnitude of the Nyquist curve. The
+ `blend_fraction` parameter can be used to start the rescaling prior
+ to reaching `max_curve_magnitude`, giving less confusing plots. Some
+ default parameter values have been adjusted to improve Nyquist
+ plots.
+
+
+Deprecations
+............
+
+The following functions have been newly deprecated in this release and
+generate a warning message when used:
+
+* `FrequencyResponseData.response`: use
+ `FrequencyResponseData.complex` to return the complex value of the
+ frequency response.
+
+* `FrequencyResponseData.fresp`: use `FrequencyResponseData.frdata
+ ` to access the raw 3D frequency response
+ data array.
+
+The listed items are slated to be removed in future releases (usually
+the next major or minor version update).
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 5fdf9113d..ded0c7087 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -7,4 +7,4 @@ sphinx-copybutton
numpydoc
ipykernel
nbsphinx
-docutils==0.16 # pin until sphinx_rtd_theme is compatible with 0.17 or later
+docutils
diff --git a/doc/test_sphinxdocs.py b/doc/test_sphinxdocs.py
index 1a49f357c..80024c23d 100644
--- a/doc/test_sphinxdocs.py
+++ b/doc/test_sphinxdocs.py
@@ -48,7 +48,6 @@
# Functons that we can skip
object_skiplist = [
- control.NamedSignal, # np.ndarray members cause errors
control.FrequencyResponseList, # Use FrequencyResponseData
control.TimeResponseList, # Use TimeResponseData
control.common_timebase, # mainly internal use