From 78f5aec6f35b69d33584da3ce4830e7eced2f7c5 Mon Sep 17 00:00:00 2001 From: Sawyer Fuller Date: Mon, 17 Apr 2023 15:58:05 -0700 Subject: [PATCH 1/3] fix bug in namedio unit test that was treating static SS systems the same as transfer functions. tests to track down missing names on some systems when converting to LinearIOSystem --- control/tests/namedio_test.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/control/tests/namedio_test.py b/control/tests/namedio_test.py index 4925e9790..bec0049d9 100644 --- a/control/tests/namedio_test.py +++ b/control/tests/namedio_test.py @@ -90,8 +90,10 @@ def test_named_ss(): (lambda t, x, u, params: -x, None), {'inputs': 2, 'outputs':2, 'states':2}], [ct.ss, ([[1, 2], [3, 4]], [[0], [1]], [[1, 0]], 0), {}], + [ct.ss, ([], [], [], 3), {}], # static system [ct.StateSpace, ([[1, 2], [3, 4]], [[0], [1]], [[1, 0]], 0), {}], [ct.tf, ([1, 2], [3, 4, 5]), {}], + [ct.tf, (2, 3), {}], # static system [ct.TransferFunction, ([1, 2], [3, 4, 5]), {}], ]) def test_io_naming(fun, args, kwargs): @@ -112,7 +114,7 @@ def test_io_naming(fun, args, kwargs): assert sys_g.name == 'sys[0]' assert sys_g.input_labels == [f'u[{i}]' for i in range(sys_g.ninputs)] assert sys_g.output_labels == [f'y[{i}]' for i in range(sys_g.noutputs)] - if sys_g.nstates: + if sys_g.nstates is not None: assert sys_g.state_labels == [f'x[{i}]' for i in range(sys_g.nstates)] # @@ -128,7 +130,7 @@ def test_io_naming(fun, args, kwargs): sys_r.set_outputs(output_labels) assert sys_r.output_labels == output_labels - if sys_g.nstates: + if sys_g.nstates is not None: state_labels = [f'x{i}' for i in range(sys_g.nstates)] sys_r.set_states(state_labels) assert sys_r.state_labels == state_labels @@ -143,7 +145,7 @@ def test_io_naming(fun, args, kwargs): sys_k = fun(state_labels, output_labels, input_labels, name='mysys') elif sys_g.nstates is None: - # Don't pass state labels + # Don't pass state labels if TransferFunction sys_k = fun( *args, inputs=input_labels, outputs=output_labels, name='mysys') @@ -155,7 +157,7 @@ def test_io_naming(fun, args, kwargs): assert sys_k.name == 'mysys' assert sys_k.input_labels == input_labels assert sys_k.output_labels == output_labels - if sys_g.nstates: + if sys_g.nstates is not None: assert sys_k.state_labels == state_labels # @@ -193,6 +195,24 @@ def test_io_naming(fun, args, kwargs): assert sys_tf.input_labels == input_labels assert sys_tf.output_labels == output_labels + # + # Convert the system to a LinearIOSystem and make sure labels transfer + # + if not isinstance( + sys_r, (ct.FrequencyResponseData, ct.NonlinearIOSystem)) and \ + ct.slycot_check(): + sys_lio = ct.LinearIOSystem(sys_r) + assert sys_lio != sys_r + assert sys_lio.input_labels == input_labels + assert sys_lio.output_labels == output_labels + + # Reassign system and signal names + sys_lio = ct.LinearIOSystem( + sys_g, inputs=input_labels, outputs=output_labels, name='new') + assert sys_lio.name == 'new' + assert sys_lio.input_labels == input_labels + assert sys_lio.output_labels == output_labels + # Internal testing of StateSpace initialization def test_init_namedif(): From ebabbd6f8481a47adb83fe7c6368275b6b8a624c Mon Sep 17 00:00:00 2001 From: Sawyer Fuller Date: Mon, 17 Apr 2023 17:02:07 -0700 Subject: [PATCH 2/3] add unit tests for interconnect with static system --- control/tests/interconnect_test.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/control/tests/interconnect_test.py b/control/tests/interconnect_test.py index 2c29aeaca..3d2f0c7d7 100644 --- a/control/tests/interconnect_test.py +++ b/control/tests/interconnect_test.py @@ -68,8 +68,13 @@ def test_interconnect_implicit(): ki = ct.tf(random.uniform(1, 10), [1, 0]) C = ct.tf2io(kp + ki, inputs='e', outputs='u', name='C') + # same but static C2 + C2 = ct.tf(random.uniform(1, 10), 1, + inputs='e', outputs='u', name='C2') + # Block diagram computation Tss = ct.feedback(P * C, 1) + Tss2 = ct.feedback(P * C2, 1) # Construct the interconnection explicitly Tio_exp = ct.interconnect( @@ -93,6 +98,15 @@ def test_interconnect_implicit(): np.testing.assert_almost_equal(Tio_sum.C, Tss.C) np.testing.assert_almost_equal(Tio_sum.D, Tss.D) + # test whether signal names work for static system C2 + Tio_sum2 = ct.interconnect( + [C2, P, sumblk], inputs='r', outputs='y') + + np.testing.assert_almost_equal(Tio_sum2.A, Tss2.A) + np.testing.assert_almost_equal(Tio_sum2.B, Tss2.B) + np.testing.assert_almost_equal(Tio_sum2.C, Tss2.C) + np.testing.assert_almost_equal(Tio_sum2.D, Tss2.D) + # Setting connections to False should lead to an empty connection map empty = ct.interconnect( (C, P, sumblk), connections=False, inplist=['r'], outlist=['y']) @@ -237,17 +251,17 @@ def test_linear_interconnect(): ss_ctrl = ct.ss(1, 2, 1, 2, inputs='e', outputs='u') ss_plant = ct.ss(1, 2, 1, 2, inputs='u', outputs='y') nl_ctrl = ct.NonlinearIOSystem( - lambda t, x, u, params: x*x, + lambda t, x, u, params: x*x, lambda t, x, u, params: u*x, states=1, inputs='e', outputs='u') nl_plant = ct.NonlinearIOSystem( - lambda t, x, u, params: x*x, + lambda t, x, u, params: x*x, lambda t, x, u, params: u*x, states=1, inputs='u', outputs='y') assert isinstance(ct.interconnect((tf_ctrl, tf_plant), inputs='e', outputs='y'), ct.LinearIOSystem) assert isinstance(ct.interconnect((ss_ctrl, ss_plant), inputs='e', outputs='y'), ct.LinearIOSystem) assert isinstance(ct.interconnect((tf_ctrl, ss_plant), inputs='e', outputs='y'), ct.LinearIOSystem) assert isinstance(ct.interconnect((ss_ctrl, tf_plant), inputs='e', outputs='y'), ct.LinearIOSystem) - + assert ~isinstance(ct.interconnect((nl_ctrl, ss_plant), inputs='e', outputs='y'), ct.LinearIOSystem) assert ~isinstance(ct.interconnect((nl_ctrl, tf_plant), inputs='e', outputs='y'), ct.LinearIOSystem) assert ~isinstance(ct.interconnect((ss_ctrl, nl_plant), inputs='e', outputs='y'), ct.LinearIOSystem) From 8b5eb47e60f8263da33609528bde34b5bf2be8a8 Mon Sep 17 00:00:00 2001 From: Sawyer Fuller Date: Mon, 17 Apr 2023 22:13:44 -0700 Subject: [PATCH 3/3] squashed noslycot losing signal names bug, added unit tests --- control/statesp.py | 13 ++++++++----- control/tests/namedio_test.py | 19 +++++++++++++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/control/statesp.py b/control/statesp.py index 41f92ae21..99c5fe9dc 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -170,9 +170,9 @@ class StateSpace(LTI): The StateSpace class is used to represent state-space realizations of linear time-invariant (LTI) systems: - + .. math:: - + dx/dt &= A x + B u \\ y &= C x + D u @@ -1561,7 +1561,8 @@ def _convert_to_statespace(sys): return StateSpace( ssout[1][:states, :states], ssout[2][:states, :sys.ninputs], ssout[3][:sys.noutputs, :states], ssout[4], sys.dt, - inputs=sys.input_labels, outputs=sys.output_labels) + inputs=sys.input_labels, outputs=sys.output_labels, + name=sys.name) except ImportError: # No Slycot. Scipy tf->ss can't handle MIMO, but static # MIMO is an easy special case we can check for here @@ -1574,7 +1575,9 @@ def _convert_to_statespace(sys): for i, j in itertools.product(range(sys.noutputs), range(sys.ninputs)): D[i, j] = sys.num[i][j][0] / sys.den[i][j][0] - return StateSpace([], [], [], D, sys.dt) + return StateSpace([], [], [], D, sys.dt, + inputs=sys.input_labels, outputs=sys.output_labels, + name=sys.name) else: if sys.ninputs != 1 or sys.noutputs != 1: raise TypeError("No support for MIMO without slycot") @@ -1586,7 +1589,7 @@ def _convert_to_statespace(sys): sp.signal.tf2ss(squeeze(sys.num), squeeze(sys.den)) return StateSpace( A, B, C, D, sys.dt, inputs=sys.input_labels, - outputs=sys.output_labels) + outputs=sys.output_labels, name=sys.name) elif isinstance(sys, FrequencyResponseData): raise TypeError("Can't convert FRD to StateSpace system.") diff --git a/control/tests/namedio_test.py b/control/tests/namedio_test.py index bec0049d9..3203214d6 100644 --- a/control/tests/namedio_test.py +++ b/control/tests/namedio_test.py @@ -241,14 +241,29 @@ def test_init_namedif(): # Test state space conversion def test_convert_to_statespace(): - # Set up the initial system - sys = ct.tf(ct.rss(2, 1, 1)) + # Set up the initial systems + sys = ct.tf(ct.rss(2, 1, 1), inputs='u', outputs='y', name='sys') + sys_static = ct.tf(1, 2, inputs='u', outputs='y', name='sys_static') + + # check that name, inputs, and outputs passed through + sys_new = ct.ss(sys) + assert sys_new.name == 'sys' + assert sys_new.input_labels == ['u'] + assert sys_new.output_labels == ['y'] + sys_new = ct.ss(sys_static) + assert sys_new.name == 'sys_static' + assert sys_new.input_labels == ['u'] + assert sys_new.output_labels == ['y'] # Make sure we can rename system name, inputs, outputs sys_new = ct.ss(sys, inputs='u', outputs='y', name='new') assert sys_new.name == 'new' assert sys_new.input_labels == ['u'] assert sys_new.output_labels == ['y'] + sys_new = ct.ss(sys_static, inputs='u', outputs='y', name='new') + assert sys_new.name == 'new' + assert sys_new.input_labels == ['u'] + assert sys_new.output_labels == ['y'] # Try specifying the state names (via low level test) with pytest.warns(UserWarning, match="non-unique state space realization"): 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