From 2123d52577b99860db970535f6e4e97eff81994c Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 2 Dec 2021 13:06:29 +0100 Subject: [PATCH 1/8] remove support declaration for EOL Python 2 and 3.6 --- setup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.py b/setup.py index 0de0e0cfe..a028361b0 100644 --- a/setup.py +++ b/setup.py @@ -16,10 +16,7 @@ Intended Audience :: Science/Research Intended Audience :: Developers License :: OSI Approved :: BSD License -Programming Language :: Python :: 2 -Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 -Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 From 7d97ef9fb177af5be15851258e2e1dd862158947 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 2 Dec 2021 13:22:14 +0100 Subject: [PATCH 2/8] remove python2 future imports --- control/delay.py | 1 - control/frdata.py | 1 - control/margins.py | 3 --- control/modelsimp.py | 3 --- control/phaseplot.py | 3 --- control/statesp.py | 4 ---- control/tests/convert_test.py | 2 -- control/tests/delay_test.py | 3 --- control/tests/iosys_test.py | 4 +--- control/tests/margin_test.py | 1 - control/xferfcn.py | 4 ---- doc/pvtol-nested.rst | 5 +---- ...check-controllability-and-observability.py | 2 -- examples/pvtol-nested.py | 8 +++---- examples/tfvis.py | 21 ++++++++----------- 15 files changed, 14 insertions(+), 51 deletions(-) diff --git a/control/delay.py b/control/delay.py index d6350d45b..b5867ada8 100644 --- a/control/delay.py +++ b/control/delay.py @@ -42,7 +42,6 @@ # # $Id$ -from __future__ import division __all__ = ['pade'] diff --git a/control/frdata.py b/control/frdata.py index 5e2f3f2e1..e9790bc26 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -35,7 +35,6 @@ # Author: M.M. (Rene) van Paassen (using xferfcn.py as basis) # Date: 02 Oct 12 -from __future__ import division """ Frequency response data representation and functions. diff --git a/control/margins.py b/control/margins.py index 48e0c6cc2..41739704e 100644 --- a/control/margins.py +++ b/control/margins.py @@ -9,9 +9,6 @@ margins.margin """ -# Python 3 compatibility (needs to go here) -from __future__ import print_function - """Copyright (c) 2011 by California Institute of Technology All rights reserved. diff --git a/control/modelsimp.py b/control/modelsimp.py index ec015c16b..76c385e80 100644 --- a/control/modelsimp.py +++ b/control/modelsimp.py @@ -40,9 +40,6 @@ # # $Id$ -# Python 3 compatibility -from __future__ import print_function - # External packages and modules import numpy as np import warnings diff --git a/control/phaseplot.py b/control/phaseplot.py index 83108ec01..6a4be5ca6 100644 --- a/control/phaseplot.py +++ b/control/phaseplot.py @@ -34,9 +34,6 @@ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -# Python 3 compatibility -from __future__ import print_function - import numpy as np import matplotlib.pyplot as mpl diff --git a/control/statesp.py b/control/statesp.py index 4dfdf5e95..0477d4503 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -8,10 +8,6 @@ """ -# Python 3 compatibility (needs to go here) -from __future__ import print_function -from __future__ import division # for _convert_to_statespace - """Copyright (c) 2010 by California Institute of Technology All rights reserved. diff --git a/control/tests/convert_test.py b/control/tests/convert_test.py index 7570b07b4..36eac223c 100644 --- a/control/tests/convert_test.py +++ b/control/tests/convert_test.py @@ -14,8 +14,6 @@ """ -from __future__ import print_function -from warnings import warn import numpy as np import pytest diff --git a/control/tests/delay_test.py b/control/tests/delay_test.py index 533eb4a72..25f37eeb5 100644 --- a/control/tests/delay_test.py +++ b/control/tests/delay_test.py @@ -4,8 +4,6 @@ Primitive; ideally test to numerical limits """ -from __future__ import division - import numpy as np import pytest @@ -94,4 +92,3 @@ def testT0(self): np.array(refnum), np.array(num)) np.testing.assert_array_almost_equal_nulp( np.array(refden), np.array(den)) - diff --git a/control/tests/iosys_test.py b/control/tests/iosys_test.py index 4c8001797..bb975364b 100644 --- a/control/tests/iosys_test.py +++ b/control/tests/iosys_test.py @@ -8,12 +8,10 @@ created for that purpose. """ -from __future__ import print_function import re import numpy as np import pytest -import scipy as sp import control as ct from control import iosys as ios @@ -1270,7 +1268,7 @@ def test_lineariosys_statespace(self, tsys): (2, 2, 'rss', ct.LinearIOSystem.__rsub__, 2, 2), (2, 2, 2, ct.LinearIOSystem.__rsub__, 2, 2), (2, 2, np.random.rand(2, 2), ct.LinearIOSystem.__rsub__, 2, 2), - + ]) def test_operand_conversion(self, Pout, Pin, C, op, PCout, PCin): P = ct.LinearIOSystem( diff --git a/control/tests/margin_test.py b/control/tests/margin_test.py index a1246103f..07e21114f 100644 --- a/control/tests/margin_test.py +++ b/control/tests/margin_test.py @@ -6,7 +6,6 @@ BG, 30 Jun 2020 -- convert to pytest, gh-425 BG, 16 Nov 2020 -- pick from gh-438 and add discrete test """ -from __future__ import print_function import numpy as np import pytest diff --git a/control/xferfcn.py b/control/xferfcn.py index 356bf0e18..856b421ef 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -7,10 +7,6 @@ for the python-control library. """ -# Python 3 compatibility (needs to go here) -from __future__ import print_function -from __future__ import division - """Copyright (c) 2010 by California Institute of Technology All rights reserved. diff --git a/doc/pvtol-nested.rst b/doc/pvtol-nested.rst index f9a4538a8..08858be7b 100644 --- a/doc/pvtol-nested.rst +++ b/doc/pvtol-nested.rst @@ -17,8 +17,5 @@ Code Notes ..... -1. Importing `print_function` from `__future__` in line 11 is only -required if using Python 2.7. - -2. The environment variable `PYCONTROL_TEST_EXAMPLES` is used for +1. The environment variable `PYCONTROL_TEST_EXAMPLES` is used for testing to turn off plotting of the outputs. diff --git a/examples/check-controllability-and-observability.py b/examples/check-controllability-and-observability.py index 399693781..67ecdf26c 100644 --- a/examples/check-controllability-and-observability.py +++ b/examples/check-controllability-and-observability.py @@ -4,8 +4,6 @@ RMM, 6 Sep 2010 """ -from __future__ import print_function - import numpy as np # Load the scipy functions from control.matlab import * # Load the controls systems library diff --git a/examples/pvtol-nested.py b/examples/pvtol-nested.py index 7b48d2bb5..24cd7d1c5 100644 --- a/examples/pvtol-nested.py +++ b/examples/pvtol-nested.py @@ -8,8 +8,6 @@ # package. # -from __future__ import print_function - import os import matplotlib.pyplot as plt # MATLAB plotting functions from control.matlab import * # MATLAB-like functions @@ -30,7 +28,7 @@ # Inner loop control design # # This is the controller for the pitch dynamics. Goal is to have -# fast response for the pitch dynamics so that we can use this as a +# fast response for the pitch dynamics so that we can use this as a # control for the lateral dynamics # @@ -40,7 +38,7 @@ Li = Pi*Ci # Bode plot for the open loop process -plt.figure(1) +plt.figure(1) bode(Pi) # Bode plot for the loop transfer function, with margins @@ -137,7 +135,7 @@ # Add a box in the region we are going to expand plt.plot([-2, -2, 1, 1, -2], [-4, 4, 4, -4, -4], 'r-') -# Expanded region +# Expanded region plt.figure(8) plt.clf() nyquist(L) diff --git a/examples/tfvis.py b/examples/tfvis.py index f05a45780..30a084ffb 100644 --- a/examples/tfvis.py +++ b/examples/tfvis.py @@ -1,8 +1,5 @@ #!/usr/bin/python # needs pmw (in pypi, conda-forge) -# For Python 2, needs future (in conda pypi and "default") - -from __future__ import print_function """ Simple GUI application for visualizing how the poles/zeros of the transfer function effects the bode, nyquist and step response of a SISO system """ @@ -20,7 +17,7 @@ notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name of the project author nor the names of its +3. Neither the name of the project author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. @@ -146,7 +143,7 @@ def set_poles(self, poles): self.denominator = make_poly(poles) self.denominator_widget.setentry( ' '.join([format(i,'.3g') for i in self.denominator])) - + def set_zeros(self, zeros): """ Set the zeros to the new positions""" self.numerator = make_poly(zeros) @@ -208,7 +205,7 @@ def __init__(self, parent): self.canvas_step.get_tk_widget().grid(row=1, column=0, padx=0, pady=0) - self.canvas_nyquist = FigureCanvasTkAgg(self.f_nyquist, + self.canvas_nyquist = FigureCanvasTkAgg(self.f_nyquist, master=self.figure) self.canvas_nyquist.draw() self.canvas_nyquist.get_tk_widget().grid(row=1, column=1, @@ -221,7 +218,7 @@ def __init__(self, parent): self.canvas_pzmap.mpl_connect('motion_notify_event', self.mouse_move) - self.apply() + self.apply() def button_press(self, event): """ Handle button presses, detect if we are going to move @@ -276,12 +273,12 @@ def button_release(self, event): self.zeros = tfcn.zero() self.poles = tfcn.pole() self.sys = tfcn - self.redraw() + self.redraw() def mouse_move(self, event): """ Handle mouse movement, redraw pzmap while drag/dropping """ if (self.move_zero != None and - event.xdata != None and + event.xdata != None and event.ydata != None): if (self.index1 == self.index2): @@ -320,7 +317,7 @@ def apply(self): self.zeros = tfcn.zero() self.poles = tfcn.pole() self.sys = tfcn - self.redraw() + self.redraw() def draw_pz(self, tfcn): """Draw pzmap""" @@ -338,7 +335,7 @@ def draw_pz(self, tfcn): def redraw(self): """ Redraw all diagrams """ self.draw_pz(self.sys) - + self.f_bode.clf() plt.figure(self.f_bode.number) control.matlab.bode(self.sys, logspace(-2, 2, 1000)) @@ -376,7 +373,7 @@ def handler(): # Launch a GUI for the Analysis module root = tkinter.Tk() root.protocol("WM_DELETE_WINDOW", handler) - Pmw.initialise(root) + Pmw.initialise(root) root.title('Analysis of Linear Systems') Analysis(root) root.mainloop() From f8534a0e896e03a9e3c8e979bcbf99b98ced38d8 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 2 Dec 2021 13:25:26 +0100 Subject: [PATCH 3/8] remove travis config --- .travis.yml | 112 ---------------------------------------------------- 1 file changed, 112 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8d8c76262..000000000 --- a/.travis.yml +++ /dev/null @@ -1,112 +0,0 @@ -sudo: false -language: python -dist: xenial - -services: - - xvfb - -cache: - apt: true - pip: true - directories: - - $HOME/.cache/pip - - $HOME/.local - -# Test against earliest supported (Python 3) release and latest stable release -python: - - "3.9" - - "3.6" - -env: - - SCIPY=scipy SLYCOT=conda # default, with slycot via conda - - SCIPY=scipy SLYCOT= # default, w/out slycot - -# Add optional builds that test against latest version of slycot, python -jobs: - include: - - name: "Python 3.9, slycot=source, array and matrix" - python: "3.9" - env: SCIPY=scipy SLYCOT=source PYTHON_CONTROL_ARRAY_AND_MATRIX=1 - # Because there were significant changes in SciPy between v0 and v1, we - # also test against the latest v0 (without Slycot) for old pythons. - # newer pythons should always use newer SciPy. - - name: "Python 2.7, Scipy 0.19.1" - python: "2.7" - env: SCIPY="scipy==0.19.1" SLYCOT= - - name: "Python 3.6, Scipy 0.19.1" - python: "3.6" - env: SCIPY="scipy==0.19.1" SLYCOT= - - allow_failures: - - env: SCIPY=scipy SLYCOT=source - - env: SCIPY=scipy SLYCOT=source PYTHON_CONTROL_ARRAY_AND_MATRIX=1 - - -# install required system libraries -before_install: - # Install gfortran for testing slycot; use apt-get instead of conda in - # order to include the proper CXXABI dependency (updated in GCC 4.9) - # Note: these commands should match the slycot .travis.yml configuration - - if [[ "$SLYCOT" = "source" ]]; then - sudo apt-get update -qq; - sudo apt-get install liblapack-dev libblas-dev; - sudo apt-get install gfortran; - fi - # use miniconda to install numpy/scipy, to avoid lengthy build from source - - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then - wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; - else - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; - fi - - bash miniconda.sh -b -p $HOME/miniconda - - export PATH="$HOME/miniconda/bin:$PATH" - - hash -r - - conda config --set always_yes yes --set changeps1 no - - conda update -q conda - - conda config --add channels python-control - - conda info -a - - conda create -q -n test-environment python="$TRAVIS_PYTHON_VERSION" pip coverage - - source activate test-environment - # Install scikit-build for the build process if slycot is being used - - if [[ "$SLYCOT" = "source" ]]; then - conda install openblas; - conda install -c conda-forge cmake scikit-build; - fi - # Make sure to look in the right place for python libraries (for slycot) - - export LIBRARY_PATH="$HOME/miniconda/envs/test-environment/lib" - - conda install pytest - # coveralls not in conda repos => install via pip instead - - pip install coveralls - -# Install packages -install: - # Install packages needed by python-control - - conda install $SCIPY matplotlib - - # Figure out how to build slycot - # source: use "Unix Makefiles" as generator; Ninja cannot handle Fortran - # conda: use pre-compiled version of slycot on conda-forge - - if [[ "$SLYCOT" = "source" ]]; then - git clone https://github.com/python-control/Slycot.git slycot; - cd slycot; python setup.py install -G "Unix Makefiles"; cd ..; - elif [[ "$SLYCOT" = "conda" ]]; then - conda install -c conda-forge slycot; - fi - -# command to run tests -script: - - 'if [ $SLYCOT != "" ]; then python -c "import slycot"; fi' - - coverage run -m pytest control/tests - - # only run examples if Slycot is install - # set PYTHONPATH for examples - # pmw needed for examples/tfvis.py - # future is needed for Python 2, also for examples/tfvis.py - - if [[ "$SLYCOT" != "" ]]; then - export PYTHONPATH=$PWD; - conda install -c conda-forge pmw future; - (cd examples; bash run_examples.sh); - fi - -after_success: - - coveralls From 12f9014ebac2cb75f586e9a592a6c856e97f1ffc Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 2 Dec 2021 13:26:47 +0100 Subject: [PATCH 4/8] bump minimum python version in GHA conda tests --- .github/workflows/python-package-conda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 464624949..10cf2d1a9 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -10,7 +10,7 @@ jobs: strategy: max-parallel: 5 matrix: - python-version: [3.6, 3.9] + python-version: [3.7, 3.9] slycot: ["", "conda"] array-and-matrix: [0] include: From 19cc79ac5e8b351d722347ace61cd6bbcb0e0f9d Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 2 Dec 2021 17:27:31 +0100 Subject: [PATCH 5/8] replace np.dot with @ matmul operator where applicable --- control/flatsys/flatsys.py | 3 +-- control/flatsys/linflat.py | 12 ++++----- control/frdata.py | 10 +++---- control/iosys.py | 12 ++++----- control/modelsimp.py | 10 +++---- control/optimal.py | 48 ++++++++++++++-------------------- control/statefbk.py | 15 +++++------ control/statesp.py | 38 +++++++++++++-------------- control/timeresp.py | 11 ++++---- examples/slycot-import-test.py | 2 +- 10 files changed, 72 insertions(+), 89 deletions(-) diff --git a/control/flatsys/flatsys.py b/control/flatsys/flatsys.py index bbf1e7fc7..9ea40f2fb 100644 --- a/control/flatsys/flatsys.py +++ b/control/flatsys/flatsys.py @@ -462,8 +462,7 @@ def traj_const(null_coeffs): for type, fun, lb, ub in traj_constraints: if type == sp.optimize.LinearConstraint: # `fun` is A matrix associated with polytope... - values.append( - np.dot(fun, np.hstack([states, inputs]))) + values.append(fun @ np.hstack([states, inputs])) elif type == sp.optimize.NonlinearConstraint: values.append(fun(states, inputs)) else: diff --git a/control/flatsys/linflat.py b/control/flatsys/linflat.py index 1e96a23d2..931446ca8 100644 --- a/control/flatsys/linflat.py +++ b/control/flatsys/linflat.py @@ -110,7 +110,7 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None, # Compute the flat output variable z = C x Cfz = np.zeros(np.shape(linsys.C)); Cfz[0, 0] = 1 - self.Cf = np.dot(Cfz, Tr) + self.Cf = Cfz @ Tr # Compute the flat flag from the state (and input) def forward(self, x, u): @@ -122,11 +122,11 @@ def forward(self, x, u): x = np.reshape(x, (-1, 1)) u = np.reshape(u, (1, -1)) zflag = [np.zeros(self.nstates + 1)] - zflag[0][0] = np.dot(self.Cf, x) + zflag[0][0] = self.Cf @ x H = self.Cf # initial state transformation for i in range(1, self.nstates + 1): - zflag[0][i] = np.dot(H, np.dot(self.A, x) + np.dot(self.B, u)) - H = np.dot(H, self.A) # derivative for next iteration + zflag[0][i] = H @ (self.A @ x + self.B @ u) + H = H @ self.A # derivative for next iteration return zflag # Compute state and input from flat flag @@ -137,6 +137,6 @@ def reverse(self, zflag): """ z = zflag[0][0:-1] - x = np.dot(self.Tinv, z) - u = zflag[0][-1] - np.dot(self.F, z) + x = self.Tinv @ z + u = zflag[0][-1] - self.F @ z return np.reshape(x, self.nstates), np.reshape(u, self.ninputs) diff --git a/control/frdata.py b/control/frdata.py index e9790bc26..6aefe82c7 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -542,13 +542,9 @@ def feedback(self, other=1, sign=-1): # TODO: is there a reason to use linalg.solve instead of linalg.inv? # https://github.com/python-control/python-control/pull/314#discussion_r294075154 for k, w in enumerate(other.omega): - fresp[:, :, k] = np.dot( - self.fresp[:, :, k], - linalg.solve( - eye(self.ninputs) - + np.dot(other.fresp[:, :, k], self.fresp[:, :, k]), - eye(self.ninputs)) - ) + fresp[:, :, k] = self.fresp[:, :, k] @ linalg.solve( + eye(self.ninputs) + other.fresp[:, :, k] @ self.fresp[:, :, k], + eye(self.ninputs)) return FRD(fresp, other.omega, smooth=(self.ifunc is not None)) diff --git a/control/iosys.py b/control/iosys.py index 2c9e3aba5..1fed5c90f 100644 --- a/control/iosys.py +++ b/control/iosys.py @@ -843,14 +843,14 @@ def _update_params(self, params={}, warning=True): def _rhs(self, t, x, u): # Convert input to column vector and then change output to 1D array - xdot = np.dot(self.A, np.reshape(x, (-1, 1))) \ - + np.dot(self.B, np.reshape(u, (-1, 1))) + xdot = self.A @ np.reshape(x, (-1, 1)) \ + + self.B @ np.reshape(u, (-1, 1)) return np.array(xdot).reshape((-1,)) def _out(self, t, x, u): # Convert input to column vector and then change output to 1D array - y = np.dot(self.C, np.reshape(x, (-1, 1))) \ - + np.dot(self.D, np.reshape(u, (-1, 1))) + y = self.C @ np.reshape(x, (-1, 1)) \ + + self.D @ np.reshape(u, (-1, 1)) return np.array(y).reshape((-1,)) @@ -1197,7 +1197,7 @@ def _out(self, t, x, u): ulist, ylist = self._compute_static_io(t, x, u) # Make the full set of subsystem outputs to system output - return np.dot(self.output_map, ylist) + return self.output_map @ ylist def _compute_static_io(self, t, x, u): # Figure out the total number of inputs and outputs @@ -1239,7 +1239,7 @@ def _compute_static_io(self, t, x, u): output_index += sys.noutputs # Compute inputs based on connection map - new_ulist = np.dot(self.connect_map, ylist[:noutputs]) \ + new_ulist = self.connect_map @ ylist[:noutputs] \ + np.dot(self.input_map, u) # Check to see if any of the inputs changed diff --git a/control/modelsimp.py b/control/modelsimp.py index 76c385e80..f43acc2fd 100644 --- a/control/modelsimp.py +++ b/control/modelsimp.py @@ -93,7 +93,7 @@ def hsvd(sys): Wc = gram(sys, 'c') Wo = gram(sys, 'o') - WoWc = np.dot(Wo, Wc) + WoWc = Wo @ Wc w, v = np.linalg.eig(WoWc) hsv = np.sqrt(w) @@ -192,10 +192,10 @@ def modred(sys, ELIM, method='matchdc'): A22I_A21 = A22I_A21_B2[:, :A21.shape[1]] A22I_B2 = A22I_A21_B2[:, A21.shape[1]:] - Ar = A11 - np.dot(A12, A22I_A21) - Br = B1 - np.dot(A12, A22I_B2) - Cr = C1 - np.dot(C2, A22I_A21) - Dr = sys.D - np.dot(C2, A22I_B2) + Ar = A11 - A12 @ A22I_A21 + Br = B1 - A12 @ A22I_B2 + Cr = C1 - C2 @ A22I_A21 + Dr = sys.D - C2 @ A22I_B2 elif method == 'truncate': # if truncate, simply discard state x2 Ar = A11 diff --git a/control/optimal.py b/control/optimal.py index 76e9a2d31..dd09532c5 100644 --- a/control/optimal.py +++ b/control/optimal.py @@ -387,33 +387,29 @@ def _constraint_function(self, coeffs): # Evaluate the constraint function along the trajectory value = [] for i, t in enumerate(self.timepts): - for type, fun, lb, ub in self.trajectory_constraints: + for ctype, fun, lb, ub in self.trajectory_constraints: if np.all(lb == ub): # Skip equality constraints continue - elif type == opt.LinearConstraint: + elif ctype == opt.LinearConstraint: # `fun` is the A matrix associated with the polytope... - value.append( - np.dot(fun, np.hstack([states[:, i], inputs[:, i]]))) - elif type == opt.NonlinearConstraint: + value.append(fun @ np.hstack([states[:, i], inputs[:, i]])) + elif ctype == opt.NonlinearConstraint: value.append(fun(states[:, i], inputs[:, i])) else: - raise TypeError("unknown constraint type %s" % - constraint[0]) + raise TypeError(f"unknown constraint type {ctype}") # Evaluate the terminal constraint functions - for type, fun, lb, ub in self.terminal_constraints: + for ctype, fun, lb, ub in self.terminal_constraints: if np.all(lb == ub): # Skip equality constraints continue - elif type == opt.LinearConstraint: - value.append( - np.dot(fun, np.hstack([states[:, i], inputs[:, i]]))) - elif type == opt.NonlinearConstraint: + elif ctype == opt.LinearConstraint: + value.append(fun @ np.hstack([states[:, i], inputs[:, i]])) + elif ctype == opt.NonlinearConstraint: value.append(fun(states[:, i], inputs[:, i])) else: - raise TypeError("unknown constraint type %s" % - constraint[0]) + raise TypeError(f"unknown constraint type {ctype}") # Update statistics self.constraint_evaluations += 1 @@ -475,33 +471,29 @@ def _eqconst_function(self, coeffs): # Evaluate the constraint function along the trajectory value = [] for i, t in enumerate(self.timepts): - for type, fun, lb, ub in self.trajectory_constraints: + for ctype, fun, lb, ub in self.trajectory_constraints: if np.any(lb != ub): # Skip inequality constraints continue - elif type == opt.LinearConstraint: + elif ctype == opt.LinearConstraint: # `fun` is the A matrix associated with the polytope... - value.append( - np.dot(fun, np.hstack([states[:, i], inputs[:, i]]))) - elif type == opt.NonlinearConstraint: + value.append(fun @ np.hstack([states[:, i], inputs[:, i]])) + elif ctype == opt.NonlinearConstraint: value.append(fun(states[:, i], inputs[:, i])) else: - raise TypeError("unknown constraint type %s" % - constraint[0]) + raise TypeError(f"unknown constraint type {ctype}") # Evaluate the terminal constraint functions - for type, fun, lb, ub in self.terminal_constraints: + for ctype, fun, lb, ub in self.terminal_constraints: if np.any(lb != ub): # Skip inequality constraints continue - elif type == opt.LinearConstraint: - value.append( - np.dot(fun, np.hstack([states[:, i], inputs[:, i]]))) - elif type == opt.NonlinearConstraint: + elif ctype == opt.LinearConstraint: + value.append(fun @ np.hstack([states[:, i], inputs[:, i]])) + elif ctype == opt.NonlinearConstraint: value.append(fun(states[:, i], inputs[:, i])) else: - raise TypeError("unknown constraint type %s" % - constraint[0]) + raise TypeError("unknown constraint type {ctype}") # Update statistics self.eqconst_evaluations += 1 diff --git a/control/statefbk.py b/control/statefbk.py index 58d19f0ea..e82923bb4 100644 --- a/control/statefbk.py +++ b/control/statefbk.py @@ -393,8 +393,7 @@ def lqe(*args, **keywords): N.shape[0] != ninputs or N.shape[1] != noutputs): raise ControlDimension("incorrect covariance matrix dimensions") - # P, E, LT = care(A.T, C.T, G @ Q @ G.T, R) - P, E, LT = care(A.T, C.T, np.dot(np.dot(G, Q), G.T), R) + P, E, LT = care(A.T, C.T, G @ Q @ G.T, R) return _ssmatrix(LT.T), _ssmatrix(P), E @@ -439,7 +438,7 @@ def acker(A, B, poles): n = np.size(p) pmat = p[n-1] * np.linalg.matrix_power(a, 0) for i in np.arange(1, n): - pmat = pmat + np.dot(p[n-i-1], np.linalg.matrix_power(a, i)) + pmat = pmat + p[n-i-1] * np.linalg.matrix_power(a, i) K = np.linalg.solve(ct, pmat) K = K[-1][:] # Extract the last row @@ -556,7 +555,7 @@ def lqr(*args, **keywords): # Now compute the return value # We assume that R is positive definite and, hence, invertible - K = np.linalg.solve(R, np.dot(B.T, X) + N.T) + K = np.linalg.solve(R, B.T @ X + N.T) S = X E = w[0:nstates] @@ -594,7 +593,7 @@ def ctrb(A, B): # Construct the controllability matrix ctrb = np.hstack( - [bmat] + [np.dot(np.linalg.matrix_power(amat, i), bmat) + [bmat] + [np.linalg.matrix_power(amat, i) @ bmat for i in range(1, n)]) return _ssmatrix(ctrb) @@ -628,7 +627,7 @@ def obsv(A, C): n = np.shape(amat)[0] # Construct the observability matrix - obsv = np.vstack([cmat] + [np.dot(cmat, np.linalg.matrix_power(amat, i)) + obsv = np.vstack([cmat] + [cmat @ np.linalg.matrix_power(amat, i) for i in range(1, n)]) return _ssmatrix(obsv) @@ -701,10 +700,10 @@ def gram(sys, type): raise ControlSlycot("can't find slycot module 'sb03md'") if type == 'c': tra = 'T' - C = -np.dot(sys.B, sys.B.transpose()) + C = -sys.B @ sys.B.T elif type == 'o': tra = 'N' - C = -np.dot(sys.C.transpose(), sys.C) + C = -sys.C.T @ sys.C n = sys.nstates U = np.zeros((n, n)) A = np.array(sys.A) # convert to NumPy array for slycot diff --git a/control/statesp.py b/control/statesp.py index 0477d4503..ca0e0a7d1 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -712,11 +712,11 @@ def __mul__(self, other): (concatenate((other.A, zeros((other.A.shape[0], self.A.shape[1]))), axis=1), - concatenate((np.dot(self.B, other.C), self.A), axis=1)), + concatenate((self.B @ other.C, self.A), axis=1)), axis=0) - B = concatenate((other.B, np.dot(self.B, other.D)), axis=0) - C = concatenate((np.dot(self.D, other.C), self.C), axis=1) - D = np.dot(self.D, other.D) + B = concatenate((other.B, self.B @ other.D), axis=0) + C = concatenate((self.D @ other.C, self.C), axis=1) + D = self.D @ other.D return StateSpace(A, B, C, D, dt) @@ -741,8 +741,8 @@ def __rmul__(self, other): # try to treat this as a matrix try: X = _ssmatrix(other) - C = np.dot(X, self.C) - D = np.dot(X, self.D) + C = X @ self.C + D = X @ self.D return StateSpace(self.A, self.B, C, D, self.dt) except Exception as e: @@ -915,10 +915,8 @@ def horner(self, x, warn_infinite=True): # TODO: can this be vectorized? for idx, x_idx in enumerate(x_arr): try: - out[:, :, idx] = np.dot( - self.C, - solve(x_idx * eye(self.nstates) - self.A, self.B)) \ - + self.D + xr = solve(x_idx * eye(self.nstates) - self.A, self.B) + out[:, :, idx] = self.C @ xr + self.D except LinAlgError: # Issue a warning messsage, for consistency with xferfcn if warn_infinite: @@ -1019,7 +1017,7 @@ def feedback(self, other=1, sign=-1): C2 = other.C D2 = other.D - F = eye(self.ninputs) - sign * np.dot(D2, D1) + F = eye(self.ninputs) - sign * D2 @ D1 if matrix_rank(F) != self.ninputs: raise ValueError( "I - sign * D2 * D1 is singular to working precision.") @@ -1033,20 +1031,20 @@ def feedback(self, other=1, sign=-1): E_D2 = E_D2_C2[:, :other.ninputs] E_C2 = E_D2_C2[:, other.ninputs:] - T1 = eye(self.noutputs) + sign * np.dot(D1, E_D2) - T2 = eye(self.ninputs) + sign * np.dot(E_D2, D1) + T1 = eye(self.noutputs) + sign * D1 @ E_D2 + T2 = eye(self.ninputs) + sign * E_D2 @ D1 A = concatenate( (concatenate( - (A1 + sign * np.dot(np.dot(B1, E_D2), C1), - sign * np.dot(B1, E_C2)), axis=1), + (A1 + sign * B1 @ E_D2 @ C1, + sign * B1 @ E_C2), axis=1), concatenate( - (np.dot(B2, np.dot(T1, C1)), - A2 + sign * np.dot(np.dot(B2, D1), E_C2)), axis=1)), + (B2 @ T1 @ C1, + A2 + sign * B2 @ D1 @ E_C2), axis=1)), axis=0) - B = concatenate((np.dot(B1, T2), np.dot(np.dot(B2, D1), T2)), axis=0) - C = concatenate((np.dot(T1, C1), sign * np.dot(D1, E_C2)), axis=1) - D = np.dot(D1, T2) + B = concatenate((B1 @ T2, B2 @ D1 @ T2), axis=0) + C = concatenate((T1 @ C1, sign * D1 @ E_C2), axis=1) + D = D1 @ T2 return StateSpace(A, B, C, D, dt) diff --git a/control/timeresp.py b/control/timeresp.py index 75e1dcf0b..527e26e76 100644 --- a/control/timeresp.py +++ b/control/timeresp.py @@ -997,7 +997,6 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False, # Separate out the discrete and continuous time cases if isctime(sys, strict=True): # Solve the differential equation, copied from scipy.signal.ltisys. - dot = np.dot # Faster and shorter code # Faster algorithm if U is zero # (if not None, it was converted to array above) @@ -1005,8 +1004,8 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False, # Solve using matrix exponential expAdt = sp.linalg.expm(A * dt) for i in range(1, n_steps): - xout[:, i] = dot(expAdt, xout[:, i-1]) - yout = dot(C, xout) + xout[:, i] = expAdt @ xout[:, i-1] + yout = C @ xout # General algorithm that interpolates U in between output points else: @@ -1034,9 +1033,9 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False, Bd0 = expM[:n_states, n_states:n_states + n_inputs] - Bd1 for i in range(1, n_steps): - xout[:, i] = (dot(Ad, xout[:, i-1]) + dot(Bd0, U[:, i-1]) + - dot(Bd1, U[:, i])) - yout = dot(C, xout) + dot(D, U) + xout[:, i] = (Ad @ xout[:, i-1] + + Bd0 @ U[:, i-1] + Bd1 @ U[:, i]) + yout = C @ xout + D @ U tout = T else: diff --git a/examples/slycot-import-test.py b/examples/slycot-import-test.py index c2c78fa89..2df9b5b23 100644 --- a/examples/slycot-import-test.py +++ b/examples/slycot-import-test.py @@ -39,6 +39,6 @@ dico = 'D' # Discrete system _, _, _, _, _, K, _ = sb01bd(n, m, npp, alpha, A, B, w, dico, tol=0.0, ldwork=None) print("[slycot] K = ", K) - print("[slycot] eigs = ", np.linalg.eig(A + np.dot(B, K))[0]) + print("[slycot] eigs = ", np.linalg.eig(A + B @ K)[0]) else: print("Slycot is not installed.") From 6b96c7f17ef23c766931b90a467880460e93c0e4 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 2 Dec 2021 16:06:48 +0100 Subject: [PATCH 6/8] replace ndarray.dot() with @ --- control/canonical.py | 16 ++++++++-------- control/frdata.py | 6 +++--- control/mateqn.py | 30 +++++++++++++++--------------- control/statesp.py | 34 +++++++++++++++++----------------- control/timeresp.py | 2 +- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/control/canonical.py b/control/canonical.py index 45846147f..7b2b58ef7 100644 --- a/control/canonical.py +++ b/control/canonical.py @@ -8,7 +8,7 @@ import numpy as np -from numpy import zeros, zeros_like, shape, poly, iscomplex, vstack, hstack, dot, \ +from numpy import zeros, zeros_like, shape, poly, iscomplex, vstack, hstack, \ transpose, empty, finfo, float64 from numpy.linalg import solve, matrix_rank, eig @@ -149,7 +149,7 @@ def observable_form(xsys): raise ValueError("Transformation matrix singular to working precision.") # Finally, compute the output matrix - zsys.B = Tzx.dot(xsys.B) + zsys.B = Tzx @ xsys.B return zsys, Tzx @@ -189,13 +189,13 @@ def rsolve(M, y): # Update the system matrices if not inverse: - zsys.A = rsolve(T, dot(T, zsys.A)) / timescale - zsys.B = dot(T, zsys.B) / timescale + zsys.A = rsolve(T, T @ zsys.A) / timescale + zsys.B = T @ zsys.B / timescale zsys.C = rsolve(T, zsys.C) else: - zsys.A = solve(T, zsys.A).dot(T) / timescale + zsys.A = solve(T, zsys.A) @ T / timescale zsys.B = solve(T, zsys.B) / timescale - zsys.C = zsys.C.dot(T) + zsys.C = zsys.C @ T return zsys @@ -405,8 +405,8 @@ def bdschur(a, condmax=None, sort=None): permidx = np.hstack([blkidxs[i] for i in sortidx]) rperm = np.eye(amodal.shape[0])[permidx] - tmodal = tmodal.dot(rperm) - amodal = rperm.dot(amodal).dot(rperm.T) + tmodal = tmodal @ rperm + amodal = rperm @ amodal @ rperm.T blksizes = blksizes[sortidx] elif sort is None: diff --git a/control/frdata.py b/control/frdata.py index 6aefe82c7..1ac2f8b08 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -47,7 +47,7 @@ from warnings import warn import numpy as np from numpy import angle, array, empty, ones, \ - real, imag, absolute, eye, linalg, where, dot, sort + real, imag, absolute, eye, linalg, where, sort from scipy.interpolate import splprep, splev from .lti import LTI, _process_frequency_response from . import config @@ -301,7 +301,7 @@ def __mul__(self, other): fresp = empty((outputs, inputs, len(self.omega)), dtype=self.fresp.dtype) for i in range(len(self.omega)): - fresp[:, :, i] = dot(self.fresp[:, :, i], other.fresp[:, :, i]) + fresp[:, :, i] = self.fresp[:, :, i] @ other.fresp[:, :, i] return FRD(fresp, self.omega, smooth=(self.ifunc is not None) and (other.ifunc is not None)) @@ -329,7 +329,7 @@ def __rmul__(self, other): fresp = empty((outputs, inputs, len(self.omega)), dtype=self.fresp.dtype) for i in range(len(self.omega)): - fresp[:, :, i] = dot(other.fresp[:, :, i], self.fresp[:, :, i]) + fresp[:, :, i] = other.fresp[:, :, i] @ self.fresp[:, :, i] return FRD(fresp, self.omega, smooth=(self.ifunc is not None) and (other.ifunc is not None)) diff --git a/control/mateqn.py b/control/mateqn.py index 28b01d287..575e66ec7 100644 --- a/control/mateqn.py +++ b/control/mateqn.py @@ -35,7 +35,7 @@ # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. -from numpy import shape, size, asarray, copy, zeros, eye, dot, \ +from numpy import shape, size, asarray, copy, zeros, eye, \ finfo, inexact, atleast_2d from scipy.linalg import eigvals, solve_discrete_are, solve from .exception import ControlSlycot, ControlArgument @@ -624,9 +624,9 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True): # Calculate the gain matrix G if size(R_b) == 1: - G = dot(dot(1/(R_ba), asarray(B_ba).T), X) + G = 1/(R_ba) * asarray(B_ba).T @ X else: - G = dot(solve(R_ba, asarray(B_ba).T), X) + G = solve(R_ba, asarray(B_ba).T) @ X # Return the solution X, the closed-loop eigenvalues L and # the gain matrix G @@ -732,9 +732,9 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True): # Calculate the gain matrix G if size(R_b) == 1: - G = dot(1/(R_b), dot(asarray(B_b).T, dot(X, E_b)) + asarray(S_b).T) + G = 1/(R_b) * (asarray(B_b).T @ X @ E_b + asarray(S_b).T) else: - G = solve(R_b, dot(asarray(B_b).T, dot(X, E_b)) + asarray(S_b).T) + G = solve(R_b, asarray(B_b).T @ X @ E_b + asarray(S_b).T) # Return the solution X, the closed-loop eigenvalues L and # the gain matrix G @@ -794,8 +794,8 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True): Rmat = _ssmatrix(R) Qmat = _ssmatrix(Q) X = solve_discrete_are(A, B, Qmat, Rmat) - G = solve(B.T.dot(X).dot(B) + Rmat, B.T.dot(X).dot(A)) - L = eigvals(A - B.dot(G)) + G = solve(B.T @ X @ B + Rmat, B.T @ X @ A) + L = eigvals(A - B @ G) return _ssmatrix(X), L, _ssmatrix(G) @@ -926,11 +926,11 @@ def dare_old(A, B, Q, R, S=None, E=None, stabilizing=True): # Calculate the gain matrix G if size(R_b) == 1: - G = dot(1/(dot(asarray(B_ba).T, dot(X, B_ba)) + R_ba), - dot(asarray(B_ba).T, dot(X, A_ba))) + G = (1/(asarray(B_ba).T @ X @ B_ba + R_ba) * + asarray(B_ba).T @ X @ A_ba) else: - G = solve(dot(asarray(B_ba).T, dot(X, B_ba)) + R_ba, - dot(asarray(B_ba).T, dot(X, A_ba))) + G = solve(asarray(B_ba).T @ X @ B_ba + R_ba, + asarray(B_ba).T @ X @ A_ba) # Return the solution X, the closed-loop eigenvalues L and # the gain matrix G @@ -1036,11 +1036,11 @@ def dare_old(A, B, Q, R, S=None, E=None, stabilizing=True): # Calculate the gain matrix G if size(R_b) == 1: - G = dot(1/(dot(asarray(B_b).T, dot(X, B_b)) + R_b), - dot(asarray(B_b).T, dot(X, A_b)) + asarray(S_b).T) + G = (1/(asarray(B_b).T @ X @ B_b + R_b) * + (asarray(B_b).T @ X @ A_b + asarray(S_b).T)) else: - G = solve(dot(asarray(B_b).T, dot(X, B_b)) + R_b, - dot(asarray(B_b).T, dot(X, A_b)) + asarray(S_b).T) + G = solve(asarray(B_b).T @ X @ B_b + R_b, + asarray(B_b).T @ X @ A_b + asarray(S_b).T) # Return the solution X, the closed-loop eigenvalues L and # the gain matrix G diff --git a/control/statesp.py b/control/statesp.py index ca0e0a7d1..0f1c560e2 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -49,8 +49,8 @@ import math import numpy as np -from numpy import any, array, asarray, concatenate, cos, delete, \ - dot, empty, exp, eye, isinf, ones, pad, sin, zeros, squeeze, pi +from numpy import any, asarray, concatenate, cos, delete, \ + empty, exp, eye, isinf, ones, pad, sin, zeros, squeeze from numpy.random import rand, randn from numpy.linalg import solve, eigvals, matrix_rank from numpy.linalg.linalg import LinAlgError @@ -1126,23 +1126,23 @@ def lft(self, other, nu=-1, ny=-1): H22 = TH[ny:, self.nstates + other.nstates + self.ninputs - nu:] Ares = np.block([ - [A + B2.dot(T21), B2.dot(T22)], - [Bbar1.dot(T11), Abar + Bbar1.dot(T12)] + [A + B2 @ T21, B2 @ T22], + [Bbar1 @ T11, Abar + Bbar1 @ T12] ]) Bres = np.block([ - [B1 + B2.dot(H21), B2.dot(H22)], - [Bbar1.dot(H11), Bbar2 + Bbar1.dot(H12)] + [B1 + B2 @ H21, B2 @ H22], + [Bbar1 @ H11, Bbar2 + Bbar1 @ H12] ]) Cres = np.block([ - [C1 + D12.dot(T21), D12.dot(T22)], - [Dbar21.dot(T11), Cbar2 + Dbar21.dot(T12)] + [C1 + D12 @ T21, D12 @ T22], + [Dbar21 @ T11, Cbar2 + Dbar21 @ T12] ]) Dres = np.block([ - [D11 + D12.dot(H21), D12.dot(H22)], - [Dbar21.dot(H11), Dbar22 + Dbar21.dot(H12)] + [D11 + D12 @ H21, D12 @ H22], + [Dbar21 @ H11, Dbar22 + Dbar21 @ H12] ]) return StateSpace(Ares, Bres, Cres, Dres, dt) @@ -1381,13 +1381,13 @@ def dynamics(self, t, x, u=None): if np.size(x) != self.nstates: raise ValueError("len(x) must be equal to number of states") if u is None: - return self.A.dot(x).reshape((-1,)) # return as row vector + return (self.A @ x).reshape((-1,)) # return as row vector else: # received t, x, and u, ignore t u = np.reshape(u, (-1, 1)) # force to column in case matrix if np.size(u) != self.ninputs: raise ValueError("len(u) must be equal to number of inputs") - return self.A.dot(x).reshape((-1,)) \ - + self.B.dot(u).reshape((-1,)) # return as row vector + return (self.A @ x).reshape((-1,)) \ + + (self.B @ u).reshape((-1,)) # return as row vector def output(self, t, x, u=None): """Compute the output of the system @@ -1424,13 +1424,13 @@ def output(self, t, x, u=None): raise ValueError("len(x) must be equal to number of states") if u is None: - return self.C.dot(x).reshape((-1,)) # return as row vector + return (self.C @ x).reshape((-1,)) # return as row vector else: # received t, x, and u, ignore t u = np.reshape(u, (-1, 1)) # force to a column in case matrix if np.size(u) != self.ninputs: raise ValueError("len(u) must be equal to number of inputs") - return self.C.dot(x).reshape((-1,)) \ - + self.D.dot(u).reshape((-1,)) # return as row vector + return (self.C @ x).reshape((-1,)) \ + + (self.D @ u).reshape((-1,)) # return as row vector def _isstatic(self): """True if and only if the system has no dynamics, that is, @@ -1623,7 +1623,7 @@ def _rss_generate(states, inputs, outputs, cdtype, strictly_proper=False): while True: T = randn(states, states) try: - A = dot(solve(T, A), T) # A = T \ A * T + A = solve(T, A) @ T # A = T \ A @ T break except LinAlgError: # In the unlikely event that T is rank-deficient, iterate again. diff --git a/control/timeresp.py b/control/timeresp.py index 527e26e76..3f3eacc27 100644 --- a/control/timeresp.py +++ b/control/timeresp.py @@ -1972,7 +1972,7 @@ def _ideal_tfinal_and_dt(sys, is_step=True): # Incorporate balancing to outer factors l[perm, :] *= np.reciprocal(sca)[:, None] r[perm, :] *= sca[:, None] - w, v = sys_ss.C.dot(r), l.T.conj().dot(sys_ss.B) + w, v = sys_ss.C @ r, l.T.conj() @ sys_ss.B origin = False # Computing the "size" of the response of each simple mode From de8596985284dcf91ae91c5fb3c1cacccc159a0d Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 2 Dec 2021 17:27:44 +0100 Subject: [PATCH 7/8] replace np.dot with @ matmul operator where applicable in tests --- control/tests/canonical_test.py | 5 ++--- control/tests/discrete_test.py | 2 +- control/tests/iosys_test.py | 34 ++++++++++++++++++--------------- control/tests/modelsimp_test.py | 6 +++--- control/tests/statesp_test.py | 4 ++-- control/tests/timeresp_test.py | 8 ++------ 6 files changed, 29 insertions(+), 30 deletions(-) diff --git a/control/tests/canonical_test.py b/control/tests/canonical_test.py index 0db6b924c..f8a62cba8 100644 --- a/control/tests/canonical_test.py +++ b/control/tests/canonical_test.py @@ -384,9 +384,8 @@ def test_modal_form(A_true, B_true, C_true, D_true): # Make sure Hankel coefficients are OK for i in range(A.shape[0]): np.testing.assert_almost_equal( - np.dot(np.dot(C_true, np.linalg.matrix_power(A_true, i)), - B_true), - np.dot(np.dot(C, np.linalg.matrix_power(A, i)), B)) + C_true @ np.linalg.matrix_power(A_true, i) @ B_true, + C @ np.linalg.matrix_power(A, i) @ B) @slycotonly diff --git a/control/tests/discrete_test.py b/control/tests/discrete_test.py index 5a1a367ab..cb0ce3c76 100644 --- a/control/tests/discrete_test.py +++ b/control/tests/discrete_test.py @@ -416,7 +416,7 @@ def test_sample_ss(self, tsys): for sys in (sys1, sys2): for h in (0.1, 0.5, 1, 2): Ad = I + h * sys.A - Bd = h * sys.B + 0.5 * h**2 * np.dot(sys.A, sys.B) + Bd = h * sys.B + 0.5 * h**2 * sys.A @ sys.B sysd = sample_system(sys, h, method='zoh') np.testing.assert_array_almost_equal(sysd.A, Ad) np.testing.assert_array_almost_equal(sysd.B, Bd) diff --git a/control/tests/iosys_test.py b/control/tests/iosys_test.py index bb975364b..5fd83e946 100644 --- a/control/tests/iosys_test.py +++ b/control/tests/iosys_test.py @@ -56,7 +56,7 @@ def test_linear_iosys(self, tsys): for x, u in (([0, 0], 0), ([1, 0], 0), ([0, 1], 0), ([0, 0], 1)): np.testing.assert_array_almost_equal( np.reshape(iosys._rhs(0, x, u), (-1, 1)), - np.dot(linsys.A, np.reshape(x, (-1, 1))) + np.dot(linsys.B, u)) + linsys.A @ np.reshape(x, (-1, 1)) + linsys.B * u) # Make sure that simulations also line up T, U, X0 = tsys.T, tsys.U, tsys.X0 @@ -152,11 +152,13 @@ def test_nonlinear_iosys(self, tsys): # Create a nonlinear system with the same dynamics nlupd = lambda t, x, u, params: \ - np.reshape(np.dot(linsys.A, np.reshape(x, (-1, 1))) - + np.dot(linsys.B, u), (-1,)) + np.reshape(linsys.A @ np.reshape(x, (-1, 1)) + + linsys.B @ np.reshape(u, (-1, 1)), + (-1,)) nlout = lambda t, x, u, params: \ - np.reshape(np.dot(linsys.C, np.reshape(x, (-1, 1))) - + np.dot(linsys.D, u), (-1,)) + np.reshape(linsys.C @ np.reshape(x, (-1, 1)) + + linsys.D @ np.reshape(u, (-1, 1)), + (-1,)) nlsys = ios.NonlinearIOSystem(nlupd, nlout, inputs=1, outputs=1) # Make sure that simulations also line up @@ -905,12 +907,12 @@ def test_params(self, tsys): def test_named_signals(self, tsys): sys1 = ios.NonlinearIOSystem( updfcn = lambda t, x, u, params: np.array( - np.dot(tsys.mimo_linsys1.A, np.reshape(x, (-1, 1))) \ - + np.dot(tsys.mimo_linsys1.B, np.reshape(u, (-1, 1))) + tsys.mimo_linsys1.A @ np.reshape(x, (-1, 1)) \ + + tsys.mimo_linsys1.B @ np.reshape(u, (-1, 1)) ).reshape(-1,), outfcn = lambda t, x, u, params: np.array( - np.dot(tsys.mimo_linsys1.C, np.reshape(x, (-1, 1))) \ - + np.dot(tsys.mimo_linsys1.D, np.reshape(u, (-1, 1))) + tsys.mimo_linsys1.C @ np.reshape(x, (-1, 1)) \ + + tsys.mimo_linsys1.D @ np.reshape(u, (-1, 1)) ).reshape(-1,), inputs = ['u[0]', 'u[1]'], outputs = ['y[0]', 'y[1]'], @@ -1138,8 +1140,8 @@ def test_named_signals_linearize_inconsistent(self, tsys): def updfcn(t, x, u, params): """2 inputs, 2 states""" return np.array( - np.dot(tsys.mimo_linsys1.A, np.reshape(x, (-1, 1))) - + np.dot(tsys.mimo_linsys1.B, np.reshape(u, (-1, 1))) + tsys.mimo_linsys1.A @ np.reshape(x, (-1, 1)) + + tsys.mimo_linsys1.B @ np.reshape(u, (-1, 1)) ).reshape(-1,) def outfcn(t, x, u, params): @@ -1413,11 +1415,13 @@ def test_linear_interconnection(): outputs = ('y[0]', 'y[1]'), name = 'sys2') nl_sys2 = ios.NonlinearIOSystem( lambda t, x, u, params: np.array( - np.dot(ss_sys2.A, np.reshape(x, (-1, 1))) \ - + np.dot(ss_sys2.B, np.reshape(u, (-1, 1)))).reshape((-1,)), + ss_sys2.A @ np.reshape(x, (-1, 1)) \ + + ss_sys2.B @ np.reshape(u, (-1, 1)) + ).reshape((-1,)), lambda t, x, u, params: np.array( - np.dot(ss_sys2.C, np.reshape(x, (-1, 1))) \ - + np.dot(ss_sys2.D, np.reshape(u, (-1, 1)))).reshape((-1,)), + ss_sys2.C @ np.reshape(x, (-1, 1)) \ + + ss_sys2.D @ np.reshape(u, (-1, 1)) + ).reshape((-1,)), states = 2, inputs = ('u[0]', 'u[1]'), outputs = ('y[0]', 'y[1]'), diff --git a/control/tests/modelsimp_test.py b/control/tests/modelsimp_test.py index fd474f9d0..70e94dd91 100644 --- a/control/tests/modelsimp_test.py +++ b/control/tests/modelsimp_test.py @@ -95,9 +95,9 @@ def testMarkovResults(self, k, m, n): Hd = c2d(Hc, Ts, 'zoh') # Compute the Markov parameters from state space - Mtrue = np.hstack([Hd.D] + [np.dot( - Hd.C, np.dot(np.linalg.matrix_power(Hd.A, i), - Hd.B)) for i in range(m-1)]) + Mtrue = np.hstack([Hd.D] + [ + Hd.C @ np.linalg.matrix_power(Hd.A, i) @ Hd.B + for i in range(m-1)]) # Generate input/output data T = np.array(range(n)) * Ts diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index dac8043df..bdd17b79a 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -592,12 +592,12 @@ def test_matrix_static_gain(self): g3 = StateSpace([], [], [], d2.T) h1 = g1 * g2 - np.testing.assert_allclose(np.dot(d1, d2), h1.D) + np.testing.assert_allclose(d1 @ d2, h1.D) h2 = g1 + g3 np.testing.assert_allclose(d1 + d2.T, h2.D) h3 = g1.feedback(g2) np.testing.assert_array_almost_equal( - solve(np.eye(2) + np.dot(d1, d2), d1), h3.D) + solve(np.eye(2) + d1 @ d2, d1), h3.D) h4 = g1.append(g2) np.testing.assert_allclose(block_diag(d1, d2), h4.D) diff --git a/control/tests/timeresp_test.py b/control/tests/timeresp_test.py index bb53c310a..13a509e48 100644 --- a/control/tests/timeresp_test.py +++ b/control/tests/timeresp_test.py @@ -1010,9 +1010,7 @@ def test_squeeze(self, fcn, nstate, nout, ninp, squeeze, shape1, shape2): # Generate the time and input vectors tvec = np.linspace(0, 1, 8) - uvec = np.dot( - np.ones((sys.ninputs, 1)), - np.reshape(np.sin(tvec), (1, 8))) + uvec = np.ones((sys.ninputs, 1)) @ np.reshape(np.sin(tvec), (1, 8)) # # Pass squeeze argument and make sure the shape is correct @@ -1144,9 +1142,7 @@ def test_squeeze_0_8_4(self, nstate, nout, ninp, squeeze, shape): # Generate system, time, and input vectors sys = ct.rss(nstate, nout, ninp, strictly_proper=True) tvec = np.linspace(0, 1, 8) - uvec = np.dot( - np.ones((sys.ninputs, 1)), - np.reshape(np.sin(tvec), (1, 8))) + uvec =np.ones((sys.ninputs, 1)) @ np.reshape(np.sin(tvec), (1, 8)) _, yvec = ct.initial_response(sys, tvec, 1, squeeze=squeeze) assert yvec.shape == shape From 5b28121cf0744cf2eb360de4b89980c181991a9a Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 2 Dec 2021 17:53:37 +0100 Subject: [PATCH 8/8] replace ndarray.dot in tests --- control/tests/canonical_test.py | 22 +++++----- control/tests/mateqn_test.py | 76 ++++++++++++++++----------------- control/tests/statefbk_test.py | 16 +++---- control/tests/statesp_test.py | 18 ++++---- control/tests/timeresp_test.py | 2 +- 5 files changed, 67 insertions(+), 67 deletions(-) diff --git a/control/tests/canonical_test.py b/control/tests/canonical_test.py index f8a62cba8..f822955fc 100644 --- a/control/tests/canonical_test.py +++ b/control/tests/canonical_test.py @@ -29,9 +29,9 @@ def test_reachable_form(self): [-0.74855725, -0.39136285, -0.18142339, -0.50356997], [-0.40688007, 0.81416369, 0.38002113, -0.16483334], [-0.44769516, 0.15654653, -0.50060858, 0.72419146]]) - A = np.linalg.solve(T_true, A_true).dot(T_true) + A = np.linalg.solve(T_true, A_true) @ T_true B = np.linalg.solve(T_true, B_true) - C = C_true.dot(T_true) + C = C_true @ T_true D = D_true # Create a state space system and convert it to the reachable canonical form @@ -77,9 +77,9 @@ def test_observable_form(self): [-0.74855725, -0.39136285, -0.18142339, -0.50356997], [-0.40688007, 0.81416369, 0.38002113, -0.16483334], [-0.44769516, 0.15654653, -0.50060858, 0.72419146]]) - A = np.linalg.solve(T_true, A_true).dot(T_true) + A = np.linalg.solve(T_true, A_true) @ T_true B = np.linalg.solve(T_true, B_true) - C = C_true.dot(T_true) + C = C_true @ T_true D = D_true # Create a state space system and convert it to the observable canonical form @@ -266,7 +266,7 @@ def test_bdschur_ref(eigvals, condmax, blksizes): bdiag_b = scipy.linalg.block_diag(*extract_bdiag(b, test_blksizes)) np.testing.assert_array_almost_equal(bdiag_b, b) - np.testing.assert_array_almost_equal(solve(t, a).dot(t), b) + np.testing.assert_array_almost_equal(solve(t, a) @ t, b) @slycotonly @@ -357,9 +357,9 @@ def test_modal_form(A_true, B_true, C_true, D_true): [-0.74855725, -0.39136285, -0.18142339, -0.50356997], [-0.40688007, 0.81416369, 0.38002113, -0.16483334], [-0.44769516, 0.15654653, -0.50060858, 0.72419146]]) - A = np.linalg.solve(T_true, A_true).dot(T_true) + A = np.linalg.solve(T_true, A_true) @ T_true B = np.linalg.solve(T_true, B_true) - C = C_true.dot(T_true) + C = C_true @ T_true D = D_true # Create a state space system and convert it to modal canonical form @@ -370,7 +370,7 @@ def test_modal_form(A_true, B_true, C_true, D_true): np.testing.assert_array_almost_equal(sys_check.A, a_bds) np.testing.assert_array_almost_equal(T_check, t_bds) np.testing.assert_array_almost_equal(sys_check.B, np.linalg.solve(t_bds, B)) - np.testing.assert_array_almost_equal(sys_check.C, C.dot(t_bds)) + np.testing.assert_array_almost_equal(sys_check.C, C @ t_bds) np.testing.assert_array_almost_equal(sys_check.D, D) # canonical_form(...,'modal') is the same as modal_form with default parameters @@ -403,7 +403,7 @@ def test_modal_form_condmax(condmax, len_blksizes): np.testing.assert_array_almost_equal(zsys.A, amodal) np.testing.assert_array_almost_equal(t, tmodal) np.testing.assert_array_almost_equal(zsys.B, np.linalg.solve(tmodal, xsys.B)) - np.testing.assert_array_almost_equal(zsys.C, xsys.C.dot(tmodal)) + np.testing.assert_array_almost_equal(zsys.C, xsys.C @ tmodal) np.testing.assert_array_almost_equal(zsys.D, xsys.D) @@ -421,13 +421,13 @@ def test_modal_form_sort(sys_type): xsys = ss(a, [[1],[0],[0],[0],], [0,0,0,1], 0, dt) zsys, t = modal_form(xsys, sort=True) - my_amodal = np.linalg.solve(tmodal, a).dot(tmodal) + my_amodal = np.linalg.solve(tmodal, a) @ tmodal np.testing.assert_array_almost_equal(amodal, my_amodal) np.testing.assert_array_almost_equal(t, tmodal) np.testing.assert_array_almost_equal(zsys.A, amodal) np.testing.assert_array_almost_equal(zsys.B, np.linalg.solve(tmodal, xsys.B)) - np.testing.assert_array_almost_equal(zsys.C, xsys.C.dot(tmodal)) + np.testing.assert_array_almost_equal(zsys.C, xsys.C @ tmodal) np.testing.assert_array_almost_equal(zsys.D, xsys.D) diff --git a/control/tests/mateqn_test.py b/control/tests/mateqn_test.py index facb1ce08..62fca6bd3 100644 --- a/control/tests/mateqn_test.py +++ b/control/tests/mateqn_test.py @@ -52,13 +52,13 @@ def test_lyap(self): Q = array([[1,0],[0,1]]) X = lyap(A,Q) # print("The solution obtained is ", X) - assert_array_almost_equal(A.dot(X) + X.dot(A.T) + Q, zeros((2,2))) + assert_array_almost_equal(A @ X + X @ A.T + Q, zeros((2,2))) A = array([[1, 2],[-3, -4]]) Q = array([[3, 1],[1, 1]]) X = lyap(A,Q) # print("The solution obtained is ", X) - assert_array_almost_equal(A.dot(X) + X.dot(A.T) + Q, zeros((2,2))) + assert_array_almost_equal(A @ X + X @ A.T + Q, zeros((2,2))) def test_lyap_sylvester(self): A = 5 @@ -66,14 +66,14 @@ def test_lyap_sylvester(self): C = array([2, 1]) X = lyap(A,B,C) # print("The solution obtained is ", X) - assert_array_almost_equal(A * X + X.dot(B) + C, zeros((1,2))) + assert_array_almost_equal(A * X + X @ B + C, zeros((1,2))) A = array([[2,1],[1,2]]) B = array([[1,2],[0.5,0.1]]) C = array([[1,0],[0,1]]) X = lyap(A,B,C) # print("The solution obtained is ", X) - assert_array_almost_equal(A.dot(X) + X.dot(B) + C, zeros((2,2))) + assert_array_almost_equal(A @ X + X @ B + C, zeros((2,2))) def test_lyap_g(self): A = array([[-1, 2],[-3, -4]]) @@ -81,7 +81,7 @@ def test_lyap_g(self): E = array([[1,2],[2,1]]) X = lyap(A,Q,None,E) # print("The solution obtained is ", X) - assert_array_almost_equal(A.dot(X).dot(E.T) + E.dot(X).dot(A.T) + Q, + assert_array_almost_equal(A @ X @ E.T + E @ X @ A.T + Q, zeros((2,2))) def test_dlyap(self): @@ -89,13 +89,13 @@ def test_dlyap(self): Q = array([[1,0],[0,1]]) X = dlyap(A,Q) # print("The solution obtained is ", X) - assert_array_almost_equal(A.dot(X).dot(A.T) - X + Q, zeros((2,2))) + assert_array_almost_equal(A @ X @ A.T - X + Q, zeros((2,2))) A = array([[-0.6, 0],[-0.1, -0.4]]) Q = array([[3, 1],[1, 1]]) X = dlyap(A,Q) # print("The solution obtained is ", X) - assert_array_almost_equal(A.dot(X).dot(A.T) - X + Q, zeros((2,2))) + assert_array_almost_equal(A @ X @ A.T - X + Q, zeros((2,2))) def test_dlyap_g(self): A = array([[-0.6, 0],[-0.1, -0.4]]) @@ -103,7 +103,7 @@ def test_dlyap_g(self): E = array([[1, 1],[2, 1]]) X = dlyap(A,Q,None,E) # print("The solution obtained is ", X) - assert_array_almost_equal(A.dot(X).dot(A.T) - E.dot(X).dot(E.T) + Q, + assert_array_almost_equal(A @ X @ A.T - E @ X @ E.T + Q, zeros((2,2))) def test_dlyap_sylvester(self): @@ -112,14 +112,14 @@ def test_dlyap_sylvester(self): C = array([2, 1]) X = dlyap(A,B,C) # print("The solution obtained is ", X) - assert_array_almost_equal(A * X.dot(B.T) - X + C, zeros((1,2))) + assert_array_almost_equal(A * X @ B.T - X + C, zeros((1,2))) A = array([[2,1],[1,2]]) B = array([[1,2],[0.5,0.1]]) C = array([[1,0],[0,1]]) X = dlyap(A,B,C) # print("The solution obtained is ", X) - assert_array_almost_equal(A.dot(X).dot(B.T) - X + C, zeros((2,2))) + assert_array_almost_equal(A @ X @ B.T - X + C, zeros((2,2))) def test_care(self): A = array([[-2, -1],[-1, -1]]) @@ -128,10 +128,10 @@ def test_care(self): X,L,G = care(A,B,Q) # print("The solution obtained is", X) - M = A.T.dot(X) + X.dot(A) - X.dot(B).dot(B.T).dot(X) + Q + M = A.T @ X + X @ A - X @ B @ B.T @ X + Q assert_array_almost_equal(M, zeros((2,2))) - assert_array_almost_equal(B.T.dot(X), G) + assert_array_almost_equal(B.T @ X, G) def test_care_g(self): A = array([[-2, -1],[-1, -1]]) @@ -143,11 +143,11 @@ def test_care_g(self): X,L,G = care(A,B,Q,R,S,E) # print("The solution obtained is", X) - Gref = solve(R, B.T.dot(X).dot(E) + S.T) + Gref = solve(R, B.T @ X @ E + S.T) assert_array_almost_equal(Gref, G) assert_array_almost_equal( - A.T.dot(X).dot(E) + E.T.dot(X).dot(A) - - (E.T.dot(X).dot(B) + S).dot(Gref) + Q, + A.T @ X @ E + E.T @ X @ A + - (E.T @ X @ B + S) @ Gref + Q, zeros((2,2))) def test_care_g2(self): @@ -160,10 +160,10 @@ def test_care_g2(self): X,L,G = care(A,B,Q,R,S,E) # print("The solution obtained is", X) - Gref = 1/R * (B.T.dot(X).dot(E) + S.T) + Gref = 1/R * (B.T @ X @ E + S.T) assert_array_almost_equal( - A.T.dot(X).dot(E) + E.T.dot(X).dot(A) - - (E.T.dot(X).dot(B) + S).dot(Gref) + Q , + A.T @ X @ E + E.T @ X @ A + - (E.T @ X @ B + S) @ Gref + Q , zeros((2,2))) assert_array_almost_equal(Gref , G) @@ -175,12 +175,12 @@ def test_dare(self): X,L,G = dare(A,B,Q,R) # print("The solution obtained is", X) - Gref = solve(B.T.dot(X).dot(B) + R, B.T.dot(X).dot(A)) + Gref = solve(B.T @ X @ B + R, B.T @ X @ A) assert_array_almost_equal(Gref, G) assert_array_almost_equal( - X, A.T.dot(X).dot(A) - A.T.dot(X).dot(B).dot(Gref) + Q) + X, A.T @ X @ A - A.T @ X @ B @ Gref + Q) # check for stable closed loop - lam = eigvals(A - B.dot(G)) + lam = eigvals(A - B @ G) assert_array_less(abs(lam), 1.0) A = array([[1, 0],[-1, 1]]) @@ -190,15 +190,15 @@ def test_dare(self): X,L,G = dare(A,B,Q,R) # print("The solution obtained is", X) - AtXA = A.T.dot(X).dot(A) - AtXB = A.T.dot(X).dot(B) - BtXA = B.T.dot(X).dot(A) - BtXB = B.T.dot(X).dot(B) + AtXA = A.T @ X @ A + AtXB = A.T @ X @ B + BtXA = B.T @ X @ A + BtXB = B.T @ X @ B assert_array_almost_equal( - X, AtXA - AtXB.dot(solve(BtXB + R, BtXA)) + Q) + X, AtXA - AtXB @ solve(BtXB + R, BtXA) + Q) assert_array_almost_equal(BtXA / (BtXB + R), G) # check for stable closed loop - lam = eigvals(A - B.dot(G)) + lam = eigvals(A - B @ G) assert_array_less(abs(lam), 1.0) def test_dare_g(self): @@ -211,13 +211,13 @@ def test_dare_g(self): X,L,G = dare(A,B,Q,R,S,E) # print("The solution obtained is", X) - Gref = solve(B.T.dot(X).dot(B) + R, B.T.dot(X).dot(A) + S.T) + Gref = solve(B.T @ X @ B + R, B.T @ X @ A + S.T) assert_array_almost_equal(Gref, G) assert_array_almost_equal( - E.T.dot(X).dot(E), - A.T.dot(X).dot(A) - (A.T.dot(X).dot(B) + S).dot(Gref) + Q) + E.T @ X @ E, + A.T @ X @ A - (A.T @ X @ B + S) @ Gref + Q) # check for stable closed loop - lam = eigvals(A - B.dot(G), E) + lam = eigvals(A - B @ G, E) assert_array_less(abs(lam), 1.0) def test_dare_g2(self): @@ -230,16 +230,16 @@ def test_dare_g2(self): X, L, G = dare(A, B, Q, R, S, E) # print("The solution obtained is", X) - AtXA = A.T.dot(X).dot(A) - AtXB = A.T.dot(X).dot(B) - BtXA = B.T.dot(X).dot(A) - BtXB = B.T.dot(X).dot(B) - EtXE = E.T.dot(X).dot(E) + AtXA = A.T @ X @ A + AtXB = A.T @ X @ B + BtXA = B.T @ X @ A + BtXB = B.T @ X @ B + EtXE = E.T @ X @ E assert_array_almost_equal( - EtXE, AtXA - (AtXB + S).dot(solve(BtXB + R, BtXA + S.T)) + Q) + EtXE, AtXA - (AtXB + S) @ solve(BtXB + R, BtXA + S.T) + Q) assert_array_almost_equal((BtXA + S.T) / (BtXB + R), G) # check for stable closed loop - lam = eigvals(A - B.dot(G), E) + lam = eigvals(A - B @ G, E) assert_array_less(abs(lam), 1.0) def test_raise(self): diff --git a/control/tests/statefbk_test.py b/control/tests/statefbk_test.py index 0f73d787c..03e1ff344 100644 --- a/control/tests/statefbk_test.py +++ b/control/tests/statefbk_test.py @@ -203,7 +203,7 @@ def testPlace(self, matarrayin): P = matarrayin([-0.5 + 1j, -0.5 - 1j, -5.0566, -8.6659]) K = place(A, B, P) assert ismatarrayout(K) - P_placed = np.linalg.eigvals(A - B.dot(K)) + P_placed = np.linalg.eigvals(A - B @ K) self.checkPlaced(P, P_placed) # Test that the dimension checks work. @@ -228,7 +228,7 @@ def testPlace_varga_continuous(self, matarrayin): P = [-2., -2.] K = place_varga(A, B, P) - P_placed = np.linalg.eigvals(A - B.dot(K)) + P_placed = np.linalg.eigvals(A - B @ K) self.checkPlaced(P, P_placed) # Test that the dimension checks work. @@ -241,7 +241,7 @@ def testPlace_varga_continuous(self, matarrayin): B = matarrayin([[0], [1]]) P = matarrayin([-20 + 10*1j, -20 - 10*1j]) K = place_varga(A, B, P) - P_placed = np.linalg.eigvals(A - B.dot(K)) + P_placed = np.linalg.eigvals(A - B @ K) self.checkPlaced(P, P_placed) @@ -261,7 +261,7 @@ def testPlace_varga_continuous_partial_eigs(self, matarrayin): alpha = -1.5 K = place_varga(A, B, P, alpha=alpha) - P_placed = np.linalg.eigvals(A - B.dot(K)) + P_placed = np.linalg.eigvals(A - B @ K) # No guarantee of the ordering, so sort them self.checkPlaced(P_expected, P_placed) @@ -275,7 +275,7 @@ def testPlace_varga_discrete(self, matarrayin): P = matarrayin([0.5, 0.5]) K = place_varga(A, B, P, dtime=True) - P_placed = np.linalg.eigvals(A - B.dot(K)) + P_placed = np.linalg.eigvals(A - B @ K) # No guarantee of the ordering, so sort them self.checkPlaced(P, P_placed) @@ -293,12 +293,12 @@ def testPlace_varga_discrete_partial_eigs(self, matarrayin): P_expected = np.array([0.5, 0.6]) alpha = 0.51 K = place_varga(A, B, P, dtime=True, alpha=alpha) - P_placed = np.linalg.eigvals(A - B.dot(K)) + P_placed = np.linalg.eigvals(A - B @ K) self.checkPlaced(P_expected, P_placed) def check_LQR(self, K, S, poles, Q, R): - S_expected = asmatarrayout(np.sqrt(Q.dot(R))) + S_expected = asmatarrayout(np.sqrt(Q @ R)) K_expected = asmatarrayout(S_expected / R) poles_expected = -np.squeeze(np.asarray(K_expected)) np.testing.assert_array_almost_equal(S, S_expected) @@ -373,7 +373,7 @@ def test_lqr_call_format(self): K, S, E = lqr(sys.A, sys.B, sys.C, R, Q) def check_LQE(self, L, P, poles, G, QN, RN): - P_expected = asmatarrayout(np.sqrt(G.dot(QN.dot(G).dot(RN)))) + P_expected = asmatarrayout(np.sqrt(G @ QN @ G @ RN)) L_expected = asmatarrayout(P_expected / RN) poles_expected = -np.squeeze(np.asarray(L_expected)) np.testing.assert_array_almost_equal(P, P_expected) diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index bdd17b79a..78eacf857 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -767,18 +767,19 @@ def test_horner(self, sys322): [[1, 1], [[1], [1]], np.atleast_2d([1,1]).T]) @pytest.mark.parametrize('u', [0, 1, np.atleast_1d(2)]) def test_dynamics_and_output_siso(self, x, u, sys121): + uref = np.atleast_1d(u) assert_array_almost_equal( sys121.dynamics(0, x, u), - sys121.A.dot(x).reshape((-1,)) + sys121.B.dot(u).reshape((-1,))) + (sys121.A @ x).reshape((-1,)) + (sys121.B @ uref).reshape((-1,))) assert_array_almost_equal( sys121.output(0, x, u), - sys121.C.dot(x).reshape((-1,)) + sys121.D.dot(u).reshape((-1,))) + (sys121.C @ x).reshape((-1,)) + (sys121.D @ uref).reshape((-1,))) assert_array_almost_equal( sys121.dynamics(0, x), - sys121.A.dot(x).reshape((-1,))) + (sys121.A @ x).reshape((-1,))) assert_array_almost_equal( sys121.output(0, x), - sys121.C.dot(x).reshape((-1,))) + (sys121.C @ x).reshape((-1,))) # too few and too many states and inputs @pytest.mark.parametrize('x', [0, 1, [], [1, 2, 3], np.atleast_1d(2)]) @@ -801,16 +802,16 @@ def test_error_u_dynamics_output_siso(self, u, sys121): def test_dynamics_and_output_mimo(self, x, u, sys222): assert_array_almost_equal( sys222.dynamics(0, x, u), - sys222.A.dot(x).reshape((-1,)) + sys222.B.dot(u).reshape((-1,))) + (sys222.A @ x).reshape((-1,)) + (sys222.B @ u).reshape((-1,))) assert_array_almost_equal( sys222.output(0, x, u), - sys222.C.dot(x).reshape((-1,)) + sys222.D.dot(u).reshape((-1,))) + (sys222.C @ x).reshape((-1,)) + (sys222.D @ u).reshape((-1,))) assert_array_almost_equal( sys222.dynamics(0, x), - sys222.A.dot(x).reshape((-1,))) + (sys222.A @ x).reshape((-1,))) assert_array_almost_equal( sys222.output(0, x), - sys222.C.dot(x).reshape((-1,))) + (sys222.C @ x).reshape((-1,))) # too few and too many states and inputs @pytest.mark.parametrize('x', [0, 1, [1, 1, 1]]) @@ -1095,4 +1096,3 @@ def test_latex_repr_testsize(editsdefaults): gstatic = ss([], [], [], 1) assert gstatic._repr_latex_() is None - diff --git a/control/tests/timeresp_test.py b/control/tests/timeresp_test.py index 13a509e48..61c0cae38 100644 --- a/control/tests/timeresp_test.py +++ b/control/tests/timeresp_test.py @@ -743,7 +743,7 @@ def test_lsim_double_integrator(self, u, x0, xtrue): _t, yout, xout = forced_response(sys, t, u, x0, return_x=True) np.testing.assert_array_almost_equal(xout, xtrue, decimal=6) - ytrue = np.squeeze(np.asarray(C.dot(xtrue))) + ytrue = np.squeeze(np.asarray(C @ xtrue)) np.testing.assert_array_almost_equal(yout, ytrue, decimal=6) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy