From d5156e574241c4697960afe097e2884edd4f83f9 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 9 Feb 2025 12:06:53 +0200 Subject: [PATCH 01/22] Lint: remove unused variables, or prefix with `_` Where it's clear that function is called for side effects (e.g., in a q`with pytest.raises` block), don't assign function output. Where it's not clear, e.g., binary ops on LTI objects, call result `_sys` or similar. There are plenty of in-between cases: for those I chose based on understandability. --- control/tests/bdalg_test.py | 10 +-- control/tests/bspline_test.py | 24 +++--- control/tests/ctrlplot_test.py | 34 +-------- control/tests/descfcn_test.py | 6 +- control/tests/discrete_test.py | 119 +++++++++++++++-------------- control/tests/flatsys_test.py | 2 - control/tests/frd_test.py | 15 ++-- control/tests/freqplot_test.py | 2 +- control/tests/interconnect_test.py | 8 +- control/tests/iosys_test.py | 114 ++++++++++++++------------- control/tests/lti_test.py | 10 +-- control/tests/matlab_test.py | 42 +++++----- control/tests/namedio_test.py | 8 +- control/tests/nlsys_test.py | 2 +- control/tests/nyquist_test.py | 6 +- control/tests/optimal_test.py | 38 ++++----- control/tests/phaseplot_test.py | 4 +- control/tests/pzmap_test.py | 4 +- control/tests/statefbk_test.py | 12 +-- control/tests/statesp_test.py | 2 +- control/tests/stochsys_test.py | 10 +-- control/tests/timeplot_test.py | 9 +-- control/tests/timeresp_test.py | 3 +- control/tests/trdata_test.py | 2 +- 24 files changed, 218 insertions(+), 268 deletions(-) diff --git a/control/tests/bdalg_test.py b/control/tests/bdalg_test.py index 50ae9e8a9..cec10f904 100644 --- a/control/tests/bdalg_test.py +++ b/control/tests/bdalg_test.py @@ -347,19 +347,19 @@ def test_bdalg_udpate_names_errors(): sys2 = ctrl.rss(2, 1, 1) with pytest.raises(ValueError, match="number of inputs does not match"): - sys = ctrl.series(sys1, sys2, inputs=2) + ctrl.series(sys1, sys2, inputs=2) with pytest.raises(ValueError, match="number of outputs does not match"): - sys = ctrl.series(sys1, sys2, outputs=2) + ctrl.series(sys1, sys2, outputs=2) with pytest.raises(ValueError, match="number of states does not match"): - sys = ctrl.series(sys1, sys2, states=2) + ctrl.series(sys1, sys2, states=2) with pytest.raises(ValueError, match="number of states does not match"): - sys = ctrl.series(ctrl.tf(sys1), ctrl.tf(sys2), states=2) + ctrl.series(ctrl.tf(sys1), ctrl.tf(sys2), states=2) with pytest.raises(TypeError, match="unrecognized keywords"): - sys = ctrl.series(sys1, sys2, dt=1) + ctrl.series(sys1, sys2, dt=1) class TestEnsureTf: diff --git a/control/tests/bspline_test.py b/control/tests/bspline_test.py index 0ac59094d..0494e1252 100644 --- a/control/tests/bspline_test.py +++ b/control/tests/bspline_test.py @@ -182,40 +182,40 @@ def test_kinematic_car_multivar(): def test_bspline_errors(): # Breakpoints must be a 1D array, in increasing order with pytest.raises(NotImplementedError, match="not yet supported"): - basis = fs.BSplineFamily([[0, 1, 3], [0, 2, 3]], [3, 3]) + fs.BSplineFamily([[0, 1, 3], [0, 2, 3]], [3, 3]) with pytest.raises(ValueError, match="breakpoints must be convertable to a 1D array"): - basis = fs.BSplineFamily([[[0, 1], [0, 1]], [[0, 1], [0, 1]]], [3, 3]) + fs.BSplineFamily([[[0, 1], [0, 1]], [[0, 1], [0, 1]]], [3, 3]) with pytest.raises(ValueError, match="must have at least 2 values"): - basis = fs.BSplineFamily([10], 2) + fs.BSplineFamily([10], 2) with pytest.raises(ValueError, match="must be strictly increasing"): - basis = fs.BSplineFamily([1, 3, 2], 2) + fs.BSplineFamily([1, 3, 2], 2) # Smoothness can't be more than dimension of splines - basis = fs.BSplineFamily([0, 1], 4, 3) # OK + fs.BSplineFamily([0, 1], 4, 3) # OK with pytest.raises(ValueError, match="degree must be greater"): - basis = fs.BSplineFamily([0, 1], 4, 4) # not OK + fs.BSplineFamily([0, 1], 4, 4) # not OK # nvars must be an integer with pytest.raises(TypeError, match="vars must be an integer"): - basis = fs.BSplineFamily([0, 1], 4, 3, vars=['x1', 'x2']) + fs.BSplineFamily([0, 1], 4, 3, vars=['x1', 'x2']) # degree, smoothness must match nvars with pytest.raises(ValueError, match="length of 'degree' does not match"): - basis = fs.BSplineFamily([0, 1], [4, 4, 4], 3, vars=2) + fs.BSplineFamily([0, 1], [4, 4, 4], 3, vars=2) # degree, smoothness must be list of ints - basis = fs.BSplineFamily([0, 1], [4, 4], 3, vars=2) # OK + fs.BSplineFamily([0, 1], [4, 4], 3, vars=2) # OK with pytest.raises(ValueError, match="could not parse 'degree'"): - basis = fs.BSplineFamily([0, 1], [4, '4'], 3, vars=2) + fs.BSplineFamily([0, 1], [4, '4'], 3, vars=2) # degree must be strictly positive with pytest.raises(ValueError, match="'degree'; must be at least 1"): - basis = fs.BSplineFamily([0, 1], 0, 1) + fs.BSplineFamily([0, 1], 0, 1) # smoothness must be non-negative with pytest.raises(ValueError, match="'smoothness'; must be at least 0"): - basis = fs.BSplineFamily([0, 1], 2, -1) + fs.BSplineFamily([0, 1], 2, -1) diff --git a/control/tests/ctrlplot_test.py b/control/tests/ctrlplot_test.py index b7192c844..36965f78d 100644 --- a/control/tests/ctrlplot_test.py +++ b/control/tests/ctrlplot_test.py @@ -74,7 +74,6 @@ def setup_plot_arguments(resp_fcn, plot_fcn, compute_time_response=True): case ct.gangof4_response, _: args1 = (sys1, sys1c) args2 = (sys2, sys1c) - default_labels = ["P=sys[1]", "P=sys[2]"] case ct.frequency_response, ct.nichols_plot: args1 = (sys1, None) # to allow *fmt in linestyle test @@ -234,10 +233,10 @@ def test_plot_ax_processing(resp_fcn, plot_fcn): # Call the plotting function, passing the axes if resp_fcn is not None: resp = resp_fcn(*args, **kwargs, **resp_kwargs) - cplt4 = resp.plot(**kwargs, **meth_kwargs, ax=ax) + resp.plot(**kwargs, **meth_kwargs, ax=ax) else: # No response function available; just plot the data - cplt4 = plot_fcn(*args, **kwargs, **plot_kwargs, ax=ax) + plot_fcn(*args, **kwargs, **plot_kwargs, ax=ax) # Check to make sure original settings did not change assert fig._suptitle.get_text() == title @@ -326,19 +325,9 @@ def test_plot_label_processing(resp_fcn, plot_fcn): @pytest.mark.parametrize("resp_fcn, plot_fcn", resp_plot_fcns) @pytest.mark.usefixtures('mplcleanup') def test_plot_linestyle_processing(resp_fcn, plot_fcn): - # Create some systems to use - sys1 = ct.rss(2, 1, 1, strictly_proper=True, name="sys[1]") - sys1c = ct.rss(4, 1, 1, strictly_proper=True, name="sys[1]_C") - sys2 = ct.rss(4, 1, 1, strictly_proper=True, name="sys[2]") - # Set up arguments args1, args2, _, kwargs, meth_kwargs, plot_kwargs, resp_kwargs = \ setup_plot_arguments(resp_fcn, plot_fcn) - default_labels = ["sys[1]", "sys[2]"] - expected_labels = ["sys1_", "sys2_"] - match resp_fcn, plot_fcn: - case ct.gangof4_response, _: - default_labels = ["P=sys[1]", "P=sys[2]"] # Set line color cplt1 = plot_fcn(*args1, **kwargs, **plot_kwargs, color='r') @@ -486,16 +475,10 @@ def test_mimo_plot_legend_processing(resp_fcn, plot_fcn): @pytest.mark.parametrize("resp_fcn, plot_fcn", resp_plot_fcns) @pytest.mark.usefixtures('mplcleanup') def test_plot_title_processing(resp_fcn, plot_fcn): - # Create some systems to use - sys1 = ct.rss(2, 1, 1, strictly_proper=True, name="sys[1]") - sys1c = ct.rss(4, 1, 1, strictly_proper=True, name="sys[1]_C") - sys2 = ct.rss(2, 1, 1, strictly_proper=True, name="sys[2]") - # Set up arguments args1, args2, argsc, kwargs, meth_kwargs, plot_kwargs, resp_kwargs = \ setup_plot_arguments(resp_fcn, plot_fcn) default_title = "sys[1], sys[2]" - expected_title = "sys1_, sys2_" match resp_fcn, plot_fcn: case ct.gangof4_response, _: default_title = "P=sys[1], C=sys[1]_C, P=sys[2], C=sys[1]_C" @@ -582,11 +565,9 @@ def test_plot_title_processing(resp_fcn, plot_fcn): @pytest.mark.usefixtures('mplcleanup') def test_tickmark_label_processing(plot_fcn): # Generate the response that we will use for plotting - top_row, bot_row = 0, -1 match plot_fcn: case ct.bode_plot: resp = ct.frequency_response(ct.rss(4, 2, 2)) - top_row = 1 case ct.time_response_plot: resp = ct.step_response(ct.rss(4, 2, 2)) case ct.gangof4_plot: @@ -620,20 +601,9 @@ def test_tickmark_label_processing(plot_fcn): @pytest.mark.parametrize("resp_fcn, plot_fcn", resp_plot_fcns) @pytest.mark.usefixtures('mplcleanup', 'editsdefaults') def test_rcParams(resp_fcn, plot_fcn): - # Create some systems to use - sys1 = ct.rss(2, 1, 1, strictly_proper=True, name="sys[1]") - sys1c = ct.rss(4, 1, 1, strictly_proper=True, name="sys[1]_C") - sys2 = ct.rss(2, 1, 1, strictly_proper=True, name="sys[2]") - # Set up arguments args1, args2, argsc, kwargs, meth_kwargs, plot_kwargs, resp_kwargs = \ setup_plot_arguments(resp_fcn, plot_fcn) - default_title = "sys[1], sys[2]" - expected_title = "sys1_, sys2_" - match resp_fcn, plot_fcn: - case ct.gangof4_response, _: - default_title = "P=sys[1], C=sys[1]_C, P=sys[2], C=sys[1]_C" - # Create new set of rcParams my_rcParams = {} for key in ct.ctrlplot.rcParams: diff --git a/control/tests/descfcn_test.py b/control/tests/descfcn_test.py index a5f7a06c2..e91738e82 100644 --- a/control/tests/descfcn_test.py +++ b/control/tests/descfcn_test.py @@ -205,12 +205,12 @@ def test_describing_function_exceptions(): assert saturation(3) == 2 # Turn off the bias check - bias = ct.describing_function(saturation, 0, zero_check=False) + ct.describing_function(saturation, 0, zero_check=False) # Function should evaluate to zero at zero amplitude f = lambda x: x + 0.5 with pytest.raises(ValueError, match="must evaluate to zero"): - bias = ct.describing_function(f, 0, zero_check=True) + ct.describing_function(f, 0, zero_check=True) # Evaluate at a negative amplitude with pytest.raises(ValueError, match="cannot evaluate"): @@ -236,4 +236,4 @@ def test_describing_function_exceptions(): # Describing function plot for non-describing function object resp = ct.frequency_response(H_simple) with pytest.raises(TypeError, match="data must be DescribingFunction"): - cplt = ct.describing_function_plot(resp) + ct.describing_function_plot(resp) diff --git a/control/tests/discrete_test.py b/control/tests/discrete_test.py index 9dbc3eb00..9b87bd61b 100644 --- a/control/tests/discrete_test.py +++ b/control/tests/discrete_test.py @@ -231,14 +231,14 @@ def testisctime(self, tsys): def testAddition(self, tsys): # State space addition - sys = tsys.siso_ss1 + tsys.siso_ss1d - sys = tsys.siso_ss1 + tsys.siso_ss1c - sys = tsys.siso_ss1c + tsys.siso_ss1 - sys = tsys.siso_ss1d + tsys.siso_ss1 - sys = tsys.siso_ss1c + tsys.siso_ss1c - sys = tsys.siso_ss1d + tsys.siso_ss1d - sys = tsys.siso_ss3d + tsys.siso_ss3d - sys = tsys.siso_ss1d + tsys.siso_ss3d + _sys = tsys.siso_ss1 + tsys.siso_ss1d + _sys = tsys.siso_ss1 + tsys.siso_ss1c + _sys = tsys.siso_ss1c + tsys.siso_ss1 + _sys = tsys.siso_ss1d + tsys.siso_ss1 + _sys = tsys.siso_ss1c + tsys.siso_ss1c + _sys = tsys.siso_ss1d + tsys.siso_ss1d + _sys = tsys.siso_ss3d + tsys.siso_ss3d + _sys = tsys.siso_ss1d + tsys.siso_ss3d with pytest.raises(ValueError): StateSpace.__add__(tsys.mimo_ss1c, tsys.mimo_ss1d) @@ -246,14 +246,14 @@ def testAddition(self, tsys): StateSpace.__add__(tsys.mimo_ss1d, tsys.mimo_ss2d) # Transfer function addition - sys = tsys.siso_tf1 + tsys.siso_tf1d - sys = tsys.siso_tf1 + tsys.siso_tf1c - sys = tsys.siso_tf1c + tsys.siso_tf1 - sys = tsys.siso_tf1d + tsys.siso_tf1 - sys = tsys.siso_tf1c + tsys.siso_tf1c - sys = tsys.siso_tf1d + tsys.siso_tf1d - sys = tsys.siso_tf2d + tsys.siso_tf2d - sys = tsys.siso_tf1d + tsys.siso_tf3d + _sys = tsys.siso_tf1 + tsys.siso_tf1d + _sys = tsys.siso_tf1 + tsys.siso_tf1c + _sys = tsys.siso_tf1c + tsys.siso_tf1 + _sys = tsys.siso_tf1d + tsys.siso_tf1 + _sys = tsys.siso_tf1c + tsys.siso_tf1c + _sys = tsys.siso_tf1d + tsys.siso_tf1d + _sys = tsys.siso_tf2d + tsys.siso_tf2d + _sys = tsys.siso_tf1d + tsys.siso_tf3d with pytest.raises(ValueError): TransferFunction.__add__(tsys.siso_tf1c, tsys.siso_tf1d) @@ -261,22 +261,22 @@ def testAddition(self, tsys): TransferFunction.__add__(tsys.siso_tf1d, tsys.siso_tf2d) # State space + transfer function - sys = tsys.siso_ss1c + tsys.siso_tf1c - sys = tsys.siso_tf1c + tsys.siso_ss1c - sys = tsys.siso_ss1d + tsys.siso_tf1d - sys = tsys.siso_tf1d + tsys.siso_ss1d + _sys = tsys.siso_ss1c + tsys.siso_tf1c + _sys = tsys.siso_tf1c + tsys.siso_ss1c + _sys = tsys.siso_ss1d + tsys.siso_tf1d + _sys = tsys.siso_tf1d + tsys.siso_ss1d with pytest.raises(ValueError): TransferFunction.__add__(tsys.siso_tf1c, tsys.siso_ss1d) def testMultiplication(self, tsys): # State space multiplication - sys = tsys.siso_ss1 * tsys.siso_ss1d - sys = tsys.siso_ss1 * tsys.siso_ss1c - sys = tsys.siso_ss1c * tsys.siso_ss1 - sys = tsys.siso_ss1d * tsys.siso_ss1 - sys = tsys.siso_ss1c * tsys.siso_ss1c - sys = tsys.siso_ss1d * tsys.siso_ss1d - sys = tsys.siso_ss1d * tsys.siso_ss3d + _sys = tsys.siso_ss1 * tsys.siso_ss1d + _sys = tsys.siso_ss1 * tsys.siso_ss1c + _sys = tsys.siso_ss1c * tsys.siso_ss1 + _sys = tsys.siso_ss1d * tsys.siso_ss1 + _sys = tsys.siso_ss1c * tsys.siso_ss1c + _sys = tsys.siso_ss1d * tsys.siso_ss1d + _sys = tsys.siso_ss1d * tsys.siso_ss3d with pytest.raises(ValueError): StateSpace.__mul__(tsys.mimo_ss1c, tsys.mimo_ss1d) @@ -284,13 +284,13 @@ def testMultiplication(self, tsys): StateSpace.__mul__(tsys.mimo_ss1d, tsys.mimo_ss2d) # Transfer function multiplication - sys = tsys.siso_tf1 * tsys.siso_tf1d - sys = tsys.siso_tf1 * tsys.siso_tf1c - sys = tsys.siso_tf1c * tsys.siso_tf1 - sys = tsys.siso_tf1d * tsys.siso_tf1 - sys = tsys.siso_tf1c * tsys.siso_tf1c - sys = tsys.siso_tf1d * tsys.siso_tf1d - sys = tsys.siso_tf1d * tsys.siso_tf3d + _sys = tsys.siso_tf1 * tsys.siso_tf1d + _sys = tsys.siso_tf1 * tsys.siso_tf1c + _sys = tsys.siso_tf1c * tsys.siso_tf1 + _sys = tsys.siso_tf1d * tsys.siso_tf1 + _sys = tsys.siso_tf1c * tsys.siso_tf1c + _sys = tsys.siso_tf1d * tsys.siso_tf1d + _sys = tsys.siso_tf1d * tsys.siso_tf3d with pytest.raises(ValueError): TransferFunction.__mul__(tsys.siso_tf1c, tsys.siso_tf1d) @@ -298,10 +298,10 @@ def testMultiplication(self, tsys): TransferFunction.__mul__(tsys.siso_tf1d, tsys.siso_tf2d) # State space * transfer function - sys = tsys.siso_ss1c * tsys.siso_tf1c - sys = tsys.siso_tf1c * tsys.siso_ss1c - sys = tsys.siso_ss1d * tsys.siso_tf1d - sys = tsys.siso_tf1d * tsys.siso_ss1d + _sys = tsys.siso_ss1c * tsys.siso_tf1c + _sys = tsys.siso_tf1c * tsys.siso_ss1c + _sys = tsys.siso_ss1d * tsys.siso_tf1d + _sys = tsys.siso_tf1d * tsys.siso_ss1d with pytest.raises(ValueError): TransferFunction.__mul__(tsys.siso_tf1c, tsys.siso_ss1d) @@ -309,13 +309,13 @@ def testMultiplication(self, tsys): def testFeedback(self, tsys): # State space feedback - sys = feedback(tsys.siso_ss1, tsys.siso_ss1d) - sys = feedback(tsys.siso_ss1, tsys.siso_ss1c) - sys = feedback(tsys.siso_ss1c, tsys.siso_ss1) - sys = feedback(tsys.siso_ss1d, tsys.siso_ss1) - sys = feedback(tsys.siso_ss1c, tsys.siso_ss1c) - sys = feedback(tsys.siso_ss1d, tsys.siso_ss1d) - sys = feedback(tsys.siso_ss1d, tsys.siso_ss3d) + _sys = feedback(tsys.siso_ss1, tsys.siso_ss1d) + _sys = feedback(tsys.siso_ss1, tsys.siso_ss1c) + _sys = feedback(tsys.siso_ss1c, tsys.siso_ss1) + _sys = feedback(tsys.siso_ss1d, tsys.siso_ss1) + _sys = feedback(tsys.siso_ss1c, tsys.siso_ss1c) + _sys = feedback(tsys.siso_ss1d, tsys.siso_ss1d) + _sys = feedback(tsys.siso_ss1d, tsys.siso_ss3d) with pytest.raises(ValueError): feedback(tsys.mimo_ss1c, tsys.mimo_ss1d) @@ -323,13 +323,13 @@ def testFeedback(self, tsys): feedback(tsys.mimo_ss1d, tsys.mimo_ss2d) # Transfer function feedback - sys = feedback(tsys.siso_tf1, tsys.siso_tf1d) - sys = feedback(tsys.siso_tf1, tsys.siso_tf1c) - sys = feedback(tsys.siso_tf1c, tsys.siso_tf1) - sys = feedback(tsys.siso_tf1d, tsys.siso_tf1) - sys = feedback(tsys.siso_tf1c, tsys.siso_tf1c) - sys = feedback(tsys.siso_tf1d, tsys.siso_tf1d) - sys = feedback(tsys.siso_tf1d, tsys.siso_tf3d) + _sys = feedback(tsys.siso_tf1, tsys.siso_tf1d) + _sys = feedback(tsys.siso_tf1, tsys.siso_tf1c) + _sys = feedback(tsys.siso_tf1c, tsys.siso_tf1) + _sys = feedback(tsys.siso_tf1d, tsys.siso_tf1) + _sys = feedback(tsys.siso_tf1c, tsys.siso_tf1c) + _sys = feedback(tsys.siso_tf1d, tsys.siso_tf1d) + _sys = feedback(tsys.siso_tf1d, tsys.siso_tf3d) with pytest.raises(ValueError): feedback(tsys.siso_tf1c, tsys.siso_tf1d) @@ -337,10 +337,11 @@ def testFeedback(self, tsys): feedback(tsys.siso_tf1d, tsys.siso_tf2d) # State space, transfer function - sys = feedback(tsys.siso_ss1c, tsys.siso_tf1c) - sys = feedback(tsys.siso_tf1c, tsys.siso_ss1c) - sys = feedback(tsys.siso_ss1d, tsys.siso_tf1d) - sys = feedback(tsys.siso_tf1d, tsys.siso_ss1d) + _sys = feedback(tsys.siso_ss1c, tsys.siso_tf1c) + _sys = feedback(tsys.siso_tf1c, tsys.siso_ss1c) + _sys = feedback(tsys.siso_ss1d, tsys.siso_tf1d) + + _sys = feedback(tsys.siso_tf1d, tsys.siso_ss1d) with pytest.raises(ValueError): feedback(tsys.siso_tf1c, tsys.siso_ss1d) @@ -416,11 +417,11 @@ def test_sample_system_prewarp_warning(self, tsys, plantname, discretization_typ wwarp = 1 Ts = 0.1 with pytest.warns(UserWarning, match="prewarp_frequency ignored: incompatible conversion"): - plant_d_warped = plant.sample(Ts, discretization_type, prewarp_frequency=wwarp) + plant.sample(Ts, discretization_type, prewarp_frequency=wwarp) with pytest.warns(UserWarning, match="prewarp_frequency ignored: incompatible conversion"): - plant_d_warped = sample_system(plant, Ts, discretization_type, prewarp_frequency=wwarp) + sample_system(plant, Ts, discretization_type, prewarp_frequency=wwarp) with pytest.warns(UserWarning, match="prewarp_frequency ignored: incompatible conversion"): - plant_d_warped = c2d(plant, Ts, discretization_type, prewarp_frequency=wwarp) + c2d(plant, Ts, discretization_type, prewarp_frequency=wwarp) def test_sample_system_errors(self, tsys): # Check errors diff --git a/control/tests/flatsys_test.py b/control/tests/flatsys_test.py index 10c512bca..c53cf2e9c 100644 --- a/control/tests/flatsys_test.py +++ b/control/tests/flatsys_test.py @@ -543,7 +543,6 @@ def test_point_to_point_errors(self): x0 = [1, 0]; u0 = [0] xf = [0, 0]; uf = [0] Tf = 10 - T = np.linspace(0, Tf, 500) # Cost function timepts = np.linspace(0, Tf, 10) @@ -658,7 +657,6 @@ def test_solve_flat_ocp_errors(self): x0 = [1, 0]; u0 = [0] xf = [0, 0]; uf = [0] Tf = 10 - T = np.linspace(0, Tf, 500) # Cost function timepts = np.linspace(0, Tf, 10) diff --git a/control/tests/frd_test.py b/control/tests/frd_test.py index 54cc94b51..f19396ae0 100644 --- a/control/tests/frd_test.py +++ b/control/tests/frd_test.py @@ -362,7 +362,6 @@ def testAgainstOctave(self): np.array([[1.0, 0], [0, 0], [0, 1]]), np.eye(3), np.zeros((3, 2))) omega = np.logspace(-1, 2, 10) - chkpts = omega[::3] f1 = frd(sys, omega) np.testing.assert_array_almost_equal( (f1.frequency_response([1.0])[0] * @@ -379,13 +378,13 @@ def test_frequency_mismatch(self, recwarn): sys1 = frd([1, 2, 3], [4, 5, 6]) sys2 = frd([2, 3, 4], [5, 6, 7]) with pytest.raises(NotImplementedError): - sys = sys1 + sys2 + sys1 + sys2 # One frequency range is a subset of another sys1 = frd([1, 2, 3], [4, 5, 6]) sys2 = frd([2, 3], [4, 5]) with pytest.raises(NotImplementedError): - sys = sys1 + sys2 + sys1 + sys2 def test_size_mismatch(self): sys1 = frd(ct.rss(2, 2, 2), np.logspace(-1, 1, 10)) @@ -393,16 +392,16 @@ def test_size_mismatch(self): # Different number of inputs sys2 = frd(ct.rss(3, 1, 2), np.logspace(-1, 1, 10)) with pytest.raises(ValueError): - sys = sys1 + sys2 + sys1 + sys2 # Different number of outputs sys2 = frd(ct.rss(3, 2, 1), np.logspace(-1, 1, 10)) with pytest.raises(ValueError): - sys = sys1 + sys2 + sys1 + sys2 # Inputs and outputs don't match with pytest.raises(ValueError): - sys = sys2 * sys1 + sys2 * sys1 # Feedback mismatch with pytest.raises(ValueError): @@ -801,9 +800,9 @@ def test_unrecognized_keyword(self): h = TransferFunction([1], [1, 2, 2]) omega = np.logspace(-1, 2, 10) with pytest.raises(TypeError, match="unrecognized keyword"): - sys = FrequencyResponseData(h, omega, unknown=None) + FrequencyResponseData(h, omega, unknown=None) with pytest.raises(TypeError, match="unrecognized keyword"): - sys = ct.frd(h, omega, unknown=None) + ct.frd(h, omega, unknown=None) def test_named_signals(): diff --git a/control/tests/freqplot_test.py b/control/tests/freqplot_test.py index 0b951865a..4ca97b840 100644 --- a/control/tests/freqplot_test.py +++ b/control/tests/freqplot_test.py @@ -167,7 +167,7 @@ def test_line_styles(plt_fcn): sys3 = ct.tf([0.2, 0.1], [1, 0.1, 0.3, 0.1, 0.1], name='sys3') # Create a plot for the first system, with custom styles - lines_default = plt_fcn(sys1) + plt_fcn(sys1) # Now create a plot using *fmt customization lines_fmt = plt_fcn(sys2, None, 'r--') diff --git a/control/tests/interconnect_test.py b/control/tests/interconnect_test.py index d124859fc..e4f8c6e07 100644 --- a/control/tests/interconnect_test.py +++ b/control/tests/interconnect_test.py @@ -46,15 +46,15 @@ def test_summing_junction(inputs, output, dimension, D): def test_summation_exceptions(): # Bad input description with pytest.raises(ValueError, match="could not parse input"): - sumblk = ct.summing_junction(np.pi, 'y') + ct.summing_junction(np.pi, 'y') # Bad output description with pytest.raises(ValueError, match="could not parse output"): - sumblk = ct.summing_junction('u', np.pi) + ct.summing_junction('u', np.pi) # Bad input dimension with pytest.raises(ValueError, match="unrecognized dimension"): - sumblk = ct.summing_junction('u', 'y', dimension=False) + ct.summing_junction('u', 'y', dimension=False) @pytest.mark.parametrize("dim", [1, 3]) @@ -346,7 +346,7 @@ def test_interconnect_exceptions(): # NonlinearIOSytem with pytest.raises(TypeError, match="unrecognized keyword"): - nlios = ct.NonlinearIOSystem( + ct.NonlinearIOSystem( None, lambda t, x, u, params: u*u, input_count=1, output_count=1) # Summing junction diff --git a/control/tests/iosys_test.py b/control/tests/iosys_test.py index 1eb1a1fdf..535bb9551 100644 --- a/control/tests/iosys_test.py +++ b/control/tests/iosys_test.py @@ -1412,7 +1412,7 @@ def test_operand_incompatible(self, Pout, Pin, C, op): C = ct.rss(2, 2, 3) with pytest.raises(ValueError, match="incompatible"): - PC = op(P, C) + op(P, C) @pytest.mark.parametrize( "C, op", [ @@ -1709,9 +1709,9 @@ def test_interconnect_unused_input(): with pytest.warns( UserWarning, match=r"Unused input\(s\) in InterconnectedSystem"): - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y']) + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y']) with warnings.catch_warnings(): # no warning if output explicitly ignored, various argument forms @@ -1719,45 +1719,43 @@ def test_interconnect_unused_input(): # strip out matrix warnings warnings.filterwarnings("ignore", "the matrix subclass", category=PendingDeprecationWarning) - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_inputs=['n']) + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_inputs=['n']) - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_inputs=['s.n']) + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_inputs=['s.n']) # no warning if auto-connect disabled - h = ct.interconnect([g,s,k], - connections=False) + ct.interconnect([g,s,k], + connections=False) # warn if explicity ignored input in fact used with pytest.warns( UserWarning, - match=r"Input\(s\) specified as ignored is \(are\) used:") \ - as record: - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_inputs=['u','n']) + match=r"Input\(s\) specified as ignored is \(are\) used:"): + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_inputs=['u','n']) with pytest.warns( UserWarning, - match=r"Input\(s\) specified as ignored is \(are\) used:") \ - as record: - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_inputs=['k.e','n']) + match=r"Input\(s\) specified as ignored is \(are\) used:"): + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_inputs=['k.e','n']) # error if ignored signal doesn't exist with pytest.raises(ValueError): - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_inputs=['v']) + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_inputs=['v']) def test_interconnect_unused_output(): @@ -1779,10 +1777,10 @@ def test_interconnect_unused_output(): with pytest.warns( UserWarning, - match=r"Unused output\(s\) in InterconnectedSystem:") as record: - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y']) + match=r"Unused output\(s\) in InterconnectedSystem:"): + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y']) # no warning if output explicitly ignored @@ -1791,43 +1789,43 @@ def test_interconnect_unused_output(): # strip out matrix warnings warnings.filterwarnings("ignore", "the matrix subclass", category=PendingDeprecationWarning) - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_outputs=['dy']) + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_outputs=['dy']) - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_outputs=['g.dy']) + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_outputs=['g.dy']) # no warning if auto-connect disabled - h = ct.interconnect([g,s,k], - connections=False) + ct.interconnect([g,s,k], + connections=False) # warn if explicity ignored output in fact used with pytest.warns( UserWarning, match=r"Output\(s\) specified as ignored is \(are\) used:"): - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_outputs=['dy','u']) + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_outputs=['dy','u']) with pytest.warns( UserWarning, match=r"Output\(s\) specified as ignored is \(are\) used:"): - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_outputs=['dy', ('k.u')]) + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_outputs=['dy', ('k.u')]) # error if ignored signal doesn't exist with pytest.raises(ValueError): - h = ct.interconnect([g,s,k], - inputs=['r'], - outputs=['y'], - ignore_outputs=['v']) + ct.interconnect([g,s,k], + inputs=['r'], + outputs=['y'], + ignore_outputs=['v']) def test_interconnect_add_unused(): @@ -1900,11 +1898,11 @@ def test_input_output_broadcasting(): # Specify only some of the initial conditions with pytest.warns(UserWarning, match="X0 too short; padding"): - resp_short = ct.input_output_response(sys, T, [U[0], [0, 1]], [X0, 1]) + ct.input_output_response(sys, T, [U[0], [0, 1]], [X0, 1]) # Make sure that inconsistent settings don't work with pytest.raises(ValueError, match="inconsistent"): - resp_bad = ct.input_output_response( + ct.input_output_response( sys, T, (U[0, :], U[:2, :-1]), [X0, P0]) @pytest.mark.parametrize("nstates, ninputs, noutputs", [ diff --git a/control/tests/lti_test.py b/control/tests/lti_test.py index 9edf09013..661b7cd70 100644 --- a/control/tests/lti_test.py +++ b/control/tests/lti_test.py @@ -26,10 +26,10 @@ def test_poles(self, fun, args): np.testing.assert_allclose(poles(sys), 42) with pytest.raises(AttributeError, match="no attribute 'pole'"): - pole_list = sys.pole() + sys.pole() with pytest.raises(AttributeError, match="no attribute 'pole'"): - pole_list = ct.pole(sys) + ct.pole(sys) @pytest.mark.parametrize("fun, args", [ [tf, (126, [-1, 42])], @@ -41,10 +41,10 @@ def test_zeros(self, fun, args): np.testing.assert_allclose(zeros(sys), 42) with pytest.raises(AttributeError, match="no attribute 'zero'"): - zero_list = sys.zero() + sys.zero() with pytest.raises(AttributeError, match="no attribute 'zero'"): - zero_list = ct.zero(sys) + ct.zero(sys) def test_issiso(self): assert issiso(1) @@ -295,7 +295,7 @@ def test_squeeze_exceptions(self, fcn): sys = fcn(ct.rss(2, 1, 1)) with pytest.raises(ValueError, match="unknown squeeze value"): - resp = sys.frequency_response([1], squeeze='siso') + sys.frequency_response([1], squeeze='siso') with pytest.raises(ValueError, match="unknown squeeze value"): sys([1j], squeeze='siso') with pytest.raises(ValueError, match="unknown squeeze value"): diff --git a/control/tests/matlab_test.py b/control/tests/matlab_test.py index b7e0d25d2..e4de5bd9d 100644 --- a/control/tests/matlab_test.py +++ b/control/tests/matlab_test.py @@ -130,33 +130,33 @@ def mimo(self): def testParallel(self, siso): """Call parallel()""" - sys1 = parallel(siso.ss1, siso.ss2) - sys1 = parallel(siso.ss1, siso.tf2) - sys1 = parallel(siso.tf1, siso.ss2) - sys1 = parallel(1, siso.ss2) - sys1 = parallel(1, siso.tf2) - sys1 = parallel(siso.ss1, 1) - sys1 = parallel(siso.tf1, 1) + _sys1 = parallel(siso.ss1, siso.ss2) + _sys1 = parallel(siso.ss1, siso.tf2) + _sys1 = parallel(siso.tf1, siso.ss2) + _sys1 = parallel(1, siso.ss2) + _sys1 = parallel(1, siso.tf2) + _sys1 = parallel(siso.ss1, 1) + _sys1 = parallel(siso.tf1, 1) def testSeries(self, siso): """Call series()""" - sys1 = series(siso.ss1, siso.ss2) - sys1 = series(siso.ss1, siso.tf2) - sys1 = series(siso.tf1, siso.ss2) - sys1 = series(1, siso.ss2) - sys1 = series(1, siso.tf2) - sys1 = series(siso.ss1, 1) - sys1 = series(siso.tf1, 1) + _sys1 = series(siso.ss1, siso.ss2) + _sys1 = series(siso.ss1, siso.tf2) + _sys1 = series(siso.tf1, siso.ss2) + _sys1 = series(1, siso.ss2) + _sys1 = series(1, siso.tf2) + _sys1 = series(siso.ss1, 1) + _sys1 = series(siso.tf1, 1) def testFeedback(self, siso): """Call feedback()""" - sys1 = feedback(siso.ss1, siso.ss2) - sys1 = feedback(siso.ss1, siso.tf2) - sys1 = feedback(siso.tf1, siso.ss2) - sys1 = feedback(1, siso.ss2) - sys1 = feedback(1, siso.tf2) - sys1 = feedback(siso.ss1, 1) - sys1 = feedback(siso.tf1, 1) + _sys1 = feedback(siso.ss1, siso.ss2) + _sys1 = feedback(siso.ss1, siso.tf2) + _sys1 = feedback(siso.tf1, siso.ss2) + _sys1 = feedback(1, siso.ss2) + _sys1 = feedback(1, siso.tf2) + _sys1 = feedback(siso.ss1, 1) + _sys1 = feedback(siso.tf1, 1) def testPoleZero(self, siso): """Call pole() and zero()""" diff --git a/control/tests/namedio_test.py b/control/tests/namedio_test.py index 34feb5b35..9ef0e04dc 100644 --- a/control/tests/namedio_test.py +++ b/control/tests/namedio_test.py @@ -285,7 +285,7 @@ def test_duplicate_sysname(): # strip out matrix warnings warnings.filterwarnings("ignore", "the matrix subclass", category=PendingDeprecationWarning) - res = sys * sys + sys * sys # Generate a warning if the system is named sys = ct.rss(4, 1, 1) @@ -293,7 +293,7 @@ def test_duplicate_sysname(): sys.updfcn, sys.outfcn, inputs=sys.ninputs, outputs=sys.noutputs, states=sys.nstates, name='sys') with pytest.warns(UserWarning, match="duplicate object found"): - res = sys * sys + sys * sys # Finding signals @@ -332,10 +332,10 @@ def test_find_signals(): # Invalid signal names def test_invalid_signal_names(): with pytest.raises(ValueError, match="invalid signal name"): - sys = ct.rss(4, inputs="input.signal", outputs=1) + ct.rss(4, inputs="input.signal", outputs=1) with pytest.raises(ValueError, match="invalid system name"): - sys = ct.rss(4, inputs=1, outputs=1, name="system.subsys") + ct.rss(4, inputs=1, outputs=1, name="system.subsys") # Negative system spect diff --git a/control/tests/nlsys_test.py b/control/tests/nlsys_test.py index 4b1a235c0..b14a619e0 100644 --- a/control/tests/nlsys_test.py +++ b/control/tests/nlsys_test.py @@ -98,7 +98,7 @@ def test_nlsys_impulse(): # Impulse_response (not implemented) with pytest.raises(ValueError, match="system must be LTI"): - resp_nl = ct.impulse_response(sys_nl, timepts) + ct.impulse_response(sys_nl, timepts) # Test nonlinear systems that are missing inputs or outputs diff --git a/control/tests/nyquist_test.py b/control/tests/nyquist_test.py index 3b27ee27c..42bb210c4 100644 --- a/control/tests/nyquist_test.py +++ b/control/tests/nyquist_test.py @@ -436,7 +436,7 @@ def test_nyquist_legacy(): sys = (0.02 * s**3 - 0.1 * s) / (s**4 + s**3 + s**2 + 0.25 * s + 0.04) with pytest.warns(UserWarning, match="indented contour may miss"): - response = ct.nyquist_plot(sys) + ct.nyquist_plot(sys) def test_discrete_nyquist(): # TODO: add tests to make sure plots make sense @@ -512,7 +512,7 @@ def test_nyquist_frd(): # Computing Nyquist response w/ different frequencies OK if given as a list nyqresp = ct.nyquist_response([sys1, sys2]) - cplt = nyqresp.plot() + nyqresp.plot() warnings.resetwarnings() @@ -522,7 +522,7 @@ def test_no_indent_pole(): sys = ((1 + 5/s)/(1 + 0.5/s))**2 # Double-Lag-Compensator with pytest.raises(RuntimeError, match="evaluate at a pole"): - resp = ct.nyquist_response( + ct.nyquist_response( sys, warn_encirclements=False, indent_direction='none') diff --git a/control/tests/optimal_test.py b/control/tests/optimal_test.py index 4ea436515..5546739a1 100644 --- a/control/tests/optimal_test.py +++ b/control/tests/optimal_test.py @@ -81,7 +81,7 @@ def test_finite_horizon_simple(method): sys, time, x0, cost, constraints, squeeze=True, trajectory_method=method, terminal_cost=cost) # include to match MPT3 formulation - t, u_openloop = res.time, res.inputs + _t, u_openloop = res.time, res.inputs np.testing.assert_almost_equal( u_openloop, [-1, -1, 0.1393, 0.3361, -5.204e-16], decimal=4) @@ -264,7 +264,7 @@ def test_mpc_iosystem_continuous(): # Continuous time MPC controller not implemented with pytest.raises(NotImplementedError): - ctrl = opt.create_mpc_iosystem(sys, T, cost) + opt.create_mpc_iosystem(sys, T, cost) # Test various constraint combinations; need to use a somewhat convoluted @@ -315,7 +315,7 @@ def test_constraint_specification(constraint_list): # Compute optimal control and compare against MPT3 solution x0 = [4, 0] res = optctrl.compute_trajectory(x0, squeeze=True) - t, u_openloop = res.time, res.inputs + _t, u_openloop = res.time, res.inputs np.testing.assert_almost_equal( u_openloop, [-1, -1, 0.1393, 0.3361, -5.204e-16], decimal=3) @@ -352,7 +352,7 @@ def test_terminal_constraints(sys_args): # Find a path to the origin x0 = np.array([4, 3]) res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True) - t, u1, x1 = res.time, res.inputs, res.states + _t, u1, x1 = res.time, res.inputs, res.states # Bug prior to SciPy 1.6 will result in incorrect results if NumpyVersion(sp.__version__) < '1.6.0': @@ -401,7 +401,7 @@ def test_terminal_constraints(sys_args): # Find a path to the origin res = optctrl.compute_trajectory( x0, squeeze=True, return_x=True, initial_guess=u1) - t, u2, x2 = res.time, res.inputs, res.states + _t, u2, x2 = res.time, res.inputs, res.states # Not all configurations are able to converge (?) if res.success: @@ -416,7 +416,7 @@ def test_terminal_constraints(sys_args): optctrl = opt.OptimalControlProblem( sys, time, cost, constraints, terminal_constraints=final_point) res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True) - t, u3, x3 = res.time, res.inputs, res.states + _t, u3, x3 = res.time, res.inputs, res.states # Check the answers only if we converged if res.success: @@ -448,7 +448,7 @@ def test_optimal_logging(capsys): # Solve it, with logging turned on (with warning due to mixed constraints) with pytest.warns(sp.optimize.OptimizeWarning, match="Equality and inequality .* same element"): - res = opt.solve_optimal_trajectory( + opt.solve_optimal_trajectory( sys, time, x0, cost, input_constraint, terminal_cost=cost, terminal_constraints=state_constraint, log=True) @@ -513,21 +513,21 @@ def test_ocp_argument_errors(): # Trajectory constraints not in the right form with pytest.raises(TypeError, match="constraints must be a list"): - res = opt.solve_optimal_trajectory(sys, time, x0, cost, np.eye(2)) + opt.solve_optimal_trajectory(sys, time, x0, cost, np.eye(2)) # Terminal constraints not in the right form with pytest.raises(TypeError, match="constraints must be a list"): - res = opt.solve_optimal_trajectory( + opt.solve_optimal_trajectory( sys, time, x0, cost, constraints, terminal_constraints=np.eye(2)) # Initial guess in the wrong shape with pytest.raises(ValueError, match="initial guess is the wrong shape"): - res = opt.solve_optimal_trajectory( + opt.solve_optimal_trajectory( sys, time, x0, cost, constraints, initial_guess=np.zeros((4,1,1))) # Unrecognized arguments with pytest.raises(TypeError, match="unrecognized keyword"): - res = opt.solve_optimal_trajectory( + opt.solve_optimal_trajectory( sys, time, x0, cost, constraints, terminal_constraint=None) with pytest.raises(TypeError, match="unrecognized keyword"): @@ -541,21 +541,21 @@ def test_ocp_argument_errors(): # Unrecognized trajectory constraint type constraints = [(None, np.eye(3), [0, 0, 0], [0, 0, 0])] with pytest.raises(TypeError, match="unknown trajectory constraint type"): - res = opt.solve_optimal_trajectory( + opt.solve_optimal_trajectory( sys, time, x0, cost, trajectory_constraints=constraints) # Unrecognized terminal constraint type with pytest.raises(TypeError, match="unknown terminal constraint type"): - res = opt.solve_optimal_trajectory( + opt.solve_optimal_trajectory( sys, time, x0, cost, terminal_constraints=constraints) # Discrete time system checks: solve_ivp keywords not allowed sys = ct.rss(2, 1, 1, dt=True) with pytest.raises(TypeError, match="solve_ivp method, kwargs not allowed"): - res = opt.solve_optimal_trajectory( + opt.solve_optimal_trajectory( sys, time, x0, cost, solve_ivp_method='LSODA') with pytest.raises(TypeError, match="solve_ivp method, kwargs not allowed"): - res = opt.solve_optimal_trajectory( + opt.solve_optimal_trajectory( sys, time, x0, cost, solve_ivp_kwargs={'eps': 0.1}) @@ -629,7 +629,7 @@ def test_equality_constraints(): # Find a path to the origin x0 = np.array([4, 3]) res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True) - t, u1, x1 = res.time, res.inputs, res.states + _t, u1, x1 = res.time, res.inputs, res.states # Bug prior to SciPy 1.6 will result in incorrect results if NumpyVersion(sp.__version__) < '1.6.0': @@ -649,7 +649,7 @@ def final_point_eval(x, u): # Find a path to the origin x0 = np.array([4, 3]) res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True) - t, u2, x2 = res.time, res.inputs, res.states + _t, u2, x2 = res.time, res.inputs, res.states np.testing.assert_almost_equal(x2[:,-1], 0, decimal=4) np.testing.assert_almost_equal(u1, u2) np.testing.assert_almost_equal(x1, x2) @@ -794,7 +794,7 @@ def test_oep_argument_errors(): # Unrecognized arguments with pytest.raises(TypeError, match="unrecognized keyword"): - res = opt.solve_optimal_estimate(sys, timepts, Y, U, cost, unknown=True) + opt.solve_optimal_estimate(sys, timepts, Y, U, cost, unknown=True) with pytest.raises(TypeError, match="unrecognized keyword"): oep = opt.OptimalEstimationProblem(sys, timepts, cost, unknown=True) @@ -807,4 +807,4 @@ def test_oep_argument_errors(): # Incorrect number of signals with pytest.raises(ValueError, match="incorrect length"): oep = opt.OptimalEstimationProblem(sys, timepts, cost) - mhe = oep.create_mhe_iosystem(estimate_labels=['x1', 'x2', 'x3']) + oep.create_mhe_iosystem(estimate_labels=['x1', 'x2', 'x3']) diff --git a/control/tests/phaseplot_test.py b/control/tests/phaseplot_test.py index 106dee6f0..db6c61dfc 100644 --- a/control/tests/phaseplot_test.py +++ b/control/tests/phaseplot_test.py @@ -123,11 +123,11 @@ def test_helper_functions(func, args, kwargs): sys = ct.nlsys( lambda t, x, u, params: [x[0] - 3*x[1], -3*x[0] + x[1]], states=2, inputs=0) - out = func(sys, [-1, 1, -1, 1], *args, **kwargs) + _out = func(sys, [-1, 1, -1, 1], *args, **kwargs) # Test with function rhsfcn = lambda t, x: sys.dynamics(t, x, 0, {}) - out = func(rhsfcn, [-1, 1, -1, 1], *args, **kwargs) + _out = func(rhsfcn, [-1, 1, -1, 1], *args, **kwargs) @pytest.mark.usefixtures('mplcleanup') diff --git a/control/tests/pzmap_test.py b/control/tests/pzmap_test.py index 438732b84..64bbdee3e 100644 --- a/control/tests/pzmap_test.py +++ b/control/tests/pzmap_test.py @@ -111,10 +111,10 @@ def test_pzmap_raises(): sys1 = ct.rss(2, 1, 1) sys2 = sys1.sample(0.1) with pytest.raises(ValueError, match="incompatible time bases"): - pzdata = ct.pole_zero_plot([sys1, sys2], grid=True) + ct.pole_zero_plot([sys1, sys2], grid=True) with pytest.warns(UserWarning, match="axis already exists"): - fig, ax = plt.figure(), plt.axes() + _fig, ax = plt.figure(), plt.axes() ct.pole_zero_plot(sys1, ax=ax, grid='empty') diff --git a/control/tests/statefbk_test.py b/control/tests/statefbk_test.py index ebf531546..3f4b4849a 100644 --- a/control/tests/statefbk_test.py +++ b/control/tests/statefbk_test.py @@ -860,7 +860,7 @@ def test_lqr_integral_discrete(self): K, _, _ = ct.lqr( sys, np.eye(sys.nstates + nintegrators), np.eye(sys.ninputs), integral_action=C_int) - Kp, Ki = K[:, :sys.nstates], K[:, sys.nstates:] + Kp, _Ki = K[:, :sys.nstates], K[:, sys.nstates:] # Create an I/O system for the controller ctrl, clsys = ct.create_statefbk_iosystem( @@ -1237,20 +1237,10 @@ def test_create_statefbk_errors(): def test_create_statefbk_params(unicycle): - # Speeds and angles at which to compute the gains - speeds = [1, 5, 10] - angles = np.linspace(0, pi/2, 4) - points = list(itertools.product(speeds, angles)) - - # Gains for each speed (using LQR controller) Q = np.identity(unicycle.nstates) R = np.identity(unicycle.ninputs) gain, _, _ = ct.lqr(unicycle.linearize([0, 0, 0], [5, 0]), Q, R) - # - # Schedule on desired speed and angle - # - # Create a linear controller ctrl, clsys = ct.create_statefbk_iosystem(unicycle, gain) assert [k for k in ctrl.params.keys()] == [] diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index b0ddea616..03aeafcd7 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -144,7 +144,7 @@ def test_constructor(self, sys322ABCD, dt, argfun): def test_constructor_invalid(self, args, exc, errmsg): """Test invalid input to StateSpace() constructor""" - with pytest.raises(exc, match=errmsg) as w: + with pytest.raises(exc, match=errmsg): StateSpace(*args) with pytest.raises(exc, match=errmsg): ss(*args) diff --git a/control/tests/stochsys_test.py b/control/tests/stochsys_test.py index 0bbf49b57..dae282f76 100644 --- a/control/tests/stochsys_test.py +++ b/control/tests/stochsys_test.py @@ -225,26 +225,25 @@ def test_estimator_iosys_ctime(sys_args): def test_estimator_errors(): sys = ct.drss(4, 2, 2, strictly_proper=True) - P0 = np.eye(sys.nstates) QN = np.eye(sys.ninputs) RN = np.eye(sys.noutputs) with pytest.raises(TypeError, match="unrecognized keyword"): - estim = ct.create_estimator_iosystem(sys, QN, RN, unknown=True) + ct.create_estimator_iosystem(sys, QN, RN, unknown=True) with pytest.raises(ct.ControlArgument, match=".* system must be a linear"): sys_tf = ct.tf([1], [1, 1], dt=True) - estim = ct.create_estimator_iosystem(sys_tf, QN, RN) + ct.create_estimator_iosystem(sys_tf, QN, RN) with pytest.raises(ValueError, match="output must be full state"): C = np.eye(2, 4) - estim = ct.create_estimator_iosystem(sys, QN, RN, C=C) + ct.create_estimator_iosystem(sys, QN, RN, C=C) with pytest.raises(ValueError, match="output is the wrong size"): sys_fs = ct.drss(4, 4, 2, strictly_proper=True) sys_fs.C = np.eye(4) C = np.eye(1, 4) - estim = ct.create_estimator_iosystem(sys_fs, QN, RN, C=C) + ct.create_estimator_iosystem(sys_fs, QN, RN, C=C) def test_white_noise(): @@ -426,7 +425,6 @@ def test_mhe(): V = np.array( [0 if i % 2 == 1 else 1 if i % 4 == 0 else -1 for i, t in enumerate(timepts)]).reshape(1, -1) * 0.1 - W = np.sin(timepts / dt) * 1e-3 # Create a moving horizon estimator traj_cost = opt.gaussian_likelihood_cost(sys, Rv, Rw) diff --git a/control/tests/timeplot_test.py b/control/tests/timeplot_test.py index 9525c7e02..c0531d367 100644 --- a/control/tests/timeplot_test.py +++ b/control/tests/timeplot_test.py @@ -312,15 +312,15 @@ def test_combine_time_responses(): with pytest.raises(ValueError, match="must have the same number"): resp = ct.step_response(ct.rss(4, 2, 3), timepts) - combresp = ct.combine_time_responses([resp1, resp]) + ct.combine_time_responses([resp1, resp]) with pytest.raises(ValueError, match="trace labels does not match"): - combresp = ct.combine_time_responses( + ct.combine_time_responses( [resp1, resp2], trace_labels=["T1", "T2", "T3"]) with pytest.raises(ValueError, match="must have the same time"): resp = ct.step_response(ct.rss(4, 2, 3), timepts/2) - combresp6 = ct.combine_time_responses([resp1, resp]) + ct.combine_time_responses([resp1, resp]) @pytest.mark.parametrize("resp_fcn", [ @@ -415,13 +415,10 @@ def test_timeplot_trace_labels(resp_fcn): # Figure out the expected shape of the system match resp_fcn: case ct.step_response | ct.impulse_response: - shape = (2, 2) kwargs = {} case ct.initial_response: - shape = (2, 1) kwargs = {} case ct.forced_response | ct.input_output_response: - shape = (4, 1) # outputs and inputs both plotted T = np.linspace(0, 10) U = [np.sin(T), np.cos(T)] kwargs = {'T': T, 'U': U} diff --git a/control/tests/timeresp_test.py b/control/tests/timeresp_test.py index a410bf30f..dfacfd51e 100644 --- a/control/tests/timeresp_test.py +++ b/control/tests/timeresp_test.py @@ -1178,7 +1178,6 @@ 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.ones((sys.ninputs, 1)) @ np.reshape(np.sin(tvec), (1, 8)) _, yvec = ct.initial_response(sys, tvec, 1, squeeze=squeeze) assert yvec.shape == shape @@ -1303,7 +1302,7 @@ def test_no_pandas(): # Convert to pandas with pytest.raises(ImportError, match="pandas"): - df = resp.to_pandas() + resp.to_pandas() # https://github.com/python-control/python-control/issues/1014 diff --git a/control/tests/trdata_test.py b/control/tests/trdata_test.py index 7d0c20e7a..b84369d72 100644 --- a/control/tests/trdata_test.py +++ b/control/tests/trdata_test.py @@ -214,7 +214,7 @@ def test_response_copy(): # Unknown keyword with pytest.raises(TypeError, match="unrecognized keywords"): - response_bad_kw = response_mimo(input=0) + response_mimo(input=0) def test_trdata_labels(): From 2a1833171a3e0abf2584246d909e80178780ecd5 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 9 Feb 2025 12:19:06 +0200 Subject: [PATCH 02/22] Remove unused imports Where imports were test fixtures, replaced with `@pytest.mark.usefixtures('nameoffixture')`. --- control/tests/bspline_test.py | 2 -- control/tests/conftest.py | 3 --- control/tests/frd_test.py | 4 +--- control/tests/freqplot_test.py | 1 - control/tests/interconnect_test.py | 1 - control/tests/iosys_test.py | 1 - control/tests/kwargs_test.py | 1 - control/tests/lti_test.py | 2 -- control/tests/matlab2_test.py | 1 - control/tests/modelsimp_test.py | 1 - control/tests/namedio_test.py | 1 - control/tests/phaseplot_test.py | 1 - control/tests/rlocus_test.py | 1 - control/tests/statesp_test.py | 33 ++++++++++++------------------ control/tests/stochsys_test.py | 3 +-- control/tests/timeplot_test.py | 3 +-- control/tests/timeresp_test.py | 1 - 17 files changed, 16 insertions(+), 44 deletions(-) diff --git a/control/tests/bspline_test.py b/control/tests/bspline_test.py index 0494e1252..e15915182 100644 --- a/control/tests/bspline_test.py +++ b/control/tests/bspline_test.py @@ -11,11 +11,9 @@ import numpy as np import pytest -import scipy as sp import control as ct import control.flatsys as fs -import control.optimal as opt def test_bspline_basis(): Tf = 10 diff --git a/control/tests/conftest.py b/control/tests/conftest.py index bf3920a02..c10dcc225 100644 --- a/control/tests/conftest.py +++ b/control/tests/conftest.py @@ -1,8 +1,5 @@ """conftest.py - pytest local plugins, fixtures, marks and functions.""" -import os -from contextlib import contextmanager - import matplotlib as mpl import numpy as np import pytest diff --git a/control/tests/frd_test.py b/control/tests/frd_test.py index f19396ae0..ab5d3bf5e 100644 --- a/control/tests/frd_test.py +++ b/control/tests/frd_test.py @@ -3,8 +3,6 @@ RvP, 4 Oct 2012 """ -import sys as pysys - import numpy as np import matplotlib.pyplot as plt import pytest @@ -13,7 +11,7 @@ from control.statesp import StateSpace from control.xferfcn import TransferFunction from control.frdata import frd, _convert_to_frd, FrequencyResponseData -from control import bdalg, evalfr, freqplot +from control import bdalg, freqplot from control.tests.conftest import slycotonly from control.exception import pandas_check diff --git a/control/tests/freqplot_test.py b/control/tests/freqplot_test.py index 4ca97b840..c0dfa4030 100644 --- a/control/tests/freqplot_test.py +++ b/control/tests/freqplot_test.py @@ -8,7 +8,6 @@ import pytest import control as ct -from control.tests.conftest import editsdefaults, slycotonly pytestmark = pytest.mark.usefixtures("mplcleanup") diff --git a/control/tests/interconnect_test.py b/control/tests/interconnect_test.py index e4f8c6e07..aea3cbbc6 100644 --- a/control/tests/interconnect_test.py +++ b/control/tests/interconnect_test.py @@ -15,7 +15,6 @@ import pytest import numpy as np -import scipy as sp import math import control as ct diff --git a/control/tests/iosys_test.py b/control/tests/iosys_test.py index 535bb9551..c7a9212e0 100644 --- a/control/tests/iosys_test.py +++ b/control/tests/iosys_test.py @@ -18,7 +18,6 @@ import control as ct import control.flatsys as fs -from control.tests.conftest import slycotonly class TestIOSys: diff --git a/control/tests/kwargs_test.py b/control/tests/kwargs_test.py index 2e4919004..2660922f5 100644 --- a/control/tests/kwargs_test.py +++ b/control/tests/kwargs_test.py @@ -13,7 +13,6 @@ import inspect import warnings -import matplotlib.pyplot as plt import pytest import control diff --git a/control/tests/lti_test.py b/control/tests/lti_test.py index 661b7cd70..17dc7796e 100644 --- a/control/tests/lti_test.py +++ b/control/tests/lti_test.py @@ -12,8 +12,6 @@ from control.lti import LTI, bandwidth, damp, dcgain, evalfr, poles, zeros from control.tests.conftest import slycotonly -from .conftest import editsdefaults - class TestLTI: @pytest.mark.parametrize("fun, args", [ diff --git a/control/tests/matlab2_test.py b/control/tests/matlab2_test.py index 4d135e33e..f8b0d2b40 100644 --- a/control/tests/matlab2_test.py +++ b/control/tests/matlab2_test.py @@ -16,7 +16,6 @@ from control.matlab import ss, step, impulse, initial, lsim, dcgain, ss2tf from control.timeresp import _check_convert_array -from control.tests.conftest import slycotonly class TestControlMatlab: diff --git a/control/tests/modelsimp_test.py b/control/tests/modelsimp_test.py index 7dcda6296..043b481ce 100644 --- a/control/tests/modelsimp_test.py +++ b/control/tests/modelsimp_test.py @@ -3,7 +3,6 @@ RMM, 30 Mar 2011 (based on TestModelSimp from v0.4a) """ -import math import warnings import numpy as np diff --git a/control/tests/namedio_test.py b/control/tests/namedio_test.py index 9ef0e04dc..1b80c921c 100644 --- a/control/tests/namedio_test.py +++ b/control/tests/namedio_test.py @@ -8,7 +8,6 @@ created for that purpose. """ -import re from copy import copy import warnings diff --git a/control/tests/phaseplot_test.py b/control/tests/phaseplot_test.py index db6c61dfc..d4287f7cc 100644 --- a/control/tests/phaseplot_test.py +++ b/control/tests/phaseplot_test.py @@ -19,7 +19,6 @@ import control as ct import control.phaseplot as pp from control import phase_plot -from control.tests.conftest import mplcleanup # Legacy tests diff --git a/control/tests/rlocus_test.py b/control/tests/rlocus_test.py index 2e74f8649..4fcd7ee58 100644 --- a/control/tests/rlocus_test.py +++ b/control/tests/rlocus_test.py @@ -161,7 +161,6 @@ def test_rlocus_default_wn(self): # that will take a long time to do the calculation (minutes). # import scipy as sp - import signal # Define a system that exhibits this behavior sys = ct.tf(*sp.signal.zpk2tf( diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index 03aeafcd7..468b6917c 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -8,7 +8,6 @@ """ import operator -import platform import numpy as np import pytest @@ -23,10 +22,7 @@ from control.statesp import StateSpace, _convert_to_statespace, \ _rss_generate, _statesp_defaults, drss, linfnorm, rss, ss, tf2ss from control.xferfcn import TransferFunction, ss2tf - -from .conftest import assert_tf_close_coeff, editsdefaults, \ - ignore_future_warning, slycotonly - +from .conftest import assert_tf_close_coeff class TestStateSpace: """Tests for the StateSpace class.""" @@ -232,7 +228,7 @@ def test_zero_empty(self): sys = _convert_to_statespace(TransferFunction([1], [1, 2, 1])) np.testing.assert_array_equal(sys.zeros(), np.array([])) - @slycotonly + @pytest.mark.usefixtures("slycotonly") def test_zero_siso(self, sys222): """Evaluate the zeros of a SISO system.""" # extract only first input / first output system of sys222. This system is denoted sys111 @@ -262,7 +258,7 @@ def test_zero_mimo_sys222_square(self, sys222): true_z = np.sort([-10.568501, 3.368501]) np.testing.assert_array_almost_equal(z, true_z) - @slycotonly + @pytest.mark.usefixtures("slycotonly") def test_zero_mimo_sys623_non_square(self, sys623): """Evaluate the zeros of a non square MIMO system.""" @@ -409,7 +405,7 @@ def test_add_sub_mimo_siso(self): ss2tf(result).minreal(), ) - @slycotonly + @pytest.mark.usefixtures("slycotonly") @pytest.mark.parametrize( "left, right, expected", [ @@ -484,7 +480,7 @@ def test_mul_mimo_siso(self, left, right, expected): ss2tf(result).minreal(), ) - @slycotonly + @pytest.mark.usefixtures("slycotonly") @pytest.mark.parametrize( "left, right, expected", [ @@ -559,7 +555,7 @@ def test_rmul_mimo_siso(self, left, right, expected): ss2tf(result).minreal(), ) - @slycotonly + @pytest.mark.usefixtures("slycotonly") @pytest.mark.parametrize("power", [0, 1, 3, -3]) @pytest.mark.parametrize("sysname", ["sys222", "sys322"]) def test_pow(self, request, sysname, power): @@ -578,7 +574,7 @@ def test_pow(self, request, sysname, power): np.testing.assert_allclose(expected.C, result.C) np.testing.assert_allclose(expected.D, result.D) - @slycotonly + @pytest.mark.usefixtures("slycotonly") @pytest.mark.parametrize("order", ["left", "right"]) @pytest.mark.parametrize("sysname", ["sys121", "sys222", "sys322"]) def test_pow_inv(self, request, sysname, order): @@ -602,7 +598,7 @@ def test_pow_inv(self, request, sysname, order): # Check that the output is the same as the input np.testing.assert_allclose(R.outputs, U) - @slycotonly + @pytest.mark.usefixtures("slycotonly") def test_truediv(self, sys222, sys322): """Test state space truediv""" for sys in [sys222, sys322]: @@ -621,7 +617,7 @@ def test_truediv(self, sys222, sys322): ss2tf(result).minreal(), ) - @slycotonly + @pytest.mark.usefixtures("slycotonly") def test_rtruediv(self, sys222, sys322): """Test state space rtruediv""" for sys in [sys222, sys322]: @@ -722,7 +718,7 @@ def test_freq_resp(self): mag, phase, omega = sys.freqresp(true_omega) np.testing.assert_almost_equal(mag, true_mag) - @slycotonly + @pytest.mark.usefixtures("slycotonly") def test_minreal(self): """Test a minreal model reduction.""" # A = [-2, 0.5, 0; 0.5, -0.3, 0; 0, 0, -0.1] @@ -1516,16 +1512,14 @@ def dt_siso(self, request): name, systype, sysargs, dt, refgpeak, reffpeak = request.param return ct.c2d(systype(*sysargs), dt), refgpeak, reffpeak - @slycotonly - @pytest.mark.usefixtures('ignore_future_warning') + @pytest.mark.usefixtures('slycotonly', 'ignore_future_warning') def test_linfnorm_ct_siso(self, ct_siso): sys, refgpeak, reffpeak = ct_siso gpeak, fpeak = linfnorm(sys) np.testing.assert_allclose(gpeak, refgpeak) np.testing.assert_allclose(fpeak, reffpeak) - @slycotonly - @pytest.mark.usefixtures('ignore_future_warning') + @pytest.mark.usefixtures('slycotonly', 'ignore_future_warning') def test_linfnorm_dt_siso(self, dt_siso): sys, refgpeak, reffpeak = dt_siso gpeak, fpeak = linfnorm(sys) @@ -1533,8 +1527,7 @@ def test_linfnorm_dt_siso(self, dt_siso): np.testing.assert_allclose(gpeak, refgpeak) np.testing.assert_allclose(fpeak, reffpeak) - @slycotonly - @pytest.mark.usefixtures('ignore_future_warning') + @pytest.mark.usefixtures('slycotonly', 'ignore_future_warning') def test_linfnorm_ct_mimo(self, ct_siso): siso, refgpeak, reffpeak = ct_siso sys = ct.append(siso, siso) diff --git a/control/tests/stochsys_test.py b/control/tests/stochsys_test.py index dae282f76..a6c2d90bc 100644 --- a/control/tests/stochsys_test.py +++ b/control/tests/stochsys_test.py @@ -6,7 +6,7 @@ import control as ct import control.optimal as opt -from control import lqe, dlqe, rss, drss, tf, ss, ControlArgument, slycot_check +from control import lqe, dlqe, rss, tf, ControlArgument, slycot_check from math import log, pi # Utility function to check LQE answer @@ -476,7 +476,6 @@ def test_indices(ctrl_indices, dist_indices): sysm = ct.ss(sys.A, sys.B[:, ctrl_idx], sys.C, sys.D[:, ctrl_idx]) # Set the simulation time based on the slowest system pole - from math import log T = 10 # Generate a system response with no disturbances diff --git a/control/tests/timeplot_test.py b/control/tests/timeplot_test.py index c0531d367..888ff9080 100644 --- a/control/tests/timeplot_test.py +++ b/control/tests/timeplot_test.py @@ -7,8 +7,7 @@ import pytest import control as ct -from control.tests.conftest import mplcleanup, slycotonly - +from control.tests.conftest import slycotonly # Detailed test of (almost) all functionality # diff --git a/control/tests/timeresp_test.py b/control/tests/timeresp_test.py index dfacfd51e..83ea02f32 100644 --- a/control/tests/timeresp_test.py +++ b/control/tests/timeresp_test.py @@ -5,7 +5,6 @@ import numpy as np import pytest -import scipy as sp import control as ct from control import StateSpace, TransferFunction, c2d, isctime, ss2tf, tf2ss From 1bc01197004d3055e9348b29304b323fba4c4399 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 10:10:39 +0200 Subject: [PATCH 03/22] Remove imports needed for `eval` Provide relevant symbols via `locals` argument to eval. --- control/tests/config_test.py | 5 +---- control/tests/namedio_test.py | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/control/tests/config_test.py b/control/tests/config_test.py index 646a20a16..281e4f2fa 100644 --- a/control/tests/config_test.py +++ b/control/tests/config_test.py @@ -339,11 +339,8 @@ def test_system_indexing(self): {'dt': 0.1} ]) def test_repr_format(self, kwargs): - from ..statesp import StateSpace - from numpy import array - sys = ct.ss([[1]], [[1]], [[1]], [[0]], **kwargs) - new = eval(repr(sys)) + new = eval(repr(sys), locals={'StateSpace':ct.StateSpace, 'array':np.array}) for attr in ['A', 'B', 'C', 'D']: assert getattr(new, attr) == getattr(sys, attr) for prop in ['input_labels', 'output_labels', 'state_labels']: diff --git a/control/tests/namedio_test.py b/control/tests/namedio_test.py index 1b80c921c..961237b7a 100644 --- a/control/tests/namedio_test.py +++ b/control/tests/namedio_test.py @@ -366,8 +366,6 @@ def test_negative_system_spec(): # Named signal representations def test_named_signal_repr(): - from numpy import array - from ..iosys import NamedSignal sys = ct.rss( states=2, inputs=['u1', 'u2'], outputs=['y1', 'y2'], state_prefix='xi') @@ -375,6 +373,8 @@ def test_named_signal_repr(): for signal in ['inputs', 'outputs', 'states']: sig_orig = getattr(resp, signal) - sig_eval = eval(repr(sig_orig)) + sig_eval = eval(repr(sig_orig), + locals={'array': np.array, + 'NamedSignal': ct.NamedSignal}) assert sig_eval.signal_labels == sig_orig.signal_labels assert sig_eval.trace_labels == sig_orig.trace_labels From df756eb9b3da3bfe8a66105bd680b8127e952595 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sat, 15 Feb 2025 14:28:48 +0200 Subject: [PATCH 04/22] Fix incorrect variable names in f-strings --- control/tests/ctrlplot_test.py | 2 +- control/tests/docstrings_test.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/control/tests/ctrlplot_test.py b/control/tests/ctrlplot_test.py index 36965f78d..8a24e900e 100644 --- a/control/tests/ctrlplot_test.py +++ b/control/tests/ctrlplot_test.py @@ -514,7 +514,7 @@ def test_plot_title_processing(resp_fcn, plot_fcn): case ct.input_output_response, _: title_prefix = "Input/output response for " case _: - raise RuntimeError(f"didn't recognize {resp_fnc}, {plot_fnc}") + raise RuntimeError(f"didn't recognize {resp_fcn}, {plot_fcn}") # Generate the first plot, with default title cplt1 = plot_fcn(*args1, **kwargs, **plot_kwargs) diff --git a/control/tests/docstrings_test.py b/control/tests/docstrings_test.py index 3b7de8b8c..d13d395d3 100644 --- a/control/tests/docstrings_test.py +++ b/control/tests/docstrings_test.py @@ -309,7 +309,7 @@ def test_deprecated_functions(module, prefix): # Get the docstring (skip w/ warning if there isn't one) if obj.__doc__ is None: - _warn(f"{objname} is missing docstring") + _warn(f"{obj} is missing docstring") continue else: docstring = inspect.getdoc(obj) @@ -320,13 +320,13 @@ def test_deprecated_functions(module, prefix): if ".. deprecated::" in doc_extended: # Make sure a FutureWarning is issued if not re.search("FutureWarning", source): - _fail(f"{objname} deprecated but does not issue " + _fail(f"{obj} deprecated but does not issue " "FutureWarning") else: if re.search(name + r"(\(\))? is deprecated", docstring) or \ re.search(name + r"(\(\))? is deprecated", source): _fail( - f"{objname} deprecated but with non-standard " + f"{obj} deprecated but with non-standard " "docs/warnings") # From 48d61950a419487be70b5061e66b703d9fc2ec2a Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 10:35:02 +0200 Subject: [PATCH 05/22] Apply ruff checks to control/tests/ --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a81fc117c..d3754dc52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,6 @@ filterwarnings = [ # TODO: expand to cover all code include = ['control/**.py'] -exclude = ['control/tests/*.py'] [tool.ruff.lint] select = [ From 20f4b7660b28479c16a48839d29bc251181d4482 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 10:35:21 +0200 Subject: [PATCH 06/22] Remove unused variable fail_if_missing in test_parameter_docs --- control/tests/docstrings_test.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/control/tests/docstrings_test.py b/control/tests/docstrings_test.py index d13d395d3..809eec3a1 100644 --- a/control/tests/docstrings_test.py +++ b/control/tests/docstrings_test.py @@ -143,14 +143,6 @@ def test_parameter_docs(module, prefix): continue # Don't fail on non-top-level functions without parameter lists - # TODO: may be able to delete this - if prefix != "" and inspect.getmodule(obj) != module and \ - doc is not None and doc["Parameters"] == [] and \ - doc["Returns"] == [] and doc["Yields"] == []: - fail_if_missing = False - else: - fail_if_missing = True - _info(f"Checking function {objname} against numpydoc", 2) _check_numpydoc_style(obj, doc) From 3729d8912deddd482fa10086a3d32d47ad33ba8a Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 10:38:49 +0200 Subject: [PATCH 07/22] Remove unused obj and objname in test_iosys_attribute_lists --- control/tests/docstrings_test.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/control/tests/docstrings_test.py b/control/tests/docstrings_test.py index 809eec3a1..d01284e2e 100644 --- a/control/tests/docstrings_test.py +++ b/control/tests/docstrings_test.py @@ -497,13 +497,6 @@ def test_iosys_attribute_lists(cls, ignore_future_warning): # Skip hidden and ignored attributes; methods checked elsewhere continue - # Get the object associated with this attribute - obj = getattr(cls, name, getattr(sys, name)) - if getattr(obj, '__module__', None): - objname = ".".join([obj.__module__.removeprefix("control."), name]) - else: - objname = name - # Try to find documentation in primary class if _check_parameter_docs( cls.__name__, name, docstring, fail_if_missing=False): From 36d5e8a6800632669d45328825c8cbcdc8642802 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 10:39:04 +0200 Subject: [PATCH 08/22] Remove unused variable docstring in test_iosys_container_classes --- control/tests/docstrings_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/control/tests/docstrings_test.py b/control/tests/docstrings_test.py index d01284e2e..ae0fb5001 100644 --- a/control/tests/docstrings_test.py +++ b/control/tests/docstrings_test.py @@ -524,7 +524,6 @@ def test_iosys_container_classes(cls): # Create a system that we can scan for attributes sys = cls(states=2, outputs=1, inputs=1) - docstring = inspect.getdoc(cls) with warnings.catch_warnings(): warnings.simplefilter('ignore') # debug via sphinx, not here doc = npd.FunctionDoc(cls) From 7fe9d4ef4f5db9bbb3bf7894a59ae820b407b6ac Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 12:01:29 +0200 Subject: [PATCH 09/22] Import numpy for symbol np.array Symbol reference in lambda which is never called. --- control/tests/kwargs_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/control/tests/kwargs_test.py b/control/tests/kwargs_test.py index 2660922f5..fcdf2cb68 100644 --- a/control/tests/kwargs_test.py +++ b/control/tests/kwargs_test.py @@ -15,6 +15,8 @@ import pytest +import numpy as np + import control import control.flatsys import control.tests.descfcn_test as descfcn_test From ba33c21e01e220b1587244ecbe76130aa936681e Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 12:02:16 +0200 Subject: [PATCH 10/22] Remove ineffective testFeedback2 in frd_test.py The test had no assertions, but have been intended to test MIMO feedback; for this see testMIMOfb in same file. --- control/tests/frd_test.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/control/tests/frd_test.py b/control/tests/frd_test.py index ab5d3bf5e..8bf606424 100644 --- a/control/tests/frd_test.py +++ b/control/tests/frd_test.py @@ -180,11 +180,6 @@ def testFeedback(self, frd_fcn): f1.feedback().frequency_response(chkpts)[0], h1.feedback().frequency_response(chkpts)[0]) - def testFeedback2(self): - h2 = StateSpace([[-1.0, 0], [0, -2.0]], [[0.4], [0.1]], - [[1.0, 0], [0, 1]], [[0.0], [0.0]]) - # h2.feedback([[0.3, 0.2], [0.1, 0.1]]) - def testAppendSiso(self): # Create frequency responses d1 = np.array([1 + 2j, 1 - 2j, 1 + 4j, 1 - 4j, 1 + 6j, 1 - 6j]) From 2156adf276a2a971393bc49b5ab054aeaa246da6 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 12:05:32 +0200 Subject: [PATCH 11/22] Add assertion in testUnwrap in matlab_test --- control/tests/matlab_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/control/tests/matlab_test.py b/control/tests/matlab_test.py index e4de5bd9d..c6a45e2a2 100644 --- a/control/tests/matlab_test.py +++ b/control/tests/matlab_test.py @@ -582,10 +582,11 @@ def testOpers(self, siso): # siso.tf1 / siso.ss2 def testUnwrap(self): - """Call unwrap()""" + # control.matlab.unwrap phase = np.array(range(1, 100)) / 10. wrapped = phase % (2 * np.pi) unwrapped = unwrap(wrapped) + np.testing.assert_array_almost_equal(phase, unwrapped) def testSISOssdata(self, siso): """Call ssdata() From 69afb8266eb5e4633e8a77c9c592307577c8eb9e Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 12:05:57 +0200 Subject: [PATCH 12/22] Remove unused steady-state output variable --- control/tests/optimal_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/control/tests/optimal_test.py b/control/tests/optimal_test.py index 5546739a1..9677e3d6f 100644 --- a/control/tests/optimal_test.py +++ b/control/tests/optimal_test.py @@ -186,7 +186,6 @@ def test_mpc_iosystem_aircraft(): # compute the steady state values for a particular value of the input ud = np.array([0.8, -0.3]) xd = np.linalg.inv(np.eye(5) - A) @ B @ ud - yd = C @ xd # provide constraints on the system signals constraints = [opt.input_range_constraint(sys, [-5, -6], [5, 6])] From 4e0b9c1a4447a939c775e6916dcf9246fa46e345 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 12:06:23 +0200 Subject: [PATCH 13/22] Mark unused test function with noqa The function doesn't exist, but the test is never called. --- control/tests/rlocus_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control/tests/rlocus_test.py b/control/tests/rlocus_test.py index 4fcd7ee58..4d3a08206 100644 --- a/control/tests/rlocus_test.py +++ b/control/tests/rlocus_test.py @@ -134,7 +134,7 @@ def test_root_locus_zoom(self): ax_rlocus.set_xlim((-10.813628105112421, 14.760795435937652)) ax_rlocus.set_ylim((-35.61713798641108, 33.879716621220311)) plt.get_current_fig_manager().toolbar.mode = 'zoom rect' - _RLClickDispatcher(event, system, fig, ax_rlocus, '-') + _RLClickDispatcher(event, system, fig, ax_rlocus, '-') # noqa: F821 zoom_x = ax_rlocus.lines[-2].get_data()[0][0:5] zoom_y = ax_rlocus.lines[-2].get_data()[1][0:5] From 2d5738a957c6dd46d4ee723cea7d521ff8a9b381 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 12:11:26 +0200 Subject: [PATCH 14/22] Fix bugs and lint errors in test_timeresp_aliases --- control/tests/timeresp_test.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/control/tests/timeresp_test.py b/control/tests/timeresp_test.py index 27da2520b..8bbd27d73 100644 --- a/control/tests/timeresp_test.py +++ b/control/tests/timeresp_test.py @@ -1427,40 +1427,40 @@ def test_timeresp_aliases(): # Aliases resp_short = ct.input_output_response(sys, timepts, 1, X0=[1, 1]) - np.testing.assert_allclose(resp_long.states, resp_posn.states) + np.testing.assert_allclose(resp_long.states, resp_short.states) # Legacy with pytest.warns(PendingDeprecationWarning, match="legacy"): resp_legacy = ct.input_output_response(sys, timepts, 1, x0=[1, 1]) - np.testing.assert_allclose(resp_long.states, resp_posn.states) + np.testing.assert_allclose(resp_long.states, resp_legacy.states) # Check for multiple values: full keyword and alias with pytest.raises(TypeError, match="multiple"): - resp_multiple = ct.input_output_response( + ct.input_output_response( sys, timepts, 1, initial_state=[1, 2], X0=[1, 1]) # Check for multiple values: positional and keyword with pytest.raises(TypeError, match="multiple"): - resp_multiple = ct.input_output_response( + ct.input_output_response( sys, timepts, 1, [1, 2], initial_state=[1, 1]) # Check for multiple values: positional and alias with pytest.raises(TypeError, match="multiple"): - resp_multiple = ct.input_output_response( + ct.input_output_response( sys, timepts, 1, [1, 2], X0=[1, 1]) # Make sure that LTI functions check for keywords with pytest.raises(TypeError, match="unrecognized keyword"): - resp = ct.forced_response(sys, timepts, 1, unknown=True) + ct.forced_response(sys, timepts, 1, unknown=True) with pytest.raises(TypeError, match="unrecognized keyword"): - resp = ct.impulse_response(sys, timepts, unknown=True) + ct.impulse_response(sys, timepts, unknown=True) with pytest.raises(TypeError, match="unrecognized keyword"): - resp = ct.initial_response(sys, timepts, [1, 2], unknown=True) + ct.initial_response(sys, timepts, [1, 2], unknown=True) with pytest.raises(TypeError, match="unrecognized keyword"): - resp = ct.step_response(sys, timepts, unknown=True) + ct.step_response(sys, timepts, unknown=True) with pytest.raises(TypeError, match="unrecognized keyword"): - info = ct.step_info(sys, timepts, unknown=True) + ct.step_info(sys, timepts, unknown=True) From fb0519cd2a7347dc8effaf3ba6fc17b7c0aec878 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 12:21:31 +0200 Subject: [PATCH 15/22] Correct use of slycotonly in statesp_test.py slycontonly is not a fixture. --- control/tests/statesp_test.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index 3f5384484..0806696f0 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -22,7 +22,7 @@ from control.statesp import StateSpace, _convert_to_statespace, \ _rss_generate, _statesp_defaults, drss, linfnorm, rss, ss, tf2ss from control.xferfcn import TransferFunction, ss2tf -from .conftest import assert_tf_close_coeff +from .conftest import assert_tf_close_coeff, slycotonly class TestStateSpace: """Tests for the StateSpace class.""" @@ -228,7 +228,7 @@ def test_zero_empty(self): sys = _convert_to_statespace(TransferFunction([1], [1, 2, 1])) np.testing.assert_array_equal(sys.zeros(), np.array([])) - @pytest.mark.usefixtures("slycotonly") + @slycotonly def test_zero_siso(self, sys222): """Evaluate the zeros of a SISO system.""" # extract only first input / first output system of sys222. This system is denoted sys111 @@ -258,7 +258,7 @@ def test_zero_mimo_sys222_square(self, sys222): true_z = np.sort([-10.568501, 3.368501]) np.testing.assert_array_almost_equal(z, true_z) - @pytest.mark.usefixtures("slycotonly") + @slycotonly def test_zero_mimo_sys623_non_square(self, sys623): """Evaluate the zeros of a non square MIMO system.""" @@ -405,7 +405,7 @@ def test_add_sub_mimo_siso(self): ss2tf(result).minreal(), ) - @pytest.mark.usefixtures("slycotonly") + @slycotonly @pytest.mark.parametrize( "left, right, expected", [ @@ -480,7 +480,7 @@ def test_mul_mimo_siso(self, left, right, expected): ss2tf(result).minreal(), ) - @pytest.mark.usefixtures("slycotonly") + @slycotonly @pytest.mark.parametrize( "left, right, expected", [ @@ -555,7 +555,7 @@ def test_rmul_mimo_siso(self, left, right, expected): ss2tf(result).minreal(), ) - @pytest.mark.usefixtures("slycotonly") + @slycotonly @pytest.mark.parametrize("power", [0, 1, 3, -3]) @pytest.mark.parametrize("sysname", ["sys222", "sys322"]) def test_pow(self, request, sysname, power): @@ -574,7 +574,7 @@ def test_pow(self, request, sysname, power): np.testing.assert_allclose(expected.C, result.C) np.testing.assert_allclose(expected.D, result.D) - @pytest.mark.usefixtures("slycotonly") + @slycotonly @pytest.mark.parametrize("order", ["left", "right"]) @pytest.mark.parametrize("sysname", ["sys121", "sys222", "sys322"]) def test_pow_inv(self, request, sysname, order): @@ -598,7 +598,7 @@ def test_pow_inv(self, request, sysname, order): # Check that the output is the same as the input np.testing.assert_allclose(R.outputs, U) - @pytest.mark.usefixtures("slycotonly") + @slycotonly def test_truediv(self, sys222, sys322): """Test state space truediv""" for sys in [sys222, sys322]: @@ -617,7 +617,7 @@ def test_truediv(self, sys222, sys322): ss2tf(result).minreal(), ) - @pytest.mark.usefixtures("slycotonly") + @slycotonly def test_rtruediv(self, sys222, sys322): """Test state space rtruediv""" for sys in [sys222, sys322]: @@ -718,7 +718,7 @@ def test_freq_resp(self): mag, phase, omega = sys.freqresp(true_omega) np.testing.assert_almost_equal(mag, true_mag) - @pytest.mark.usefixtures("slycotonly") + @slycotonly def test_minreal(self): """Test a minreal model reduction.""" # A = [-2, 0.5, 0; 0.5, -0.3, 0; 0, 0, -0.1] @@ -1512,14 +1512,16 @@ def dt_siso(self, request): name, systype, sysargs, dt, refgpeak, reffpeak = request.param return ct.c2d(systype(*sysargs), dt), refgpeak, reffpeak - @pytest.mark.usefixtures('slycotonly', 'ignore_future_warning') + @slycotonly + @pytest.mark.usefixtures('ignore_future_warning') def test_linfnorm_ct_siso(self, ct_siso): sys, refgpeak, reffpeak = ct_siso gpeak, fpeak = linfnorm(sys) np.testing.assert_allclose(gpeak, refgpeak) np.testing.assert_allclose(fpeak, reffpeak) - @pytest.mark.usefixtures('slycotonly', 'ignore_future_warning') + @slycotonly + @pytest.mark.usefixtures('ignore_future_warning') def test_linfnorm_dt_siso(self, dt_siso): sys, refgpeak, reffpeak = dt_siso gpeak, fpeak = linfnorm(sys) @@ -1527,7 +1529,8 @@ def test_linfnorm_dt_siso(self, dt_siso): np.testing.assert_allclose(gpeak, refgpeak) np.testing.assert_allclose(fpeak, reffpeak) - @pytest.mark.usefixtures('slycotonly', 'ignore_future_warning') + @slycotonly + @pytest.mark.usefixtures('ignore_future_warning') def test_linfnorm_ct_mimo(self, ct_siso): siso, refgpeak, reffpeak = ct_siso sys = ct.append(siso, siso) From c6d26f1c8c63e4dee8621c305bb7176a5899754a Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 12:30:17 +0200 Subject: [PATCH 16/22] Remove unused ss_{siso,mimo} variables It looks like a block of code was copied-and-pasted between two test functions; removed the unused variable in each case. --- control/tests/frd_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/control/tests/frd_test.py b/control/tests/frd_test.py index 8bf606424..1b370c629 100644 --- a/control/tests/frd_test.py +++ b/control/tests/frd_test.py @@ -572,7 +572,6 @@ def test_truediv_mimo_siso(self): omega = np.logspace(-1, 1, 10) tf_mimo = TransferFunction([1], [1, 0]) * np.eye(2) frd_mimo = frd(tf_mimo, omega) - ss_mimo = ct.tf2ss(tf_mimo) tf_siso = TransferFunction([1], [1, 1]) frd_siso = frd(tf_siso, omega) expected = frd(tf_mimo.__truediv__(tf_siso), omega) @@ -601,7 +600,6 @@ def test_rtruediv_mimo_siso(self): ss_mimo = ct.tf2ss(tf_mimo) tf_siso = TransferFunction([1], [1, 1]) frd_siso = frd(tf_siso, omega) - ss_siso = ct.tf2ss(tf_siso) expected = frd(tf_siso.__rtruediv__(tf_mimo), omega) # Test division of MIMO FRD by SISO FRD From 22db2bfffe9ef012a89d028c59c87800b90b2279 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 14:13:00 +0200 Subject: [PATCH 17/22] Don't use keyword args for eval Only works from Python 3.13. --- control/tests/config_test.py | 2 +- control/tests/namedio_test.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/control/tests/config_test.py b/control/tests/config_test.py index 281e4f2fa..be3fba5c9 100644 --- a/control/tests/config_test.py +++ b/control/tests/config_test.py @@ -340,7 +340,7 @@ def test_system_indexing(self): ]) def test_repr_format(self, kwargs): sys = ct.ss([[1]], [[1]], [[1]], [[0]], **kwargs) - new = eval(repr(sys), locals={'StateSpace':ct.StateSpace, 'array':np.array}) + new = eval(repr(sys), None, {'StateSpace':ct.StateSpace, 'array':np.array}) for attr in ['A', 'B', 'C', 'D']: assert getattr(new, attr) == getattr(sys, attr) for prop in ['input_labels', 'output_labels', 'state_labels']: diff --git a/control/tests/namedio_test.py b/control/tests/namedio_test.py index 961237b7a..ad74d27ba 100644 --- a/control/tests/namedio_test.py +++ b/control/tests/namedio_test.py @@ -374,7 +374,8 @@ def test_named_signal_repr(): for signal in ['inputs', 'outputs', 'states']: sig_orig = getattr(resp, signal) sig_eval = eval(repr(sig_orig), - locals={'array': np.array, - 'NamedSignal': ct.NamedSignal}) + None, + {'array': np.array, + 'NamedSignal': ct.NamedSignal}) assert sig_eval.signal_labels == sig_orig.signal_labels assert sig_eval.trace_labels == sig_orig.trace_labels From 5ff3c0c17c9954e3d388c5585a6aebf2f396f324 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 15:38:17 +0200 Subject: [PATCH 18/22] Handle deprecation warnings from conda setup in Github actions --- .github/workflows/python-package-conda.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 00e03e0e0..b3c127829 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -38,7 +38,9 @@ jobs: activate-environment: test-env environment-file: .github/conda-env/test-env.yml miniforge-version: latest - channels: conda-forge + channels: + - defaults + - conda-forge channel-priority: strict auto-update-conda: false auto-activate-base: false From 51eb00a99feb751bb4afc2a4f9dae3b54a36eae1 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 19:39:50 +0200 Subject: [PATCH 19/22] Remove redundant kwarg test entries --- control/tests/kwargs_test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/control/tests/kwargs_test.py b/control/tests/kwargs_test.py index e7f69e58e..4a342160e 100644 --- a/control/tests/kwargs_test.py +++ b/control/tests/kwargs_test.py @@ -343,9 +343,6 @@ def test_response_plot_kwargs(data_fcn, plot_fcn, mimo): 'PoleZeroList.plot': test_response_plot_kwargs, 'InterconnectedSystem.__init__': interconnect_test.test_interconnect_exceptions, - 'StateSpace.__init__': - interconnect_test.test_interconnect_exceptions, - 'StateSpace.sample': test_unrecognized_kwargs, 'NonlinearIOSystem.__init__': interconnect_test.test_interconnect_exceptions, 'StateSpace.__init__': test_unrecognized_kwargs, From e29ba64324f8d15f3e026166d00f8c15665414ce Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 19:43:35 +0200 Subject: [PATCH 20/22] Add assertion to check result of markov call --- control/tests/modelsimp_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/control/tests/modelsimp_test.py b/control/tests/modelsimp_test.py index 043b481ce..e09446073 100644 --- a/control/tests/modelsimp_test.py +++ b/control/tests/modelsimp_test.py @@ -123,6 +123,7 @@ def testMarkovSignature(self): inp = np.array([1, 2]) outp = np.array([2, 4]) mrk = markov(outp, inp, 1, transpose=False) + np.testing.assert_almost_equal(mrk, 2.) # Test mimo example # Mechanical Vibrations: Theory and Application, SI Edition, 1st ed. From ff4d7b68e3358b918146dfc51c75edb64275262a Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 19:43:51 +0200 Subject: [PATCH 21/22] Remove unused variables in test_optimal_doc --- control/tests/optimal_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/control/tests/optimal_test.py b/control/tests/optimal_test.py index 9677e3d6f..fa8fcb941 100644 --- a/control/tests/optimal_test.py +++ b/control/tests/optimal_test.py @@ -731,8 +731,6 @@ def vehicle_output(t, x, u, params): initial_guess[0, :] = (xf[0] - x0[0]) / Tf # Steering = rate required to turn to proper slope in first segment - straight_seg_length = timepts[-2] - timepts[1] - curved_seg_length = (Tf - straight_seg_length)/2 approximate_angle = math.atan2(xf[1] - x0[1], xf[0] - x0[0]) initial_guess[1, 0] = approximate_angle / (timepts[1] - timepts[0]) initial_guess[1, -1] = -approximate_angle / (timepts[-1] - timepts[-2]) From c907a4fc396e3c534b9d465b074ee35a959c3605 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sun, 16 Feb 2025 19:48:15 +0200 Subject: [PATCH 22/22] Revert "Handle deprecation warnings from conda setup in Github actions" This reverts commit 5ff3c0c17c9954e3d388c5585a6aebf2f396f324. --- .github/workflows/python-package-conda.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index b3c127829..00e03e0e0 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -38,9 +38,7 @@ jobs: activate-environment: test-env environment-file: .github/conda-env/test-env.yml miniforge-version: latest - channels: - - defaults - - conda-forge + channels: conda-forge channel-priority: strict auto-update-conda: false auto-activate-base: false 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