def test_evalfr(self): """Evaluate the frequency response at one frequency.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) resp = [[4.37636761487965e-05 - 0.0152297592997812j, -0.792603938730853 + 0.0261706783369803j], [-0.331544857768052 + 0.0576105032822757j, 0.128919037199125 - 0.143824945295405j]] # Correct versions of the call np.testing.assert_almost_equal(evalfr(sys, 1j), resp) np.testing.assert_almost_equal(sys._evalfr(1.), resp) # Deprecated version of the call (should generate warning) import warnings with warnings.catch_warnings(record=True) as w: # Set up warnings filter to only show warnings in control module warnings.filterwarnings("ignore") warnings.filterwarnings("always", module="control") # Make sure that we get a pending deprecation warning sys.evalfr(1.) assert len(w) == 1 assert issubclass(w[-1].category, PendingDeprecationWarning)
def test_minrealStaticGain(self): """Regression: minreal on static gain was failing""" g1 = StateSpace([],[],[],[1]) g2 = g1.minreal() np.testing.assert_array_equal(g1.A, g2.A) np.testing.assert_array_equal(g1.B, g2.B) np.testing.assert_array_equal(g1.C, g2.C) np.testing.assert_array_equal(g1.D, g2.D)
def testZero(self): """Evaluate the zeros of a SISO system.""" sys = StateSpace(self.sys1.A, [[3.], [-2.], [4.]], [[-1., 3., 2.]], [[-4.]]) z = sys.zero() np.testing.assert_array_almost_equal(z, [4.26864638637134, -3.75932319318567 + 1.10087776649554j, -3.75932319318567 - 1.10087776649554j])
def testMIMO(self): sys = StateSpace([[-0.5, 0.0], [0.0, -1.0]], [[1.0, 0.0], [0.0, 1.0]], [[1.0, 0.0], [0.0, 1.0]], [[0.0, 0.0], [0.0, 0.0]]) omega = np.logspace(-1, 2, 10) f1 = FRD(sys, omega) np.testing.assert_array_almost_equal( sys.freqresp([0.1, 1.0, 10])[0], f1.freqresp([0.1, 1.0, 10])[0]) np.testing.assert_array_almost_equal( sys.freqresp([0.1, 1.0, 10])[1], f1.freqresp([0.1, 1.0, 10])[1])
def testMIMOfb2(self): sys = StateSpace(np.matrix('-2.0 0 0; 0 -1 1; 0 0 -3'), np.matrix('1.0 0; 0 0; 0 1'), np.eye(3), np.zeros((3,2))) omega = np.logspace(-1, 2, 10) K = np.matrix('1 0.3 0; 0.1 0 0') f1 = FRD(sys, omega).feedback(K) f2 = FRD(sys.feedback(K), omega) np.testing.assert_array_almost_equal( f1.freqresp([0.1, 1.0, 10])[0], f2.freqresp([0.1, 1.0, 10])[0]) np.testing.assert_array_almost_equal( f1.freqresp([0.1, 1.0, 10])[1], f2.freqresp([0.1, 1.0, 10])[1])
def test_siso(self): B = np.matrix('0;1') D = 0 sys = StateSpace(self.A,B,self.C,D) # test frequency response frq=sys.freqresp(self.omega) # test bode plot bode(sys) # Convert to transfer function and test bode systf = tf(sys) bode(systf)
def testEvalFr(self): """Evaluate the frequency response at one frequency.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) resp = [[4.37636761487965e-05 - 0.0152297592997812j, -0.792603938730853 + 0.0261706783369803j], [-0.331544857768052 + 0.0576105032822757j, 0.128919037199125 - 0.143824945295405j]] np.testing.assert_almost_equal(sys.evalfr(1.), resp)
def test_scalarStaticGain(self): """Regression: can we create a scalar static gain?""" g1=StateSpace([],[],[],[2]) g2=StateSpace([],[],[],[3]) # make sure StateSpace internals, specifically ABC matrix # sizes, are OK for LTI operations g3 = g1*g2 self.assertEqual(6, g3.D[0,0]) g4 = g1+g2 self.assertEqual(5, g4.D[0,0]) g5 = g1.feedback(g2) self.assertAlmostEqual(2./7, g5.D[0,0]) g6 = g1.append(g2) np.testing.assert_array_equal(np.diag([2,3]),g6.D)
def test_lft(self): """ test lft function with result obtained from matlab implementation""" # test case A = [[1, 2, 3], [1, 4, 5], [2, 3, 4]] B = [[0, 2], [5, 6], [5, 2]] C = [[1, 4, 5], [2, 3, 0]] D = [[0, 0], [3, 0]] P = StateSpace(A, B, C, D) Ak = [[0, 2, 3], [2, 3, 5], [2, 1, 9]] Bk = [[1, 1], [2, 3], [9, 4]] Ck = [[1, 4, 5], [2, 3, 6]] Dk = [[0, 2], [0, 0]] K = StateSpace(Ak, Bk, Ck, Dk) # case 1 pk = P.lft(K, 2, 1) Amatlab = [1, 2, 3, 4, 6, 12, 1, 4, 5, 17, 38, 61, 2, 3, 4, 9, 26, 37, 2, 3, 0, 3, 14, 18, 4, 6, 0, 8, 27, 35, 18, 27, 0, 29, 109, 144] Bmatlab = [0, 10, 10, 7, 15, 58] Cmatlab = [1, 4, 5, 0, 0, 0] Dmatlab = [0] np.testing.assert_allclose(np.array(pk.A).reshape(-1), Amatlab) np.testing.assert_allclose(np.array(pk.B).reshape(-1), Bmatlab) np.testing.assert_allclose(np.array(pk.C).reshape(-1), Cmatlab) np.testing.assert_allclose(np.array(pk.D).reshape(-1), Dmatlab) # case 2 pk = P.lft(K) Amatlab = [1, 2, 3, 4, 6, 12, -3, -2, 5, 11, 14, 31, -2, -3, 4, 3, 2, 7, 0.6, 3.4, 5, -0.6, -0.4, 0, 0.8, 6.2, 10, 0.2, -4.2, -4, 7.4, 33.6, 45, -0.4, -8.6, -3] Bmatlab = [] Cmatlab = [] Dmatlab = [] np.testing.assert_allclose(np.array(pk.A).reshape(-1), Amatlab) np.testing.assert_allclose(np.array(pk.B).reshape(-1), Bmatlab) np.testing.assert_allclose(np.array(pk.C).reshape(-1), Cmatlab) np.testing.assert_allclose(np.array(pk.D).reshape(-1), Dmatlab)
def setUp(self): """Set up a MIMO system to test operations on.""" # sys1: 3-states square system (2 inputs x 2 outputs) A322 = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B322 = [[1., 4.], [-3., -3.], [-2., 1.]] C322 = [[4., 2., -3.], [1., 4., 3.]] D322 = [[-2., 4.], [0., 1.]] self.sys322 = StateSpace(A322, B322, C322, D322) # sys1: 2-states square system (2 inputs x 2 outputs) A222 = [[4., 1.], [2., -3]] B222 = [[5., 2.], [-3., -3.]] C222 = [[2., -4], [0., 1.]] D222 = [[3., 2.], [1., -1.]] self.sys222 = StateSpace(A222, B222, C222, D222) # sys3: 6 states non square system (2 inputs x 3 outputs) A623 = np.array([[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 3, 0, 0, 0], [0, 0, 0, -4, 0, 0], [0, 0, 0, 0, -1, 0], [0, 0, 0, 0, 0, 3]]) B623 = np.array([[0, -1], [-1, 0], [1, -1], [0, 0], [0, 1], [-1, -1]]) C623 = np.array([[1, 0, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1], [0, 0, 1, 0, 0, 1]]) D623 = np.zeros((3, 2)) self.sys623 = StateSpace(A623, B623, C623, D623)
def sys121(self): """2 state, 1 input, 1 output (siso) system""" A121 = [[4., 1.], [2., -3]] B121 = [[5.], [-3.]] C121 = [[2., -4]] D121 = [[3.]] return StateSpace(A121, B121, C121, D121)
def test_dcgain_integrator(self): """DC gain when eigenvalue at DC returns appropriately sized array of nan""" # the SISO case is also tested in test_dc_gain_{cont,discr} import itertools # iterate over input and output sizes, and continuous (dt=None) and discrete (dt=True) time for inputs,outputs,dt in itertools.product(range(1,6),range(1,6),[None,True]): states = max(inputs,outputs) # a matrix that is singular at DC, and has no "useless" states as in _remove_useless_states a = np.triu(np.tile(2,(states,states))) # eigenvalues all +2, except for ... a[0,0] = 0 if dt is None else 1 b = np.eye(max(inputs,states))[:states,:inputs] c = np.eye(max(outputs,states))[:outputs,:states] d = np.zeros((outputs,inputs)) sys = StateSpace(a,b,c,d,dt) dc = np.squeeze(np.tile(np.nan,(outputs,inputs))) np.testing.assert_array_equal(dc, sys.dcgain())
def test_remove_useless_states(self): """Regression: _remove_useless_states gives correct ABC sizes.""" g1 = StateSpace(np.zeros((3, 3)), np.zeros((3, 4)), np.zeros((5, 3)), np.zeros((5, 4))) assert (0, 0) == g1.A.shape assert (0, 4) == g1.B.shape assert (5, 0) == g1.C.shape assert (5, 4) == g1.D.shape assert 0 == g1.states
def sys221(self): """2-states, 2 inputs x 1 output""" A222 = [[4., 1.], [2., -3]] B222 = [[5., 2.], [-3., -3.]] C221 = [[0., 1.]] D221 = [[1., -1.]] return StateSpace(A222, B222, C221, D221)
def test_remove_useless_states(self): """Regression: _remove_useless_states gives correct ABC sizes""" g1 = StateSpace(np.zeros((3, 3)), np.zeros((3, 4)), np.zeros((5, 3)), np.zeros((5, 4))) self.assertEqual((0, 0), g1.A.shape) self.assertEqual((0, 4), g1.B.shape) self.assertEqual((5, 0), g1.C.shape) self.assertEqual((5, 4), g1.D.shape) self.assertEqual(0, g1.states)
def test_constructor_warns(self, sys322ABCD): """Test ambiguos input to StateSpace() constructor""" with pytest.warns(UserWarning, match="received multiple dt"): sys = StateSpace(*(sys322ABCD + (0.1, )), dt=0.2) np.testing.assert_almost_equal(sys.A, sys322ABCD[0]) np.testing.assert_almost_equal(sys.B, sys322ABCD[1]) np.testing.assert_almost_equal(sys.C, sys322ABCD[2]) np.testing.assert_almost_equal(sys.D, sys322ABCD[3]) assert sys.dt == 0.1
def dsystem_dt(request): """Test systems for test_discrete""" # SISO state space systems with either fixed or unspecified sampling times sys = rss(3, 1, 1) # MIMO state space systems with either fixed or unspecified sampling times A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] dt = request.param systems = { 'sssiso': StateSpace(sys.A, sys.B, sys.C, sys.D, dt), 'ssmimo': StateSpace(A, B, C, D, dt), 'tf': TransferFunction([1, 1], [1, 2, 1], dt) } return systems
def test_siso(): """Test SISO frequency response""" A = np.array([[1, 1], [0, 1]]) B = np.array([[0], [1]]) C = np.array([[1, 0]]) D = 0 sys = StateSpace(A, B, C, D) omega = np.linspace(10e-2, 10e2, 1000) # test frequency response sys.freqresp(omega) # test bode plot bode(sys) # Convert to transfer function and test bode systf = tf(sys) bode(systf)
def testSSSS2(self): """State space system with state space feedback block, including a direct feedthrough term.""" sys3 = StateSpace([[-1., 4.], [2., -3]], [[2.], [3.]], [[-3., 1.]], [[-2.]]) sys4 = StateSpace([[-3., -2.], [1., 4.]], [[-2.], [-6.]], [[2., -3.]], [[3.]]) ans1 = feedback(sys3, sys4) ans2 = feedback(sys3, sys4, 1.) np.testing.assert_array_almost_equal( ans1.A, [[-4.6, 5.2, 0.8, -1.2], [-3.4, -1.2, 1.2, -1.8], [-1.2, 0.4, -1.4, -4.4], [-3.6, 1.2, 5.8, -3.2]]) np.testing.assert_array_almost_equal(ans1.B, [[-0.4], [-0.6], [-0.8], [-2.4]]) np.testing.assert_array_almost_equal(ans1.C, [[0.6, -0.2, -0.8, 1.2]]) np.testing.assert_array_almost_equal(ans1.D, [[0.4]]) np.testing.assert_array_almost_equal( ans2.A, [[ -3.57142857142857, 4.85714285714286, 0.571428571428571, -0.857142857142857 ], [ -1.85714285714286, -1.71428571428571, 0.857142857142857, -1.28571428571429 ], [ 0.857142857142857, -0.285714285714286, -1.85714285714286, -3.71428571428571 ], [ 2.57142857142857, -0.857142857142857, 4.42857142857143, -1.14285714285714 ]]) np.testing.assert_array_almost_equal( ans2.B, [[0.285714285714286], [0.428571428571429], [0.571428571428571], [1.71428571428571]]) np.testing.assert_array_almost_equal(ans2.C, [[ -0.428571428571429, 0.142857142857143, -0.571428571428571, 0.857142857142857 ]]) np.testing.assert_array_almost_equal(ans2.D, [[-0.285714285714286]])
def testEvalFr(self): """Evaluate the frequency response at one frequency.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) resp = [[ 4.37636761487965e-05 - 0.0152297592997812j, -0.792603938730853 + 0.0261706783369803j ], [ -0.331544857768052 + 0.0576105032822757j, 0.128919037199125 - 0.143824945295405j ]] np.testing.assert_almost_equal(sys.evalfr(1.), resp)
def test_latex_repr(gmats, ref, repr_type, num_format, editsdefaults): """Test `._latex_repr_` with different config values This is a 'gold image' test, so if you change behaviour, you'll need to regenerate the reference results. Try something like: control.reset_defaults() print(f'p3_p : {g1._repr_latex_()!r}') """ from control import set_defaults if num_format is not None: set_defaults('statesp', latex_num_format=num_format) if repr_type is not None: set_defaults('statesp', latex_repr_type=repr_type) g = StateSpace(*gmats) refkey = "{}_{}".format(refkey_n[num_format], refkey_r[repr_type]) assert g._repr_latex_() == ref[refkey]
def test_dc_gain_integrator(self, outputs, inputs, dt): """DC gain when eigenvalue at DC returns appropriately sized array of nan. the SISO case is also tested in test_dc_gain_{cont,discr} time systems (dt=0) """ states = max(inputs, outputs) # a matrix that is singular at DC, and has no "useless" states as in # _remove_useless_states a = np.triu(np.tile(2, (states, states))) # eigenvalues all +2, except for ... a[0, 0] = 0 if dt in [0, None] else 1 b = np.eye(max(inputs, states))[:states, :inputs] c = np.eye(max(outputs, states))[:outputs, :states] d = np.zeros((outputs, inputs)) sys = StateSpace(a, b, c, d, dt) dc = np.squeeze(np.full_like(d, np.nan)) np.testing.assert_array_equal(dc, sys.dcgain())
def test_copy_constructor(self): # Create a set of matrices for a simple linear system A = np.array([[-1]]) B = np.array([[1]]) C = np.array([[1]]) D = np.array([[0]]) # Create the first linear system and a copy linsys = StateSpace(A, B, C, D) cpysys = StateSpace(linsys) # Change the original A matrix A[0, 0] = -2 np.testing.assert_array_equal(linsys.A, [[-1]]) # original value np.testing.assert_array_equal(cpysys.A, [[-1]]) # original value # Change the A matrix for the original system linsys.A[0, 0] = -3 np.testing.assert_array_equal(cpysys.A, [[-1]]) # original value
def sys623(self): """sys3: 6 states non square system (2 inputs x 3 outputs)""" A623 = np.array([[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 3, 0, 0, 0], [0, 0, 0, -4, 0, 0], [0, 0, 0, 0, -1, 0], [0, 0, 0, 0, 0, 3]]) B623 = np.array([[0, -1], [-1, 0], [1, -1], [0, 0], [0, 1], [-1, -1]]) C623 = np.array([[1, 0, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1], [0, 0, 1, 0, 0, 1]]) D623 = np.zeros((3, 2)) return StateSpace(A623, B623, C623, D623)
def test_matlab_style_constructor(self): """Use (deprecated) matrix-style construction string""" with pytest.deprecated_call(): sys = StateSpace("-1 1; 0 2", "0; 1", "1, 0", "0") assert sys.A.shape == (2, 2) assert sys.B.shape == (2, 1) assert sys.C.shape == (1, 2) assert sys.D.shape == (1, 1) for X in [sys.A, sys.B, sys.C, sys.D]: assert ismatarrayout(X)
def testMinreal(self): """Test a minreal model reduction""" #A = [-2, 0.5, 0; 0.5, -0.3, 0; 0, 0, -0.1] A = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] #B = [0.3, -1.3; 0.1, 0; 1, 0] B = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] #C = [0, 0.1, 0; -0.3, -0.2, 0] C = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] #D = [0 -0.8; -0.3 0] D = [[0., -0.8], [-0.3, 0.]] # sys = ss(A, B, C, D) sys = StateSpace(A, B, C, D) sysr = sys.minreal() self.assertEqual(sysr.states, 2) self.assertEqual(sysr.inputs, sys.inputs) self.assertEqual(sysr.outputs, sys.outputs) np.testing.assert_array_almost_equal( eigvals(sysr.A), [-2.136154, -0.1638459])
def test_sample_system_prewarping(self): """test that prewarping works when converting from cont to discrete time system""" A = np.array([ [ 0.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00], [-3.81097561e+01, -1.12500000e+00, 0.00000000e+00, 0.00000000e+00], [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00], [ 0.00000000e+00, 0.00000000e+00, -1.66356135e+04, -1.34748470e+01]]) B = np.array([ [ 0. ], [ 38.1097561 ],[ 0. ],[16635.61352143]]) C = np.array([[0.90909091, 0. , 0.09090909, 0. ],]) wwarp = 50 Ts = 0.025 plant = StateSpace(A,B,C,0) plant = ss2tf(plant) plant_d_warped = plant.sample(Ts, 'bilinear', prewarp_frequency=wwarp) np.testing.assert_array_almost_equal( evalfr(plant, wwarp*1j), evalfr(plant_d_warped, np.exp(wwarp*1j*Ts)), decimal=4)
def setUp(self): """This contains some random LTI systems and scalars for testing.""" # Two random SISO systems. self.sys1 = TransferFunction([1, 2], [1, 2, 3]) self.sys2 = StateSpace([[1., 4.], [3., 2.]], [[1.], [-4.]], [[1., 0.]], [[0.]]) # Two random scalars. self.x1 = 2.5 self.x2 = -3.
def h2syn(P, nmeas, ncon): """H_2 control synthesis for plant P. Parameters ---------- P: partitioned lti plant (State-space sys) nmeas: number of measurements (input to controller) ncon: number of control inputs (output from controller) Returns ------- K: controller to stabilize P (State-space sys) Raises ------ ImportError if slycot routine sb10hd is not loaded See Also -------- StateSpace Examples -------- >>> K = h2syn(P,nmeas,ncon) """ #Check for ss system object, need a utility for this? #TODO: Check for continous or discrete, only continuous supported right now # if isCont(): # dico = 'C' # elif isDisc(): # dico = 'D' # else: dico = 'C' try: from slycot import sb10hd except ImportError: raise ControlSlycot("can't find slycot subroutine sb10hd") n = np.size(P.A, 0) m = np.size(P.B, 1) np = np.size(P.C, 0) out = sb10hd(n, m, np, ncon, nmeas, P.A, P.B, P.C, P.D) Ak = out[0] Bk = out[1] Ck = out[2] Dk = out[3] K = StateSpace(Ak, Bk, Ck, Dk) return K
def test_lft(self): """ test lft function with result obtained from matlab implementation""" # test case A = [[1, 2, 3], [1, 4, 5], [2, 3, 4]] B = [[0, 2], [5, 6], [5, 2]] C = [[1, 4, 5], [2, 3, 0]] D = [[0, 0], [3, 0]] P = StateSpace(A, B, C, D) Ak = [[0, 2, 3], [2, 3, 5], [2, 1, 9]] Bk = [[1, 1], [2, 3], [9, 4]] Ck = [[1, 4, 5], [2, 3, 6]] Dk = [[0, 2], [0, 0]] K = StateSpace(Ak, Bk, Ck, Dk) # case 1 pk = P.lft(K, 2, 1) Amatlab = [ 1, 2, 3, 4, 6, 12, 1, 4, 5, 17, 38, 61, 2, 3, 4, 9, 26, 37, 2, 3, 0, 3, 14, 18, 4, 6, 0, 8, 27, 35, 18, 27, 0, 29, 109, 144 ] Bmatlab = [0, 10, 10, 7, 15, 58] Cmatlab = [1, 4, 5, 0, 0, 0] Dmatlab = [0] np.testing.assert_allclose(np.array(pk.A).reshape(-1), Amatlab) np.testing.assert_allclose(np.array(pk.B).reshape(-1), Bmatlab) np.testing.assert_allclose(np.array(pk.C).reshape(-1), Cmatlab) np.testing.assert_allclose(np.array(pk.D).reshape(-1), Dmatlab) # case 2 pk = P.lft(K) Amatlab = [ 1, 2, 3, 4, 6, 12, -3, -2, 5, 11, 14, 31, -2, -3, 4, 3, 2, 7, 0.6, 3.4, 5, -0.6, -0.4, 0, 0.8, 6.2, 10, 0.2, -4.2, -4, 7.4, 33.6, 45, -0.4, -8.6, -3 ] Bmatlab = [] Cmatlab = [] Dmatlab = [] np.testing.assert_allclose(np.array(pk.A).reshape(-1), Amatlab) np.testing.assert_allclose(np.array(pk.B).reshape(-1), Bmatlab) np.testing.assert_allclose(np.array(pk.C).reshape(-1), Cmatlab) np.testing.assert_allclose(np.array(pk.D).reshape(-1), Dmatlab)
def test_constructor(self, sys322ABCD, dt, argfun): """Test different ways to call the StateSpace() constructor""" args, kwargs = argfun(sys322ABCD + dt) sys = StateSpace(*args, **kwargs) dtref = defaults['control.default_dt'] if len(dt) == 0 else dt[0] np.testing.assert_almost_equal(sys.A, sys322ABCD[0]) np.testing.assert_almost_equal(sys.B, sys322ABCD[1]) np.testing.assert_almost_equal(sys.C, sys322ABCD[2]) np.testing.assert_almost_equal(sys.D, sys322ABCD[3]) assert sys.dt == dtref
def mimoss(self, request): """Test system with various dt values""" n = 5 m = 3 p = 2 bx, bu = np.mgrid[1:n + 1, 1:m + 1] cy, cx = np.mgrid[1:p + 1, 1:n + 1] dy, du = np.mgrid[1:p + 1, 1:m + 1] return StateSpace( np.eye(5) + np.eye(5, 5, 1), bx * bu, cy * cx, dy * du, request.param)
def sys222(self): """2-states square system (2 inputs x 2 outputs)""" A222 = [[4., 1.], [2., -3]] B222 = [[5., 2.], [-3., -3.]] C222 = [[2., -4], [0., 1.]] D222 = [[3., 2.], [1., -1.]] return StateSpace(A222, B222, C222, D222)
def test_matrixStaticGain(self): """Regression: can we create matrix static gains?""" d1 = np.matrix([[1,2,3],[4,5,6]]) d2 = np.matrix([[7,8],[9,10],[11,12]]) g1=StateSpace([],[],[],d1) # _remove_useless_states was making A = [[0]] self.assertEqual((0,0), g1.A.shape) g2=StateSpace([],[],[],d2) g3=StateSpace([],[],[],d2.T) h1 = g1*g2 np.testing.assert_array_equal(d1*d2, h1.D) h2 = g1+g3 np.testing.assert_array_equal(d1+d2.T, h2.D) h3 = g1.feedback(g2) np.testing.assert_array_almost_equal(solve(np.eye(2)+d1*d2,d1), h3.D) h4 = g1.append(g2) np.testing.assert_array_equal(block_diag(d1,d2),h4.D)
def setUp(self): self.sys1 = TransferFunction([1, 2], [1, 2, 3]) self.sys2 = TransferFunction([1], [1, 2, 3, 4]) self.sys3 = StateSpace([[1., 4.], [3., 2.]], [[1.], [-4.]], [[1., 0.]], [[0.]]) s = TransferFunction([1, 0], [1]) self.sys4 = (8.75*(4*s**2+0.4*s+1))/((100*s+1)*(s**2+0.22*s+1)) * \ 1./(s**2/(10.**2)+2*0.04*s/10.+1) self.stability_margins4 = [ 2.2716, 97.5941, 1.0454, 10.0053, 0.0850, 0.4973 ]
def testMIMOMult(self): sys = StateSpace([[-0.5, 0.0], [0.0, -1.0]], [[1.0, 0.0], [0.0, 1.0]], [[1.0, 0.0], [0.0, 1.0]], [[0.0, 0.0], [0.0, 0.0]]) omega = np.logspace(-1, 2, 10) f1 = FRD(sys, omega) f2 = FRD(sys, omega) np.testing.assert_array_almost_equal( (f1 * f2).frequency_response([0.1, 1.0, 10])[0], (sys * sys).frequency_response([0.1, 1.0, 10])[0]) np.testing.assert_array_almost_equal( (f1 * f2).frequency_response([0.1, 1.0, 10])[1], (sys * sys).frequency_response([0.1, 1.0, 10])[1])
def test_call(self, dt, omega, resp): """Evaluate the frequency response at single frequencies""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) if dt: sys = sample_system(sys, dt) s = np.exp(omega * 1j * dt) else: s = omega * 1j # Correct versions of the call np.testing.assert_allclose(evalfr(sys, s), resp, atol=1e-3) np.testing.assert_allclose(sys(s), resp, atol=1e-3) # Deprecated name of the call (should generate error) with pytest.raises(AttributeError): sys.evalfr(omega)
def test_str(self, sys322): """Test that printing the system works""" tsys = sys322 tref = ("A = [[-3. 4. 2.]\n" " [-1. -3. 0.]\n" " [ 2. 5. 3.]]\n" "\n" "B = [[ 1. 4.]\n" " [-3. -3.]\n" " [-2. 1.]]\n" "\n" "C = [[ 4. 2. -3.]\n" " [ 1. 4. 3.]]\n" "\n" "D = [[-2. 4.]\n" " [ 0. 1.]]\n") assert str(tsys) == tref tsysdtunspec = StateSpace(tsys.A, tsys.B, tsys.C, tsys.D, True) assert str(tsysdtunspec) == tref + "\ndt = True\n" sysdt1 = StateSpace(tsys.A, tsys.B, tsys.C, tsys.D, 1.) assert str(sysdt1) == tref + "\ndt = {}\n".format(1.)
def testMIMOZero_nonsquare(self): """Evaluate the zeros of a MIMO system.""" z = np.sort(self.sys1.zero()) true_z = np.sort([44.41465, -0.490252, -5.924398]) np.testing.assert_array_almost_equal(z, true_z) A = np.array([[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 3, 0, 0, 0], [0, 0, 0, -4, 0, 0], [0, 0, 0, 0, -1, 0], [0, 0, 0, 0, 0, 3]]) B = np.array([[0, -1], [-1, 0], [1, -1], [0, 0], [0, 1], [-1, -1]]) C = np.array([[1, 0, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1], [0, 0, 1, 0, 0, 1]]) D = np.zeros((3, 2)) sys = StateSpace(A, B, C, D) z = np.sort(sys.zero()) true_z = np.sort([2., -1.]) np.testing.assert_array_almost_equal(z, true_z)
def testAppendTF(self): """Test appending a state-space system with a tf""" A1 = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] B1 = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] C1 = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] D1 = [[0., -0.8], [-0.3, 0.]] s = TransferFunction([1, 0], [1]) h = 1/(s+1)/(s+2) sys1 = StateSpace(A1, B1, C1, D1) sys2 = _convertToStateSpace(h) sys3c = sys1.append(sys2) np.testing.assert_array_almost_equal(sys1.A, sys3c.A[:3,:3]) np.testing.assert_array_almost_equal(sys1.B, sys3c.B[:3,:2]) np.testing.assert_array_almost_equal(sys1.C, sys3c.C[:2,:3]) np.testing.assert_array_almost_equal(sys1.D, sys3c.D[:2,:2]) np.testing.assert_array_almost_equal(sys2.A, sys3c.A[3:,3:]) np.testing.assert_array_almost_equal(sys2.B, sys3c.B[3:,2:]) np.testing.assert_array_almost_equal(sys2.C, sys3c.C[2:,3:]) np.testing.assert_array_almost_equal(sys2.D, sys3c.D[2:,2:]) np.testing.assert_array_almost_equal(sys3c.A[:3,3:], np.zeros( (3, 2)) ) np.testing.assert_array_almost_equal(sys3c.A[3:,:3], np.zeros( (2, 3)) )
def test_array_access_ss(self): sys1 = StateSpace([[1., 2.], [3., 4.]], [[5., 6.], [6., 8.]], [[9., 10.], [11., 12.]], [[13., 14.], [15., 16.]], 1) sys1_11 = sys1[0, 1] np.testing.assert_array_almost_equal(sys1_11.A, sys1.A) np.testing.assert_array_almost_equal(sys1_11.B, sys1.B[:, [1]]) np.testing.assert_array_almost_equal(sys1_11.C, sys1.C[[0], :]) np.testing.assert_array_almost_equal(sys1_11.D, sys1.D[0, 1]) assert sys1.dt == sys1_11.dt
def testAppendSS(self): """Test appending two state-space systems""" A1 = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] B1 = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] C1 = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] D1 = [[0., -0.8], [-0.3, 0.]] A2 = [[-1.]] B2 = [[1.2]] C2 = [[0.5]] D2 = [[0.4]] A3 = [[-2, 0.5, 0, 0], [0.5, -0.3, 0, 0], [0, 0, -0.1, 0], [0, 0, 0., -1.]] B3 = [[0.3, -1.3, 0], [0.1, 0., 0], [1.0, 0.0, 0], [0., 0, 1.2]] C3 = [[0., 0.1, 0.0, 0.0], [-0.3, -0.2, 0.0, 0.0], [0., 0., 0., 0.5]] D3 = [[0., -0.8, 0.], [-0.3, 0., 0.], [0., 0., 0.4]] sys1 = StateSpace(A1, B1, C1, D1) sys2 = StateSpace(A2, B2, C2, D2) sys3 = StateSpace(A3, B3, C3, D3) sys3c = sys1.append(sys2) np.testing.assert_array_almost_equal(sys3.A, sys3c.A) np.testing.assert_array_almost_equal(sys3.B, sys3c.B) np.testing.assert_array_almost_equal(sys3.C, sys3c.C) np.testing.assert_array_almost_equal(sys3.D, sys3c.D)
def setUp(self): """Set up a MIMO system to test operations on.""" A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] a = [[4., 1.], [2., -3]] b = [[5., 2.], [-3., -3.]] c = [[2., -4], [0., 1.]] d = [[3., 2.], [1., -1.]] self.sys1 = StateSpace(A, B, C, D) self.sys2 = StateSpace(a, b, c, d)
def test_dcgain_discr(self): """Test DC gain for discrete-time state-space systems""" # static gain sys = StateSpace([], [], [], 2, True) np.testing.assert_equal(sys.dcgain(), 2) # averaging filter sys = StateSpace(0.5, 0.5, 1, 0, True) np.testing.assert_almost_equal(sys.dcgain(), 1) # differencer sys = StateSpace(0, 1, -1, 1, True) np.testing.assert_equal(sys.dcgain(), 0) # summer sys = StateSpace(1, 1, 1, 0, True) np.testing.assert_equal(sys.dcgain(), np.nan)
def testFreqResp(self): """Evaluate the frequency response at multiple frequencies.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) truemag = [[[0.0852992637230322, 0.00103596611395218], [0.935374692849736, 0.799380720864549]], [[0.55656854563842, 0.301542699860857], [0.609178071542849, 0.0382108097985257]]] truephase = [[[-0.566195599644593, -1.68063565332582], [3.0465958317514, 3.14141384339534]], [[2.90457947657161, 3.10601268291914], [-0.438157380501337, -1.40720969147217]]] trueomega = [0.1, 10.] mag, phase, omega = sys.freqresp(trueomega) np.testing.assert_almost_equal(mag, truemag) np.testing.assert_almost_equal(phase, truephase) np.testing.assert_equal(omega, trueomega)
def test_dcgain(self): sys = StateSpace(-2.,6.,5.,0) np.testing.assert_equal(sys.dcgain(), 15.) sys2 = StateSpace(-2, [6., 4.], [[5.],[7.],[11]], np.zeros((3,2))) expected = np.array([[15., 10.], [21., 14.], [33., 22.]]) np.testing.assert_array_equal(sys2.dcgain(), expected) sys3 = StateSpace(0., 1., 1., 0.) np.testing.assert_equal(sys3.dcgain(), np.nan)
def test_dcgain_cont(self): """Test DC gain for continuous-time state-space systems""" sys = StateSpace(-2.,6.,5.,0) np.testing.assert_equal(sys.dcgain(), 15.) sys2 = StateSpace(-2, [6., 4.], [[5.],[7.],[11]], np.zeros((3,2))) expected = np.array([[15., 10.], [21., 14.], [33., 22.]]) np.testing.assert_array_equal(sys2.dcgain(), expected) sys3 = StateSpace(0., 1., 1., 0.) np.testing.assert_equal(sys3.dcgain(), np.nan)
class TestStateSpace(unittest.TestCase): """Tests for the StateSpace class.""" def setUp(self): """Set up a MIMO system to test operations on.""" A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] a = [[4., 1.], [2., -3]] b = [[5., 2.], [-3., -3.]] c = [[2., -4], [0., 1.]] d = [[3., 2.], [1., -1.]] self.sys1 = StateSpace(A, B, C, D) self.sys2 = StateSpace(a, b, c, d) def testPole(self): """Evaluate the poles of a MIMO system.""" p = self.sys1.pole() np.testing.assert_array_almost_equal(p, [3.34747678408874, -3.17373839204437 + 1.47492908003839j, -3.17373839204437 - 1.47492908003839j]) def testZero(self): """Evaluate the zeros of a SISO system.""" sys = StateSpace(self.sys1.A, [[3.], [-2.], [4.]], [[-1., 3., 2.]], [[-4.]]) z = sys.zero() np.testing.assert_array_almost_equal(z, [4.26864638637134, -3.75932319318567 + 1.10087776649554j, -3.75932319318567 - 1.10087776649554j]) def testAdd(self): """Add two MIMO systems.""" A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.], [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]] B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]] C = [[4., 2., -3., 2., -4.], [1., 4., 3., 0., 1.]] D = [[1., 6.], [1., 0.]] sys = self.sys1 + self.sys2 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def testSub(self): """Subtract two MIMO systems.""" A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.], [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]] B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]] C = [[4., 2., -3., -2., 4.], [1., 4., 3., 0., -1.]] D = [[-5., 2.], [-1., 2.]] sys = self.sys1 - self.sys2 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def testMul(self): """Multiply two MIMO systems.""" A = [[4., 1., 0., 0., 0.], [2., -3., 0., 0., 0.], [2., 0., -3., 4., 2.], [-6., 9., -1., -3., 0.], [-4., 9., 2., 5., 3.]] B = [[5., 2.], [-3., -3.], [7., -2.], [-12., -3.], [-5., -5.]] C = [[-4., 12., 4., 2., -3.], [0., 1., 1., 4., 3.]] D = [[-2., -8.], [1., -1.]] sys = self.sys1 * self.sys2 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def testEvalFr(self): """Evaluate the frequency response at one frequency.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) resp = [[4.37636761487965e-05 - 0.0152297592997812j, -0.792603938730853 + 0.0261706783369803j], [-0.331544857768052 + 0.0576105032822757j, 0.128919037199125 - 0.143824945295405j]] np.testing.assert_almost_equal(sys.evalfr(1.), resp) def testFreqResp(self): """Evaluate the frequency response at multiple frequencies.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) truemag = [[[0.0852992637230322, 0.00103596611395218], [0.935374692849736, 0.799380720864549]], [[0.55656854563842, 0.301542699860857], [0.609178071542849, 0.0382108097985257]]] truephase = [[[-0.566195599644593, -1.68063565332582], [3.0465958317514, 3.14141384339534]], [[2.90457947657161, 3.10601268291914], [-0.438157380501337, -1.40720969147217]]] trueomega = [0.1, 10.] mag, phase, omega = sys.freqresp(trueomega) np.testing.assert_almost_equal(mag, truemag) np.testing.assert_almost_equal(phase, truephase) np.testing.assert_equal(omega, trueomega)
class TestStateSpace(unittest.TestCase): """Tests for the StateSpace class.""" def setUp(self): """Set up a MIMO system to test operations on.""" A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] a = [[4., 1.], [2., -3]] b = [[5., 2.], [-3., -3.]] c = [[2., -4], [0., 1.]] d = [[3., 2.], [1., -1.]] self.sys1 = StateSpace(A, B, C, D) self.sys2 = StateSpace(a, b, c, d) def testPole(self): """Evaluate the poles of a MIMO system.""" p = self.sys1.pole() np.testing.assert_array_almost_equal(p, [3.34747678408874, -3.17373839204437 + 1.47492908003839j, -3.17373839204437 - 1.47492908003839j]) def testZero(self): """Evaluate the zeros of a SISO system.""" sys = StateSpace(self.sys1.A, [[3.], [-2.], [4.]], [[-1., 3., 2.]], [[-4.]]) z = sys.zero() np.testing.assert_array_almost_equal(z, [4.26864638637134, -3.75932319318567 + 1.10087776649554j, -3.75932319318567 - 1.10087776649554j]) def testAdd(self): """Add two MIMO systems.""" A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.], [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]] B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]] C = [[4., 2., -3., 2., -4.], [1., 4., 3., 0., 1.]] D = [[1., 6.], [1., 0.]] sys = self.sys1 + self.sys2 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def testSub(self): """Subtract two MIMO systems.""" A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.], [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]] B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]] C = [[4., 2., -3., -2., 4.], [1., 4., 3., 0., -1.]] D = [[-5., 2.], [-1., 2.]] sys = self.sys1 - self.sys2 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def testMul(self): """Multiply two MIMO systems.""" A = [[4., 1., 0., 0., 0.], [2., -3., 0., 0., 0.], [2., 0., -3., 4., 2.], [-6., 9., -1., -3., 0.], [-4., 9., 2., 5., 3.]] B = [[5., 2.], [-3., -3.], [7., -2.], [-12., -3.], [-5., -5.]] C = [[-4., 12., 4., 2., -3.], [0., 1., 1., 4., 3.]] D = [[-2., -8.], [1., -1.]] sys = self.sys1 * self.sys2 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def testEvalFr(self): """Evaluate the frequency response at one frequency.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) resp = [[4.37636761487965e-05 - 0.0152297592997812j, -0.792603938730853 + 0.0261706783369803j], [-0.331544857768052 + 0.0576105032822757j, 0.128919037199125 - 0.143824945295405j]] np.testing.assert_almost_equal(sys.evalfr(1.), resp) def testFreqResp(self): """Evaluate the frequency response at multiple frequencies.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) truemag = [[[0.0852992637230322, 0.00103596611395218], [0.935374692849736, 0.799380720864549]], [[0.55656854563842, 0.301542699860857], [0.609178071542849, 0.0382108097985257]]] truephase = [[[-0.566195599644593, -1.68063565332582], [3.0465958317514, 3.14141384339534]], [[2.90457947657161, 3.10601268291914], [-0.438157380501337, -1.40720969147217]]] trueomega = [0.1, 10.] mag, phase, omega = sys.freqresp(trueomega) np.testing.assert_almost_equal(mag, truemag) np.testing.assert_almost_equal(phase, truephase) np.testing.assert_equal(omega, trueomega) def testMinreal(self): """Test a minreal model reduction""" #A = [-2, 0.5, 0; 0.5, -0.3, 0; 0, 0, -0.1] A = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] #B = [0.3, -1.3; 0.1, 0; 1, 0] B = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] #C = [0, 0.1, 0; -0.3, -0.2, 0] C = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] #D = [0 -0.8; -0.3 0] D = [[0., -0.8], [-0.3, 0.]] # sys = ss(A, B, C, D) sys = StateSpace(A, B, C, D) sysr = sys.minreal() self.assertEqual(sysr.states, 2) self.assertEqual(sysr.inputs, sys.inputs) self.assertEqual(sysr.outputs, sys.outputs) np.testing.assert_array_almost_equal( eigvals(sysr.A), [-2.136154, -0.1638459]) def testAppendSS(self): """Test appending two state-space systems""" A1 = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] B1 = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] C1 = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] D1 = [[0., -0.8], [-0.3, 0.]] A2 = [[-1.]] B2 = [[1.2]] C2 = [[0.5]] D2 = [[0.4]] A3 = [[-2, 0.5, 0, 0], [0.5, -0.3, 0, 0], [0, 0, -0.1, 0], [0, 0, 0., -1.]] B3 = [[0.3, -1.3, 0], [0.1, 0., 0], [1.0, 0.0, 0], [0., 0, 1.2]] C3 = [[0., 0.1, 0.0, 0.0], [-0.3, -0.2, 0.0, 0.0], [0., 0., 0., 0.5]] D3 = [[0., -0.8, 0.], [-0.3, 0., 0.], [0., 0., 0.4]] sys1 = StateSpace(A1, B1, C1, D1) sys2 = StateSpace(A2, B2, C2, D2) sys3 = StateSpace(A3, B3, C3, D3) sys3c = sys1.append(sys2) np.testing.assert_array_almost_equal(sys3.A, sys3c.A) np.testing.assert_array_almost_equal(sys3.B, sys3c.B) np.testing.assert_array_almost_equal(sys3.C, sys3c.C) np.testing.assert_array_almost_equal(sys3.D, sys3c.D) def testAppendTF(self): """Test appending a state-space system with a tf""" A1 = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] B1 = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] C1 = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] D1 = [[0., -0.8], [-0.3, 0.]] s = TransferFunction([1, 0], [1]) h = 1/(s+1)/(s+2) sys1 = StateSpace(A1, B1, C1, D1) sys2 = _convertToStateSpace(h) sys3c = sys1.append(sys2) np.testing.assert_array_almost_equal(sys1.A, sys3c.A[:3,:3]) np.testing.assert_array_almost_equal(sys1.B, sys3c.B[:3,:2]) np.testing.assert_array_almost_equal(sys1.C, sys3c.C[:2,:3]) np.testing.assert_array_almost_equal(sys1.D, sys3c.D[:2,:2]) np.testing.assert_array_almost_equal(sys2.A, sys3c.A[3:,3:]) np.testing.assert_array_almost_equal(sys2.B, sys3c.B[3:,2:]) np.testing.assert_array_almost_equal(sys2.C, sys3c.C[2:,3:]) np.testing.assert_array_almost_equal(sys2.D, sys3c.D[2:,2:]) np.testing.assert_array_almost_equal(sys3c.A[:3,3:], np.zeros( (3, 2)) ) np.testing.assert_array_almost_equal(sys3c.A[3:,:3], np.zeros( (2, 3)) ) def testArrayAccessSS(self): sys1 = StateSpace([[1., 2.], [3., 4.]], [[5., 6.], [6., 8.]], [[9., 10.], [11., 12.]], [[13., 14.], [15., 16.]], 1) sys1_11 = sys1[0,1] np.testing.assert_array_almost_equal(sys1_11.A, sys1.A) np.testing.assert_array_almost_equal(sys1_11.B, sys1.B[:,1]) np.testing.assert_array_almost_equal(sys1_11.C, sys1.C[0,:]) np.testing.assert_array_almost_equal(sys1_11.D, sys1.D[0,1]) assert sys1.dt == sys1_11.dt
class TestStateSpace(unittest.TestCase): """Tests for the StateSpace class.""" def setUp(self): """Set up a MIMO system to test operations on.""" # sys1: 3-states square system (2 inputs x 2 outputs) A322 = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B322 = [[1., 4.], [-3., -3.], [-2., 1.]] C322 = [[4., 2., -3.], [1., 4., 3.]] D322 = [[-2., 4.], [0., 1.]] self.sys322 = StateSpace(A322, B322, C322, D322) # sys1: 2-states square system (2 inputs x 2 outputs) A222 = [[4., 1.], [2., -3]] B222 = [[5., 2.], [-3., -3.]] C222 = [[2., -4], [0., 1.]] D222 = [[3., 2.], [1., -1.]] self.sys222 = StateSpace(A222, B222, C222, D222) # sys3: 6 states non square system (2 inputs x 3 outputs) A623 = np.array([[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 3, 0, 0, 0], [0, 0, 0, -4, 0, 0], [0, 0, 0, 0, -1, 0], [0, 0, 0, 0, 0, 3]]) B623 = np.array([[0, -1], [-1, 0], [1, -1], [0, 0], [0, 1], [-1, -1]]) C623 = np.array([[1, 0, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1], [0, 0, 1, 0, 0, 1]]) D623 = np.zeros((3, 2)) self.sys623 = StateSpace(A623, B623, C623, D623) def test_pole(self): """Evaluate the poles of a MIMO system.""" p = np.sort(self.sys322.pole()) true_p = np.sort([3.34747678408874, -3.17373839204437 + 1.47492908003839j, -3.17373839204437 - 1.47492908003839j]) np.testing.assert_array_almost_equal(p, true_p) def test_zero_empty(self): """Test to make sure zero() works with no zeros in system.""" sys = _convertToStateSpace(TransferFunction([1], [1, 2, 1])) np.testing.assert_array_equal(sys.zero(), np.array([])) @unittest.skipIf(not slycot_check(), "slycot not installed") def test_zero_siso(self): """Evaluate the zeros of a SISO system.""" # extract only first input / first output system of sys222. This system is denoted sys111 # or tf111 tf111 = ss2tf(self.sys222) sys111 = tf2ss(tf111[0, 0]) # compute zeros as root of the characteristic polynomial at the numerator of tf111 # this method is simple and assumed as valid in this test true_z = np.sort(tf111[0, 0].zero()) # Compute the zeros through ab08nd, which is tested here z = np.sort(sys111.zero()) np.testing.assert_almost_equal(true_z, z) @unittest.skipIf(not slycot_check(), "slycot not installed") def test_zero_mimo_sys322_square(self): """Evaluate the zeros of a square MIMO system.""" z = np.sort(self.sys322.zero()) true_z = np.sort([44.41465, -0.490252, -5.924398]) np.testing.assert_array_almost_equal(z, true_z) @unittest.skipIf(not slycot_check(), "slycot not installed") def test_zero_mimo_sys222_square(self): """Evaluate the zeros of a square MIMO system.""" z = np.sort(self.sys222.zero()) true_z = np.sort([-10.568501, 3.368501]) np.testing.assert_array_almost_equal(z, true_z) @unittest.skipIf(not slycot_check(), "slycot not installed") def test_zero_mimo_sys623_non_square(self): """Evaluate the zeros of a non square MIMO system.""" z = np.sort(self.sys623.zero()) true_z = np.sort([2., -1.]) np.testing.assert_array_almost_equal(z, true_z) def test_add_ss(self): """Add two MIMO systems.""" A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.], [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]] B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]] C = [[4., 2., -3., 2., -4.], [1., 4., 3., 0., 1.]] D = [[1., 6.], [1., 0.]] sys = self.sys322 + self.sys222 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def test_subtract_ss(self): """Subtract two MIMO systems.""" A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.], [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]] B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]] C = [[4., 2., -3., -2., 4.], [1., 4., 3., 0., -1.]] D = [[-5., 2.], [-1., 2.]] sys = self.sys322 - self.sys222 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def test_multiply_ss(self): """Multiply two MIMO systems.""" A = [[4., 1., 0., 0., 0.], [2., -3., 0., 0., 0.], [2., 0., -3., 4., 2.], [-6., 9., -1., -3., 0.], [-4., 9., 2., 5., 3.]] B = [[5., 2.], [-3., -3.], [7., -2.], [-12., -3.], [-5., -5.]] C = [[-4., 12., 4., 2., -3.], [0., 1., 1., 4., 3.]] D = [[-2., -8.], [1., -1.]] sys = self.sys322 * self.sys222 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def test_evalfr(self): """Evaluate the frequency response at one frequency.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) resp = [[4.37636761487965e-05 - 0.0152297592997812j, -0.792603938730853 + 0.0261706783369803j], [-0.331544857768052 + 0.0576105032822757j, 0.128919037199125 - 0.143824945295405j]] # Correct versions of the call np.testing.assert_almost_equal(evalfr(sys, 1j), resp) np.testing.assert_almost_equal(sys._evalfr(1.), resp) # Deprecated version of the call (should generate warning) import warnings with warnings.catch_warnings(record=True) as w: # Set up warnings filter to only show warnings in control module warnings.filterwarnings("ignore") warnings.filterwarnings("always", module="control") # Make sure that we get a pending deprecation warning sys.evalfr(1.) assert len(w) == 1 assert issubclass(w[-1].category, PendingDeprecationWarning) @unittest.skipIf(not slycot_check(), "slycot not installed") def test_freq_resp(self): """Evaluate the frequency response at multiple frequencies.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) true_mag = [[[0.0852992637230322, 0.00103596611395218], [0.935374692849736, 0.799380720864549]], [[0.55656854563842, 0.301542699860857], [0.609178071542849, 0.0382108097985257]]] true_phase = [[[-0.566195599644593, -1.68063565332582], [3.0465958317514, 3.14141384339534]], [[2.90457947657161, 3.10601268291914], [-0.438157380501337, -1.40720969147217]]] true_omega = [0.1, 10.] mag, phase, omega = sys.freqresp(true_omega) np.testing.assert_almost_equal(mag, true_mag) np.testing.assert_almost_equal(phase, true_phase) np.testing.assert_equal(omega, true_omega) @unittest.skipIf(not slycot_check(), "slycot not installed") def test_minreal(self): """Test a minreal model reduction.""" # A = [-2, 0.5, 0; 0.5, -0.3, 0; 0, 0, -0.1] A = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] # B = [0.3, -1.3; 0.1, 0; 1, 0] B = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] # C = [0, 0.1, 0; -0.3, -0.2, 0] C = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] # D = [0 -0.8; -0.3 0] D = [[0., -0.8], [-0.3, 0.]] # sys = ss(A, B, C, D) sys = StateSpace(A, B, C, D) sysr = sys.minreal() self.assertEqual(sysr.states, 2) self.assertEqual(sysr.inputs, sys.inputs) self.assertEqual(sysr.outputs, sys.outputs) np.testing.assert_array_almost_equal( eigvals(sysr.A), [-2.136154, -0.1638459]) def test_append_ss(self): """Test appending two state-space systems.""" A1 = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] B1 = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] C1 = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] D1 = [[0., -0.8], [-0.3, 0.]] A2 = [[-1.]] B2 = [[1.2]] C2 = [[0.5]] D2 = [[0.4]] A3 = [[-2, 0.5, 0, 0], [0.5, -0.3, 0, 0], [0, 0, -0.1, 0], [0, 0, 0., -1.]] B3 = [[0.3, -1.3, 0], [0.1, 0., 0], [1.0, 0.0, 0], [0., 0, 1.2]] C3 = [[0., 0.1, 0.0, 0.0], [-0.3, -0.2, 0.0, 0.0], [0., 0., 0., 0.5]] D3 = [[0., -0.8, 0.], [-0.3, 0., 0.], [0., 0., 0.4]] sys1 = StateSpace(A1, B1, C1, D1) sys2 = StateSpace(A2, B2, C2, D2) sys3 = StateSpace(A3, B3, C3, D3) sys3c = sys1.append(sys2) np.testing.assert_array_almost_equal(sys3.A, sys3c.A) np.testing.assert_array_almost_equal(sys3.B, sys3c.B) np.testing.assert_array_almost_equal(sys3.C, sys3c.C) np.testing.assert_array_almost_equal(sys3.D, sys3c.D) def test_append_tf(self): """Test appending a state-space system with a tf""" A1 = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] B1 = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] C1 = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] D1 = [[0., -0.8], [-0.3, 0.]] s = TransferFunction([1, 0], [1]) h = 1 / (s + 1) / (s + 2) sys1 = StateSpace(A1, B1, C1, D1) sys2 = _convertToStateSpace(h) sys3c = sys1.append(sys2) np.testing.assert_array_almost_equal(sys1.A, sys3c.A[:3, :3]) np.testing.assert_array_almost_equal(sys1.B, sys3c.B[:3, :2]) np.testing.assert_array_almost_equal(sys1.C, sys3c.C[:2, :3]) np.testing.assert_array_almost_equal(sys1.D, sys3c.D[:2, :2]) np.testing.assert_array_almost_equal(sys2.A, sys3c.A[3:, 3:]) np.testing.assert_array_almost_equal(sys2.B, sys3c.B[3:, 2:]) np.testing.assert_array_almost_equal(sys2.C, sys3c.C[2:, 3:]) np.testing.assert_array_almost_equal(sys2.D, sys3c.D[2:, 2:]) np.testing.assert_array_almost_equal(sys3c.A[:3, 3:], np.zeros((3, 2))) np.testing.assert_array_almost_equal(sys3c.A[3:, :3], np.zeros((2, 3))) def test_array_access_ss(self): sys1 = StateSpace([[1., 2.], [3., 4.]], [[5., 6.], [6., 8.]], [[9., 10.], [11., 12.]], [[13., 14.], [15., 16.]], 1) sys1_11 = sys1[0, 1] np.testing.assert_array_almost_equal(sys1_11.A, sys1.A) np.testing.assert_array_almost_equal(sys1_11.B, sys1.B[:, 1]) np.testing.assert_array_almost_equal(sys1_11.C, sys1.C[0, :]) np.testing.assert_array_almost_equal(sys1_11.D, sys1.D[0, 1]) assert sys1.dt == sys1_11.dt def test_dc_gain_cont(self): """Test DC gain for continuous-time state-space systems.""" sys = StateSpace(-2., 6., 5., 0) np.testing.assert_equal(sys.dcgain(), 15.) sys2 = StateSpace(-2, [6., 4.], [[5.], [7.], [11]], np.zeros((3, 2))) expected = np.array([[15., 10.], [21., 14.], [33., 22.]]) np.testing.assert_array_equal(sys2.dcgain(), expected) sys3 = StateSpace(0., 1., 1., 0.) np.testing.assert_equal(sys3.dcgain(), np.nan) def test_dc_gain_discr(self): """Test DC gain for discrete-time state-space systems.""" # static gain sys = StateSpace([], [], [], 2, True) np.testing.assert_equal(sys.dcgain(), 2) # averaging filter sys = StateSpace(0.5, 0.5, 1, 0, True) np.testing.assert_almost_equal(sys.dcgain(), 1) # differencer sys = StateSpace(0, 1, -1, 1, True) np.testing.assert_equal(sys.dcgain(), 0) # summer sys = StateSpace(1, 1, 1, 0, True) np.testing.assert_equal(sys.dcgain(), np.nan) def test_dc_gain_integrator(self): """DC gain when eigenvalue at DC returns appropriately sized array of nan.""" # the SISO case is also tested in test_dc_gain_{cont,discr} import itertools # iterate over input and output sizes, and continuous (dt=None) and discrete (dt=True) time for inputs, outputs, dt in itertools.product(range(1, 6), range(1, 6), [None, True]): states = max(inputs, outputs) # a matrix that is singular at DC, and has no "useless" states as in # _remove_useless_states a = np.triu(np.tile(2, (states, states))) # eigenvalues all +2, except for ... a[0, 0] = 0 if dt is None else 1 b = np.eye(max(inputs, states))[:states, :inputs] c = np.eye(max(outputs, states))[:outputs, :states] d = np.zeros((outputs, inputs)) sys = StateSpace(a, b, c, d, dt) dc = np.squeeze(np.tile(np.nan, (outputs, inputs))) np.testing.assert_array_equal(dc, sys.dcgain()) def test_scalar_static_gain(self): """Regression: can we create a scalar static gain?""" g1 = StateSpace([], [], [], [2]) g2 = StateSpace([], [], [], [3]) # make sure StateSpace internals, specifically ABC matrix # sizes, are OK for LTI operations g3 = g1 * g2 self.assertEqual(6, g3.D[0, 0]) g4 = g1 + g2 self.assertEqual(5, g4.D[0, 0]) g5 = g1.feedback(g2) self.assertAlmostEqual(2. / 7, g5.D[0, 0]) g6 = g1.append(g2) np.testing.assert_array_equal(np.diag([2, 3]), g6.D) def test_matrix_static_gain(self): """Regression: can we create matrix static gains?""" d1 = np.matrix([[1, 2, 3], [4, 5, 6]]) d2 = np.matrix([[7, 8], [9, 10], [11, 12]]) g1 = StateSpace([], [], [], d1) # _remove_useless_states was making A = [[0]] self.assertEqual((0, 0), g1.A.shape) g2 = StateSpace([], [], [], d2) g3 = StateSpace([], [], [], d2.T) h1 = g1 * g2 np.testing.assert_array_equal(d1 * d2, h1.D) h2 = g1 + g3 np.testing.assert_array_equal(d1 + d2.T, h2.D) h3 = g1.feedback(g2) np.testing.assert_array_almost_equal( solve(np.eye(2) + d1 * d2, d1), h3.D) h4 = g1.append(g2) np.testing.assert_array_equal(block_diag(d1, d2), h4.D) def test_remove_useless_states(self): """Regression: _remove_useless_states gives correct ABC sizes.""" g1 = StateSpace(np.zeros((3, 3)), np.zeros((3, 4)), np.zeros((5, 3)), np.zeros((5, 4))) self.assertEqual((0, 0), g1.A.shape) self.assertEqual((0, 4), g1.B.shape) self.assertEqual((5, 0), g1.C.shape) self.assertEqual((5, 4), g1.D.shape) self.assertEqual(0, g1.states) def test_bad_empty_matrices(self): """Mismatched ABCD matrices when some are empty.""" self.assertRaises(ValueError, StateSpace, [1], [], [], [1]) self.assertRaises(ValueError, StateSpace, [1], [1], [], [1]) self.assertRaises(ValueError, StateSpace, [1], [], [1], [1]) self.assertRaises(ValueError, StateSpace, [], [1], [], [1]) self.assertRaises(ValueError, StateSpace, [], [1], [1], [1]) self.assertRaises(ValueError, StateSpace, [], [], [1], [1]) self.assertRaises(ValueError, StateSpace, [1], [1], [1], []) def test_minreal_static_gain(self): """Regression: minreal on static gain was failing.""" g1 = StateSpace([], [], [], [1]) g2 = g1.minreal() np.testing.assert_array_equal(g1.A, g2.A) np.testing.assert_array_equal(g1.B, g2.B) np.testing.assert_array_equal(g1.C, g2.C) np.testing.assert_array_equal(g1.D, g2.D) def test_empty(self): """Regression: can we create an empty StateSpace object?""" g1 = StateSpace([], [], [], []) self.assertEqual(0, g1.states) self.assertEqual(0, g1.inputs) self.assertEqual(0, g1.outputs) def test_matrix_to_state_space(self): """_convertToStateSpace(matrix) gives ss([],[],[],D)""" D = np.matrix([[1, 2, 3], [4, 5, 6]]) g = _convertToStateSpace(D) def empty(shape): m = np.matrix([]) m.shape = shape return m np.testing.assert_array_equal(empty((0, 0)), g.A) np.testing.assert_array_equal(empty((0, D.shape[1])), g.B) np.testing.assert_array_equal(empty((D.shape[0], 0)), g.C) np.testing.assert_array_equal(D, g.D) def test_lft(self): """ test lft function with result obtained from matlab implementation""" # test case A = [[1, 2, 3], [1, 4, 5], [2, 3, 4]] B = [[0, 2], [5, 6], [5, 2]] C = [[1, 4, 5], [2, 3, 0]] D = [[0, 0], [3, 0]] P = StateSpace(A, B, C, D) Ak = [[0, 2, 3], [2, 3, 5], [2, 1, 9]] Bk = [[1, 1], [2, 3], [9, 4]] Ck = [[1, 4, 5], [2, 3, 6]] Dk = [[0, 2], [0, 0]] K = StateSpace(Ak, Bk, Ck, Dk) # case 1 pk = P.lft(K, 2, 1) Amatlab = [1, 2, 3, 4, 6, 12, 1, 4, 5, 17, 38, 61, 2, 3, 4, 9, 26, 37, 2, 3, 0, 3, 14, 18, 4, 6, 0, 8, 27, 35, 18, 27, 0, 29, 109, 144] Bmatlab = [0, 10, 10, 7, 15, 58] Cmatlab = [1, 4, 5, 0, 0, 0] Dmatlab = [0] np.testing.assert_allclose(np.array(pk.A).reshape(-1), Amatlab) np.testing.assert_allclose(np.array(pk.B).reshape(-1), Bmatlab) np.testing.assert_allclose(np.array(pk.C).reshape(-1), Cmatlab) np.testing.assert_allclose(np.array(pk.D).reshape(-1), Dmatlab) # case 2 pk = P.lft(K) Amatlab = [1, 2, 3, 4, 6, 12, -3, -2, 5, 11, 14, 31, -2, -3, 4, 3, 2, 7, 0.6, 3.4, 5, -0.6, -0.4, 0, 0.8, 6.2, 10, 0.2, -4.2, -4, 7.4, 33.6, 45, -0.4, -8.6, -3] Bmatlab = [] Cmatlab = [] Dmatlab = [] np.testing.assert_allclose(np.array(pk.A).reshape(-1), Amatlab) np.testing.assert_allclose(np.array(pk.B).reshape(-1), Bmatlab) np.testing.assert_allclose(np.array(pk.C).reshape(-1), Cmatlab) np.testing.assert_allclose(np.array(pk.D).reshape(-1), Dmatlab)
class TestStateSpace(unittest.TestCase): """Tests for the StateSpace class.""" def setUp(self): """Set up a MIMO system to test operations on.""" A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] a = [[4., 1.], [2., -3]] b = [[5., 2.], [-3., -3.]] c = [[2., -4], [0., 1.]] d = [[3., 2.], [1., -1.]] self.sys1 = StateSpace(A, B, C, D) self.sys2 = StateSpace(a, b, c, d) def testPole(self): """Evaluate the poles of a MIMO system.""" p = np.sort(self.sys1.pole()) true_p = np.sort([3.34747678408874, -3.17373839204437 + 1.47492908003839j, -3.17373839204437 - 1.47492908003839j]) np.testing.assert_array_almost_equal(p, true_p) def testZero(self): """Evaluate the zeros of a SISO system.""" sys = StateSpace(self.sys1.A, [[3.], [-2.], [4.]], [[-1., 3., 2.]], [[-4.]]) z = sys.zero() np.testing.assert_array_almost_equal(z, [4.26864638637134, -3.75932319318567 + 1.10087776649554j, -3.75932319318567 - 1.10087776649554j]) def testAdd(self): """Add two MIMO systems.""" A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.], [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]] B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]] C = [[4., 2., -3., 2., -4.], [1., 4., 3., 0., 1.]] D = [[1., 6.], [1., 0.]] sys = self.sys1 + self.sys2 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def testSub(self): """Subtract two MIMO systems.""" A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.], [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]] B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]] C = [[4., 2., -3., -2., 4.], [1., 4., 3., 0., -1.]] D = [[-5., 2.], [-1., 2.]] sys = self.sys1 - self.sys2 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def testMul(self): """Multiply two MIMO systems.""" A = [[4., 1., 0., 0., 0.], [2., -3., 0., 0., 0.], [2., 0., -3., 4., 2.], [-6., 9., -1., -3., 0.], [-4., 9., 2., 5., 3.]] B = [[5., 2.], [-3., -3.], [7., -2.], [-12., -3.], [-5., -5.]] C = [[-4., 12., 4., 2., -3.], [0., 1., 1., 4., 3.]] D = [[-2., -8.], [1., -1.]] sys = self.sys1 * self.sys2 np.testing.assert_array_almost_equal(sys.A, A) np.testing.assert_array_almost_equal(sys.B, B) np.testing.assert_array_almost_equal(sys.C, C) np.testing.assert_array_almost_equal(sys.D, D) def testEvalFr(self): """Evaluate the frequency response at one frequency.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) resp = [[4.37636761487965e-05 - 0.0152297592997812j, -0.792603938730853 + 0.0261706783369803j], [-0.331544857768052 + 0.0576105032822757j, 0.128919037199125 - 0.143824945295405j]] np.testing.assert_almost_equal(sys.evalfr(1.), resp) @unittest.skipIf(not slycot_check(), "slycot not installed") def testFreqResp(self): """Evaluate the frequency response at multiple frequencies.""" A = [[-2, 0.5], [0.5, -0.3]] B = [[0.3, -1.3], [0.1, 0.]] C = [[0., 0.1], [-0.3, -0.2]] D = [[0., -0.8], [-0.3, 0.]] sys = StateSpace(A, B, C, D) truemag = [[[0.0852992637230322, 0.00103596611395218], [0.935374692849736, 0.799380720864549]], [[0.55656854563842, 0.301542699860857], [0.609178071542849, 0.0382108097985257]]] truephase = [[[-0.566195599644593, -1.68063565332582], [3.0465958317514, 3.14141384339534]], [[2.90457947657161, 3.10601268291914], [-0.438157380501337, -1.40720969147217]]] trueomega = [0.1, 10.] mag, phase, omega = sys.freqresp(trueomega) np.testing.assert_almost_equal(mag, truemag) np.testing.assert_almost_equal(phase, truephase) np.testing.assert_equal(omega, trueomega) @unittest.skipIf(not slycot_check(), "slycot not installed") def testMinreal(self): """Test a minreal model reduction""" #A = [-2, 0.5, 0; 0.5, -0.3, 0; 0, 0, -0.1] A = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] #B = [0.3, -1.3; 0.1, 0; 1, 0] B = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] #C = [0, 0.1, 0; -0.3, -0.2, 0] C = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] #D = [0 -0.8; -0.3 0] D = [[0., -0.8], [-0.3, 0.]] # sys = ss(A, B, C, D) sys = StateSpace(A, B, C, D) sysr = sys.minreal() self.assertEqual(sysr.states, 2) self.assertEqual(sysr.inputs, sys.inputs) self.assertEqual(sysr.outputs, sys.outputs) np.testing.assert_array_almost_equal( eigvals(sysr.A), [-2.136154, -0.1638459]) def testAppendSS(self): """Test appending two state-space systems""" A1 = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] B1 = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] C1 = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] D1 = [[0., -0.8], [-0.3, 0.]] A2 = [[-1.]] B2 = [[1.2]] C2 = [[0.5]] D2 = [[0.4]] A3 = [[-2, 0.5, 0, 0], [0.5, -0.3, 0, 0], [0, 0, -0.1, 0], [0, 0, 0., -1.]] B3 = [[0.3, -1.3, 0], [0.1, 0., 0], [1.0, 0.0, 0], [0., 0, 1.2]] C3 = [[0., 0.1, 0.0, 0.0], [-0.3, -0.2, 0.0, 0.0], [0., 0., 0., 0.5]] D3 = [[0., -0.8, 0.], [-0.3, 0., 0.], [0., 0., 0.4]] sys1 = StateSpace(A1, B1, C1, D1) sys2 = StateSpace(A2, B2, C2, D2) sys3 = StateSpace(A3, B3, C3, D3) sys3c = sys1.append(sys2) np.testing.assert_array_almost_equal(sys3.A, sys3c.A) np.testing.assert_array_almost_equal(sys3.B, sys3c.B) np.testing.assert_array_almost_equal(sys3.C, sys3c.C) np.testing.assert_array_almost_equal(sys3.D, sys3c.D) def testAppendTF(self): """Test appending a state-space system with a tf""" A1 = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]] B1 = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]] C1 = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]] D1 = [[0., -0.8], [-0.3, 0.]] s = TransferFunction([1, 0], [1]) h = 1/(s+1)/(s+2) sys1 = StateSpace(A1, B1, C1, D1) sys2 = _convertToStateSpace(h) sys3c = sys1.append(sys2) np.testing.assert_array_almost_equal(sys1.A, sys3c.A[:3,:3]) np.testing.assert_array_almost_equal(sys1.B, sys3c.B[:3,:2]) np.testing.assert_array_almost_equal(sys1.C, sys3c.C[:2,:3]) np.testing.assert_array_almost_equal(sys1.D, sys3c.D[:2,:2]) np.testing.assert_array_almost_equal(sys2.A, sys3c.A[3:,3:]) np.testing.assert_array_almost_equal(sys2.B, sys3c.B[3:,2:]) np.testing.assert_array_almost_equal(sys2.C, sys3c.C[2:,3:]) np.testing.assert_array_almost_equal(sys2.D, sys3c.D[2:,2:]) np.testing.assert_array_almost_equal(sys3c.A[:3,3:], np.zeros( (3, 2)) ) np.testing.assert_array_almost_equal(sys3c.A[3:,:3], np.zeros( (2, 3)) ) def testArrayAccessSS(self): sys1 = StateSpace([[1., 2.], [3., 4.]], [[5., 6.], [6., 8.]], [[9., 10.], [11., 12.]], [[13., 14.], [15., 16.]], 1) sys1_11 = sys1[0,1] np.testing.assert_array_almost_equal(sys1_11.A, sys1.A) np.testing.assert_array_almost_equal(sys1_11.B, sys1.B[:,1]) np.testing.assert_array_almost_equal(sys1_11.C, sys1.C[0,:]) np.testing.assert_array_almost_equal(sys1_11.D, sys1.D[0,1]) assert sys1.dt == sys1_11.dt def test_dcgain_cont(self): """Test DC gain for continuous-time state-space systems""" sys = StateSpace(-2.,6.,5.,0) np.testing.assert_equal(sys.dcgain(), 15.) sys2 = StateSpace(-2, [6., 4.], [[5.],[7.],[11]], np.zeros((3,2))) expected = np.array([[15., 10.], [21., 14.], [33., 22.]]) np.testing.assert_array_equal(sys2.dcgain(), expected) sys3 = StateSpace(0., 1., 1., 0.) np.testing.assert_equal(sys3.dcgain(), np.nan) def test_dcgain_discr(self): """Test DC gain for discrete-time state-space systems""" # static gain sys = StateSpace([], [], [], 2, True) np.testing.assert_equal(sys.dcgain(), 2) # averaging filter sys = StateSpace(0.5, 0.5, 1, 0, True) np.testing.assert_almost_equal(sys.dcgain(), 1) # differencer sys = StateSpace(0, 1, -1, 1, True) np.testing.assert_equal(sys.dcgain(), 0) # summer sys = StateSpace(1, 1, 1, 0, True) np.testing.assert_equal(sys.dcgain(), np.nan) def test_dcgain_integrator(self): """DC gain when eigenvalue at DC returns appropriately sized array of nan""" # the SISO case is also tested in test_dc_gain_{cont,discr} import itertools # iterate over input and output sizes, and continuous (dt=None) and discrete (dt=True) time for inputs,outputs,dt in itertools.product(range(1,6),range(1,6),[None,True]): states = max(inputs,outputs) # a matrix that is singular at DC, and has no "useless" states as in _remove_useless_states a = np.triu(np.tile(2,(states,states))) # eigenvalues all +2, except for ... a[0,0] = 0 if dt is None else 1 b = np.eye(max(inputs,states))[:states,:inputs] c = np.eye(max(outputs,states))[:outputs,:states] d = np.zeros((outputs,inputs)) sys = StateSpace(a,b,c,d,dt) dc = np.squeeze(np.tile(np.nan,(outputs,inputs))) np.testing.assert_array_equal(dc, sys.dcgain()) def test_scalarStaticGain(self): """Regression: can we create a scalar static gain?""" g1=StateSpace([],[],[],[2]) g2=StateSpace([],[],[],[3]) # make sure StateSpace internals, specifically ABC matrix # sizes, are OK for LTI operations g3 = g1*g2 self.assertEqual(6, g3.D[0,0]) g4 = g1+g2 self.assertEqual(5, g4.D[0,0]) g5 = g1.feedback(g2) self.assertAlmostEqual(2./7, g5.D[0,0]) g6 = g1.append(g2) np.testing.assert_array_equal(np.diag([2,3]),g6.D) def test_matrixStaticGain(self): """Regression: can we create matrix static gains?""" d1 = np.matrix([[1,2,3],[4,5,6]]) d2 = np.matrix([[7,8],[9,10],[11,12]]) g1=StateSpace([],[],[],d1) # _remove_useless_states was making A = [[0]] self.assertEqual((0,0), g1.A.shape) g2=StateSpace([],[],[],d2) g3=StateSpace([],[],[],d2.T) h1 = g1*g2 np.testing.assert_array_equal(d1*d2, h1.D) h2 = g1+g3 np.testing.assert_array_equal(d1+d2.T, h2.D) h3 = g1.feedback(g2) np.testing.assert_array_almost_equal(solve(np.eye(2)+d1*d2,d1), h3.D) h4 = g1.append(g2) np.testing.assert_array_equal(block_diag(d1,d2),h4.D) def test_remove_useless_states(self): """Regression: _remove_useless_states gives correct ABC sizes""" g1 = StateSpace(np.zeros((3,3)), np.zeros((3,4)), np.zeros((5,3)), np.zeros((5,4))) self.assertEqual((0,0), g1.A.shape) self.assertEqual((0,4), g1.B.shape) self.assertEqual((5,0), g1.C.shape) self.assertEqual((5,4), g1.D.shape) self.assertEqual(0, g1.states) def test_BadEmptyMatrices(self): """Mismatched ABCD matrices when some are empty""" self.assertRaises(ValueError,StateSpace, [1], [], [], [1]) self.assertRaises(ValueError,StateSpace, [1], [1], [], [1]) self.assertRaises(ValueError,StateSpace, [1], [], [1], [1]) self.assertRaises(ValueError,StateSpace, [], [1], [], [1]) self.assertRaises(ValueError,StateSpace, [], [1], [1], [1]) self.assertRaises(ValueError,StateSpace, [], [], [1], [1]) self.assertRaises(ValueError,StateSpace, [1], [1], [1], []) def test_minrealStaticGain(self): """Regression: minreal on static gain was failing""" g1 = StateSpace([],[],[],[1]) g2 = g1.minreal() np.testing.assert_array_equal(g1.A, g2.A) np.testing.assert_array_equal(g1.B, g2.B) np.testing.assert_array_equal(g1.C, g2.C) np.testing.assert_array_equal(g1.D, g2.D)