|
From: <mur...@us...> - 2011-06-17 23:51:53
|
Revision: 157
http://python-control.svn.sourceforge.net/python-control/?rev=157&view=rev
Author: murrayrm
Date: 2011-06-17 23:51:46 +0000 (Fri, 17 Jun 2011)
Log Message:
-----------
Added in matrix equation solver routes (lyap, dlyap, care, dare) from Johnsson, Nordh, Olofsson, Romero). Converted test code to standard format.
Modified Paths:
--------------
trunk/ChangeLog
trunk/src/__init__.py
trunk/src/matlab.py
Added Paths:
-----------
trunk/src/mateqn.py
trunk/tests/mateqn_test.py
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2011-04-17 18:45:29 UTC (rev 156)
+++ trunk/ChangeLog 2011-06-17 23:51:46 UTC (rev 157)
@@ -1,3 +1,20 @@
+2011-06-16 Richard Murray <mu...@ma...>
+
+ * src/matlab.py: import mateqn functions
+
+ * src/__init__.py: import mateqn functions
+
+ * tests/test_all.py: added unit tests for matrix solvers, converting
+ to standard format along the way. Seems to work even if slycot
+ routines are not in place, but I'm not sure if this is for the right
+ reasons...
+
+ * src/mateqn.py: added matrix solvers from LTH (Ola Johnsson, Jerker
+ Nordh, Bjorn Olofsson, Vanessa Romero). Moved slycot function
+ checks to the portion of the code where they are used, so that
+ missing slycot doesn't mess up initialization if proper version of
+ slycot is not available.
+
2011-04-02 Richard Murray <mu...@ma...>
* src/xferfcn.py (TransferFunction.__add__): fixed bug when adding a
Modified: trunk/src/__init__.py
===================================================================
--- trunk/src/__init__.py 2011-04-17 18:45:29 UTC (rev 156)
+++ trunk/src/__init__.py 2011-06-17 23:51:46 UTC (rev 157)
@@ -65,3 +65,4 @@
from delay import *
from modelsimp import *
from rlocus import *
+from mateqn import *
Added: trunk/src/mateqn.py
===================================================================
--- trunk/src/mateqn.py (rev 0)
+++ trunk/src/mateqn.py 2011-06-17 23:51:46 UTC (rev 157)
@@ -0,0 +1,928 @@
+# mateqn.py - matrix equation solvers (Lyapunov, Riccati)
+from __future__ import division
+
+""" Implementation of the functions lyap, dlyap, care and dare
+for solution of Lyapunov and Riccati equations. """
+
+"""Copyright (c) 2011, All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the project author nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CALTECH
+OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Author: Bjorn Olofsson
+"""
+
+from numpy.linalg import inv
+from scipy import shape,size,asarray,copy,zeros,eye,dot
+
+from control.exception import ControlSlycot,ControlArgument
+
+#### Lyapunov equation solvers lyap and dlyap
+
+def lyap(A,Q,C=None,E=None):
+ """ X = lyap(A,Q) solves the continuous-time Lyapunov equation
+
+ A X + X A^T + Q = 0
+
+ where A and Q are square matrices of the same dimension.
+ Further, Q must be symmetric.
+
+ X = lyap(A,Q,C) solves the Sylvester equation
+
+ A X + X Q + C = 0
+
+ where A and Q are square matrices.
+
+ X = lyap(A,Q,None,E) solves the generalized continuous-time
+ Lyapunov equation
+
+ A X E^T + E X A^T + Q = 0
+
+ where Q is a symmetric matrix and A, Q and E are square matrices
+ of the same dimension. """
+
+ # Make sure we have access to the right slycot routines
+ try:
+ from slycot import sb03md
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sb03md'")
+
+ try:
+ from slycot import sb04md
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sb04md'")
+
+ # Reshape 1-d arrays
+ if len(shape(A)) == 1:
+ A = A.reshape(1,A.size)
+
+ if len(shape(Q)) == 1:
+ Q = Q.reshape(1,Q.size)
+
+ if C != None and len(shape(C)) == 1:
+ C = C.reshape(1,C.size)
+
+ if E != None and len(shape(E)) == 1:
+ E = E.reshape(1,E.size)
+
+ # Determine main dimensions
+ if size(A) == 1:
+ n = 1
+ else:
+ n = size(A,0)
+
+ if size(Q) == 1:
+ m = 1
+ else:
+ m = size(Q,0)
+
+ # Solve standard Lyapunov equation
+ if C==None and E==None:
+ # Check input data for consistency
+ if shape(A) != shape(Q):
+ raise ControlArgument("A and Q must be matrices of identical \
+ sizes.")
+
+ if size(A) > 1 and shape(A)[0] != shape(A)[1]:
+ raise ControlArgument("A must be a quadratic matrix.")
+
+ if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
+ raise ControlArgument("Q must be a quadratic matrix.")
+
+ if not (asarray(Q) == asarray(Q).T).all():
+ raise ControlArgument("Q must be a symmetric matrix.")
+
+ # Solve the Lyapunov equation by calling Slycot function sb03md
+ try:
+ X,scale,sep,ferr,w = sb03md(n,-Q,A,eye(n,n),'C',trana='T')
+ except ValueError, ve:
+ if ve.info < 0:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info == n+1:
+ e = ValueError("The matrix A and -A have common or very \
+ close eigenvalues.")
+ e.info = ve.info
+ else:
+ e = ValueError("The QR algorithm failed to compute all \
+ the eigenvalues (see LAPACK Library routine DGEES).")
+ e.info = ve.info
+ raise e
+
+ # Solve the Sylvester equation
+ elif C != None and E==None:
+ # Check input data for consistency
+ if size(A) > 1 and shape(A)[0] != shape(A)[1]:
+ raise ControlArgument("A must be a quadratic matrix.")
+
+ if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
+ raise ControlArgument("Q must be a quadratic matrix.")
+
+ if (size(C) > 1 and shape(C)[0] != n) or \
+ (size(C) > 1 and shape(C)[1] != m) or \
+ (size(C) == 1 and size(A) != 1) or (size(C) == 1 and size(Q) != 1):
+ raise ControlArgument("C matrix has incompatible dimensions.")
+
+ # Solve the Sylvester equation by calling the Slycot function sb04md
+ try:
+ X = sb04md(n,m,A,Q,-C)
+ except ValueError, ve:
+ if ve.info < 0:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info > m:
+ e = ValueError("A singular matrix was encountered whilst \
+ solving for the %i-th column of matrix X." % ve.info-m)
+ e.info = ve.info
+ else:
+ e = ValueError("The QR algorithm failed to compute all the \
+ eigenvalues (see LAPACK Library routine DGEES).")
+ e.info = ve.info
+ raise e
+
+ # Solve the generalized Lyapunov equation
+ elif C == None and E != None:
+ # Check input data for consistency
+ if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
+ (size(Q) > 1 and shape(Q)[0] != n) or \
+ (size(Q) == 1 and n > 1):
+ raise ControlArgument("Q must be a square matrix with the same \
+ dimension as A.")
+
+ if (size(E) > 1 and shape(E)[0] != shape(E)[1]) or \
+ (size(E) > 1 and shape(E)[0] != n) or \
+ (size(E) == 1 and n > 1):
+ raise ControlArgument("E must be a square matrix with the same \
+ dimension as A.")
+
+ if not (asarray(Q) == asarray(Q).T).all():
+ raise ControlArgument("Q must be a symmetric matrix.")
+
+ # Make sure we have access to the write slicot routine
+ try:
+ from slycot import sg03ad
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sg03ad'")
+
+ # Solve the generalized Lyapunov equation by calling Slycot
+ # function sg03ad
+ try:
+ A,E,Q,Z,X,scale,sep,ferr,alphar,alphai,beta = \
+ sg03ad('C','B','N','T','L',n,A,E,eye(n,n),eye(n,n),-Q)
+ except ValueError, ve:
+ if ve.info < 0 or ve.info > 4:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info == 1:
+ e = ValueError("The matrix contained in the upper \
+ Hessenberg part of the array A is not in \
+ upper quasitriangular form")
+ e.info = ve.info
+ elif ve.info == 2:
+ e = ValueError("The pencil A - lambda * E cannot be \
+ reduced to generalized Schur form: LAPACK \
+ routine DGEGS has failed to converge")
+ e.info = ve.info
+ elif ve.info == 4:
+ e = ValueError("The pencil A - lambda * E has a \
+ degenerate pair of eigenvalues. That is, \
+ lambda_i = lambda_j for some i and j, where \
+ lambda_i and lambda_j are eigenvalues of \
+ A - lambda * E. Hence, the equation is \
+ singular; perturbed values were \
+ used to solve the equation (but the matrices \
+ A and E are unchanged)")
+ e.info = ve.info
+ raise e
+ # Invalid set of input parameters
+ else:
+ raise ControlArgument("Invalid set of input parameters")
+
+ return X
+
+
+def dlyap(A,Q,C=None,E=None):
+ """ dlyap(A,Q) solves the discrete-time Lyapunov equation
+
+ A X A^T - X + Q = 0
+
+ where A and Q are square matrices of the same dimension. Further
+ Q must be symmetric.
+
+ dlyap(A,Q,C) solves the Sylvester equation
+
+ A X Q^T - X + C = 0
+
+ where A and Q are square matrices.
+
+ dlyap(A,Q,None,E) solves the generalized discrete-time Lyapunov
+ equation
+
+ A X A^T - E X E^T + Q = 0
+
+ where Q is a symmetric matrix and A, Q and E are square matrices
+ of the same dimension. """
+
+ # Make sure we have access to the right slycot routines
+ try:
+ from slycot import sb03md
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sb03md'")
+
+ try:
+ from slycot import sb04qd
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sb04qd'")
+
+ try:
+ from slycot import sg03ad
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sg03ad'")
+
+ # Reshape 1-d arrays
+ if len(shape(A)) == 1:
+ A = A.reshape(1,A.size)
+
+ if len(shape(Q)) == 1:
+ Q = Q.reshape(1,Q.size)
+
+ if C != None and len(shape(C)) == 1:
+ C = C.reshape(1,C.size)
+
+ if E != None and len(shape(E)) == 1:
+ E = E.reshape(1,E.size)
+
+ # Determine main dimensions
+ if size(A) == 1:
+ n = 1
+ else:
+ n = size(A,0)
+
+ if size(Q) == 1:
+ m = 1
+ else:
+ m = size(Q,0)
+
+ # Solve standard Lyapunov equation
+ if C==None and E==None:
+ # Check input data for consistency
+ if shape(A) != shape(Q):
+ raise ControlArgument("A and Q must be matrices of identical \
+ sizes.")
+
+ if size(A) > 1 and shape(A)[0] != shape(A)[1]:
+ raise ControlArgument("A must be a quadratic matrix.")
+
+ if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
+ raise ControlArgument("Q must be a quadratic matrix.")
+
+ if not (asarray(Q) == asarray(Q).T).all():
+ raise ControlArgument("Q must be a symmetric matrix.")
+
+ # Solve the Lyapunov equation by calling the Slycot function sb03md
+ try:
+ X,scale,sep,ferr,w = sb03md(n,-Q,A,eye(n,n),'D',trana='T')
+ except ValueError, ve:
+ if ve.info < 0:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ else:
+ e = ValueError("The QR algorithm failed to compute all the \
+ eigenvalues (see LAPACK Library routine DGEES).")
+ e.info = ve.info
+ raise e
+
+ # Solve the Sylvester equation
+ elif C != None and E==None:
+ # Check input data for consistency
+ if size(A) > 1 and shape(A)[0] != shape(A)[1]:
+ raise ControlArgument("A must be a quadratic matrix")
+
+ if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
+ raise ControlArgument("Q must be a quadratic matrix")
+
+ if (size(C) > 1 and shape(C)[0] != n) or \
+ (size(C) > 1 and shape(C)[1] != m) or \
+ (size(C) == 1 and size(A) != 1) or (size(C) == 1 and size(Q) != 1):
+ raise ControlArgument("C matrix has incompatible dimensions")
+
+ # Solve the Sylvester equation by calling Slycot function sb04qd
+ try:
+ X = sb04qd(n,m,-A,asarray(Q).T,C)
+ except ValueError, ve:
+ if ve.info < 0:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info > m:
+ e = ValueError("A singular matrix was encountered whilst \
+ solving for the %i-th column of matrix X." % ve.info-m)
+ e.info = ve.info
+ else:
+ e = ValueError("The QR algorithm failed to compute all the \
+ eigenvalues (see LAPACK Library routine DGEES)")
+ e.info = ve.info
+ raise e
+
+ # Solve the generalized Lyapunov equation
+ elif C == None and E != None:
+ # Check input data for consistency
+ if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
+ (size(Q) > 1 and shape(Q)[0] != n) or \
+ (size(Q) == 1 and n > 1):
+ raise ControlArgument("Q must be a square matrix with the same \
+ dimension as A.")
+
+ if (size(E) > 1 and shape(E)[0] != shape(E)[1]) or \
+ (size(E) > 1 and shape(E)[0] != n) or \
+ (size(E) == 1 and n > 1):
+ raise ControlArgument("E must be a square matrix with the same \
+ dimension as A.")
+
+ if not (asarray(Q) == asarray(Q).T).all():
+ raise ControlArgument("Q must be a symmetric matrix.")
+
+ # Solve the generalized Lyapunov equation by calling Slycot
+ # function sg03ad
+ try:
+ A,E,Q,Z,X,scale,sep,ferr,alphar,alphai,beta = \
+ sg03ad('D','B','N','T','L',n,A,E,eye(n,n),eye(n,n),-Q)
+ except ValueError, ve:
+ if ve.info < 0 or ve.info > 4:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info == 1:
+ e = ValueError("The matrix contained in the upper \
+ Hessenberg part of the array A is not in \
+ upper quasitriangular form")
+ e.info = ve.info
+ elif ve.info == 2:
+ e = ValueError("The pencil A - lambda * E cannot be \
+ reduced to generalized Schur form: LAPACK \
+ routine DGEGS has failed to converge")
+ e.info = ve.info
+ elif ve.info == 3:
+ e = ValueError("The pencil A - lambda * E has a \
+ pair of reciprocal eigenvalues. That is, \
+ lambda_i = 1/lambda_j for some i and j, \
+ where lambda_i and lambda_j are eigenvalues \
+ of A - lambda * E. Hence, the equation is \
+ singular; perturbed values were \
+ used to solve the equation (but the \
+ matrices A and E are unchanged)")
+ e.info = ve.info
+ raise e
+ # Invalid set of input parameters
+ else:
+ raise ControlArgument("Invalid set of input parameters")
+
+ return X
+
+
+
+#### Riccati equation solvers care and dare
+
+def care(A,B,Q,R=None,S=None,E=None):
+ """ (X,L,G) = care(A,B,Q) solves the continuous-time algebraic Riccati
+ equation
+
+ A^T X + X A - X B B^T X + Q = 0
+
+ where A and Q are square matrices of the same dimension. Further, Q
+ is a symmetric matrix. The function returns the solution X, the gain
+ matrix G = B^T X and the closed loop eigenvalues L, i.e., the eigenvalues
+ of A - B G.
+
+ (X,L,G) = care(A,B,Q,R,S,E) solves the generalized continuous-time
+ algebraic Riccati equation
+
+ A^T X E + E^T X A - (E^T X B + S) R^-1 (B^T X E + S^T) + Q = 0
+
+ where A, Q and E are square matrices of the same dimension. Further, Q and
+ R are symmetric matrices. The function returns the solution X, the gain
+ matrix G = R^-1 (B^T X E + S^T) and the closed loop eigenvalues L, i.e.,
+ the eigenvalues of A - B G , E. """
+
+ # Make sure we can import required slycot routine
+ try:
+ from slycot import sb02md
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sb02md'")
+
+ try:
+ from slycot import sb02mt
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sb02mt'")
+
+ # Make sure we can find the required slycot routine
+ try:
+ from slycot import sg02ad
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sg02ad'")
+
+ # Reshape 1-d arrays
+ if len(shape(A)) == 1:
+ A = A.reshape(1,A.size)
+
+ if len(shape(B)) == 1:
+ B = B.reshape(1,B.size)
+
+ if len(shape(Q)) == 1:
+ Q = Q.reshape(1,Q.size)
+
+ if R != None and len(shape(R)) == 1:
+ R = R.reshape(1,R.size)
+
+ if S != None and len(shape(S)) == 1:
+ S = S.reshape(1,S.size)
+
+ if E != None and len(shape(E)) == 1:
+ E = E.reshape(1,E.size)
+
+ # Determine main dimensions
+ if size(A) == 1:
+ n = 1
+ else:
+ n = size(A,0)
+
+ if size(B) == 1:
+ m = 1
+ else:
+ m = size(B,1)
+ if R==None:
+ R = eye(m,m)
+
+ # Solve the standard algebraic Riccati equation
+ if S==None and E==None:
+ # Check input data for consistency
+ if size(A) > 1 and shape(A)[0] != shape(A)[1]:
+ raise ControlArgument("A must be a quadratic matrix.")
+
+ if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
+ (size(Q) > 1 and shape(Q)[0] != n) or \
+ size(Q) == 1 and n > 1:
+ raise ControlArgument("Q must be a quadratic matrix of the same \
+ dimension as A.")
+
+ if (size(B) > 1 and shape(B)[0] != n) or \
+ size(B) == 1 and n > 1:
+ raise ControlArgument("Incompatible dimensions of B matrix.")
+
+ if not (asarray(Q) == asarray(Q).T).all():
+ raise ControlArgument("Q must be a symmetric matrix.")
+
+ if not (asarray(R) == asarray(R).T).all():
+ raise ControlArgument("R must be a symmetric matrix.")
+
+ # Create back-up of arrays needed for later computations
+ R_ba = copy(R)
+ B_ba = copy(B)
+
+ # Solve the standard algebraic Riccati equation by calling Slycot
+ # functions sb02mt and sb02md
+ try:
+ A_b,B_b,Q_b,R_b,L_b,ipiv,oufact,G = sb02mt(n,m,B,R)
+ except ValueError, ve:
+ if ve.info < 0:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info == m+1:
+ e = ValueError("The matrix R is numerically singular.")
+ e.info = ve.info
+ else:
+ e = ValueError("The %i-th element of d in the UdU (LdL) \
+ factorization is zero." % ve.info)
+ e.info = ve.info
+ raise e
+
+ try:
+ X,rcond,w,S_o,U,A_inv = sb02md(n,A,G,Q,'C')
+ except ValueError, ve:
+ if ve.info < 0 or ve.info > 5:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info == 1:
+ e = ValueError("The matrix A is (numerically) singular in \
+ discrete-time case.")
+ e.info = ve.info
+ elif ve.info == 2:
+ e = ValueError("The Hamiltonian or symplectic matrix H cannot \
+ be reduced to real Schur form.")
+ e.info = ve.info
+ elif ve.info == 3:
+ e = ValueError("The real Schur form of the Hamiltonian or \
+ symplectic matrix H cannot be appropriately ordered.")
+ e.info = ve.info
+ elif ve.info == 4:
+ e = ValueError("The Hamiltonian or symplectic matrix H has \
+ less than n stable eigenvalues.")
+ e.info = ve.info
+ elif ve.info == 5:
+ e = ValueError("The N-th order system of linear algebraic \
+ equations is singular to working precision.")
+ e.info = ve.info
+ raise e
+
+ # Calculate the gain matrix G
+ if size(R_b) == 1:
+ G = dot(dot(1/(R_ba),asarray(B_ba).T) , X)
+ else:
+ G = dot(dot(inv(R_ba),asarray(B_ba).T) , X)
+
+ # Return the solution X, the closed-loop eigenvalues L and
+ # the gain matrix G
+ return (X , w[:n] , G )
+
+ # Solve the generalized algebraic Riccati equation
+ elif S != None and E != None:
+ # Check input data for consistency
+ if size(A) > 1 and shape(A)[0] != shape(A)[1]:
+ raise ControlArgument("A must be a quadratic matrix.")
+
+ if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
+ (size(Q) > 1 and shape(Q)[0] != n) or \
+ size(Q) == 1 and n > 1:
+ raise ControlArgument("Q must be a quadratic matrix of the same \
+ dimension as A.")
+
+ if (size(B) > 1 and shape(B)[0] != n) or \
+ size(B) == 1 and n > 1:
+ raise ControlArgument("Incompatible dimensions of B matrix.")
+
+ if (size(E) > 1 and shape(E)[0] != shape(E)[1]) or \
+ (size(E) > 1 and shape(E)[0] != n) or \
+ size(E) == 1 and n > 1:
+ raise ControlArgument("E must be a quadratic matrix of the same \
+ dimension as A.")
+
+ if (size(R) > 1 and shape(R)[0] != shape(R)[1]) or \
+ (size(R) > 1 and shape(R)[0] != m) or \
+ size(R) == 1 and m > 1:
+ raise ControlArgument("R must be a quadratic matrix of the same \
+ dimension as the number of columns in the B matrix.")
+
+ if (size(S) > 1 and shape(S)[0] != n) or \
+ (size(S) > 1 and shape(S)[1] != m) or \
+ size(S) == 1 and n > 1 or \
+ size(S) == 1 and m > 1:
+ raise ControlArgument("Incompatible dimensions of S matrix.")
+
+ if not (asarray(Q) == asarray(Q).T).all():
+ raise ControlArgument("Q must be a symmetric matrix.")
+
+ if not (asarray(R) == asarray(R).T).all():
+ raise ControlArgument("R must be a symmetric matrix.")
+
+ # Create back-up of arrays needed for later computations
+ R_b = copy(R)
+ B_b = copy(B)
+ E_b = copy(E)
+ S_b = copy(S)
+
+ # Solve the generalized algebraic Riccati equation by calling the
+ # Slycot function sg02ad
+ try:
+ rcondu,X,alfar,alfai,beta,S_o,T,U,iwarn = \
+ sg02ad('C','B','N','U','N','N','S','R',n,m,0,A,E,B,Q,R,S)
+ except ValueError, ve:
+ if ve.info < 0 or ve.info > 7:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info == 1:
+ e = ValueError("The computed extended matrix pencil is \
+ singular, possibly due to rounding errors.")
+ e.info = ve.info
+ elif ve.info == 2:
+ e = ValueError("The QZ algorithm failed.")
+ e.info = ve.info
+ elif ve.info == 3:
+ e = ValueError("Reordering of the generalized eigenvalues \
+ failed.")
+ e.info = ve.info
+ elif ve.info == 4:
+ e = ValueError("After reordering, roundoff changed values of \
+ some complex eigenvalues so that leading \
+ eigenvalues in the generalized Schur form no \
+ longer satisfy the stability condition; this \
+ could also be caused due to scaling.")
+ e.info = ve.info
+ elif ve.info == 5:
+ e = ValueError("The computed dimension of the solution does \
+ not equal N.")
+ e.info = ve.info
+ elif ve.info == 6:
+ e = ValueError("The spectrum is too close to the boundary of \
+ the stability domain.")
+ e.info = ve.info
+ elif ve.info == 7:
+ e = ValueError("A singular matrix was encountered during the \
+ computation of the solution matrix X.")
+ e.info = ve.info
+ raise e
+
+ # Calculate the closed-loop eigenvalues L
+ L = zeros((n,1))
+ L.dtype = 'complex64'
+ for i in range(n):
+ L[i] = (alfar[i] + alfai[i]*1j)/beta[i]
+
+ # Calculate the gain matrix G
+ if size(R_b) == 1:
+ G = dot(1/(R_b),dot(asarray(B_b).T,dot(X,E_b))+asarray(S_b).T)
+ else:
+ G = dot(inv(R_b),dot(asarray(B_b).T,dot(X,E_b))+asarray(S_b).T)
+
+ # Return the solution X, the closed-loop eigenvalues L and
+ # the gain matrix G
+ return (X , L , G)
+
+ # Invalid set of input parameters
+ else:
+ raise ControlArgument("Invalid set of input parameters.")
+
+
+def dare(A,B,Q,R,S=None,E=None):
+ """ (X,L,G) = dare(A,B,Q,R) solves the discrete-time algebraic Riccati
+ equation
+
+ A^T X A - X - A^T X B (B^T X B + R)^-1 B^T X A + Q = 0
+
+ where A and Q are square matrices of the same dimension. Further, Q
+ is a symmetric matrix. The function returns the solution X, the gain
+ matrix G = (B^T X B + R)^-1 B^T X A and the closed loop eigenvalues L,
+ i.e., the eigenvalues of A - B G.
+
+ (X,L,G) = dare(A,B,Q,R,S,E) solves the generalized discrete-time algebraic
+ Riccati equation
+
+ A^T X A - E^T X E - (A^T X B + S) (B^T X B + R)^-1 (B^T X A + S^T) +
+ + Q = 0
+
+ where A, Q and E are square matrices of the same dimension. Further, Q and
+ R are symmetric matrices. The function returns the solution X, the gain
+ matrix G = (B^T X B + R)^-1 (B^T X A + S^T) and the closed loop
+ eigenvalues L, i.e., the eigenvalues of A - B G , E. """
+
+ # Make sure we can import required slycot routine
+ try:
+ from slycot import sb02md
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sb02md'")
+
+ try:
+ from slycot import sb02mt
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sb02mt'")
+
+ # Make sure we can find the required slycot routine
+ try:
+ from slycot import sg02ad
+ except ImportError:
+ raise ControlSlycot("can't find slycot module 'sg02ad'")
+
+ # Reshape 1-d arrays
+ if len(shape(A)) == 1:
+ A = A.reshape(1,A.size)
+
+ if len(shape(B)) == 1:
+ B = B.reshape(1,B.size)
+
+ if len(shape(Q)) == 1:
+ Q = Q.reshape(1,Q.size)
+
+ if R != None and len(shape(R)) == 1:
+ R = R.reshape(1,R.size)
+
+ if S != None and len(shape(S)) == 1:
+ S = S.reshape(1,S.size)
+
+ if E != None and len(shape(E)) == 1:
+ E = E.reshape(1,E.size)
+
+ # Determine main dimensions
+ if size(A) == 1:
+ n = 1
+ else:
+ n = size(A,0)
+
+ if size(B) == 1:
+ m = 1
+ else:
+ m = size(B,1)
+
+ # Solve the standard algebraic Riccati equation
+ if S==None and E==None:
+ # Check input data for consistency
+ if size(A) > 1 and shape(A)[0] != shape(A)[1]:
+ raise ControlArgument("A must be a quadratic matrix.")
+
+ if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
+ (size(Q) > 1 and shape(Q)[0] != n) or \
+ size(Q) == 1 and n > 1:
+ raise ControlArgument("Q must be a quadratic matrix of the same \
+ dimension as A.")
+
+ if (size(B) > 1 and shape(B)[0] != n) or \
+ size(B) == 1 and n > 1:
+ raise ControlArgument("Incompatible dimensions of B matrix.")
+
+ if not (asarray(Q) == asarray(Q).T).all():
+ raise ControlArgument("Q must be a symmetric matrix.")
+
+ if not (asarray(R) == asarray(R).T).all():
+ raise ControlArgument("R must be a symmetric matrix.")
+
+ # Create back-up of arrays needed for later computations
+ A_ba = copy(A)
+ R_ba = copy(R)
+ B_ba = copy(B)
+
+ # Solve the standard algebraic Riccati equation by calling Slycot
+ # functions sb02mt and sb02md
+ try:
+ A_b,B_b,Q_b,R_b,L_b,ipiv,oufact,G = sb02mt(n,m,B,R)
+ except ValueError, ve:
+ if ve.info < 0:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info == m+1:
+ e = ValueError("The matrix R is numerically singular.")
+ e.info = ve.info
+ else:
+ e = ValueError("The %i-th element of d in the UdU (LdL) \
+ factorization is zero." % ve.info)
+ e.info = ve.info
+ raise e
+
+ try:
+ X,rcond,w,S,U,A_inv = sb02md(n,A,G,Q,'D')
+ except ValueError, ve:
+ if ve.info < 0 or ve.info > 5:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info == 1:
+ e = ValueError("The matrix A is (numerically) singular in \
+ discrete-time case.")
+ e.info = ve.info
+ elif ve.info == 2:
+ e = ValueError("The Hamiltonian or symplectic matrix H cannot \
+ be reduced to real Schur form.")
+ e.info = ve.info
+ elif ve.info == 3:
+ e = ValueError("The real Schur form of the Hamiltonian or \
+ symplectic matrix H cannot be appropriately ordered.")
+ e.info = ve.info
+ elif ve.info == 4:
+ e = ValueError("The Hamiltonian or symplectic matrix H has \
+ less than n stable eigenvalues.")
+ e.info = ve.info
+ elif ve.info == 5:
+ e = ValueError("The N-th order system of linear algebraic \
+ equations is singular to working precision.")
+ e.info = ve.info
+ raise e
+
+ # Calculate the gain matrix G
+ if size(R_b) == 1:
+ G = dot( 1/(dot(asarray(B_ba).T,dot(X,B_ba))+R_ba) , \
+ dot(asarray(B_ba).T,dot(X,A_ba)) )
+ else:
+ G = dot( inv(dot(asarray(B_ba).T,dot(X,B_ba))+R_ba) , \
+ dot(asarray(B_ba).T,dot(X,A_ba)) )
+
+ # Return the solution X, the closed-loop eigenvalues L and
+ # the gain matrix G
+ return (X , w[:n] , G)
+
+ # Solve the generalized algebraic Riccati equation
+ elif S != None and E != None:
+ # Check input data for consistency
+ if size(A) > 1 and shape(A)[0] != shape(A)[1]:
+ raise ControlArgument("A must be a quadratic matrix.")
+
+ if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
+ (size(Q) > 1 and shape(Q)[0] != n) or \
+ size(Q) == 1 and n > 1:
+ raise ControlArgument("Q must be a quadratic matrix of the same \
+ dimension as A.")
+
+ if (size(B) > 1 and shape(B)[0] != n) or \
+ size(B) == 1 and n > 1:
+ raise ControlArgument("Incompatible dimensions of B matrix.")
+
+ if (size(E) > 1 and shape(E)[0] != shape(E)[1]) or \
+ (size(E) > 1 and shape(E)[0] != n) or \
+ size(E) == 1 and n > 1:
+ raise ControlArgument("E must be a quadratic matrix of the same \
+ dimension as A.")
+
+ if (size(R) > 1 and shape(R)[0] != shape(R)[1]) or \
+ (size(R) > 1 and shape(R)[0] != m) or \
+ size(R) == 1 and m > 1:
+ raise ControlArgument("R must be a quadratic matrix of the same \
+ dimension as the number of columns in the B matrix.")
+
+ if (size(S) > 1 and shape(S)[0] != n) or \
+ (size(S) > 1 and shape(S)[1] != m) or \
+ size(S) == 1 and n > 1 or \
+ size(S) == 1 and m > 1:
+ raise ControlArgument("Incompatible dimensions of S matrix.")
+
+ if not (asarray(Q) == asarray(Q).T).all():
+ raise ControlArgument("Q must be a symmetric matrix.")
+
+ if not (asarray(R) == asarray(R).T).all():
+ raise ControlArgument("R must be a symmetric matrix.")
+
+ # Create back-up of arrays needed for later computations
+ A_b = copy(A)
+ R_b = copy(R)
+ B_b = copy(B)
+ E_b = copy(E)
+ S_b = copy(S)
+
+ # Solve the generalized algebraic Riccati equation by calling the
+ # Slycot function sg02ad
+ try:
+ rcondu,X,alfar,alfai,beta,S_o,T,U,iwarn = \
+ sg02ad('D','B','N','U','N','N','S','R',n,m,0,A,E,B,Q,R,S)
+ except ValueError, ve:
+ if ve.info < 0 or ve.info > 7:
+ e = ValueError(ve.message)
+ e.info = ve.info
+ elif ve.info == 1:
+ e = ValueError("The computed extended matrix pencil is \
+ singular, possibly due to rounding errors.")
+ e.info = ve.info
+ elif ve.info == 2:
+ e = ValueError("The QZ algorithm failed.")
+ e.info = ve.info
+ elif ve.info == 3:
+ e = ValueError("Reordering of the generalized eigenvalues \
+ failed.")
+ e.info = ve.info
+ elif ve.info == 4:
+ e = ValueError("After reordering, roundoff changed values of \
+ some complex eigenvalues so that leading \
+ eigenvalues in the generalized Schur form no \
+ longer satisfy the stability condition; this \
+ could also be caused due to scaling.")
+ e.info = ve.info
+ elif ve.info == 5:
+ e = ValueError("The computed dimension of the solution does \
+ not equal N.")
+ e.info = ve.info
+ elif ve.info == 6:
+ e = ValueError("The spectrum is too close to the boundary of \
+ the stability domain.")
+ e.info = ve.info
+ elif ve.info == 7:
+ e = ValueError("A singular matrix was encountered during the \
+ computation of the solution matrix X.")
+ e.info = ve.info
+ raise e
+
+ L = zeros((n,1))
+ L.dtype = 'complex64'
+ for i in range(n):
+ L[i] = (alfar[i] + alfai[i]*1j)/beta[i]
+
+ # Calculate the gain matrix G
+ if size(R_b) == 1:
+ G = dot( 1/(dot(asarray(B_b).T,dot(X,B_b))+R_b) , \
+ dot(asarray(B_b).T,dot(X,A_b)) + asarray(S_b).T)
+ else:
+ G = dot( inv(dot(asarray(B_b).T,dot(X,B_b))+R_b) , \
+ dot(asarray(B_b).T,dot(X,A_b)) + asarray(S_b).T)
+
+ # Return the solution X, the closed-loop eigenvalues L and
+ # the gain matrix G
+ return (X , L , G)
+
+ # Invalid set of input parameters
+ else:
+ raise ControlArgument("Invalid set of input parameters.")
Modified: trunk/src/matlab.py
===================================================================
--- trunk/src/matlab.py 2011-04-17 18:45:29 UTC (rev 156)
+++ trunk/src/matlab.py 2011-06-17 23:51:46 UTC (rev 157)
@@ -81,6 +81,7 @@
from statefbk import ctrb, obsv, gram, place, lqr
from delay import pade
from modelsimp import hsvd, balred, modred
+from mateqn import lyap, dlyap, dare, care
__doc__ += """
The control.matlab module defines functions that are roughly the
@@ -255,9 +256,9 @@
lti/conj - complex conjugation of model coefficients
Matrix equation solvers and linear algebra
- lyap, dlyap - solve Lyapunov equations
+\* lyap, dlyap - solve Lyapunov equations
lyapchol, dlyapchol - square-root Lyapunov solvers
- care, dare - solve algebraic Riccati equations
+\* care, dare - solve algebraic Riccati equations
gcare, gdare - generalized Riccati solvers
bdschur - block diagonalization of a square matrix
Added: trunk/tests/mateqn_test.py
===================================================================
--- trunk/tests/mateqn_test.py (rev 0)
+++ trunk/tests/mateqn_test.py 2011-06-17 23:51:46 UTC (rev 157)
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+#
+# mateqn_test.py - test wuit for matrix equation solvers
+#
+#! Currently uses numpy.testing framework; will dump you out of unittest
+#! if an error occurs. Should figure out the right way to fix this.
+
+""" Test cases for lyap, dlyap, care and dare functions in the file
+pyctrl_lin_alg.py. """
+
+"""Copyright (c) 2011, All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the project author nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CALTECH
+OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Author: Bjorn Olofsson
+"""
+
+import unittest
+from numpy import array
+from numpy.testing import assert_array_almost_equal
+from numpy.linalg import inv
+from scipy import zeros,dot
+from control.mateqn import lyap,dlyap,care,dare
+
+# Test cases (lyap and dlyap)
+
+class TestMatrixEquations(unittest.TestCase):
+ """These are tests for the matrix equation solvers in mateqn.py"""
+
+ def setUp(self):
+ # Make sure that the slycot functions that we require exist
+ from slycot import sb02md, sb02mt, sb03md, sb04md, sg02ad, \
+ sg03ad, sb04qd
+
+ 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(dot(A,X)+dot(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(dot(A,X)+dot(X,A.T)+Q,zeros((2,2)))
+
+ 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(dot(A,X)+dot(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(dot(A,X)+dot(X,B)+C,zeros((2,2)))
+
+ def test_lyap_g(self):
+ A = array([[-1, 2],[-3, -4]])
+ Q = array([[3, 1],[1, 1]])
+ E = array([[1,2],[2,1]])
+ X = lyap(A,Q,None,E)
+ # print "The solution obtained is ", X
+ assert_array_almost_equal(dot(A,dot(X,E.T)) + dot(E,dot(X,A.T)) + Q, \
+ zeros((2,2)))
+
+ def test_dlyap(self):
+ A = array([[-0.6, 0],[-0.1, -0.4]])
+ Q = array([[1,0],[0,1]])
+ X = dlyap(A,Q)
+ # print "The solution obtained is ", X
+ assert_array_almost_equal(dot(A,dot(X,A.T))-X+Q,zeros((2,2)))
+
+ A = array([[-0.6, 0],[-0.1, -0.4]])
+ Q = array([[3, 1],[1, 1]])
+ X = dlyap(A,Q)
+ # print "The solution obtained is ", X
+ assert_array_almost_equal(dot(A,dot(X,A.T))-X+Q,zeros((2,2)))
+
+ def test_dlyap_g(self):
+ A = array([[-0.6, 0],[-0.1, -0.4]])
+ Q = array([[3, 1],[1, 1]])
+ E = array([[1, 1],[2, 1]])
+ X = dlyap(A,Q,None,E)
+ # print "The solution obtained is ", X
+ assert_array_almost_equal(dot(A,dot(X,A.T))-dot(E,dot(X,E.T))+Q, \
+ zeros((2,2)))
+
+ def test_dlyap_sylvester(self):
+ A = 5
+ B = array([[4, 3], [4, 3]])
+ C = array([2, 1])
+ X = dlyap(A,B,C)
+ # print "The solution obtained is ", X
+ assert_array_almost_equal(dot(A,dot(X,B.T))-X+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 = dlyap(A,B,C)
+ # print "The solution obtained is ", X
+ assert_array_almost_equal(dot(A,dot(X,B.T))-X+C,zeros((2,2)))
+
+ 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
+ assert_array_almost_equal(dot(A.T,X) + dot(X,A) - \
+ dot(X,dot(B,dot(B.T,X))) + Q , zeros((2,2)))
+ assert_array_almost_equal(dot(B.T,X) , G)
+
+ def test_care_g(self):
+ A = array([[-2, -1],[-1, -1]])
+ Q = array([[0, 0],[0, 1]])
+ B = array([[1, 0],[0, 4]])
+ R = array([[2, 0],[0, 1]])
+ S = array([[0, 0],[0, 0]])
+ E = array([[2, 1],[1, 2]])
+
+ X,L,G = care(A,B,Q,R,S,E)
+ # print "The solution obtained is", X
+ assert_array_almost_equal(dot(A.T,dot(X,E)) + dot(E.T,dot(X,A)) - \
+ dot(dot(dot(E.T,dot(X,B))+S,inv(R) ) ,
+ dot(B.T,dot(X,E))+S.T ) + Q , \
+ zeros((2,2)))
+ assert_array_almost_equal(dot( inv(R) , dot(B.T,dot(X,E)) + S.T) , G)
+
+ 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
+ assert_array_almost_equal(dot(A.T,dot(X,E)) + dot(E.T,dot(X,A)) - \
+ dot( dot( dot(E.T,dot(X,B))+S,1/R ) , dot(B.T,dot(X,E))+S.T ) \
+ + Q , zeros((2,2)))
+ assert_array_almost_equal(dot( 1/R , dot(B.T,dot(X,E)) + S.T) , G)
+
+ def test_dare(self):
+ A = array([[-0.6, 0],[-0.1, -0.4]])
+ Q = array([[2, 1],[1, 0]])
+ B = array([[2, 1],[0, 1]])
+ R = array([[1, 0],[0, 1]])
+
+ X,L,G = dare(A,B,Q,R)
+ # print "The solution obtained is", X
+ assert_array_almost_equal(dot(A.T,dot(X,A))-X-dot(dot(dot(A.T,dot(X,B)) , \
+ inv(dot(B.T,dot(X,B))+R)) , dot(B.T,dot(X,A))) + Q , zeros((2,2)) )
+ assert_array_almost_equal( dot( inv( dot(B.T,dot(X,B)) + R) , \
+ dot(B.T,dot(X,A)) ) , G)
+
+ A = array([[1, 0],[-1, 1]])
+ Q = array([[0, 1],[1, 1]])
+ B = array([[1],[0]])
+ R = 2
+
+ X,L,G = dare(A,B,Q,R)
+ # print "The solution obtained is", X
+ assert_array_almost_equal(dot(A.T,dot(X,A))-X-dot(dot(dot(A.T,dot(X,B)) , \
+ inv(dot(B.T,dot(X,B))+R)) , dot(B.T,dot(X,A))) + Q , zeros((2,2)) )
+ assert_array_almost_equal( dot( 1 / ( dot(B.T,dot(X,B)) + R) , \
+ dot(B.T,dot(X,A)) ) , G)
+
+ def test_dare_g(self):
+ A = array([[-0.6, 0],[-0.1, -0.4]])
+ Q = array([[2, 1],[1, 3]])
+ B = array([[1, 5],[2, 4]])
+ R = array([[1, 0],[0, 1]])
+ S = array([[1, 0],[2, 0]])
+ E = array([[2, 1],[1, 2]])
+
+ X,L,G = dare(A,B,Q,R,S,E)
+ # print "The solution obtained is", X
+ assert_array_almost_equal(dot(A.T,dot(X,A))-dot(E.T,dot(X,E)) - \
+ dot( dot(A.T,dot(X,B))+S , dot( inv(dot(B.T,dot(X,B)) + R) ,
+ dot(B.T,dot(X,A))+S.T)) + Q , zeros((2,2)) )
+ assert_array_almost_equal( dot( inv( dot(B.T,dot(X,B)) + R) , \
+ dot(B.T,dot(X,A)) + S.T ) , G)
+
+ A = array([[-0.6, 0],[-0.1, -0.4]])
+ Q = array([[2, 1],[1, 3]])
+ B = array([[1],[2]])
+ R = 1
+ S = array([[1],[2]])
+ E = array([[2, 1],[1, 2]])
+
+ X,L,G = dare(A,B,Q,R,S,E)
+ # print "The solution obtained is", X
+ assert_array_almost_equal(dot(A.T,dot(X,A))-dot(E.T,dot(X,E)) - \
+ dot( dot(A.T,dot(X,B))+S , dot( inv(dot(B.T,dot(X,B)) + R) ,
+ dot(B.T,dot(X,A))+S.T)) + Q , zeros((2,2)) )
+ assert_array_almost_equal( dot( 1 / ( dot(B.T,dot(X,B)) + R) , \
+ dot(B.T,dot(X,A)) + S.T ) , G)
+
+def suite():
+ return unittest.TestLoader().loadTestsFromTestCase(TestMatrixEquations)
+
+if __name__ == "__main__":
+ unittest.main()
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|