def testDcgain(self):
        #Create different forms of a SISO system
        A, B, C, D = self.siso_ss1.A, self.siso_ss1.B, self.siso_ss1.C, \
                     self.siso_ss1.D
        Z, P, k = sp.signal.ss2zpk(A, B, C, D)
        num, den = sp.signal.ss2tf(A, B, C, D)
        sys_ss = self.siso_ss1

        #Compute the gain with ``dcgain``
        gain_abcd = dcgain(A, B, C, D)
        gain_zpk = dcgain(Z, P, k)
        gain_numden = dcgain(np.squeeze(num), den)
        gain_sys_ss = dcgain(sys_ss)
        # print('\ngain_abcd:', gain_abcd, 'gain_zpk:', gain_zpk)
        # print('gain_numden:', gain_numden, 'gain_sys_ss:', gain_sys_ss)

        #Compute the gain with a long simulation
        t = linspace(0, 1000, 1000)
        y, _t = step(sys_ss, t)
        gain_sim = y[-1]
        # print('gain_sim:', gain_sim)

        #All gain values must be approximately equal to the known gain
        np.testing.assert_array_almost_equal(
            [gain_abcd, gain_zpk, gain_numden, gain_sys_ss, gain_sim],
            [59, 59, 59, 59, 59])

        if slycot_check():
            # Test with MIMO system, which contains ``siso_ss1`` twice
            gain_mimo = dcgain(self.mimo_ss1)
            # print('gain_mimo: \n', gain_mimo)
            np.testing.assert_array_almost_equal(gain_mimo,
                                                 [[59., 0], [0, 59.]])
Example #2
0
    def testLsim(self):
        t = np.linspace(0, 1, 10)

        #compute step response - test with state space, and transfer function
        #objects
        u = np.array([1., 1, 1, 1, 1, 1, 1, 1, 1, 1])
        youttrue = np.array([9., 17.6457, 24.7072, 30.4855, 35.2234, 39.1165,
                             42.3227, 44.9694, 47.1599, 48.9776])
        yout, tout, _xout = lsim(self.siso_ss1, u, t)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)
        yout, _t, _xout = lsim(self.siso_tf3, u, t)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)

        #test with initial value and special algorithm for ``U=0``
        u=0
        x0 = np.matrix(".5; 1.")
        youttrue = np.array([11., 8.1494, 5.9361, 4.2258, 2.9118, 1.9092,
                             1.1508, 0.5833, 0.1645, -0.1391])
        yout, _t, _xout = lsim(self.siso_ss1, u, t, x0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)

        if slycot_check():
            #Test MIMO system, which contains ``siso_ss1`` twice
            #first system: initial value, second system: step response
            u = np.array([[0., 1.], [0, 1], [0, 1], [0, 1], [0, 1],
                          [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]])
            x0 = np.matrix(".5; 1; 0; 0")
            youttrue = np.array([[11., 9.], [8.1494, 17.6457],
                                 [5.9361, 24.7072], [4.2258, 30.4855],
                                 [2.9118, 35.2234], [1.9092, 39.1165],
                                 [1.1508, 42.3227], [0.5833, 44.9694],
                                 [0.1645, 47.1599], [-0.1391, 48.9776]])
            yout, _t, _xout = lsim(self.mimo_ss1, u, t, x0)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
    def testConvertMIMO(self):
        """Test state space to transfer function conversion.

        Do a MIMO conversion and make sure that it is processed
        correctly both with and without slycot

        Example from issue gh-120, jgoppert
        """

        # Set up a 1x3 transfer function (should always work)
        tsys = tf([[[-235, 1.146e4],
                    [-235, 1.146E4],
                    [-235, 1.146E4, 0]]],
                  [[[1, 48.78, 0],
                    [1, 48.78, 0, 0],
                    [0.008, 1.39, 48.78]]])

        # Convert to state space and look for an error
        if (not slycot_check()):
            with pytest.raises(TypeError):
                tf2ss(tsys)
        else:
            ssys = tf2ss(tsys)
            assert ssys.B.shape[1] == 3
            assert ssys.C.shape[0] == 1
Example #4
0
    def test_dcgain(self):
        """Test function dcgain with different systems"""
        if slycot_check():
            #Test MIMO systems
            A, B, C, D = self.make_MIMO_mats()

            gain1 = dcgain(ss(A, B, C, D))
            gain2 = dcgain(A, B, C, D)
            sys_tf = ss2tf(A, B, C, D)
            gain3 = dcgain(sys_tf)
            gain4 = dcgain(sys_tf.num, sys_tf.den)
            #print("gain1:", gain1)

            assert_array_almost_equal(gain1,
                                      array([[0.0269, 0.], [0., 0.0269]]),
                                      decimal=4)
            assert_array_almost_equal(gain1, gain2)
            assert_array_almost_equal(gain3, gain4)
            assert_array_almost_equal(gain1, gain4)

        #Test SISO systems
        A, B, C, D = self.make_SISO_mats()

        gain1 = dcgain(ss(A, B, C, D))
        assert_array_almost_equal(gain1, array([[0.0269]]), decimal=4)
    def testDcgain(self):
        #Create different forms of a SISO system
        A, B, C, D = self.siso_ss1.A, self.siso_ss1.B, self.siso_ss1.C, \
                     self.siso_ss1.D
        Z, P, k = sp.signal.ss2zpk(A, B, C, D)
        num, den = sp.signal.ss2tf(A, B, C, D)
        sys_ss = self.siso_ss1

        #Compute the gain with ``dcgain``
        gain_abcd = dcgain(A, B, C, D)
        gain_zpk = dcgain(Z, P, k)
        gain_numden = dcgain(np.squeeze(num), den)
        gain_sys_ss = dcgain(sys_ss)
        # print('\ngain_abcd:', gain_abcd, 'gain_zpk:', gain_zpk)
        # print('gain_numden:', gain_numden, 'gain_sys_ss:', gain_sys_ss)

        #Compute the gain with a long simulation
        t = linspace(0, 1000, 1000)
        y, _t = step(sys_ss, t)
        gain_sim = y[-1]
        # print('gain_sim:', gain_sim)

        #All gain values must be approximately equal to the known gain
        np.testing.assert_array_almost_equal(
            [gain_abcd, gain_zpk, gain_numden, gain_sys_ss,
             gain_sim],
            [59, 59, 59, 59, 59])

        if slycot_check():
            # Test with MIMO system, which contains ``siso_ss1`` twice
            gain_mimo = dcgain(self.mimo_ss1)
            # print('gain_mimo: \n', gain_mimo)
            np.testing.assert_array_almost_equal(gain_mimo, [[59., 0 ],
                                                             [0,  59.]])
    def test_care_g2(self):
        A = array([[-2, -1],[-1, -1]])
        Q = array([[0, 0],[0, 1]])
        B = array([[1],[0]])
        R = 1
        S = array([[1],[0]])
        E = array([[2, 1],[1, 2]])

        X,L,G = care(A,B,Q,R,S,E)
        # print("The solution obtained is", X)
        Gref = 1/R * (B.T @ X @ E + S.T)
        assert_array_almost_equal(
            A.T @ X @ E + E.T @ X @ A
            - (E.T @ X @ B + S) @ Gref + Q ,
            zeros((2,2)))
        assert_array_almost_equal(Gref , G)

        # Compare methods
        if slycot_check():
            X_scipy, L_scipy, G_scipy = care(
                A, B, Q, R, S, E, method='scipy')
            X_slycot, L_slycot, G_slycot = care(
                A, B, Q, R, S, E, method='slycot')
            assert_array_almost_equal(X_scipy, X_slycot)
            assert_array_almost_equal(L_scipy, L_slycot)
            assert_array_almost_equal(G_scipy, G_slycot)
    def testLsim(self):
        t = np.linspace(0, 1, 10)

        #compute step response - test with state space, and transfer function
        #objects
        u = np.array([1., 1, 1, 1, 1, 1, 1, 1, 1, 1])
        youttrue = np.array([9., 17.6457, 24.7072, 30.4855, 35.2234, 39.1165,
                             42.3227, 44.9694, 47.1599, 48.9776])
        yout, tout, _xout = lsim(self.siso_ss1, u, t)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)
        yout, _t, _xout = lsim(self.siso_tf3, u, t)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)

        #test with initial value and special algorithm for ``U=0``
        u=0
        x0 = np.matrix(".5; 1.")
        youttrue = np.array([11., 8.1494, 5.9361, 4.2258, 2.9118, 1.9092,
                             1.1508, 0.5833, 0.1645, -0.1391])
        yout, _t, _xout = lsim(self.siso_ss1, u, t, x0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)

        if slycot_check():
            #Test MIMO system, which contains ``siso_ss1`` twice
            #first system: initial value, second system: step response
            u = np.array([[0., 1.], [0, 1], [0, 1], [0, 1], [0, 1],
                          [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]])
            x0 = np.matrix(".5; 1; 0; 0")
            youttrue = np.array([[11., 9.], [8.1494, 17.6457],
                                 [5.9361, 24.7072], [4.2258, 30.4855],
                                 [2.9118, 35.2234], [1.9092, 39.1165],
                                 [1.1508, 42.3227], [0.5833, 44.9694],
                                 [0.1645, 47.1599], [-0.1391, 48.9776]])
            yout, _t, _xout = lsim(self.mimo_ss1, u, t, x0)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
    def test_DLQE(self, matarrayin, method):
        if method == 'slycot' and not slycot_check():
            return

        A, G, C, QN, RN = (matarrayin([[X]]) for X in [0., .1, 1., 10., 2.])
        L, P, poles = dlqe(A, G, C, QN, RN, method=method)
        self.check_DLQE(L, P, poles, G, QN, RN)
 def test_DLQR_3args(self, matarrayin, matarrayout, method):
     if method == 'slycot' and not slycot_check():
         return
     dsys = ss(0., 1., 1., 0., .1)
     Q, R = (matarrayin([[X]]) for X in [10., 2.])
     K, S, poles = dlqr(dsys, Q, R, method=method)
     self.check_DLQR(K, S, poles, Q, R)
    def test_dcgain(self):
        """Test function dcgain with different systems"""
        if slycot_check():
            #Test MIMO systems
            A, B, C, D = self.make_MIMO_mats()

            gain1 = dcgain(ss(A, B, C, D))
            gain2 = dcgain(A, B, C, D)
            sys_tf = ss2tf(A, B, C, D)
            gain3 = dcgain(sys_tf)
            gain4 = dcgain(sys_tf.num, sys_tf.den)
            #print("gain1:", gain1)

            assert_array_almost_equal(gain1,
                                      array([[0.0269, 0.    ],
                                             [0.    , 0.0269]]),
                                      decimal=4)
            assert_array_almost_equal(gain1, gain2)
            assert_array_almost_equal(gain3, gain4)
            assert_array_almost_equal(gain1, gain4)

        #Test SISO systems
        A, B, C, D = self.make_SISO_mats()

        gain1 = dcgain(ss(A, B, C, D))
        assert_array_almost_equal(gain1,
                                  array([[0.0269]]),
                                  decimal=4)
Example #11
0
class TestHinf(unittest.TestCase):
    def setUp(self):
        # Use array instead of matrix (and save old value to restore at end)
        control.use_numpy_matrix(False)
        
    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testHinfsyn(self):
        """Test hinfsyn"""
        p = control.ss(-1, [[1, 1]], [[1], [1]], [[0, 1], [1, 0]])
        k, cl, gam, rcond = control.robust.hinfsyn(p, 1, 1)
        # from Octave, which also uses SB10AD:
        #   a= -1; b1= 1; b2= 1; c1= 1; c2= 1; d11= 0; d12= 1; d21= 1; d22= 0;
        #   g = ss(a,[b1,b2],[c1;c2],[d11,d12;d21,d22]);
        #   [k,cl] = hinfsyn(g,1,1);
        np.testing.assert_array_almost_equal(k.A, [[-3]])
        np.testing.assert_array_almost_equal(k.B, [[1]])
        np.testing.assert_array_almost_equal(k.C, [[-1]])
        np.testing.assert_array_almost_equal(k.D, [[0]])
        np.testing.assert_array_almost_equal(cl.A, [[-1, -1], [1, -3]])
        np.testing.assert_array_almost_equal(cl.B, [[1], [1]])
        np.testing.assert_array_almost_equal(cl.C, [[1, -1]])
        np.testing.assert_array_almost_equal(cl.D, [[0]])

    # TODO: add more interesting examples

    def tearDown(self):
        control.config.reset_defaults()
Example #12
0
    def test_squeeze(self, fcn, nstate, nout, ninp, omega, squeeze, shape,
                     omega_type):
        """Test correct behavior of frequencey response squeeze parameter."""
        # Create the system to be tested
        if fcn == ct.frd:
            sys = fcn(ct.rss(nstate, nout, ninp), [1e-2, 1e-1, 1, 1e1, 1e2])
        elif fcn == ct.tf and (nout > 1 or ninp > 1) and not slycot_check():
            pytest.skip("Conversion of MIMO systems to transfer functions "
                        "requires slycot.")
        else:
            sys = fcn(ct.rss(nstate, nout, ninp))

        if omega_type == "numpy":
            omega = np.asarray(omega)
            isscalar = omega.ndim == 0
            # keep the ndarray type even for scalars
            s = np.asarray(omega * 1j)
        else:
            isscalar = not hasattr(omega, '__len__')
            if isscalar:
                s = omega * 1J
            else:
                s = [w * 1J for w in omega]

        # Call the transfer function directly and make sure shape is correct
        assert sys(s, squeeze=squeeze).shape == shape

        # Make sure that evalfr also works as expected
        assert ct.evalfr(sys, s, squeeze=squeeze).shape == shape

        # Check frequency response
        mag, phase, _ = sys.frequency_response(omega, squeeze=squeeze)
        if isscalar and squeeze is not True:
            # sys.frequency_response() expects a list as an argument
            # Add the shape of the input to the expected shape
            assert mag.shape == shape + (1, )
            assert phase.shape == shape + (1, )
        else:
            assert mag.shape == shape
            assert phase.shape == shape

        # Make sure the default shape lines up with squeeze=None case
        if squeeze is None:
            assert sys(s).shape == shape

        # Changing config.default to False should return 3D frequency response
        ct.config.set_defaults('control', squeeze_frequency_response=False)
        mag, phase, _ = sys.frequency_response(omega)
        if isscalar:
            assert mag.shape == (sys.noutputs, sys.ninputs, 1)
            assert phase.shape == (sys.noutputs, sys.ninputs, 1)
            assert sys(s).shape == (sys.noutputs, sys.ninputs)
            assert ct.evalfr(sys, s).shape == (sys.noutputs, sys.ninputs)
        else:
            assert mag.shape == (sys.noutputs, sys.ninputs, len(omega))
            assert phase.shape == (sys.noutputs, sys.ninputs, len(omega))
            assert sys(s).shape == \
                (sys.noutputs, sys.ninputs, len(omega))
            assert ct.evalfr(sys, s).shape == \
                (sys.noutputs, sys.ninputs, len(omega))
    def testInitial(self):
        #Test SISO system
        sys = self.siso_ss1
        t = np.linspace(0, 1, 10)
        x0 = np.matrix(".5; 1.")
        youttrue = np.array([
            11., 8.1494, 5.9361, 4.2258, 2.9118, 1.9092, 1.1508, 0.5833,
            0.1645, -0.1391
        ])
        yout, tout = initial(sys, T=t, X0=x0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Play with arguments
        yout, tout, xout = initial(sys, T=t, X0=x0, return_x=True)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        if slycot_check():
            #Test MIMO system, which contains ``siso_ss1`` twice
            sys = self.mimo_ss1
            x0 = np.matrix(".5; 1.; .5; 1.")
            y_00, _t = initial(sys, T=t, X0=x0, input=0, output=0)
            y_11, _t = initial(sys, T=t, X0=x0, input=1, output=1)
            np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)
 def testEvalfr(self):
     w = 1j
     np.testing.assert_almost_equal(evalfr(self.siso_ss1, w), 44.8-21.4j)
     evalfr(self.siso_ss2, w)
     evalfr(self.siso_ss3, w)
     evalfr(self.siso_tf1, w)
     evalfr(self.siso_tf2, w)
     evalfr(self.siso_tf3, w)
     if slycot_check():
         np.testing.assert_array_almost_equal(
             evalfr(self.mimo_ss1, w),
             np.array( [[44.8-21.4j, 0.], [0., 44.8-21.4j]]))
 def testEvalfr(self):
     w = 1j
     np.testing.assert_almost_equal(evalfr(self.siso_ss1, w), 44.8 - 21.4j)
     evalfr(self.siso_ss2, w)
     evalfr(self.siso_ss3, w)
     evalfr(self.siso_tf1, w)
     evalfr(self.siso_tf2, w)
     evalfr(self.siso_tf3, w)
     if slycot_check():
         np.testing.assert_array_almost_equal(
             evalfr(self.mimo_ss1, w),
             np.array([[44.8 - 21.4j, 0.], [0., 44.8 - 21.4j]]))
Example #16
0
    def test_squeeze(self, fcn, nstate, nout, ninp, omega, squeeze, shape):
        # Create the system to be tested
        if fcn == ct.frd:
            sys = fcn(ct.rss(nstate, nout, ninp), [1e-2, 1e-1, 1, 1e1, 1e2])
        elif fcn == ct.tf and (nout > 1 or ninp > 1) and not slycot_check():
            pytest.skip("Conversion of MIMO systems to transfer functions "
                        "requires slycot.")
        else:
            sys = fcn(ct.rss(nstate, nout, ninp))

        # Convert the frequency list to an array for easy of use
        isscalar = not hasattr(omega, '__len__')
        omega = np.array(omega)

        # Call the transfer function directly and make sure shape is correct
        assert sys(omega * 1j, squeeze=squeeze).shape == shape

        # Make sure that evalfr also works as expected
        assert ct.evalfr(sys, omega * 1j, squeeze=squeeze).shape == shape

        # Check frequency response
        mag, phase, _ = sys.frequency_response(omega, squeeze=squeeze)
        if isscalar and squeeze is not True:
            # sys.frequency_response() expects a list as an argument
            # Add the shape of the input to the expected shape
            assert mag.shape == shape + (1, )
            assert phase.shape == shape + (1, )
        else:
            assert mag.shape == shape
            assert phase.shape == shape

        # Make sure the default shape lines up with squeeze=None case
        if squeeze is None:
            assert sys(omega * 1j).shape == shape

        # Changing config.default to False should return 3D frequency response
        ct.config.set_defaults('control', squeeze_frequency_response=False)
        mag, phase, _ = sys.frequency_response(omega)
        if isscalar:
            assert mag.shape == (sys.noutputs, sys.ninputs, 1)
            assert phase.shape == (sys.noutputs, sys.ninputs, 1)
            assert sys(omega * 1j).shape == (sys.noutputs, sys.ninputs)
            assert ct.evalfr(sys,
                             omega * 1j).shape == (sys.noutputs, sys.ninputs)
        else:
            assert mag.shape == (sys.noutputs, sys.ninputs, len(omega))
            assert phase.shape == (sys.noutputs, sys.ninputs, len(omega))
            assert sys(omega * 1j).shape == \
                (sys.noutputs, sys.ninputs, len(omega))
            assert ct.evalfr(sys, omega * 1j).shape == \
                (sys.noutputs, sys.ninputs, len(omega))
Example #17
0
    def setUp(self):
        """Set up testing parameters."""

        # Number of times to run each of the randomized tests.
        self.numTests = 1  # almost guarantees failure
        # Maximum number of states to test + 1
        self.maxStates = 4
        # Maximum number of inputs and outputs to test + 1
        # If slycot is not installed, just check SISO
        self.maxIO = 5 if slycot_check() else 2
        # Set to True to print systems to the output.
        self.debug = False
        # get consistent results
        np.random.seed(7)
Example #18
0
    def setUp(self):
        """Set up testing parameters."""

        # Number of times to run each of the randomized tests.
        self.numTests = 1  # almost guarantees failure
        # Maximum number of states to test + 1
        self.maxStates = 4
        # Maximum number of inputs and outputs to test + 1
        # If slycot is not installed, just check SISO
        self.maxIO = 5 if slycot_check() else 2
        # Set to True to print systems to the output.
        self.debug = False
        # get consistent results
        np.random.seed(7)
Example #19
0
class TestMixsyn(unittest.TestCase):
    """Test control.robust.mixsyn"""
    def setUp(self):
        # Use array instead of matrix (and save old value to restore at end)
        control.use_numpy_matrix(False)
        
    # it's a relatively simple wrapper; compare results with augw, hinfsyn
    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testSiso(self):
        """mixsyn with SISO system"""
        from control import tf, augw, hinfsyn, mixsyn
        from control import ss
        # Skogestad+Postlethwaite, Multivariable Feedback Control, 1st Ed., Example 2.11
        s = tf([1, 0], 1)
        # plant
        g = 200 / (10 * s + 1) / (0.05 * s + 1) ** 2
        # sensitivity weighting
        M = 1.5
        wb = 10
        A = 1e-4
        w1 = (s / M + wb) / (s + wb * A)
        # KS weighting
        w2 = tf(1, 1)

        p = augw(g, w1, w2)
        kref, clref, gam, rcond = hinfsyn(p, 1, 1)
        ktest, cltest, info = mixsyn(g, w1, w2)
        # check similar to S+P's example
        np.testing.assert_allclose(gam, 1.37, atol=1e-2)

        # mixsyn is a convenience wrapper around augw and hinfsyn, so
        # results will be exactly the same.  Given than, use the lazy
        # but fragile testing option.
        np.testing.assert_allclose(ktest.A, kref.A)
        np.testing.assert_allclose(ktest.B, kref.B)
        np.testing.assert_allclose(ktest.C, kref.C)
        np.testing.assert_allclose(ktest.D, kref.D)

        np.testing.assert_allclose(cltest.A, clref.A)
        np.testing.assert_allclose(cltest.B, clref.B)
        np.testing.assert_allclose(cltest.C, clref.C)
        np.testing.assert_allclose(cltest.D, clref.D)

        np.testing.assert_allclose(gam, info[0])

        np.testing.assert_allclose(rcond, info[1])

    def tearDown(self):
        control.config.reset_defaults()
Example #20
0
class TestH2(unittest.TestCase):
    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testH2syn(self):
        "Test h2syn"
        p = control.ss(-1, [1, 1], [[1], [1]], [[0, 1], [1, 0]])
        k = control.robust.h2syn(p, 1, 1)
        # from Octave, which also uses SB10HD for H-2 synthesis:
        #   a= -1; b1= 1; b2= 1; c1= 1; c2= 1; d11= 0; d12= 1; d21= 1; d22= 0;
        #   g = ss(a,[b1,b2],[c1;c2],[d11,d12;d21,d22]);
        #   k = h2syn(g,1,1);
        # the solution is the same as for the hinfsyn test
        np.testing.assert_array_almost_equal(k.A, [[-3]])
        np.testing.assert_array_almost_equal(k.B, [[1]])
        np.testing.assert_array_almost_equal(k.C, [[-1]])
        np.testing.assert_array_almost_equal(k.D, [[0]])
    def testImpulse(self):
        t = np.linspace(0, 1, 10)
        # test transfer function
        yout, tout = impulse(self.siso_tf1, T=t)
        youttrue = np.array([
            0., 0.0994, 0.1779, 0.2388, 0.2850, 0.3188, 0.3423, 0.3573, 0.3654,
            0.3679
        ])
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # produce a warning for a system with direct feedthrough
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            #Test SISO system
            sys = self.siso_ss1
            youttrue = np.array([
                86., 70.1808, 57.3753, 46.9975, 38.5766, 31.7344, 26.1668,
                21.6292, 17.9245, 14.8945
            ])
            yout, tout = impulse(sys, T=t)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(tout, t)

            # Play with arguments
            yout, tout = impulse(sys, T=t, X0=0)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(tout, t)

            X0 = np.array([0, 0])
            yout, tout = impulse(sys, T=t, X0=X0)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(tout, t)

            yout, tout, xout = impulse(sys, T=t, X0=0, return_x=True)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(tout, t)

            if slycot_check():
                #Test MIMO system, which contains ``siso_ss1`` twice
                sys = self.mimo_ss1
                y_00, _t = impulse(sys, T=t, input=0, output=0)
                y_11, _t = impulse(sys, T=t, input=1, output=1)
                np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
                np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)
Example #22
0
    def testConvertMIMO(self):
        """Test state space to transfer function conversion."""
        verbose = self.debug

        # Do a MIMO conversation and make sure that it is processed
        # correctly both with and without slycot
        #
        # Example from issue #120, jgoppert
        import control

        # Set up a transfer function (should always work)
        tfcn = control.tf(
            [[[-235, 1.146e4], [-235, 1.146E4], [-235, 1.146E4, 0]]],
            [[[1, 48.78, 0], [1, 48.78, 0, 0], [0.008, 1.39, 48.78]]])

        # Convert to state space and look for an error
        if (not slycot_check()):
            self.assertRaises(TypeError, control.tf2ss, tfcn)
Example #23
0
    def test_care(self, matarrayin):
        """Test stabilizing and anti-stabilizing feedback, continuous"""
        A = matarrayin(np.diag([1, -1]))
        B = matarrayin(np.identity(2))
        Q = matarrayin(np.identity(2))
        R = matarrayin(np.identity(2))
        S = matarrayin(np.zeros((2, 2)))
        E = matarrayin(np.identity(2))

        X, L, G = care(A, B, Q, R, S, E, stabilizing=True)
        assert np.all(np.real(L) < 0)

        if slycot_check():
            X, L, G = care(A, B, Q, R, S, E, stabilizing=False)
            assert np.all(np.real(L) > 0)
        else:
            with pytest.raises(ControlArgument, match="'scipy' not valid"):
                X, L, G = care(A, B, Q, R, S, E, stabilizing=False)
    def test_lyap(self):
        A = array([[-1, 1], [-1, 0]])
        Q = array([[1, 0], [0, 1]])
        X = lyap(A, Q)
        # print("The solution obtained is ", X)
        assert_array_almost_equal(A @ X + X @ A.T + Q, zeros((2,2)))

        A = array([[1, 2], [-3, -4]])
        Q = array([[3, 1], [1, 1]])
        X = lyap(A,Q)
        # print("The solution obtained is ", X)
        assert_array_almost_equal(A @ X + X @ A.T + Q, zeros((2,2)))

        # Compare methods
        if slycot_check():
            X_scipy = lyap(A, Q, method='scipy')
            X_slycot = lyap(A, Q, method='slycot')
            assert_array_almost_equal(X_scipy, X_slycot)
Example #25
0
class TestHinf(unittest.TestCase):
    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testHinfsyn(self):
        "Test hinfsyn"
        p = control.ss(-1, [1, 1], [[1], [1]], [[0, 1], [1, 0]])
        k, cl, gam, rcond = control.robust.hinfsyn(p, 1, 1)
        # from Octave, which also uses SB10AD:
        #   a= -1; b1= 1; b2= 1; c1= 1; c2= 1; d11= 0; d12= 1; d21= 1; d22= 0;
        #   g = ss(a,[b1,b2],[c1;c2],[d11,d12;d21,d22]);
        #   [k,cl] = hinfsyn(g,1,1);
        np.testing.assert_array_almost_equal(k.A, [[-3]])
        np.testing.assert_array_almost_equal(k.B, [[1]])
        np.testing.assert_array_almost_equal(k.C, [[-1]])
        np.testing.assert_array_almost_equal(k.D, [[0]])
        np.testing.assert_array_almost_equal(cl.A, [[-1, -1], [1, -3]])
        np.testing.assert_array_almost_equal(cl.B, [[1], [1]])
        np.testing.assert_array_almost_equal(cl.C, [[1, -1]])
        np.testing.assert_array_almost_equal(cl.D, [[0]])
    def test_care(self):
        A = array([[-2, -1],[-1, -1]])
        Q = array([[0, 0],[0, 1]])
        B = array([[1, 0],[0, 4]])

        X, L, G = care(A, B, Q)
        # print("The solution obtained is", X)
        M = A.T @ X + X @ A - X @ B @ B.T @ X + Q
        assert_array_almost_equal(M,
                                  zeros((2,2)))
        assert_array_almost_equal(B.T @ X, G)

        # Compare methods
        if slycot_check():
            X_scipy, L_scipy, G_scipy = care(A, B, Q, method='scipy')
            X_slycot, L_slycot, G_slycot = care(A, B, Q, method='slycot')
            assert_array_almost_equal(X_scipy, X_slycot)
            assert_array_almost_equal(np.sort(L_scipy), np.sort(L_slycot))
            assert_array_almost_equal(G_scipy, G_slycot)
    def testStep(self):
        t = np.linspace(0, 1, 10)
        # Test transfer function
        yout, tout = step(self.siso_tf1, T=t)
        youttrue = np.array([
            0, 0.0057, 0.0213, 0.0446, 0.0739, 0.1075, 0.1443, 0.1832, 0.2235,
            0.2642
        ])
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Test SISO system with direct feedthrough
        sys = self.siso_ss1
        youttrue = np.array([
            9., 17.6457, 24.7072, 30.4855, 35.2234, 39.1165, 42.3227, 44.9694,
            47.1599, 48.9776
        ])

        yout, tout = step(sys, T=t)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Play with arguments
        yout, tout = step(sys, T=t, X0=0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        X0 = np.array([0, 0])
        yout, tout = step(sys, T=t, X0=X0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        yout, tout, xout = step(sys, T=t, X0=0, return_x=True)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        if slycot_check():
            # Test MIMO system, which contains ``siso_ss1`` twice
            sys = self.mimo_ss1
            y_00, _t = step(sys, T=t, input=0, output=0)
            y_11, _t = step(sys, T=t, input=1, output=1)
            np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)
    def test_lyap_sylvester(self):
        A = 5
        B = array([[4, 3], [4, 3]])
        C = array([2, 1])
        X = lyap(A, B, C)
        # print("The solution obtained is ", X)
        assert_array_almost_equal(A * X + X @ B + C, zeros((1,2)))

        A = array([[2, 1], [1, 2]])
        B = array([[1, 2], [0.5, 0.1]])
        C = array([[1, 0], [0, 1]])
        X = lyap(A, B, C)
        # print("The solution obtained is ", X)
        assert_array_almost_equal(A @ X + X @ B + C, zeros((2,2)))

        # Compare methods
        if slycot_check():
            X_scipy = lyap(A, B, C, method='scipy')
            X_slycot = lyap(A, B, C, method='slycot')
            assert_array_almost_equal(X_scipy, X_slycot)
    def testImpulse(self):
        t = np.linspace(0, 1, 10)
        # test transfer function
        yout, tout = impulse(self.siso_tf1, T=t)
        youttrue = np.array([0., 0.0994, 0.1779, 0.2388, 0.2850, 0.3188,
                             0.3423, 0.3573, 0.3654, 0.3679])
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # produce a warning for a system with direct feedthrough
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            #Test SISO system
            sys = self.siso_ss1
            youttrue = np.array([86., 70.1808, 57.3753, 46.9975, 38.5766, 31.7344,
                                 26.1668, 21.6292, 17.9245, 14.8945])
            yout, tout = impulse(sys, T=t)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(tout, t)

            # Play with arguments
            yout, tout = impulse(sys, T=t, X0=0)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(tout, t)

            X0 = np.array([0, 0]);
            yout, tout = impulse(sys, T=t, X0=X0)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(tout, t)

            yout, tout, xout = impulse(sys, T=t, X0=0, return_x=True)
            np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(tout, t)

            if slycot_check():
                #Test MIMO system, which contains ``siso_ss1`` twice
                sys = self.mimo_ss1
                y_00, _t = impulse(sys, T=t, input=0, output=0)
                y_11, _t = impulse(sys, T=t, input=1, output=1)
                np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
                np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)
Example #30
0
    def testConvertMIMO(self):
        """Test state space to transfer function conversion."""
        verbose = self.debug

        # Do a MIMO conversation and make sure that it is processed
        # correctly both with and without slycot
        #
        # Example from issue #120, jgoppert
        import control

        # Set up a transfer function (should always work)
        tfcn = control.tf([[[-235, 1.146e4],
                            [-235, 1.146E4],
                            [-235, 1.146E4, 0]]],
                          [[[1, 48.78, 0],
                            [1, 48.78, 0, 0],
                            [0.008, 1.39, 48.78]]])

        # Convert to state space and look for an error
        if (not slycot_check()):
            self.assertRaises(TypeError, control.tf2ss, tfcn)
Example #31
0
class TestH2(unittest.TestCase):
    def setUp(self):
        # Use array instead of matrix (and save old value to restore at end)
        control.use_numpy_matrix(False)

    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testH2syn(self):
        """Test h2syn"""
        p = control.ss(-1, [[1, 1]], [[1], [1]], [[0, 1], [1, 0]])
        k = control.robust.h2syn(p, 1, 1)
        # from Octave, which also uses SB10HD for H-2 synthesis:
        #   a= -1; b1= 1; b2= 1; c1= 1; c2= 1; d11= 0; d12= 1; d21= 1; d22= 0;
        #   g = ss(a,[b1,b2],[c1;c2],[d11,d12;d21,d22]);
        #   k = h2syn(g,1,1);
        # the solution is the same as for the hinfsyn test
        np.testing.assert_array_almost_equal(k.A, [[-3]])
        np.testing.assert_array_almost_equal(k.B, [[1]])
        np.testing.assert_array_almost_equal(k.C, [[-1]])
        np.testing.assert_array_almost_equal(k.D, [[0]])

    def tearDown(self):
        control.config.reset_defaults()
    def testStep(self):
        t = np.linspace(0, 1, 10)
        # Test transfer function
        yout, tout = step(self.siso_tf1, T=t)
        youttrue = np.array([0, 0.0057, 0.0213, 0.0446, 0.0739,
                             0.1075, 0.1443, 0.1832, 0.2235, 0.2642])
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Test SISO system with direct feedthrough
        sys = self.siso_ss1
        youttrue = np.array([9., 17.6457, 24.7072, 30.4855, 35.2234, 39.1165,
                             42.3227, 44.9694, 47.1599, 48.9776])

        yout, tout = step(sys, T=t)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Play with arguments
        yout, tout = step(sys, T=t, X0=0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        X0 = np.array([0, 0]);
        yout, tout = step(sys, T=t, X0=X0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        yout, tout, xout = step(sys, T=t, X0=0, return_x=True)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        if slycot_check():
            # Test MIMO system, which contains ``siso_ss1`` twice
            sys = self.mimo_ss1
            y_00, _t = step(sys, T=t, input=0, output=0)
            y_11, _t = step(sys, T=t, input=1, output=1)
            np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)
class TestFreqresp(unittest.TestCase):
    def setUp(self):
        self.A = np.matrix('1,1;0,1')
        self.C = np.matrix('1,0')
        self.omega = np.linspace(10e-2, 10e2, 1000)

    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 test_doubleint(self):
        # 30 May 2016, RMM: added to replicate typecast bug in freqresp.py
        A = np.matrix('0, 1; 0, 0')
        B = np.matrix('0; 1')
        C = np.matrix('1, 0')
        D = 0
        sys = ss(A, B, C, D)
        bode(sys)

    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def test_mimo(self):
        # MIMO
        B = np.matrix('1,0;0,1')
        D = np.matrix('0,0')
        sysMIMO = ss(self.A, B, self.C, D)

        frqMIMO = sysMIMO.freqresp(self.omega)
        tfMIMO = tf(sysMIMO)
    def testInitial(self):
        #Test SISO system
        sys = self.siso_ss1
        t = np.linspace(0, 1, 10)
        x0 = np.matrix(".5; 1.")
        youttrue = np.array([11., 8.1494, 5.9361, 4.2258, 2.9118, 1.9092,
                             1.1508, 0.5833, 0.1645, -0.1391])
        yout, tout = initial(sys, T=t, X0=x0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Play with arguments
        yout, tout, xout = initial(sys, T=t, X0=x0, return_x=True)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        if slycot_check():
            #Test MIMO system, which contains ``siso_ss1`` twice
            sys = self.mimo_ss1
            x0 = np.matrix(".5; 1.; .5; 1.")
            y_00, _t = initial(sys, T=t, X0=x0, input=0, output=0)
            y_11, _t = initial(sys, T=t, X0=x0, input=1, output=1)
            np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
            np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)
Example #35
0
class TestTimeresp(unittest.TestCase):
    def setUp(self):
        """Set up some systems for testing out MATLAB functions"""
        A = np.matrix("1. -2.; 3. -4.")
        B = np.matrix("5.; 7.")
        C = np.matrix("6. 8.")
        D = np.matrix("9.")
        self.siso_ss1 = StateSpace(A, B, C, D)

        # Create some transfer functions
        self.siso_tf1 = TransferFunction([1], [1, 2, 1])
        self.siso_tf2 = _convert_to_transfer_function(self.siso_ss1)

        # Create MIMO system, contains ``siso_ss1`` twice
        A = np.matrix("1. -2. 0.  0.;"
                      "3. -4. 0.  0.;"
                      "0.  0. 1. -2.;"
                      "0.  0. 3. -4. ")
        B = np.matrix("5. 0.;" "7. 0.;" "0. 5.;" "0. 7. ")
        C = np.matrix("6. 8. 0. 0.;" "0. 0. 6. 8. ")
        D = np.matrix("9. 0.;" "0. 9. ")
        self.mimo_ss1 = StateSpace(A, B, C, D)

        # Create discrete time systems
        self.siso_dtf1 = TransferFunction([1], [1, 1, 0.25], True)
        self.siso_dtf2 = TransferFunction([1], [1, 1, 0.25], 0.2)
        self.siso_dss1 = tf2ss(self.siso_dtf1)
        self.siso_dss2 = tf2ss(self.siso_dtf2)
        self.mimo_dss1 = StateSpace(A, B, C, D, True)
        self.mimo_dss2 = c2d(self.mimo_ss1, 0.2)

    def test_step_response(self):
        # Test SISO system
        sys = self.siso_ss1
        t = np.linspace(0, 1, 10)
        youttrue = np.array([
            9., 17.6457, 24.7072, 30.4855, 35.2234, 39.1165, 42.3227, 44.9694,
            47.1599, 48.9776
        ])

        # SISO call
        tout, yout = step_response(sys, T=t)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Play with arguments
        tout, yout = step_response(sys, T=t, X0=0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        X0 = np.array([0, 0])
        tout, yout = step_response(sys, T=t, X0=X0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        tout, yout, xout = step_response(sys, T=t, X0=0, return_x=True)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Test MIMO system, which contains ``siso_ss1`` twice
        sys = self.mimo_ss1
        _t, y_00 = step_response(sys, T=t, input=0, output=0)
        _t, y_11 = step_response(sys, T=t, input=1, output=1)
        np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)

        # Make sure continuous and discrete time use same return conventions
        sysc = self.mimo_ss1
        sysd = c2d(sysc, 1)  # discrete time system
        Tvec = np.linspace(0, 10, 11)  # make sure to use integer times 0..10
        Tc, youtc = step_response(sysc, Tvec, input=0)
        Td, youtd = step_response(sysd, Tvec, input=0)
        np.testing.assert_array_equal(Tc.shape, Td.shape)
        np.testing.assert_array_equal(youtc.shape, youtd.shape)

    def test_step_info(self):
        # From matlab docs:
        sys = TransferFunction([1, 5, 5], [1, 1.65, 5, 6.5, 2])
        Strue = {
            'RiseTime': 3.8456,
            'SettlingTime': 27.9762,
            'SettlingMin': 2.0689,
            'SettlingMax': 2.6873,
            'Overshoot': 7.4915,
            'Undershoot': 0,
            'Peak': 2.6873,
            'PeakTime': 8.0530
        }

        S = step_info(sys)

        # Very arbitrary tolerance because I don't know if the
        # response from the MATLAB is really that accurate.
        # maybe it is a good idea to change the Strue to match
        # but I didn't do it because I don't know if it is
        # accurate either...
        rtol = 2e-2
        np.testing.assert_allclose(S.get('RiseTime'),
                                   Strue.get('RiseTime'),
                                   rtol=rtol)
        np.testing.assert_allclose(S.get('SettlingTime'),
                                   Strue.get('SettlingTime'),
                                   rtol=rtol)
        np.testing.assert_allclose(S.get('SettlingMin'),
                                   Strue.get('SettlingMin'),
                                   rtol=rtol)
        np.testing.assert_allclose(S.get('SettlingMax'),
                                   Strue.get('SettlingMax'),
                                   rtol=rtol)
        np.testing.assert_allclose(S.get('Overshoot'),
                                   Strue.get('Overshoot'),
                                   rtol=rtol)
        np.testing.assert_allclose(S.get('Undershoot'),
                                   Strue.get('Undershoot'),
                                   rtol=rtol)
        np.testing.assert_allclose(S.get('Peak'), Strue.get('Peak'), rtol=rtol)
        np.testing.assert_allclose(S.get('PeakTime'),
                                   Strue.get('PeakTime'),
                                   rtol=rtol)
        np.testing.assert_allclose(S.get('SteadyStateValue'), 2.50, rtol=rtol)

    def test_impulse_response(self):
        # Test SISO system
        sys = self.siso_ss1
        t = np.linspace(0, 1, 10)
        youttrue = np.array([
            86., 70.1808, 57.3753, 46.9975, 38.5766, 31.7344, 26.1668, 21.6292,
            17.9245, 14.8945
        ])
        tout, yout = impulse_response(sys, T=t)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Play with arguments
        tout, yout = impulse_response(sys, T=t, X0=0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        X0 = np.array([0, 0])
        tout, yout = impulse_response(sys, T=t, X0=X0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        tout, yout, xout = impulse_response(sys, T=t, X0=0, return_x=True)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Test MIMO system, which contains ``siso_ss1`` twice
        sys = self.mimo_ss1
        _t, y_00 = impulse_response(sys, T=t, input=0, output=0)
        _t, y_11 = impulse_response(sys, T=t, input=1, output=1)
        np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)

        # Test MIMO system, as mimo, and don't trim outputs
        sys = self.mimo_ss1
        _t, yy = impulse_response(sys, T=t, input=0)
        np.testing.assert_array_almost_equal(yy,
                                             np.vstack(
                                                 (youttrue,
                                                  np.zeros_like(youttrue))),
                                             decimal=4)

    def test_initial_response(self):
        # Test SISO system
        sys = self.siso_ss1
        t = np.linspace(0, 1, 10)
        x0 = np.array([[0.5], [1]])
        youttrue = np.array([
            11., 8.1494, 5.9361, 4.2258, 2.9118, 1.9092, 1.1508, 0.5833,
            0.1645, -0.1391
        ])
        tout, yout = initial_response(sys, T=t, X0=x0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Play with arguments
        tout, yout, xout = initial_response(sys, T=t, X0=x0, return_x=True)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)

        # Test MIMO system, which contains ``siso_ss1`` twice
        sys = self.mimo_ss1
        x0 = np.matrix(".5; 1.; .5; 1.")
        _t, y_00 = initial_response(sys, T=t, X0=x0, input=0, output=0)
        _t, y_11 = initial_response(sys, T=t, X0=x0, input=1, output=1)
        np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)

    def test_initial_response_no_trim(self):
        # test MIMO system without trimming
        t = np.linspace(0, 1, 10)
        x0 = np.matrix(".5; 1.; .5; 1.")
        youttrue = np.array([
            11., 8.1494, 5.9361, 4.2258, 2.9118, 1.9092, 1.1508, 0.5833,
            0.1645, -0.1391
        ])
        sys = self.mimo_ss1
        _t, yy = initial_response(sys, T=t, X0=x0)
        np.testing.assert_array_almost_equal(yy,
                                             np.vstack((youttrue, youttrue)),
                                             decimal=4)

    def test_forced_response(self):
        t = np.linspace(0, 1, 10)

        # compute step response - test with state space, and transfer function
        # objects
        u = np.array([1., 1, 1, 1, 1, 1, 1, 1, 1, 1])
        youttrue = np.array([
            9., 17.6457, 24.7072, 30.4855, 35.2234, 39.1165, 42.3227, 44.9694,
            47.1599, 48.9776
        ])
        tout, yout, _xout = forced_response(self.siso_ss1, t, u)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
        np.testing.assert_array_almost_equal(tout, t)
        _t, yout, _xout = forced_response(self.siso_tf2, t, u)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)

        # test with initial value and special algorithm for ``U=0``
        u = 0
        x0 = np.matrix(".5; 1.")
        youttrue = np.array([
            11., 8.1494, 5.9361, 4.2258, 2.9118, 1.9092, 1.1508, 0.5833,
            0.1645, -0.1391
        ])
        _t, yout, _xout = forced_response(self.siso_ss1, t, u, x0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)

        # Test MIMO system, which contains ``siso_ss1`` twice
        # first system: initial value, second system: step response
        u = np.array([[0., 0, 0, 0, 0, 0, 0, 0, 0, 0],
                      [1., 1, 1, 1, 1, 1, 1, 1, 1, 1]])
        x0 = np.matrix(".5; 1; 0; 0")
        youttrue = np.array([[
            11., 8.1494, 5.9361, 4.2258, 2.9118, 1.9092, 1.1508, 0.5833,
            0.1645, -0.1391
        ],
                             [
                                 9., 17.6457, 24.7072, 30.4855, 35.2234,
                                 39.1165, 42.3227, 44.9694, 47.1599, 48.9776
                             ]])
        _t, yout, _xout = forced_response(self.mimo_ss1, t, u, x0)
        np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)

        # Test discrete MIMO system to use correct convention for input
        sysc = self.mimo_ss1
        dt = t[1] - t[0]
        sysd = c2d(sysc, dt)  # discrete time system
        Tc, youtc, _xoutc = forced_response(sysc, t, u, x0)
        Td, youtd, _xoutd = forced_response(sysd, t, u, x0)
        np.testing.assert_array_equal(Tc.shape, Td.shape)
        np.testing.assert_array_equal(youtc.shape, youtd.shape)
        np.testing.assert_array_almost_equal(youtc, youtd, decimal=4)

    def test_lsim_double_integrator(self):
        # Note: scipy.signal.lsim fails if A is not invertible
        A = np.mat("0. 1.;0. 0.")
        B = np.mat("0.; 1.")
        C = np.mat("1. 0.")
        D = 0.
        sys = StateSpace(A, B, C, D)

        def check(u, x0, xtrue):
            _t, yout, xout = forced_response(sys, t, u, x0)
            np.testing.assert_array_almost_equal(xout, xtrue, decimal=6)
            ytrue = np.squeeze(np.asarray(C.dot(xtrue)))
            np.testing.assert_array_almost_equal(yout, ytrue, decimal=6)

        # test with zero input
        npts = 10
        t = np.linspace(0, 1, npts)
        u = np.zeros_like(t)
        x0 = np.array([2., 3.])
        xtrue = np.zeros((2, npts))
        xtrue[0, :] = x0[0] + t * x0[1]
        xtrue[1, :] = x0[1]
        check(u, x0, xtrue)

        # test with step input
        u = np.ones_like(t)
        xtrue = np.array([0.5 * t**2, t])
        x0 = np.array([0., 0.])
        check(u, x0, xtrue)

        # test with linear input
        u = t
        xtrue = np.array([1. / 6. * t**3, 0.5 * t**2])
        check(u, x0, xtrue)

    def test_discrete_initial(self):
        h1 = TransferFunction([1.], [1., 0.], 1.)
        t, yout = impulse_response(h1, np.arange(4))
        np.testing.assert_array_equal(yout, [0., 1., 0., 0.])

    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def test_step_robustness(self):
        "Unit test: https://github.com/python-control/python-control/issues/240"
        # Create 2 input, 2 output system
        num = [[[0], [1]], [[1], [0]]]

        den1 = [[[1], [1, 1]], [[1, 4], [1]]]
        sys1 = TransferFunction(num, den1)

        den2 = [[[1], [1e-10, 1, 1]], [[1, 4], [1]]]  # slight perturbation
        sys2 = TransferFunction(num, den2)

        # Compute step response from input 1 to output 1, 2
        t1, y1 = step_response(sys1, input=0)
        t2, y2 = step_response(sys2, input=0)
        np.testing.assert_array_almost_equal(y1, y2)

    def test_time_vector(self):
        "Unit test: https://github.com/python-control/python-control/issues/239"
        # Discrete time simulations with specified time vectors
        Tin1 = np.arange(0, 5, 1)  # matches dtf1, dss1; multiple of 0.2
        Tin2 = np.arange(0, 5, 0.2)  # matches dtf2, dss2
        Tin3 = np.arange(0, 5, 0.5)  # incompatible with 0.2

        # Initial conditions to use for the different systems
        siso_x0 = [1, 2]
        mimo_x0 = [1, 2, 3, 4]

        #
        # Easy cases: make sure that output sample time matches input
        #
        # No timebase in system => output should match input
        #
        # Initial response
        tout, yout = initial_response(self.siso_dtf1,
                                      Tin2,
                                      siso_x0,
                                      squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin2)

        # Impulse response
        tout, yout = impulse_response(self.siso_dtf1, Tin2, squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin2)

        # Step response
        tout, yout = step_response(self.siso_dtf1, Tin2, squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin2)

        # Forced response with specified time vector
        tout, yout, xout = forced_response(self.siso_dtf1,
                                           Tin2,
                                           np.sin(Tin2),
                                           squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin2)

        # Forced response with no time vector, no sample time (should use 1)
        tout, yout, xout = forced_response(self.siso_dtf1,
                                           None,
                                           np.sin(Tin1),
                                           squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin1)

        # MIMO forced response
        tout, yout, xout = forced_response(self.mimo_dss1, Tin1,
                                           (np.sin(Tin1), np.cos(Tin1)),
                                           mimo_x0)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        self.assertEqual(np.shape(tout), np.shape(yout[1, :]))
        np.testing.assert_array_equal(tout, Tin1)

        # Matching timebase in system => output should match input
        #
        # Initial response
        tout, yout = initial_response(self.siso_dtf2,
                                      Tin2,
                                      siso_x0,
                                      squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin2)

        # Impulse response
        tout, yout = impulse_response(self.siso_dtf2, Tin2, squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin2)

        # Step response
        tout, yout = step_response(self.siso_dtf2, Tin2, squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin2)

        # Forced response
        tout, yout, xout = forced_response(self.siso_dtf2,
                                           Tin2,
                                           np.sin(Tin2),
                                           squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin2)

        # Forced response with no time vector, use sample time
        tout, yout, xout = forced_response(self.siso_dtf2,
                                           None,
                                           np.sin(Tin2),
                                           squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin2)

        # Compatible timebase in system => output should match input
        #
        # Initial response
        tout, yout = initial_response(self.siso_dtf2,
                                      Tin1,
                                      siso_x0,
                                      squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin1)

        # Impulse response
        tout, yout = impulse_response(self.siso_dtf2, Tin1, squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin1)

        # Step response
        tout, yout = step_response(self.siso_dtf2, Tin1, squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin1)

        # Forced response
        tout, yout, xout = forced_response(self.siso_dtf2,
                                           Tin1,
                                           np.sin(Tin1),
                                           squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        np.testing.assert_array_equal(tout, Tin1)

        #
        # Interpolation of the input (to match scipy.signal.dlsim)
        #
        # Initial response
        tout, yout, xout = forced_response(self.siso_dtf2,
                                           Tin1,
                                           np.sin(Tin1),
                                           interpolate=True,
                                           squeeze=False)
        self.assertEqual(np.shape(tout), np.shape(yout[0, :]))
        self.assertTrue(np.allclose(tout[1:] - tout[:-1], self.siso_dtf2.dt))

        #
        # Incompatible cases: make sure an error is thrown
        #
        # System timebase and given time vector are incompatible
        #
        # Initial response
        with self.assertRaises(Exception) as context:
            tout, yout = initial_response(self.siso_dtf2,
                                          Tin3,
                                          siso_x0,
                                          squeeze=False)
        self.assertTrue(isinstance(context.exception, ValueError))

    def test_time_series_data_convention(self):
        """Make sure time series data matches documentation conventions"""
        # SISO continuous time
        t, y = step_response(self.siso_ss1)
        self.assertTrue(
            isinstance(t, np.ndarray) and not isinstance(t, np.matrix))
        self.assertTrue(len(t.shape) == 1)
        self.assertTrue(len(y.shape) == 1)  # SISO returns "scalar" output
        self.assertTrue(len(t) == len(y))  # Allows direct plotting of output

        # SISO discrete time
        t, y = step_response(self.siso_dss1)
        self.assertTrue(
            isinstance(t, np.ndarray) and not isinstance(t, np.matrix))
        self.assertTrue(len(t.shape) == 1)
        self.assertTrue(len(y.shape) == 1)  # SISO returns "scalar" output
        self.assertTrue(len(t) == len(y))  # Allows direct plotting of output

        # MIMO continuous time
        tin = np.linspace(0, 10, 100)
        uin = [np.sin(tin), np.cos(tin)]
        t, y, x = forced_response(self.mimo_ss1, tin, uin)
        self.assertTrue(
            isinstance(t, np.ndarray) and not isinstance(t, np.matrix))
        self.assertTrue(len(t.shape) == 1)
        self.assertTrue(len(y[0].shape) == 1)
        self.assertTrue(len(y[1].shape) == 1)
        self.assertTrue(len(t) == len(y[0]))
        self.assertTrue(len(t) == len(y[1]))

        # MIMO discrete time
        tin = np.linspace(0, 10, 100)
        uin = [np.sin(tin), np.cos(tin)]
        t, y, x = forced_response(self.mimo_dss1, tin, uin)
        self.assertTrue(
            isinstance(t, np.ndarray) and not isinstance(t, np.matrix))
        self.assertTrue(len(t.shape) == 1)
        self.assertTrue(len(y[0].shape) == 1)
        self.assertTrue(len(y[1].shape) == 1)
        self.assertTrue(len(t) == len(y[0]))
        self.assertTrue(len(t) == len(y[1]))

        # Allow input time as 2D array (output should be 1D)
        tin = np.array(np.linspace(0, 10, 100), ndmin=2)
        t, y = step_response(self.siso_ss1, tin)
        self.assertTrue(
            isinstance(t, np.ndarray) and not isinstance(t, np.matrix))
        self.assertTrue(len(t.shape) == 1)
        self.assertTrue(len(y.shape) == 1)  # SISO returns "scalar" output
        self.assertTrue(len(t) == len(y))  # Allows direct plotting of output
Example #36
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)
Example #37
0
#!/usr/bin/env python
#
# slycot_convert_test.py - test SLICOT-based conversions
# RMM, 30 Mar 2011 (based on TestSlycot from v0.4a)

from __future__ import print_function
import unittest
import numpy as np
from control import matlab
from control.exception import slycot_check


@unittest.skipIf(not slycot_check(), "slycot not installed")
class TestSlycot(unittest.TestCase):
    """TestSlycot compares transfer function and state space conversions for
        various numbers of inputs,outputs and states.
       1. Usually passes for SISO systems of any state dim, occasonally,
          there will be a dimension mismatch if the original randomly
          generated ss system is not minimal because td04ad returns a
          minimal system.

       2. For small systems with many inputs, n<<m, the tests fail
          because td04ad returns a minimal ss system which has fewer
          states than the original system. It is typical for systems
          with many more inputs than states to have extraneous states.

       3. For systems with larger dimensions, n~>5 and with 2 or more
          outputs the conversion to statespace (td04ad) intermittently
          results in an equivalent realization of higher order than the
          original tf order. We think this has to do with minimu
          realization tolerances in the Fortran. The algorithm doesn't
Example #38
0
class TestAugw(unittest.TestCase):
    "Test control.robust.augw"

    # tolerance for system equality
    TOL = 1e-8

    def siso_almost_equal(self,g,h):
        """siso_almost_equal(g,h) -> None
        Raises AssertionError if g and h, two SISO LTI objects, are not almost equal"""
        from control import tf, minreal
        gmh = tf(minreal(g-h,verbose=False))
        if not (gmh.num[0][0]<self.TOL).all():
            maxnum = max(abs(gmh.num[0][0]))
            raise AssertionError('systems not approx equal; max num. coeff is {}\nsys 1:\n{}\nsys 2:\n{}'.format(maxnum,g,h))


    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testSisoW1(self):
        "SISO plant with S weighting"
        from control import augw, ss
        g = ss([-1.],[1.],[1.],[1.])
        w1 = ss([-2],[2.],[1.],[2.])
        p = augw(g,w1)
        self.assertEqual(2,p.outputs)
        self.assertEqual(2,p.inputs)
        # w->z1 should be w1
        self.siso_almost_equal(w1,p[0,0])
        # w->v should be 1
        self.siso_almost_equal(ss([],[],[],[1]),p[1,0])
        # u->z1 should be -w1*g
        self.siso_almost_equal(-w1*g,p[0,1])
        # u->v should be -g
        self.siso_almost_equal(-g,p[1,1])


    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testSisoW2(self):
        "SISO plant with KS weighting"
        from control import augw, ss
        g = ss([-1.],[1.],[1.],[1.])
        w2 = ss([-2],[1.],[1.],[2.])
        p = augw(g,w2=w2)
        self.assertEqual(2,p.outputs)
        self.assertEqual(2,p.inputs)
        # w->z2 should be 0
        self.siso_almost_equal(ss([],[],[],0),p[0,0])
        # w->v should be 1
        self.siso_almost_equal(ss([],[],[],[1]),p[1,0])
        # u->z2 should be w2
        self.siso_almost_equal(w2,p[0,1])
        # u->v should be -g
        self.siso_almost_equal(-g,p[1,1])


    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testSisoW3(self):
        "SISO plant with T weighting"
        from control import augw, ss
        g = ss([-1.],[1.],[1.],[1.])
        w3 = ss([-2],[1.],[1.],[2.])
        p = augw(g,w3=w3)
        self.assertEqual(2,p.outputs)
        self.assertEqual(2,p.inputs)
        # w->z3 should be 0
        self.siso_almost_equal(ss([],[],[],0),p[0,0])
        # w->v should be 1
        self.siso_almost_equal(ss([],[],[],[1]),p[1,0])
        # u->z3 should be w3*g
        self.siso_almost_equal(w3*g,p[0,1])
        # u->v should be -g
        self.siso_almost_equal(-g,p[1,1])


    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testSisoW123(self):
        "SISO plant with all weights"
        from control import augw, ss
        g = ss([-1.],[1.],[1.],[1.])
        w1 = ss([-2.],[2.],[1.],[2.])
        w2 = ss([-3.],[3.],[1.],[3.])
        w3 = ss([-4.],[4.],[1.],[4.])
        p = augw(g,w1,w2,w3)
        self.assertEqual(4,p.outputs)
        self.assertEqual(2,p.inputs)
        # w->z1 should be w1
        self.siso_almost_equal(w1,p[0,0])
        # w->z2 should be 0
        self.siso_almost_equal(0,p[1,0])
        # w->z3 should be 0
        self.siso_almost_equal(0,p[2,0])
        # w->v should be 1
        self.siso_almost_equal(ss([],[],[],[1]),p[3,0])
        # u->z1 should be -w1*g
        self.siso_almost_equal(-w1*g,p[0,1])
        # u->z2 should be w2
        self.siso_almost_equal(w2,p[1,1])
        # u->z3 should be w3*g
        self.siso_almost_equal(w3*g,p[2,1])
        # u->v should be -g
        self.siso_almost_equal(-g,p[3,1])


    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testMimoW1(self):
        "MIMO plant with S weighting"
        from control import augw, ss
        g = ss([[-1.,-2],[-3,-4]],
               [[1.,0.],[0.,1.]],
               [[1.,0.],[0.,1.]],
               [[1.,0.],[0.,1.]])
        w1 = ss([-2],[2.],[1.],[2.])
        p = augw(g,w1)
        self.assertEqual(4,p.outputs)
        self.assertEqual(4,p.inputs)
        # w->z1 should be diag(w1,w1)
        self.siso_almost_equal(w1,p[0,0])
        self.siso_almost_equal(0, p[0,1])
        self.siso_almost_equal(0, p[1,0])
        self.siso_almost_equal(w1,p[1,1])
        # w->v should be I
        self.siso_almost_equal(1, p[2,0])
        self.siso_almost_equal(0, p[2,1])
        self.siso_almost_equal(0, p[3,0])
        self.siso_almost_equal(1, p[3,1])
        # u->z1 should be -w1*g
        self.siso_almost_equal(-w1*g[0,0],p[0,2])
        self.siso_almost_equal(-w1*g[0,1],p[0,3])
        self.siso_almost_equal(-w1*g[1,0],p[1,2])
        self.siso_almost_equal(-w1*g[1,1],p[1,3])
        # # u->v should be -g
        self.siso_almost_equal(-g[0,0],p[2,2])
        self.siso_almost_equal(-g[0,1],p[2,3])
        self.siso_almost_equal(-g[1,0],p[3,2])
        self.siso_almost_equal(-g[1,1],p[3,3])


    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testMimoW2(self):
        "MIMO plant with KS weighting"
        from control import augw, ss
        g = ss([[-1.,-2],[-3,-4]],
               [[1.,0.],[0.,1.]],
               [[1.,0.],[0.,1.]],
               [[1.,0.],[0.,1.]])
        w2 = ss([-2],[2.],[1.],[2.])
        p = augw(g,w2=w2)
        self.assertEqual(4,p.outputs)
        self.assertEqual(4,p.inputs)
        # w->z2 should be 0
        self.siso_almost_equal(0, p[0,0])
        self.siso_almost_equal(0, p[0,1])
        self.siso_almost_equal(0, p[1,0])
        self.siso_almost_equal(0, p[1,1])
        # w->v should be I
        self.siso_almost_equal(1, p[2,0])
        self.siso_almost_equal(0, p[2,1])
        self.siso_almost_equal(0, p[3,0])
        self.siso_almost_equal(1, p[3,1])
        # u->z2 should be w2
        self.siso_almost_equal(w2, p[0,2])
        self.siso_almost_equal(0,  p[0,3])
        self.siso_almost_equal(0,  p[1,2])
        self.siso_almost_equal(w2, p[1,3])
        # # u->v should be -g
        self.siso_almost_equal(-g[0,0], p[2,2])
        self.siso_almost_equal(-g[0,1], p[2,3])
        self.siso_almost_equal(-g[1,0], p[3,2])
        self.siso_almost_equal(-g[1,1], p[3,3])


    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testMimoW3(self):
        "MIMO plant with T weighting"
        from control import augw, ss
        g = ss([[-1.,-2],[-3,-4]],
               [[1.,0.],[0.,1.]],
               [[1.,0.],[0.,1.]],
               [[1.,0.],[0.,1.]])
        w3 = ss([-2],[2.],[1.],[2.])
        p = augw(g,w3=w3)
        self.assertEqual(4,p.outputs)
        self.assertEqual(4,p.inputs)
        # w->z3 should be 0
        self.siso_almost_equal(0, p[0,0])
        self.siso_almost_equal(0, p[0,1])
        self.siso_almost_equal(0, p[1,0])
        self.siso_almost_equal(0, p[1,1])
        # w->v should be I
        self.siso_almost_equal(1, p[2,0])
        self.siso_almost_equal(0, p[2,1])
        self.siso_almost_equal(0, p[3,0])
        self.siso_almost_equal(1, p[3,1])
        # u->z3 should be w3*g
        self.siso_almost_equal(w3*g[0,0], p[0,2])
        self.siso_almost_equal(w3*g[0,1], p[0,3])
        self.siso_almost_equal(w3*g[1,0], p[1,2])
        self.siso_almost_equal(w3*g[1,1], p[1,3])
        # # u->v should be -g
        self.siso_almost_equal(-g[0,0], p[2,2])
        self.siso_almost_equal(-g[0,1], p[2,3])
        self.siso_almost_equal(-g[1,0], p[3,2])
        self.siso_almost_equal(-g[1,1], p[3,3])


    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testMimoW123(self):
        "MIMO plant with all weights"
        from control import augw, ss, append
        g = ss([[-1.,-2],[-3,-4]],
               [[1.,0.],[0.,1.]],
               [[1.,0.],[0.,1.]],
               [[1.,0.],[0.,1.]])
        # this should be expaned to w1*I
        w1 = ss([-2.],[2.],[1.],[2.])
        # diagonal weighting
        w2 = append(ss([-3.],[3.],[1.],[3.]), ss([-4.],[4.],[1.],[4.]))
        # full weighting
        w3 = ss([[-4.,-5],[-6,-7]],
               [[2.,3.],[5.,7.]],
               [[11.,13.],[17.,19.]],
               [[23.,29.],[31.,37.]])
        p = augw(g,w1,w2,w3)
        self.assertEqual(8,p.outputs)
        self.assertEqual(4,p.inputs)
        # w->z1 should be w1
        self.siso_almost_equal(w1, p[0,0])
        self.siso_almost_equal(0,  p[0,1])
        self.siso_almost_equal(0,  p[1,0])
        self.siso_almost_equal(w1, p[1,1])
        # w->z2 should be 0
        self.siso_almost_equal(0, p[2,0])
        self.siso_almost_equal(0, p[2,1])
        self.siso_almost_equal(0, p[3,0])
        self.siso_almost_equal(0, p[3,1])
        # w->z3 should be 0
        self.siso_almost_equal(0, p[4,0])
        self.siso_almost_equal(0, p[4,1])
        self.siso_almost_equal(0, p[5,0])
        self.siso_almost_equal(0, p[5,1])
        # w->v should be I
        self.siso_almost_equal(1, p[6,0])
        self.siso_almost_equal(0, p[6,1])
        self.siso_almost_equal(0, p[7,0])
        self.siso_almost_equal(1, p[7,1])

        # u->z1 should be -w1*g
        self.siso_almost_equal(-w1*g[0,0], p[0,2])
        self.siso_almost_equal(-w1*g[0,1], p[0,3])
        self.siso_almost_equal(-w1*g[1,0], p[1,2])
        self.siso_almost_equal(-w1*g[1,1], p[1,3])
        # u->z2 should be w2
        self.siso_almost_equal(w2[0,0], p[2,2])
        self.siso_almost_equal(w2[0,1], p[2,3])
        self.siso_almost_equal(w2[1,0], p[3,2])
        self.siso_almost_equal(w2[1,1], p[3,3])
        # u->z3 should be w3*g
        w3g = w3*g;
        self.siso_almost_equal(w3g[0,0], p[4,2])
        self.siso_almost_equal(w3g[0,1], p[4,3])
        self.siso_almost_equal(w3g[1,0], p[5,2])
        self.siso_almost_equal(w3g[1,1], p[5,3])
        # u->v should be -g
        self.siso_almost_equal(-g[0,0], p[6,2])
        self.siso_almost_equal(-g[0,1], p[6,3])
        self.siso_almost_equal(-g[1,0], p[7,2])
        self.siso_almost_equal(-g[1,1], p[7,3])


    @unittest.skipIf(not slycot_check(), "slycot not installed")
    def testErrors(self):
        "Error cases handled"
        from control import augw,ss
        # no weights
        g1by1 = ss(-1,1,1,0)
        g2by2 = ss(-np.eye(2),np.eye(2),np.eye(2),np.zeros((2,2)))
        self.assertRaises(ValueError,augw,g1by1)
        # mismatched size of weight and plant
        self.assertRaises(ValueError,augw,g1by1,w1=g2by2)
        self.assertRaises(ValueError,augw,g1by1,w2=g2by2)
        self.assertRaises(ValueError,augw,g1by1,w3=g2by2)
Example #39
0
#!/usr/bin/env python
#
# minreal_test.py - test state space class
# Rvp, 13 Jun 2013

import unittest
import numpy as np
from scipy.linalg import eigvals
from control import matlab
from control.statesp import StateSpace
from control.xferfcn import TransferFunction
from itertools import permutations
from control.exception import slycot_check

@unittest.skipIf(not slycot_check(), "slycot not installed")
class TestMinreal(unittest.TestCase):
    """Tests for the StateSpace class."""

    def setUp(self):
        np.random.seed(5)
        # depending on the seed and minreal performance, a number of
        # reductions is produced. If random gen or minreal change, this
        # will be likely to fail
        self.nreductions = 0

    def assert_numden_almost_equal(self, n1, n2, d1, d2):
        n1[np.abs(n1) < 1e-10] = 0.
        n1 = np.trim_zeros(n1)
        d1[np.abs(d1) < 1e-10] = 0.
        d1 = np.trim_zeros(d1)
        n2[np.abs(n2) < 1e-10] = 0.
class TestStateSpace(unittest.TestCase):
    """Tests for the StateSpace class."""
    def setUp(self):
        """Set up a MIMO system to test operations on."""
        use_numpy_matrix(False)

        # 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_matlab_style_constructor(self):
        # Use (deprecated?) matrix-style construction string (w/ warnings off)
        import warnings
        warnings.filterwarnings("ignore")  # turn off warnings
        sys = StateSpace("-1 1; 0 2", "0; 1", "1, 0", "0")
        warnings.resetwarnings()  # put things back to original state
        self.assertEqual(sys.A.shape, (2, 2))
        self.assertEqual(sys.B.shape, (2, 1))
        self.assertEqual(sys.C.shape, (1, 2))
        self.assertEqual(sys.D.shape, (1, 1))
        if defaults['statesp.use_numpy_matrix']:
            for X in [sys.A, sys.B, sys.C, sys.D]:
                self.assertTrue(isinstance(X, np.matrix))
        else:
            for X in [sys.A, sys.B, sys.C, sys.D]:
                self.assertTrue(isinstance(X, np.ndarray))

    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)

            # Leave the warnings filter like we found it
            warnings.resetwarnings()

    @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)
        np.testing.assert_array_almost_equal(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.array([[1, 2, 3], [4, 5, 6]])
        d2 = np.array([[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(np.dot(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) + np.dot(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.array([[1, 2, 3], [4, 5, 6]])
        g = _convertToStateSpace(D)

        def empty(shape):
            m = np.array([])
            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)

    def test_horner(self):
        """Test horner() function"""
        # Make sure we can compute the transfer function at a complex value
        self.sys322.horner(1. + 1.j)

        # Make sure result agrees with frequency response
        mag, phase, omega = self.sys322.freqresp([1])
        np.testing.assert_array_almost_equal(
            self.sys322.horner(1.j),
            mag[:, :, 0] * np.exp(1.j * phase[:, :, 0]))

    def tearDown(self):
        reset_defaults()  # reset configuration defaults
# System matrices
A = np.matrix([[1, -1, 1.], [1, -k / m, -b / m], [1, 1, 1]])
B = np.matrix([[0], [1 / m], [1]])
C = np.matrix([[1., 0, 1.]])
sys = ss(A, B, C, 0)

# Python control may be used without slycot, for example for a pole placement.
# Eigenvalue placement
w = [-3, -2, -1]
K = place(A, B, w)
print("[python-control (from scipy)] K = ", K)
print("[python-control (from scipy)] eigs = ", np.linalg.eig(A - B * K)[0])

# Before using one of its routine, check that slycot is installed.
w = np.array([-3, -2, -1])
if slycot_check():
    # Import routine sb01bd used for pole placement.
    from slycot import sb01bd

    n = 3  # Number of states
    m = 1  # Number of inputs
    npp = 3 # Number of placed eigen values
    alpha = 1 # Maximum threshold for eigen values
    dico = 'D'  # Discrete system
    _, _, _, _, _, K, _ = sb01bd(n, m, npp, alpha, A, B, w, dico, tol=0.0, ldwork=None)
    print("[slycot] K = ", K)
    print("[slycot] eigs = ", np.linalg.eig(A + np.dot(B, K))[0])
else:
    print("Slycot is not installed.")