def solve_sylvester(a, b, q): """Computes a solution (X) to the Sylvester equation (AX + XB = Q). .. versionadded:: 0.11.0 Parameters ---------- a : array, shape (M, M) Leading matrix of the Sylvester equation b : array, shape (N, N) Trailing matrix of the Sylvester equation q : array, shape (M, N) Right-hand side Returns ------- x : array, shape (M, N) The solution to the Sylvester equation. Raises ------ LinAlgError If solution was not found Notes ----- Computes a solution to the Sylvester matrix equation via the Bartels- Stewart algorithm. The A and B matrices first undergo Schur decompositions. The resulting matrices are used to construct an alternative Sylvester equation (``RY + YS^T = F``) where the R and S matrices are in quasi-triangular form (or, when R, S or F are complex, triangular form). The simplified equation is then solved using ``*TRSYL`` from LAPACK directly. """ # Compute the Schur decomp form of a r, u = schur(a, output='real') # Compute the Schur decomp of b s, v = schur(b.conj().transpose(), output='real') # Construct f = u'*q*v f = np.dot(np.dot(u.conj().transpose(), q), v) # Call the Sylvester equation solver trsyl, = get_lapack_funcs(('trsyl', ), (r, s, f)) if trsyl == None: raise RuntimeError( 'LAPACK implementation does not contain a proper Sylvester equation solver (TRSYL)' ) y, scale, info = trsyl(r, s, f, tranb='C') y = scale * y if info < 0: raise LinAlgError("Illegal value encountered in the %d term" % (-info, )) return np.dot(np.dot(u, y), v.conj().transpose())
def solve_sylvester(a,b,q): """Computes a solution (X) to the Sylvester equation (AX + XB = Q). .. versionadded:: 0.11.0 Parameters ---------- a : array, shape (M, M) Leading matrix of the Sylvester equation b : array, shape (N, N) Trailing matrix of the Sylvester equation q : array, shape (M, N) Right-hand side Returns ------- x : array, shape (M, N) The solution to the Sylvester equation. Raises ------ LinAlgError If solution was not found Notes ----- Computes a solution to the Sylvester matrix equation via the Bartels- Stewart algorithm. The A and B matrices first undergo Schur decompositions. The resulting matrices are used to construct an alternative Sylvester equation (``RY + YS^T = F``) where the R and S matrices are in quasi-triangular form (or, when R, S or F are complex, triangular form). The simplified equation is then solved using ``*TRSYL`` from LAPACK directly. """ # Compute the Schur decomp form of a r,u = schur(a, output='real') # Compute the Schur decomp of b s,v = schur(b.conj().transpose(), output='real') # Construct f = u'*q*v f = np.dot(np.dot(u.conj().transpose(), q), v) # Call the Sylvester equation solver trsyl, = get_lapack_funcs(('trsyl',), (r,s,f)) if trsyl == None: raise RuntimeError('LAPACK implementation does not contain a proper Sylvester equation solver (TRSYL)') y, scale, info = trsyl(r, s, f, tranb='C') y = scale*y if info < 0: raise LinAlgError("Illegal value encountered in the %d term" % (-info,)) return np.dot(np.dot(u, y), v.conj().transpose())
def sqrtm(A, disp=True): """Matrix square root. Parameters ---------- A : array, shape(M,M) Matrix whose square root to evaluate disp : boolean Print warning if error in the result is estimated large instead of returning estimated error. (Default: True) Returns ------- sgnA : array, shape(M,M) Value of the sign function at A (if disp == False) errest : float Frobenius norm of the estimated error, ||err||_F / ||A||_F Notes ----- Uses algorithm by Nicholas J. Higham """ A = asarray(A) if len(A.shape)!=2: raise ValueError("Non-matrix input to matrix function.") T, Z = schur(A) T, Z = rsf2csf(T,Z) n,n = T.shape R = np.zeros((n,n),T.dtype.char) for j in range(n): R[j,j] = sqrt(T[j,j]) for i in range(j-1,-1,-1): s = 0 for k in range(i+1,j): s = s + R[i,k]*R[k,j] R[i,j] = (T[i,j] - s)/(R[i,i] + R[j,j]) R, Z = all_mat(R,Z) X = (Z * R * Z.H) if disp: nzeig = np.any(diag(T)==0) if nzeig: print "Matrix is singular and may not have a square root." return X.A else: arg2 = norm(X*X - A,'fro')**2 / norm(A,'fro') return X.A, arg2
def solve_discrete_are(a, b, q, r): """Solves the disctrete algebraic Riccati equation, or DARE, defined as (X = A'XA-(A'XB)(R+B'XB)^-1(B'XA)+Q), directly using a Schur decomposition method. Parameters ---------- a : array_like Non-singular m x m square matrix b : array_like m x n matrix q : array_like m x m square matrix r : array_like Non-singular n x n square matrix Returns ------- x : array_like Solution to the continuous Lyapunov equation Notes ----- Method taken from: Laub, "A Schur Method for Solving Algebraic Riccati Equations." U.S. Energy Research and Development Agency under contract ERDA-E(49-18)-2087. http://dspace.mit.edu/bitstream/handle/1721.1/1301/R-0859-05666488.pdf See Also -------- solve_continuous_are : Solves the continuous algebraic Riccati equation """ try: g = inv(r) except LinAlgError: raise ValueError('Matrix R in the algebraic Riccati equation solver is ill-conditioned') g = np.dot(np.dot(b, g), b.conj().transpose()) try: ait = inv(a).conj().transpose() # ait is "A inverse transpose" except LinAlgError: raise ValueError('Matrix A in the algebraic Riccati equation solver is ill-conditioned') z11 = a+np.dot(np.dot(g, ait), q) z12 = -1.0*np.dot(g, ait) z21 = -1.0*np.dot(ait, q) z22 = ait z = np.vstack((np.hstack((z11, z12)), np.hstack((z21, z22)))) # Note: we need to sort the upper left of s to lie within the unit circle, # while the lower right is outside (Laub, p. 7) [s, u, sorted] = schur(z, sort='iuc') (m,n) = u.shape u11 = u[0:m/2, 0:n/2] u12 = u[0:m/2, n/2:n] u21 = u[m/2:m, 0:n/2] u22 = u[m/2:m, n/2:n] u11i = inv(u11) return np.dot(u21, u11i)
def solve_continuous_are(a, b, q, r): """Solves the continuous algebraic Riccati equation, or CARE, defined as (A'X + XA - XBR^-1B'X+Q=0) directly using a Schur decomposition method. Parameters ---------- a : array_like m x m square matrix b : array_like m x n matrix q : array_like m x m square matrix r : array_like Non-singular n x n square matrix Returns ------- x : array_like Solution (m x m) to the continuous algebraic Riccati equation Notes ----- Method taken from: Laub, "A Schur Method for Solving Algebraic Riccati Equations." U.S. Energy Research and Development Agency under contract ERDA-E(49-18)-2087. http://dspace.mit.edu/bitstream/handle/1721.1/1301/R-0859-05666488.pdf See Also -------- solve_discrete_are : Solves the discrete algebraic Riccati equation """ try: g = inv(r) except LinAlgError: raise ValueError('Matrix R in the algebraic Riccati equation solver is ill-conditioned') g = np.dot(np.dot(b, g), b.conj().transpose()) z11 = a z12 = -1.0*g z21 = -1.0*q z22 = -1.0*a.conj().transpose() z = np.vstack((np.hstack((z11, z12)), np.hstack((z21, z22)))) # Note: we need to sort the upper left of s to have negative real parts, # while the lower right is positive real components (Laub, p. 7) [s, u, sorted] = schur(z, sort='lhp') (m, n) = u.shape u11 = u[0:m/2, 0:n/2] u12 = u[0:m/2, n/2:n] u21 = u[m/2:m, 0:n/2] u22 = u[m/2:m, n/2:n] u11i = inv(u11) return np.dot(u21, u11i)
def funm(A, func, disp=True): """Evaluate a matrix function specified by a callable. Returns the value of matrix-valued function f at A. The function f is an extension of the scalar-valued function func to matrices. Parameters ---------- A : array, shape(M,M) Matrix at which to evaluate the function func : callable Callable object that evaluates a scalar function f. Must be vectorized (eg. using vectorize). disp : boolean Print warning if error in the result is estimated large instead of returning estimated error. (Default: True) Returns ------- fA : array, shape(M,M) Value of the matrix function specified by func evaluated at A (if disp == False) errest : float 1-norm of the estimated error, ||err||_1 / ||A||_1 """ # Perform Shur decomposition (lapack ?gees) A = asarray(A) if len(A.shape)!=2: raise ValueError("Non-matrix input to matrix function.") if A.dtype.char in ['F', 'D', 'G']: cmplx_type = 1 else: cmplx_type = 0 T, Z = schur(A) T, Z = rsf2csf(T,Z) n,n = T.shape F = diag(func(diag(T))) # apply function to diagonal elements F = F.astype(T.dtype.char) # e.g. when F is real but T is complex minden = abs(T[0,0]) # implement Algorithm 11.1.1 from Golub and Van Loan # "matrix Computations." for p in range(1,n): for i in range(1,n-p+1): j = i + p s = T[i-1,j-1] * (F[j-1,j-1] - F[i-1,i-1]) ksl = slice(i,j-1) val = dot(T[i-1,ksl],F[ksl,j-1]) - dot(F[i-1,ksl],T[ksl,j-1]) s = s + val den = T[j-1,j-1] - T[i-1,i-1] if den != 0.0: s = s / den F[i-1,j-1] = s minden = min(minden,abs(den)) F = dot(dot(Z, F),transpose(conjugate(Z))) if not cmplx_type: F = toreal(F) tol = {0:feps, 1:eps}[_array_precision[F.dtype.char]] if minden == 0.0: minden = tol err = min(1, max(tol,(tol/minden)*norm(triu(T,1),1))) if product(ravel(logical_not(isfinite(F))),axis=0): err = Inf if disp: if err > 1000*tol: print "Result may be inaccurate, approximate err =", err return F else: return F, err