@@ -343,7 +343,8 @@ def dlyap(A, Q, C=None, E=None, method=None):
343343# Riccati equation solvers care and dare
344344#
345345
346- def care (A , B , Q , R = None , S = None , E = None , stabilizing = True , method = None ):
346+ def care (A , B , Q , R = None , S = None , E = None , stabilizing = True , method = None ,
347+ A_s = "A" , B_s = "B" , Q_s = "Q" , R_s = "R" , S_s = "S" , E_s = "E" ):
347348 """X, L, G = care(A, B, Q, R=None) solves the continuous-time
348349 algebraic Riccati equation
349350
@@ -428,10 +429,10 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
428429 m = B .shape [1 ]
429430
430431 # Check to make sure input matrices are the right shape and type
431- _check_shape ("A" , A , n , n , square = True )
432- _check_shape ("B" , B , n , m )
433- _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
434- _check_shape ("R" , R , m , m , square = True , symmetric = True )
432+ _check_shape (A_s , A , n , n , square = True )
433+ _check_shape (B_s , B , n , m )
434+ _check_shape (Q_s , Q , n , n , square = True , symmetric = True )
435+ _check_shape (R_s , R , m , m , square = True , symmetric = True )
435436
436437 # Solve the standard algebraic Riccati equation
437438 if S is None and E is None :
@@ -471,8 +472,8 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
471472 E = np .eye (A .shape [0 ]) if E is None else np .array (E , ndmin = 2 )
472473
473474 # Check to make sure input matrices are the right shape and type
474- _check_shape ("E" , E , n , n , square = True )
475- _check_shape ("S" , S , n , m )
475+ _check_shape (E_s , E , n , n , square = True )
476+ _check_shape (S_s , S , n , m )
476477
477478 # See if we should solve this using SciPy
478479 if method == 'scipy' :
@@ -510,8 +511,9 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
510511 # the gain matrix G
511512 return _ssmatrix (X ), L , _ssmatrix (G )
512513
513- def dare (A , B , Q , R , S = None , E = None , stabilizing = True , method = None ):
514- """(X, L, G) = dare(A, B, Q, R) solves the discrete-time algebraic Riccati
514+ def dare (A , B , Q , R , S = None , E = None , stabilizing = True , method = None ,
515+ A_s = "A" , B_s = "B" , Q_s = "Q" , R_s = "R" , S_s = "S" , E_s = "E" ):
516+ """X, L, G = dare(A, B, Q, R) solves the discrete-time algebraic Riccati
515517 equation
516518
517519 :math:`A^T X A - X - A^T X B (B^T X B + R)^{-1} B^T X A + Q = 0`
@@ -521,16 +523,17 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None):
521523 matrix G = (B^T X B + R)^-1 B^T X A and the closed loop eigenvalues L,
522524 i.e., the eigenvalues of A - B G.
523525
524- ( X, L, G) = dare(A, B, Q, R, S, E) solves the generalized discrete-time
526+ X, L, G = dare(A, B, Q, R, S, E) solves the generalized discrete-time
525527 algebraic Riccati equation
526528
527529 :math:`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`
528530
529- where A, Q and E are square matrices of the same dimension. Further, Q and
530- R are symmetric matrices. If R is None, it is set to the identity
531- matrix. The function returns the solution X, the gain
532- matrix :math:`G = (B^T X B + R)^{-1} (B^T X A + S^T)` and the closed loop
533- eigenvalues L, i.e., the eigenvalues of A - B G , E.
531+ where A, Q and E are square matrices of the same dimension. Further, Q
532+ and R are symmetric matrices. If R is None, it is set to the identity
533+ matrix. The function returns the solution X, the gain matrix :math:`G =
534+ (B^T X B + R)^{-1} (B^T X A + S^T)` and the closed loop eigenvalues L,
535+ i.e., the (generalized) eigenvalues of A - B G (with respect to E, if
536+ specified).
534537
535538 Parameters
536539 ----------
@@ -576,7 +579,14 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None):
576579 m = B .shape [1 ]
577580
578581 # Check to make sure input matrices are the right shape and type
579- _check_shape ("A" , A , n , n , square = True )
582+ _check_shape (A_s , A , n , n , square = True )
583+ _check_shape (B_s , B , n , m )
584+ _check_shape (Q_s , Q , n , n , square = True , symmetric = True )
585+ _check_shape (R_s , R , m , m , square = True , symmetric = True )
586+ if E is not None :
587+ _check_shape (E_s , E , n , n , square = True )
588+ if S is not None :
589+ _check_shape (S_s , S , n , m )
580590
581591 # Figure out how to solve the problem
582592 if method == 'scipy' and not stabilizing :
@@ -587,21 +597,11 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None):
587597 return _dare_slycot (A , B , Q , R , S , E , stabilizing )
588598
589599 else :
590- _check_shape ("B" , B , n , m )
591- _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
592- _check_shape ("R" , R , m , m , square = True , symmetric = True )
593- if E is not None :
594- _check_shape ("E" , E , n , n , square = True )
595- if S is not None :
596- _check_shape ("S" , S , n , m )
597-
598- Rmat = _ssmatrix (R )
599- Qmat = _ssmatrix (Q )
600- X = sp .linalg .solve_discrete_are (A , B , Qmat , Rmat , e = E , s = S )
600+ X = sp .linalg .solve_discrete_are (A , B , Q , R , e = E , s = S )
601601 if S is None :
602- G = solve (B .T @ X @ B + Rmat , B .T @ X @ A )
602+ G = solve (B .T @ X @ B + R , B .T @ X @ A )
603603 else :
604- G = solve (B .T @ X @ B + Rmat , B .T @ X @ A + S .T )
604+ G = solve (B .T @ X @ B + R , B .T @ X @ A + S .T )
605605 if E is None :
606606 L = eigvals (A - B @ G )
607607 else :
@@ -611,7 +611,7 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None):
611611
612612
613613def _dare_slycot (A , B , Q , R , S = None , E = None , stabilizing = True ):
614- # Make sure we can import required slycot routine
614+ # Make sure we can import required slycot routines
615615 try :
616616 from slycot import sb02md
617617 except ImportError :
@@ -622,18 +622,11 @@ def _dare_slycot(A, B, Q, R, S=None, E=None, stabilizing=True):
622622 except ImportError :
623623 raise ControlSlycot ("Can't find slycot module 'sb02mt'" )
624624
625- # Make sure we can find the required slycot routine
626625 try :
627626 from slycot import sg02ad
628627 except ImportError :
629628 raise ControlSlycot ("Can't find slycot module 'sg02ad'" )
630629
631- # Reshape input arrays
632- A = np .array (A , ndmin = 2 )
633- B = np .array (B , ndmin = 2 )
634- Q = np .array (Q , ndmin = 2 )
635- R = np .eye (B .shape [1 ]) if R is None else np .array (R , ndmin = 2 )
636-
637630 # Determine main dimensions
638631 n = A .shape [0 ]
639632 m = B .shape [1 ]
@@ -642,21 +635,6 @@ def _dare_slycot(A, B, Q, R, S=None, E=None, stabilizing=True):
642635 S = np .zeros ((n , m )) if S is None else np .array (S , ndmin = 2 )
643636 E = np .eye (A .shape [0 ]) if E is None else np .array (E , ndmin = 2 )
644637
645- # Check to make sure input matrices are the right shape and type
646- _check_shape ("A" , A , n , n , square = True )
647- _check_shape ("B" , B , n , m )
648- _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
649- _check_shape ("R" , R , m , m , square = True , symmetric = True )
650- _check_shape ("E" , E , n , n , square = True )
651- _check_shape ("S" , S , n , m )
652-
653- # Create back-up of arrays needed for later computations
654- A_b = copy (A )
655- R_b = copy (R )
656- B_b = copy (B )
657- E_b = copy (E )
658- S_b = copy (S )
659-
660638 # Solve the generalized algebraic Riccati equation by calling the
661639 # Slycot function sg02ad
662640 sort = 'S' if stabilizing else 'U'
@@ -670,7 +648,7 @@ def _dare_slycot(A, B, Q, R, S=None, E=None, stabilizing=True):
670648 L = np .array ([(alfar [i ] + alfai [i ]* 1j ) / beta [i ] for i in range (n )])
671649
672650 # Calculate the gain matrix G
673- G = solve (B_b .T @ X @ B_b + R_b , B_b .T @ X @ A_b + S_b .T )
651+ G = solve (B .T @ X @ B + R , B .T @ X @ A + S .T )
674652
675653 # Return the solution X, the closed-loop eigenvalues L and
676654 # the gain matrix G
0 commit comments