def test_mb03rd_default(self): # regression: mb03rd was failing with no third arg (X) supplied A = np.array([[6, -1, -7, -2, 2], [-3, 4, 2, -7, 6], [-6, -9, -3, -1, 10], [-2, -4, 1, 5, 7], [-7, -5, -6, 6, 7]]) Aschur, Tschur = schur(A) X = Tschur.copy() Ar, Xr, blsize, W = mb03rd(Aschur.shape[0], Aschur, X, 'U', 'N', pmax=1.0, tol=0.0) Ar2, Xr2, blsize2, W2 = mb03rd(Aschur.shape[0], Aschur) assert_allclose(Ar, Ar2) assert_allclose(Xr, Tschur.dot(Xr2))
def test_mb03rd(self): """ Test for Schur form reduction. RvP, 31 Jul 2019""" test1_A = np.array([[1., -1., 1., 2., 3., 1., 2., 3.], [1., 1., 3., 4., 2., 3., 4., 2.], [0., 0., 1., -1., 1., 5., 4., 1.], [0., 0., 0., 1., -1., 3., 1., 2.], [0., 0., 0., 1., 1., 2., 3., -1.], [0., 0., 0., 0., 0., 1., 5., 1.], [0., 0., 0., 0., 0., 0., 0.99999999, -0.99999999], [0., 0., 0., 0., 0., 0., 0.99999999, 0.99999999]]) test1_n = test1_A.shape[0] test1_Ar = np.array([ [ 1.0000, -1.0000, -1.2247, -0.7071, -3.4186, 1.4577, 0.0000, 0.0000 ], [1.0000, 1.0000, 0.0000, 1.4142, -5.1390, 3.1637, 0.0000, 0.0000], [0.0000, 0.0000, 1.0000, -1.7321, -0.0016, 2.0701, 0.0000, 0.0000], [0.0000, 0.0000, 0.5774, 1.0000, 0.7516, 1.1379, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 1.0000, -5.8606, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.1706, 1.0000, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 1.0000, -0.8850], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 1.0000], ]) test1_Xr = np.array( [[1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.9045, 0.1957], [0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 0.0000, -0.3015, 0.9755], [ 0.0000, 0.0000, 0.8165, 0.0000, -0.5768, -0.0156, -0.3015, 0.0148 ], [ 0.0000, 0.0000, -0.4082, 0.7071, -0.5768, -0.0156, 0.0000, -0.0534 ], [ 0.0000, 0.0000, -0.4082, -0.7071, -0.5768, -0.0156, 0.0000, 0.0801 ], [0.0000, 0.0000, 0.0000, 0.0000, -0.0276, 0.9805, 0.0000, 0.0267], [0.0000, 0.0000, 0.0000, 0.0000, 0.0332, -0.0066, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0011, 0.1948, 0.0000, 0.0000]]) test1_W = np.array([ 1 + 1j, 1 - 1j, 1 + 1j, 1 - 1j, 0.99999 + 0.99999j, 0.99999 - 0.99999j, 1., 1. ]) test1_pmax = 1e3 test1_tol = 0.01 # create schur form with scipy A, X = schur(test1_A) Ah, Xh = np.copy(A), np.copy(X) # on this basis, get the transform Ar, Xr, blsize, W = mb03rd(test1_n, A, X, 'U', 'S', test1_pmax, test1_tol) # ensure X and A are unchanged assert_allclose(A, Ah) assert_allclose(X, Xh) # compare to test case results assert_allclose(Ar, test1_Ar, atol=0.0001) assert_allclose(Xr, test1_Xr, atol=0.0001) assert_allclose(W, test1_W, atol=0.0001) # Test that the non sorting options do not throw errors and that Xr is # returned as None for jobx='N' for sort in ['N', 'C', 'B']: Ar, Xr, blsize, W = mb03rd(test1_n, A, X, 'N', sort, test1_pmax, test1_tol) assert Xr is None
def _bdschur_condmax_search(aschur, tschur, condmax): """Block-diagonal Schur decomposition search up to condmax Iterates mb03rd with different pmax values until: - result is non-defective; - or condition number of similarity transform is unchanging despite large pmax; - or condition number of similarity transform is close to condmax. Parameters ---------- aschur: (N, N) real ndarray Real Schur-form matrix tschur: (N, N) real ndarray Orthogonal transformation giving aschur from some initial matrix a condmax: float Maximum condition number of final transformation. Must be >= 1. Returns ------- amodal: (N, N) real ndarray block diagonal Schur form tmodal: (N, N) real ndarray similarity transformation give amodal from aschur blksizes: (M,) int ndarray Array of Schur block sizes eigvals: (N,) real or complex ndarray Eigenvalues of amodal (and a, etc.) Notes ----- Outputs as for slycot.mb03rd aschur, tschur are as returned by scipy.linalg.schur. """ try: from slycot import mb03rd except ImportError: raise ControlSlycot("can't find slycot module 'mb03rd'") # see notes on RuntimeError below pmaxlower = None # get lower bound; try condmax ** 0.5 first pmaxlower = condmax**0.5 amodal, tmodal, blksizes, eigvals = mb03rd(aschur.shape[0], aschur, tschur, pmax=pmaxlower) if np.linalg.cond(tmodal) <= condmax: reslower = amodal, tmodal, blksizes, eigvals else: pmaxlower = 1.0 amodal, tmodal, blksizes, eigvals = mb03rd(aschur.shape[0], aschur, tschur, pmax=pmaxlower) cond = np.linalg.cond(tmodal) if cond > condmax: msg = 'minimum cond={} > condmax={}; try increasing condmax'.format( cond, condmax) raise RuntimeError(msg) pmax = pmaxlower # phase 1: search for upper bound on pmax for i in range(50): amodal, tmodal, blksizes, eigvals = mb03rd(aschur.shape[0], aschur, tschur, pmax=pmax) cond = np.linalg.cond(tmodal) if cond < condmax: pmaxlower = pmax reslower = amodal, tmodal, blksizes, eigvals else: # upper bound found; go to phase 2 pmaxupper = pmax break if _bdschur_defective(blksizes, eigvals): pmax *= 2 else: return amodal, tmodal, blksizes, eigvals else: # no upper bound found; return current result return reslower # phase 2: bisection search for i in range(50): pmax = (pmaxlower * pmaxupper)**0.5 amodal, tmodal, blksizes, eigvals = mb03rd(aschur.shape[0], aschur, tschur, pmax=pmax) cond = np.linalg.cond(tmodal) if cond < condmax: if not _bdschur_defective(blksizes, eigvals): return amodal, tmodal, blksizes, eigvals pmaxlower = pmax reslower = amodal, tmodal, blksizes, eigvals else: pmaxupper = pmax if pmaxupper / pmaxlower < _PMAX_SEARCH_TOL: # hit search limit return reslower else: raise ValueError( 'bisection failed to converge; pmaxlower={}, pmaxupper={}'.format( pmaxlower, pmaxupper))