Ejemplo n.º 1
0
    def test_tb05ad_balance(self):
        """Test balancing in tb05ad.

        Tests for the cause of the problem reported in issue #11
        balancing permutations were not correctly applied to the
        C and D matrix.
        """

        # find a good test case. Some sparsity,
        # some zero eigenvalues, some non-zero eigenvalues,
        # and proof that the 1st step, with dgebal, does some
        # permutation and some scaling
        crit = False
        n = 8
        while not crit:
            A = np.random.randn(n, n)
            A[np.random.uniform(size=(n, n)) > 0.35] = 0.0

            Aeig = eig(A)[0]
            neig0 = np.sum(np.abs(Aeig) == 0)
            As, T = matrix_balance(A)
            nperm = np.sum(np.diag(T == 0))
            nscale = n - np.sum(T == 1.0)
            crit = nperm < n and nperm >= n//2 and \
                neig0 > 1 and neig0 <= 3 and nscale > 0

        # print("number of permutations", nperm, "eigenvalues=0", neig0)
        B = np.random.randn(8, 4)
        C = np.random.randn(3, 8)

        # do a run
        jomega = 1.0
        At, Bt, Ct, rcond, g_jw, ev, hinvb, info = transform.tb05ad(8,
                                                                    4,
                                                                    3,
                                                                    jomega,
                                                                    A,
                                                                    B,
                                                                    C,
                                                                    job='AG')

        # remove information on Q, in lower sub-triangle part of A
        At = np.triu(At, k=-1)

        # now after the balancing in DGEBAL, and conversion to
        # upper Hessenberg form:
        # At = Q^T * (P^-1 * A * P ) * Q
        # with Q orthogonal
        # Ct = C * P * Q
        # Bt = Q^T * P^-1 * B
        # so test with Ct * At * Bt  ==  C * A * B
        # and verify that eigenvalues of both A matrices are close
        assert_almost_equal(np.dot(np.dot(Ct, At), Bt),
                            np.dot(np.dot(C, A), B))
        # uses a sort, there is no guarantee on the order of eigenvalues
        eigAt = eig(At)[0]
        idxAt = np.argsort(eigAt)
        eigA = eig(A)[0]
        idxA = np.argsort(eigA)
        assert_almost_equal(eigA[idxA], eigAt[idxAt])
Ejemplo n.º 2
0
    def check_tb05ad_AG_NG(self, sys, jomega, job):
        """
        Check that tb05ad computes the correct frequency response when
        running jobs 'AG' and/or 'NG'.

        Inputs
        ------

        sys: A a dict of system matrices with keys 'A', 'B', and 'C'.
        jomega: A complex scalar, which is the frequency we are
                evaluating the system at.
        job: A string, either 'AG' or 'NH'

        Returns
        -------
        sys_transformed: A dict of the system matrices which have been
                         transformed according the job.
        """
        n, m = sys['B'].shape
        p = sys['C'].shape[0]
        result = transform.tb05ad(n,
                                  m,
                                  p,
                                  jomega,
                                  sys['A'],
                                  sys['B'],
                                  sys['C'],
                                  job=job)
        g_i = result[3]
        hinvb = np.linalg.solve(np.eye(n) * jomega - sys['A'], sys['B'])
        g_i_solve = sys['C'].dot(hinvb)
        assert_almost_equal(g_i_solve, g_i)
        sys_transformed = {'A': result[0], 'B': result[1], 'C': result[2]}
        return sys_transformed
Ejemplo n.º 3
0
    def check_tb05ad_NH(self, sys_transformed, sys, jomega):
        """
        Check tb05ad, computes the correct frequency response when
        job='NH' and we supply system matrices 'A', 'B', and 'C'
        which have been transformed by a previous call to tb05ad.
        We check we get the same result as computing C(sI - A)^-1B
        with the original system.

        Inputs
        ------

        sys_transformed: A a dict of the transformed (A in upper
                         hessenberg form) system matrices with keys
                         'A', 'B', and 'C'.

        sys: A dict of the original un-transformed system matrices.

        jomega: A complex scalar, which is the frequency to evaluate at.

        """

        n, m = sys_transformed['B'].shape
        p = sys_transformed['C'].shape[0]
        result = transform.tb05ad(n,
                                  m,
                                  p,
                                  jomega,
                                  sys_transformed['A'],
                                  sys_transformed['B'],
                                  sys_transformed['C'],
                                  job='NH')
        g_i = result[0]
        hinvb = np.linalg.solve(np.eye(n) * jomega - sys['A'], sys['B'])
        g_i_solve = sys['C'].dot(hinvb)
        assert_almost_equal(g_i_solve, g_i)
Ejemplo n.º 4
0
    def check_tb05ad_AG_NG(self, sys, jomega, job):
        """
        Check that tb05ad computes the correct frequency response when
        running jobs 'AG' and/or 'NG'.

        Inputs
        ------

        sys: A a dict of system matrices with keys 'A', 'B', and 'C'.
        jomega: A complex scalar, which is the frequency we are
                evaluating the system at.
        job: A string, either 'AG' or 'NH'

        Returns
        -------
        sys_transformed: A dict of the system matrices which have been
                         transformed according the job.
        """
        n, m = sys['B'].shape
        p = sys['C'].shape[0]
        result = transform.tb05ad(n, m, p, jomega,
                                  sys['A'], sys['B'], sys['C'], job=job)
        g_i = result[3]
        hinvb = np.linalg.solve(np.eye(n) * jomega - sys['A'], sys['B'])
        g_i_solve = sys['C'].dot(hinvb)
        assert_almost_equal(g_i_solve, g_i)
        sys_transformed = {'A': result[0], 'B': result[1], 'C': result[2]}
        return sys_transformed
Ejemplo n.º 5
0
    def check_tb05ad_NH(self, sys_transformed, sys, jomega):
        """
        Check tb05ad, computes the correct frequency response when
        job='NH' and we supply system matrices 'A', 'B', and 'C'
        which have been transformed by a previous call to tb05ad.
        We check we get the same result as computing C(sI - A)^-1B
        with the original system.

        Inputs
        ------

        sys_transformed: A a dict of the transformed (A in upper
                         hessenberg form) system matrices with keys
                         'A', 'B', and 'C'.

        sys: A dict of the original un-transformed system matrices.

        jomega: A complex scalar, which is the frequency to evaluate at.

        """

        n, m = sys_transformed['B'].shape
        p = sys_transformed['C'].shape[0]
        result = transform.tb05ad(n, m, p, jomega, sys_transformed['A'],
                                  sys_transformed['B'], sys_transformed['C'],
                                  job='NH')
        g_i = result[0]
        hinvb = np.linalg.solve(np.eye(n) * jomega - sys['A'], sys['B'])
        g_i_solve = sys['C'].dot(hinvb)
        assert_almost_equal(g_i_solve, g_i)
Ejemplo n.º 6
0
    def test_tb05ad_resonance(self):
        """ Test tb05ad resonance failure.

        Actually test one of the exception messages. These
        are parsed from the docstring, tests both the info index and the
        message
        """
        A = np.array([[0, -1], [1, 0]])
        B = np.array([[1], [0]])
        C = np.array([[0, 1]])
        jomega = 1j
        with self.assertRaisesRegex(
                SlycotArithmeticError,
                r"Either `freq`.* is too near to an eigenvalue of A,\n"
                r"or `rcond` is less than the machine precision EPS.") as cm:
            transform.tb05ad(2, 1, 1, jomega, A, B, C, job='NH')
        assert cm.exception.info == 2
Ejemplo n.º 7
0
 def check_tb05ad_errors(self, sys):
     """
     Check the error handling of tb05ad. We give wrong inputs and
     and check that this raises an error.
     """
     n, m = sys['B'].shape
     p = sys['C'].shape[0]
     jomega = 10 * 1j
     # test error handling
     # wrong size A
     with self.assertRaises(SlycotParameterError) as cm:
         transform.tb05ad(n + 1,
                          m,
                          p,
                          jomega,
                          sys['A'],
                          sys['B'],
                          sys['C'],
                          job='NH')
     assert cm.exception.info == -7
     # wrong size B
     with self.assertRaises(SlycotParameterError) as cm:
         transform.tb05ad(n,
                          m + 1,
                          p,
                          jomega,
                          sys['A'],
                          sys['B'],
                          sys['C'],
                          job='NH')
     assert cm.exception.info == -9
     # wrong size C
     with self.assertRaises(SlycotParameterError) as cm:
         transform.tb05ad(n,
                          m,
                          p + 1,
                          jomega,
                          sys['A'],
                          sys['B'],
                          sys['C'],
                          job='NH')
     assert cm.exception.info == -11
     # unrecognized job
     with self.assertRaises(SlycotParameterError) as cm:
         transform.tb05ad(n,
                          m,
                          p,
                          jomega,
                          sys['A'],
                          sys['B'],
                          sys['C'],
                          job='a')
     assert cm.exception.info == -1