From 981104bf9b80280ac59634d5598332b52f7eaea6 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sun, 26 Mar 2023 10:29:31 -0700 Subject: [PATCH 1/7] use obc as standard prefix for control.optimal --- examples/kincar-flatsys.py | 10 +++++----- examples/mpc_aircraft.ipynb | 8 ++++---- examples/steering-optimal.py | 24 ++++++++++++------------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/examples/kincar-flatsys.py b/examples/kincar-flatsys.py index 2ebee3133..b61a9e1c5 100644 --- a/examples/kincar-flatsys.py +++ b/examples/kincar-flatsys.py @@ -10,7 +10,7 @@ import matplotlib.pyplot as plt import control as ct import control.flatsys as fs -import control.optimal as opt +import control.optimal as obc # # System model and utility functions @@ -147,7 +147,7 @@ def plot_results(t, x, ud, rescale=True): basis = fs.PolyFamily(8) # Define the cost function (penalize lateral error and steering) -traj_cost = opt.quadratic_cost( +traj_cost = obc.quadratic_cost( vehicle_flat, np.diag([0, 0.1, 0]), np.diag([0.1, 1]), x0=xf, u0=uf) # Solve for an optimal solution @@ -168,7 +168,7 @@ def plot_results(t, x, ud, rescale=True): # Constraint the input values constraints = [ - opt.input_range_constraint(vehicle_flat, [8, -0.1], [12, 0.1]) ] + obc.input_range_constraint(vehicle_flat, [8, -0.1], [12, 0.1]) ] # TEST: Change the basis to use B-splines basis = fs.BSplineFamily([0, Tf/2, Tf], 6) @@ -198,11 +198,11 @@ def plot_results(t, x, ud, rescale=True): # # Define the cost function (mainly penalize steering angle) -traj_cost = opt.quadratic_cost( +traj_cost = obc.quadratic_cost( vehicle_flat, None, np.diag([0.1, 10]), x0=xf, u0=uf) # Set terminal cost to bring us close to xf -terminal_cost = opt.quadratic_cost(vehicle_flat, 1e3 * np.eye(3), None, x0=xf) +terminal_cost = obc.quadratic_cost(vehicle_flat, 1e3 * np.eye(3), None, x0=xf) # Change the basis to use B-splines basis = fs.BSplineFamily([0, Tf/2, Tf], [4, 6], vars=2) diff --git a/examples/mpc_aircraft.ipynb b/examples/mpc_aircraft.ipynb index 5da812eb0..a1edf3ebb 100644 --- a/examples/mpc_aircraft.ipynb +++ b/examples/mpc_aircraft.ipynb @@ -19,7 +19,7 @@ "source": [ "import control as ct\n", "import numpy as np\n", - "import control.optimal as opt\n", + "import control.optimal as obc\n", "import matplotlib.pyplot as plt" ] }, @@ -70,15 +70,15 @@ "# model.y.reference = ys;\n", "\n", "# provide constraints on the system signals\n", - "constraints = [opt.input_range_constraint(sys, [-5, -6], [5, 6])]\n", + "constraints = [obc.input_range_constraint(sys, [-5, -6], [5, 6])]\n", "\n", "# provide penalties on the system signals\n", "Q = model.C.transpose() @ np.diag([10, 10, 10, 10]) @ model.C\n", "R = np.diag([3, 2])\n", - "cost = opt.quadratic_cost(model, Q, R, x0=xd, u0=ud)\n", + "cost = obc.quadratic_cost(model, Q, R, x0=xd, u0=ud)\n", "\n", "# online MPC controller object is constructed with a horizon 6\n", - "ctrl = opt.create_mpc_iosystem(model, np.arange(0, 6) * 0.2, cost, constraints)" + "ctrl = obc.create_mpc_iosystem(model, np.arange(0, 6) * 0.2, cost, constraints)" ] }, { diff --git a/examples/steering-optimal.py b/examples/steering-optimal.py index 778ac3c25..d9bad608e 100644 --- a/examples/steering-optimal.py +++ b/examples/steering-optimal.py @@ -8,7 +8,7 @@ import numpy as np import math import control as ct -import control.optimal as opt +import control.optimal as obc import matplotlib.pyplot as plt import logging import time @@ -106,7 +106,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Set up the cost functions Q = np.diag([.1, 10, .1]) # keep lateral error low R = np.diag([.1, 1]) # minimize applied inputs -quad_cost = opt.quadratic_cost(vehicle, Q, R, x0=xf, u0=uf) +quad_cost = obc.quadratic_cost(vehicle, Q, R, x0=xf, u0=uf) # Define the time horizon (and spacing) for the optimization timepts = np.linspace(0, Tf, 20, endpoint=True) @@ -124,7 +124,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Compute the optimal control, setting step size for gradient calculation (eps) start_time = time.process_time() -result1 = opt.solve_ocp( +result1 = obc.solve_ocp( vehicle, timepts, x0, quad_cost, initial_guess=straight_line, log=True, # minimize_method='trust-constr', # minimize_options={'finite_diff_rel_step': 0.01}, @@ -158,9 +158,9 @@ def plot_lanechange(t, y, u, yf=None, figure=None): print("\nApproach 2: input cost and constraints plus terminal cost") # Add input constraint, input cost, terminal cost -constraints = [ opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ] -traj_cost = opt.quadratic_cost(vehicle, None, np.diag([0.1, 1]), u0=uf) -term_cost = opt.quadratic_cost(vehicle, np.diag([1, 10, 10]), None, x0=xf) +constraints = [ obc.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ] +traj_cost = obc.quadratic_cost(vehicle, None, np.diag([0.1, 1]), u0=uf) +term_cost = obc.quadratic_cost(vehicle, np.diag([1, 10, 10]), None, x0=xf) # Change logging to keep less information logging.basicConfig( @@ -175,7 +175,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Compute the optimal control start_time = time.process_time() -result2 = opt.solve_ocp( +result2 = obc.solve_ocp( vehicle, timepts, x0, traj_cost, constraints, terminal_cost=term_cost, initial_guess=straight_line, log=True, # minimize_method='SLSQP', minimize_options={'eps': 0.01} @@ -207,10 +207,10 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Input cost and terminal constraints R = np.diag([1, 1]) # minimize applied inputs -cost3 = opt.quadratic_cost(vehicle, np.zeros((3,3)), R, u0=uf) +cost3 = obc.quadratic_cost(vehicle, np.zeros((3,3)), R, u0=uf) constraints = [ - opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ] -terminal = [ opt.state_range_constraint(vehicle, xf, xf) ] + obc.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ] +terminal = [ obc.state_range_constraint(vehicle, xf, xf) ] # Reset logging to its default values logging.basicConfig( @@ -219,7 +219,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Compute the optimal control start_time = time.process_time() -result3 = opt.solve_ocp( +result3 = obc.solve_ocp( vehicle, timepts, x0, cost3, constraints, terminal_constraints=terminal, initial_guess=straight_line, log=False, # solve_ivp_kwargs={'atol': 1e-3, 'rtol': 1e-2}, @@ -254,7 +254,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Compute the optimal control start_time = time.process_time() -result4 = opt.solve_ocp( +result4 = obc.solve_ocp( vehicle, timepts, x0, quad_cost, constraints, terminal_constraints=terminal, From 6a18702914202cd46811c6d85b3db58ce3486aa2 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sun, 26 Mar 2023 13:32:21 -0700 Subject: [PATCH 2/7] update installation instructions per issue #850 --- README.rst | 28 +++++++++++++++-------- doc/conventions.rst | 54 ++++++++++++++++++++++----------------------- doc/intro.rst | 54 +++++++++++++++++++++++++-------------------- 3 files changed, 76 insertions(+), 60 deletions(-) diff --git a/README.rst b/README.rst index f3e3a13ff..ebcf77c43 100644 --- a/README.rst +++ b/README.rst @@ -22,14 +22,17 @@ Python Control Systems Library The Python Control Systems Library is a Python module that implements basic operations for analysis and design of feedback control systems. - Have a go now! -============== +-------------- Try out the examples in the examples folder using the binder service. .. image:: https://mybinder.org/badge_logo.svg :target: https://mybinder.org/v2/gh/python-control/python-control/HEAD +The package can also be installed on Google Colab using the commands:: + + !pip install control + import control as ct Features -------- @@ -44,7 +47,7 @@ Features - Nonlinear systems: optimization-based control, describing functions, differential flatness Links -===== +----- - Project home page: http://python-control.org - Source code repository: https://github.com/python-control/python-control @@ -52,9 +55,8 @@ Links - Issue tracker: https://github.com/python-control/python-control/issues - Mailing list: http://sourceforge.net/p/python-control/mailman/ - Dependencies -============ +------------ The package requires numpy, scipy, and matplotlib. In addition, some routines use a module called slycot, that is a Python wrapper around some FORTRAN @@ -64,6 +66,7 @@ functionality is limited or absent, and installation of slycot is recommended https://github.com/python-control/Slycot + Installation ============ @@ -73,7 +76,7 @@ Conda and conda-forge The easiest way to get started with the Control Systems library is using `Conda `_. -The Control Systems library has been packages for the `conda-forge +The Control Systems library has packages available using the `conda-forge `_ Conda channel, and as of Slycot version 0.3.4, binaries for that package are available for 64-bit Windows, OSX, and Linux. @@ -83,6 +86,10 @@ conda environment, run:: conda install -c conda-forge control slycot +Mixing packages from conda-forge and the default conda channel can +sometimes cause problems with dependencies, so it is usually best to +instally NumPy, SciPy, and Matplotlib from conda-forge as well. + Pip --- @@ -92,7 +99,8 @@ To install using pip:: pip install control If you install Slycot using pip you'll need a development environment -(e.g., Python development files, C and Fortran compilers). +(e.g., Python development files, C and Fortran compilers). Pip +installation can be particularly complicated for Windows. Installing from source ---------------------- @@ -106,11 +114,13 @@ toplevel `python-control` directory:: Article and Citation Information ================================ -An `article `_ about the library is available on IEEE Explore. If the Python Control Systems Library helped you in your research, please cite:: +An `article `_ about +the library is available on IEEE Explore. If the Python Control Systems Library helped you in your research, please cite:: @inproceedings{python-control2021, title={The Python Control Systems Library (python-control)}, - author={Fuller, Sawyer and Greiner, Ben and Moore, Jason and Murray, Richard and van Paassen, Ren{\'e} and Yorke, Rory}, + author={Fuller, Sawyer and Greiner, Ben and Moore, Jason and + Murray, Richard and van Paassen, Ren{\'e} and Yorke, Rory}, booktitle={60th IEEE Conference on Decision and Control (CDC)}, pages={4875--4881}, year={2021}, diff --git a/doc/conventions.rst b/doc/conventions.rst index 476366714..5dc2e3d76 100644 --- a/doc/conventions.rst +++ b/doc/conventions.rst @@ -6,8 +6,11 @@ Library conventions ******************* -The python-control library uses a set of standard conventions for the way -that different types of standard information used by the library. +The python-control library uses a set of standard conventions for the +way that different types of standard information used by the library. +Throughout this manual, we assume the `control` package has been +imported as `ct`. + LTI system representation ========================= @@ -29,7 +32,7 @@ of linear time-invariant (LTI) systems: where u is the input, y is the output, and x is the state. -To create a state space system, use the :func:`ss` function: +To create a state space system, use the :func:`ss` function:: sys = ct.ss(A, B, C, D) @@ -51,7 +54,7 @@ transfer functions where n is generally greater than or equal to m (for a proper transfer function). -To create a transfer function, use the :func:`tf` function: +To create a transfer function, use the :func:`tf` function:: sys = ct.tf(num, den) @@ -77,7 +80,7 @@ performed. The FRD class is also used as the return type for the :func:`frequency_response` function (and the equivalent method for the :class:`StateSpace` and :class:`TransferFunction` classes). This -object can be assigned to a tuple using +object can be assigned to a tuple using:: mag, phase, omega = response @@ -91,7 +94,7 @@ is not SISO or `squeeze` is False, the array is 3D, indexed by the output, input, and frequency. If `squeeze` is True then single-dimensional axes are removed. The processing of the `squeeze` keyword can be changed by calling the response function with a new -argument: +argument:: mag, phase, omega = response(squeeze=False) @@ -101,10 +104,10 @@ Discrete time systems A discrete time system is created by specifying a nonzero 'timebase', dt. The timebase argument can be given when a system is constructed: -* dt = 0: continuous time system (default) -* dt > 0: discrete time system with sampling period 'dt' -* dt = True: discrete time with unspecified sampling period -* dt = None: no timebase specified +* `dt = 0`: continuous time system (default) +* `dt > 0`: discrete time system with sampling period 'dt' +* `dt = True`: discrete time with unspecified sampling period +* `dt = None`: no timebase specified Only the :class:`StateSpace`, :class:`TransferFunction`, and :class:`InputOutputSystem` classes allow explicit representation of @@ -119,8 +122,8 @@ result will have the timebase of the latter system. For continuous time systems, the :func:`sample_system` function or the :meth:`StateSpace.sample` and :meth:`TransferFunction.sample` methods can be used to create a discrete time system from a continuous time system. See -:ref:`utility-and-conversions`. The default value of 'dt' can be changed by -changing the value of ``control.config.defaults['control.default_dt']``. +:ref:`utility-and-conversions`. The default value of `dt` can be changed by +changing the value of `control.config.defaults['control.default_dt']`. Conversion between representations ---------------------------------- @@ -165,10 +168,9 @@ points in time, rows are different components:: ... [ui(t1), ui(t2), ui(t3), ..., ui(tn)]] - Same for X, Y - -So, U[:,2] is the system's input at the third point in time; and U[1] or U[1,:] -is the sequence of values for the system's second input. +(and similarly for `X`, `Y`). So, `U[:, 2]` is the system's input at the +third point in time; and `U[1]` or `U[1, :]` is the sequence of values for +the system's second input. When there is only one row, a 1D object is accepted or returned, which adds convenience for SISO systems: @@ -185,8 +187,10 @@ Functions that return time responses (e.g., :func:`forced_response`, :func:`impulse_response`, :func:`input_output_response`, :func:`initial_response`, and :func:`step_response`) return a :class:`TimeResponseData` object that contains the data for the time -response. These data can be accessed via the ``time``, ``outputs``, -``states`` and ``inputs`` properties:: +response. These data can be accessed via the +:attr:`~TimeResponseData.time`, :attr:`~TimeResponseData.outputs`, +:attr:`~TimeResponseData.states` and :attr:`~TimeResponseData.inputs` +properties:: sys = ct.rss(4, 1, 1) response = ct.step_response(sys) @@ -213,13 +217,13 @@ The output of a MIMO LTI system can be plotted like this:: plot(t, y[1], label='y_1') The convention also works well with the state space form of linear -systems. If ``D`` is the feedthrough matrix (2D array) of a linear system, -and ``U`` is its input (array), then the feedthrough part of the system's +systems. If `D` is the feedthrough matrix (2D array) of a linear system, +and `U` is its input (array), then the feedthrough part of the system's response, can be computed like this:: ft = D @ U -Finally, the `to_pandas()` function can be used to create a pandas dataframe: +Finally, the `to_pandas()` function can be used to create a pandas dataframe:: df = response.to_pandas() @@ -242,16 +246,12 @@ for various types of plots and establishing the underlying representation for state space matrices. To set the default value of a configuration variable, set the appropriate -element of the `control.config.defaults` dictionary: - -.. code-block:: python +element of the `control.config.defaults` dictionary:: ct.config.defaults['module.parameter'] = value The `~control.config.set_defaults` function can also be used to set multiple -configuration parameters at the same time: - -.. code-block:: python +configuration parameters at the same time:: ct.config.set_defaults('module', param1=val1, param2=val2, ...] diff --git a/doc/intro.rst b/doc/intro.rst index ce01aca15..9d4198c56 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -31,23 +31,43 @@ some thing to keep in mind: * You must include commas in vectors. So [1 2 3] must be [1, 2, 3]. * Functions that return multiple arguments use tuples. * You cannot use braces for collections; use tuples instead. +* Time series data have time as the final index (see + :ref:`time-series-convention`). Installation ============ -The `python-control` package can be installed using pip, conda or the -standard setuptools mechanisms. The package requires `numpy`_ and -`scipy`_, and the plotting routines require `matplotlib -`_. In addition, some routines require the `slycot -`_ library in order to implement -more advanced features (including some MIMO functionality). +The `python-control` package can be installed using conda or pip. The +package requires `NumPy`_ and `SciPy`_, and the plotting routines +require `Matplotlib `_. In addition, some +routines require the `Slycot +`_ library in order to +implement more advanced features (including some MIMO functionality). +For users with the Anaconda distribution of Python, the following +command can be used:: + + conda install -c conda-forge control slycot + +This installs `slycot` and `python-control` from conda-forge, including the +`openblas` package. NumPy, SciPy, and Matplotlib will also be installed if +they are not already present. + +.. note:: + Mixing packages from conda-forge and the default conda channel + can sometimes cause problems with dependencies, so it is usually best to + instally NumPy, SciPy, and Matplotlib from conda-forge as well.) To install using pip:: pip install slycot # optional pip install control +.. note:: + If you install Slycot using pip you'll need a development + environment (e.g., Python development files, C and Fortran compilers). + Pip installation can be particularly complicated for Windows. + Many parts of `python-control` will work without `slycot`, but some functionality is limited or absent, and installation of `slycot` is recommended. Users can check to insure that slycot is installed @@ -56,28 +76,14 @@ correctly by running the command:: python -c "import slycot" and verifying that no error message appears. More information on the -slycot package can be obtained from the `slycot project page +Slycot package can be obtained from the `Slycot project page `_. -For users with the Anaconda distribution of Python, the following -commands can be used:: - - conda install numpy scipy matplotlib # if not yet installed - conda install -c conda-forge control slycot - -This installs `slycot` and `python-control` from conda-forge, including the -`openblas` package. - -Alternatively, to use setuptools, first `download the source +Alternatively, to install from source, first `download the source `_ and unpack it. To install in your home directory, use:: - python setup.py install --user - -or to install for all users (on Linux or Mac OS):: - - python setup.py build - sudo python setup.py install + pip install . Getting started =============== @@ -85,7 +91,7 @@ Getting started There are two different ways to use the package. For the default interface described in :ref:`function-ref`, simply import the control package as follows:: - >>> import control + >>> import control as ct If you want to have a MATLAB-like environment, use the :ref:`matlab-module`:: From 1d670a7f702d0d284ae35d05393c214f3b1febf7 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sun, 26 Mar 2023 14:30:20 -0700 Subject: [PATCH 3/7] deprecate ctrlutil.issys() --- control/ctrlutil.py | 3 +++ control/matlab/wrappers.py | 4 ++-- control/tests/ctrlutil_test.py | 8 +++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/control/ctrlutil.py b/control/ctrlutil.py index fa7b91ee5..425812dc1 100644 --- a/control/ctrlutil.py +++ b/control/ctrlutil.py @@ -44,6 +44,7 @@ from . import lti import numpy as np import math +import warnings __all__ = ['unwrap', 'issys', 'db2mag', 'mag2db'] @@ -99,6 +100,8 @@ def issys(obj): False """ + warnings.warn("issys() is deprecated; use isinstance(obj, ct.LTI)", + FutureWarning, stacklevel=2) return isinstance(obj, lti.LTI) def db2mag(db): diff --git a/control/matlab/wrappers.py b/control/matlab/wrappers.py index 1c4ee053a..e7d757248 100644 --- a/control/matlab/wrappers.py +++ b/control/matlab/wrappers.py @@ -5,7 +5,7 @@ import numpy as np from ..iosys import ss from ..xferfcn import tf -from ..ctrlutil import issys +from ..lti import LTI from ..exception import ControlArgument from scipy.signal import zpk2tf from warnings import warn @@ -124,7 +124,7 @@ def _parse_freqplot_args(*args): i = 0 while i < len(args): # Check to see if this is a system of some sort - if issys(args[i]): + if isinstance(args[i], LTI): # Append the system to our list of systems syslist.append(args[i]) i += 1 diff --git a/control/tests/ctrlutil_test.py b/control/tests/ctrlutil_test.py index 460ff601c..758c98b66 100644 --- a/control/tests/ctrlutil_test.py +++ b/control/tests/ctrlutil_test.py @@ -1,7 +1,8 @@ """ctrlutil_test.py""" import numpy as np - +import pytest +import control as ct from control.ctrlutil import db2mag, mag2db, unwrap class TestUtils: @@ -58,3 +59,8 @@ def test_mag2db(self): def test_mag2db_array(self): db_array = mag2db(self.mag) np.testing.assert_array_almost_equal(db_array, self.db) + + def test_issys(self): + sys = ct.rss(2, 1, 1) + with pytest.warns(FutureWarning, match="deprecated; use isinstance"): + ct.issys(sys) From 423744aea70cd0243b55546938f76aa06710a3ee Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sun, 26 Mar 2023 15:12:36 -0700 Subject: [PATCH 4/7] add documentation on scaled portions of Nyquist plot --- control/freqplot.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/control/freqplot.py b/control/freqplot.py index c20fb189e..25de3c11b 100644 --- a/control/freqplot.py +++ b/control/freqplot.py @@ -558,21 +558,21 @@ def nyquist_plot( List of linear input/output systems (single system is OK). Nyquist curves for each system are plotted on the same graph. - plot : boolean - If True, plot magnitude - - omega : array_like + omega : array_like, optional Set of frequencies to be evaluated, in rad/sec. - omega_limits : array_like of two values + omega_limits : array_like of two values, optional Limits to the range of frequencies. Ignored if omega is provided, and auto-generated if omitted. - omega_num : int + omega_num : int, optional Number of frequency samples to plot. Defaults to config.defaults['freqplot.number_of_samples']. - color : string + plot : boolean, optional + If True (default), plot the Nyquist plot. + + color : string, optional Used to specify the color of the line and arrowhead. return_contour : bool, optional @@ -690,6 +690,13 @@ def nyquist_plot( to `none` will turn off indentation. If `return_contour` is True, the exact contour used for evaluation is returned. + 3. For those portions of the Nyquist plot in which the contour is + indented to avoid poles, resuling in a scaling of the Nyquist plot, + the line styles are according to the settings of the `primary_style` + and `mirror_style` keywords. By default the scaled portions of the + primary curve use a dotted line style and the scaled portion of the + mirror image use a dashdot line style. + Examples -------- >>> G = ct.zpk([], [-1, -2, -3], gain=100) From 1aa6a7e775c114d630ab42b8e18ee99482b3efa5 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sun, 26 Mar 2023 16:58:55 -0700 Subject: [PATCH 5/7] fix sphinx version processing to point to released source code --- doc/conf.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index a76b1c486..9c28ef216 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -37,11 +37,12 @@ import re import control +# Get the version number for this commmit (including alpha/beta/rc tags) +release = re.sub('^v', '', os.popen('git describe').read().strip()) + # The short X.Y.Z version -version = re.sub(r'(\d+\.\d+\.\d+)(.*)', r'\1', control.__version__) +version = re.sub(r'(\d+\.\d+\.\d+(.post\d+)?)(.*)', r'\1', release) -# The full version, including alpha/beta/rc tags -release = control.__version__ print("version %s, release %s" % (version, release)) # -- General configuration --------------------------------------------------- @@ -206,11 +207,10 @@ def linkcode_resolve(domain, info): linespec = "" base_url = "https://github.com/python-control/python-control/blob/" - if 'dev' in control.__version__: + if release != version: # development release return base_url + "main/control/%s%s" % (fn, linespec) - else: - return base_url + "%s/control/%s%s" % ( - control.__version__, fn, linespec) + else: # specific version + return base_url + "%s/control/%s%s" % (version, fn, linespec) # Don't automaticall show all members of class in Methods & Attributes section numpydoc_show_class_members = False From 7350e7937d789554db8ee9eccda896928ca9be81 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sun, 26 Mar 2023 18:04:53 -0700 Subject: [PATCH 6/7] add known failure for OS/BLAS matrix: numpy 1.24.2 --- control/tests/flatsys_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control/tests/flatsys_test.py b/control/tests/flatsys_test.py index 95fb8cf7c..7f480f43a 100644 --- a/control/tests/flatsys_test.py +++ b/control/tests/flatsys_test.py @@ -212,7 +212,7 @@ def test_kinematic_car_ocp( elif re.match("Iteration limit.*", traj_ocp.message) and \ re.match( "conda ubuntu-3.* Generic", os.getenv('JOBNAME', '')) and \ - re.match("1.24.[01]", np.__version__): + re.match("1.24.[012]", np.__version__): pytest.xfail("gh820: iteration limit exceeded") else: From 1d7480a410a6210de2acc65d0d7b9092abcf2d43 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sun, 26 Mar 2023 19:39:39 -0700 Subject: [PATCH 7/7] reset_defaults() at start of each doctest --- control/config.py | 5 ----- doc/conf.py | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/control/config.py b/control/config.py index f7f449bb8..f75bd52db 100644 --- a/control/config.py +++ b/control/config.py @@ -76,7 +76,6 @@ def set_defaults(module, **keywords): >>> ct.defaults['freqplot.number_of_samples'] 100 >>> # do some customized freqplotting - >>> ct.reset_defaults() """ if not isinstance(module, str): @@ -209,7 +208,6 @@ def use_matlab_defaults(): -------- >>> ct.use_matlab_defaults() >>> # do some matlab style plotting - >>> ct.reset_defaults() """ set_defaults('freqplot', dB=True, deg=True, Hz=False, grid=True) @@ -229,7 +227,6 @@ def use_fbs_defaults(): -------- >>> ct.use_fbs_defaults() >>> # do some FBS style plotting - >>> ct.reset_defaults() """ set_defaults('freqplot', dB=False, deg=True, Hz=False, grid=False) @@ -263,7 +260,6 @@ class and functions. If flat is `False`, then matrices are -------- >>> ct.use_numpy_matrix(True, False) >>> # do some legacy calculations using np.matrix - >>> ct.reset_defaults() """ if flag and warn: @@ -285,7 +281,6 @@ def use_legacy_defaults(version): >>> ct.use_legacy_defaults("0.9.0") (0, 9, 0) >>> # do some legacy style plotting - >>> ct.reset_defaults() """ import re diff --git a/doc/conf.py b/doc/conf.py index 9c28ef216..5fb7342f4 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -282,4 +282,5 @@ def linkcode_resolve(domain, info): import control as ct import control.optimal as obc import control.flatsys as fs +ct.reset_defaults() """