def test_bdschur_empty(): # empty matrix in gives empty matrix out a = np.empty(shape=(0, 0)) b, t, blksizes = bdschur(a) np.testing.assert_array_equal(b, a) np.testing.assert_array_equal(t, a) np.testing.assert_array_equal(blksizes, np.array([]))
def test_modal_form_condmax(condmax, len_blksizes): # condmax passed through as expected a = companion_from_eig([-1, -2, -3, -4, -5]) amodal, tmodal, blksizes = bdschur(a, condmax=condmax) assert len(blksizes) == len_blksizes xsys = ss(a, [[1],[0],[0],[0],[0]], [0,0,0,0,1], 0) zsys, t = modal_form(xsys, condmax=condmax) np.testing.assert_array_almost_equal(zsys.A, amodal) np.testing.assert_array_almost_equal(t, tmodal) np.testing.assert_array_almost_equal(zsys.B, np.linalg.solve(tmodal, xsys.B)) np.testing.assert_array_almost_equal(zsys.C, xsys.C.dot(tmodal)) np.testing.assert_array_almost_equal(zsys.D, xsys.D)
def test_bdschur_sort(eigvals, sorted_blk_eigvals, sort): # use block diagonal form to prevent numerical complications # for discrete case, exp and log introduce round-off, can't test as compeletely a = block_diag_from_eig(eigvals) b, t, blksizes = bdschur(a, sort=sort) assert len(blksizes) == len(sorted_blk_eigvals) blocks = extract_bdiag(b, blksizes) for block, blk_eigval in zip(blocks, sorted_blk_eigvals): test_eigvals = np.linalg.eigvals(block) np.testing.assert_allclose(test_eigvals.real, blk_eigval.real) np.testing.assert_allclose(abs(test_eigvals.imag), blk_eigval.imag)
def test_bdschur_ref(eigvals, condmax, blksizes): # "reference" check # uses companion form to introduce numerical complications from numpy.linalg import solve a = companion_from_eig(eigvals) b, t, test_blksizes = bdschur(a, condmax=condmax) np.testing.assert_array_equal(np.sort(test_blksizes), np.sort(blksizes)) bdiag_b = scipy.linalg.block_diag(*extract_bdiag(b, test_blksizes)) np.testing.assert_array_almost_equal(bdiag_b, b) np.testing.assert_array_almost_equal(solve(t, a) @ t, b)
def test_modal_form_sort(sys_type): a = companion_from_eig([0.1+0.9j,0.1-0.9j, 0.2+0.8j, 0.2-0.8j]) amodal, tmodal, blksizes = bdschur(a, sort=sys_type) dt = 0 if sys_type == 'continuous' else True xsys = ss(a, [[1],[0],[0],[0],], [0,0,0,1], 0, dt) zsys, t = modal_form(xsys, sort=True) my_amodal = np.linalg.solve(tmodal, a).dot(tmodal) np.testing.assert_array_almost_equal(amodal, my_amodal) np.testing.assert_array_almost_equal(t, tmodal) np.testing.assert_array_almost_equal(zsys.A, amodal) np.testing.assert_array_almost_equal(zsys.B, np.linalg.solve(tmodal, xsys.B)) np.testing.assert_array_almost_equal(zsys.C, xsys.C.dot(tmodal)) np.testing.assert_array_almost_equal(zsys.D, xsys.D)
def test_modal_form(A_true, B_true, C_true, D_true): # Check modal_canonical corresponds to bdschur # Perform a coordinate transform with a random invertible matrix T_true = np.array([[-0.27144004, -0.39933167, 0.75634684, 0.44135471], [-0.74855725, -0.39136285, -0.18142339, -0.50356997], [-0.40688007, 0.81416369, 0.38002113, -0.16483334], [-0.44769516, 0.15654653, -0.50060858, 0.72419146]]) A = np.linalg.solve(T_true, A_true) @ T_true B = np.linalg.solve(T_true, B_true) C = C_true @ T_true D = D_true # Create a state space system and convert it to modal canonical form sys_check, T_check = modal_form(ss(A, B, C, D)) a_bds, t_bds, _ = bdschur(A) np.testing.assert_array_almost_equal(sys_check.A, a_bds) np.testing.assert_array_almost_equal(T_check, t_bds) np.testing.assert_array_almost_equal(sys_check.B, np.linalg.solve(t_bds, B)) np.testing.assert_array_almost_equal(sys_check.C, C @ t_bds) np.testing.assert_array_almost_equal(sys_check.D, D) # canonical_form(...,'modal') is the same as modal_form with default parameters cf_sys, T_cf = canonical_form(ss(A, B, C, D), 'modal') np.testing.assert_array_almost_equal(cf_sys.A, sys_check.A) np.testing.assert_array_almost_equal(cf_sys.B, sys_check.B) np.testing.assert_array_almost_equal(cf_sys.C, sys_check.C) np.testing.assert_array_almost_equal(cf_sys.D, sys_check.D) np.testing.assert_array_almost_equal(T_check, T_cf) # Make sure Hankel coefficients are OK for i in range(A.shape[0]): np.testing.assert_almost_equal( C_true @ np.linalg.matrix_power(A_true, i) @ B_true, C @ np.linalg.matrix_power(A, i) @ B)
def test_bdschur_invalid_sort(): # sort must be in ('continuous', 'discrete') with pytest.raises(ValueError): bdschur(1, sort='no-such-sort')
def test_bdschur_condmax_lt_1(): # require condmax >= 1.0 with pytest.raises(ValueError): bdschur(1, condmax=np.nextafter(1, 0))
def test_bdschur_defective(): # the eigenvalues of this simple defective matrix cannot be separated # a previous version of the bdschur would fail on this a = companion_from_eig([-1, -1]) amodal, tmodal, blksizes = bdschur(a, condmax=1e200)