Example #1
0
    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)
Example #2
0
 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)
Example #3
0
    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])
Example #4
0
 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])
Example #5
0
 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)
Example #7
0
    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)
Example #8
0
    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)
Example #9
0
    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)
Example #10
0
    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)
Example #11
0
 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)
Example #12
0
    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())
Example #13
0
 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)
Example #15
0
 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)
Example #16
0
 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
Example #17
0
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
Example #18
0
    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())
Example #19
0
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]])
Example #21
0
    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)
Example #22
0
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]
Example #23
0
    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)
Example #26
0
 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)
Example #27
0
    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])
Example #28
0
 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 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 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)
Example #33
0
    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)
Example #35
0
 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)
Example #36
0
    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)
Example #37
0
 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
     ]
Example #38
0
 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])
Example #39
0
    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)
Example #40
0
 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.)
Example #41
0
    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)
Example #42
0
 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
Example #44
0
 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)
Example #45
0
    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)
Example #46
0
    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)
Example #47
0
    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)
Example #48
0
    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)
Example #49
0
    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)
Example #50
0
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
Example #52
0
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)
Example #53
0
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)