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