33# Implementation of the functions lyap, dlyap, care and dare
44# for solution of Lyapunov and Riccati equations.
55#
6- # Author : Bjorn Olofsson
6+ # Original author : Bjorn Olofsson
77
88# Copyright (c) 2011, All rights reserved.
99
@@ -162,6 +162,7 @@ def lyap(A, Q, C=None, E=None, method=None):
162162 _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
163163
164164 if method == 'scipy' :
165+ # Solve the Lyapunov equation using SciPy
165166 return sp .linalg .solve_continuous_lyapunov (A , - Q )
166167
167168 # Solve the Lyapunov equation by calling Slycot function sb03md
@@ -177,6 +178,7 @@ def lyap(A, Q, C=None, E=None, method=None):
177178 _check_shape ("C" , C , n , m )
178179
179180 if method == 'scipy' :
181+ # Solve the Sylvester equation using SciPy
180182 return sp .linalg .solve_sylvester (A , Q , - C )
181183
182184 # Solve the Sylvester equation by calling the Slycot function sb04md
@@ -293,6 +295,7 @@ def dlyap(A, Q, C=None, E=None, method=None):
293295 _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
294296
295297 if method == 'scipy' :
298+ # Solve the Lyapunov equation using SciPy
296299 return sp .linalg .solve_discrete_lyapunov (A , Q )
297300
298301 # Solve the Lyapunov equation by calling the Slycot function sb03md
@@ -343,7 +346,8 @@ def dlyap(A, Q, C=None, E=None, method=None):
343346# Riccati equation solvers care and dare
344347#
345348
346- def care (A , B , Q , R = None , S = None , E = None , stabilizing = True , method = None ):
349+ def care (A , B , Q , R = None , S = None , E = None , stabilizing = True , method = None ,
350+ A_s = "A" , B_s = "B" , Q_s = "Q" , R_s = "R" , S_s = "S" , E_s = "E" ):
347351 """X, L, G = care(A, B, Q, R=None) solves the continuous-time
348352 algebraic Riccati equation
349353
@@ -395,24 +399,6 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
395399 # Decide what method to use
396400 method = _slycot_or_scipy (method )
397401
398- if method == 'slycot' :
399- # Make sure we can import required slycot routines
400- try :
401- from slycot import sb02md
402- except ImportError :
403- raise ControlSlycot ("Can't find slycot module 'sb02md'" )
404-
405- try :
406- from slycot import sb02mt
407- except ImportError :
408- raise ControlSlycot ("Can't find slycot module 'sb02mt'" )
409-
410- # Make sure we can find the required slycot routine
411- try :
412- from slycot import sg02ad
413- except ImportError :
414- raise ControlSlycot ("Can't find slycot module 'sg02ad'" )
415-
416402 # Reshape input arrays
417403 A = np .array (A , ndmin = 2 )
418404 B = np .array (B , ndmin = 2 )
@@ -428,10 +414,10 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
428414 m = B .shape [1 ]
429415
430416 # 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 )
417+ _check_shape (A_s , A , n , n , square = True )
418+ _check_shape (B_s , B , n , m )
419+ _check_shape (Q_s , Q , n , n , square = True , symmetric = True )
420+ _check_shape (R_s , R , m , m , square = True , symmetric = True )
435421
436422 # Solve the standard algebraic Riccati equation
437423 if S is None and E is None :
@@ -446,9 +432,16 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
446432 E , _ = np .linalg .eig (A - B @ K )
447433 return _ssmatrix (X ), E , _ssmatrix (K )
448434
449- # Create back-up of arrays needed for later computations
450- R_ba = copy (R )
451- B_ba = copy (B )
435+ # Make sure we can import required slycot routines
436+ try :
437+ from slycot import sb02md
438+ except ImportError :
439+ raise ControlSlycot ("Can't find slycot module 'sb02md'" )
440+
441+ try :
442+ from slycot import sb02mt
443+ except ImportError :
444+ raise ControlSlycot ("Can't find slycot module 'sb02mt'" )
452445
453446 # Solve the standard algebraic Riccati equation by calling Slycot
454447 # functions sb02mt and sb02md
@@ -458,7 +451,7 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
458451 X , rcond , w , S_o , U , A_inv = sb02md (n , A , G , Q , 'C' , sort = sort )
459452
460453 # Calculate the gain matrix G
461- G = solve (R_ba , B_ba .T ) @ X
454+ G = solve (R , B .T ) @ X
462455
463456 # Return the solution X, the closed-loop eigenvalues L and
464457 # the gain matrix G
@@ -471,8 +464,8 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
471464 E = np .eye (A .shape [0 ]) if E is None else np .array (E , ndmin = 2 )
472465
473466 # 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 )
467+ _check_shape (E_s , E , n , n , square = True )
468+ _check_shape (S_s , S , n , m )
476469
477470 # See if we should solve this using SciPy
478471 if method == 'scipy' :
@@ -485,11 +478,11 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
485478 eigs , _ = sp .linalg .eig (A - B @ K , E )
486479 return _ssmatrix (X ), eigs , _ssmatrix (K )
487480
488- # Create back-up of arrays needed for later computations
489- R_b = copy ( R )
490- B_b = copy ( B )
491- E_b = copy ( E )
492- S_b = copy ( S )
481+ # Make sure we can find the required slycot routine
482+ try :
483+ from slycot import sg02ad
484+ except ImportError :
485+ raise ControlSlycot ( "Can't find slycot module 'sg02ad'" )
493486
494487 # Solve the generalized algebraic Riccati equation by calling the
495488 # Slycot function sg02ad
@@ -504,14 +497,15 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
504497 L = np .array ([(alfar [i ] + alfai [i ]* 1j ) / beta [i ] for i in range (n )])
505498
506499 # Calculate the gain matrix G
507- G = solve (R_b , B_b .T @ X @ E_b + S_b .T )
500+ G = solve (R , B .T @ X @ E + S .T )
508501
509502 # Return the solution X, the closed-loop eigenvalues L and
510503 # the gain matrix G
511504 return _ssmatrix (X ), L , _ssmatrix (G )
512505
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
506+ def dare (A , B , Q , R , S = None , E = None , stabilizing = True , method = None ,
507+ A_s = "A" , B_s = "B" , Q_s = "Q" , R_s = "R" , S_s = "S" , E_s = "E" ):
508+ """X, L, G = dare(A, B, Q, R) solves the discrete-time algebraic Riccati
515509 equation
516510
517511 :math:`A^T X A - X - A^T X B (B^T X B + R)^{-1} B^T X A + Q = 0`
@@ -521,15 +515,17 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None):
521515 matrix G = (B^T X B + R)^-1 B^T X A and the closed loop eigenvalues L,
522516 i.e., the eigenvalues of A - B G.
523517
524- ( X, L, G) = dare(A, B, Q, R, S, E) solves the generalized discrete-time
518+ X, L, G = dare(A, B, Q, R, S, E) solves the generalized discrete-time
525519 algebraic Riccati equation
526520
527521 :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`
528522
529- where A, Q and E are square matrices of the same dimension. Further, Q and
530- R are symmetric matrices. The function returns the solution X, the gain
531- matrix :math:`G = (B^T X B + R)^{-1} (B^T X A + S^T)` and the closed loop
532- eigenvalues L, i.e., the eigenvalues of A - B G , E.
523+ where A, Q and E are square matrices of the same dimension. Further, Q
524+ and R are symmetric matrices. If R is None, it is set to the identity
525+ matrix. The function returns the solution X, the gain matrix :math:`G =
526+ (B^T X B + R)^{-1} (B^T X A + S^T)` and the closed loop eigenvalues L,
527+ i.e., the (generalized) eigenvalues of A - B G (with respect to E, if
528+ specified).
533529
534530 Parameters
535531 ----------
@@ -575,87 +571,43 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None):
575571 m = B .shape [1 ]
576572
577573 # Check to make sure input matrices are the right shape and type
578- _check_shape ("A" , A , n , n , square = True )
574+ _check_shape (A_s , A , n , n , square = True )
575+ _check_shape (B_s , B , n , m )
576+ _check_shape (Q_s , Q , n , n , square = True , symmetric = True )
577+ _check_shape (R_s , R , m , m , square = True , symmetric = True )
578+ if E is not None :
579+ _check_shape (E_s , E , n , n , square = True )
580+ if S is not None :
581+ _check_shape (S_s , S , n , m )
579582
580583 # Figure out how to solve the problem
581- if method == 'scipy' and not stabilizing :
582- raise ControlArgument (
583- "method='scipy' not valid when stabilizing is not True" )
584-
585- elif method == 'slycot' :
586- return _dare_slycot (A , B , Q , R , S , E , stabilizing )
584+ if method == 'scipy' :
585+ if not stabilizing :
586+ raise ControlArgument (
587+ "method='scipy' not valid when stabilizing is not True" )
587588
588- else :
589- _check_shape ("B" , B , n , m )
590- _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
591- _check_shape ("R" , R , m , m , square = True , symmetric = True )
592- if E is not None :
593- _check_shape ("E" , E , n , n , square = True )
594- if S is not None :
595- _check_shape ("S" , S , n , m )
596-
597- Rmat = _ssmatrix (R )
598- Qmat = _ssmatrix (Q )
599- X = sp .linalg .solve_discrete_are (A , B , Qmat , Rmat , e = E , s = S )
589+ X = sp .linalg .solve_discrete_are (A , B , Q , R , e = E , s = S )
600590 if S is None :
601- G = solve (B .T @ X @ B + Rmat , B .T @ X @ A )
591+ G = solve (B .T @ X @ B + R , B .T @ X @ A )
602592 else :
603- G = solve (B .T @ X @ B + Rmat , B .T @ X @ A + S .T )
593+ G = solve (B .T @ X @ B + R , B .T @ X @ A + S .T )
604594 if E is None :
605595 L = eigvals (A - B @ G )
606596 else :
607597 L , _ = sp .linalg .eig (A - B @ G , E )
608598
609599 return _ssmatrix (X ), L , _ssmatrix (G )
610600
611-
612- def _dare_slycot (A , B , Q , R , S = None , E = None , stabilizing = True ):
613601 # Make sure we can import required slycot routine
614- try :
615- from slycot import sb02md
616- except ImportError :
617- raise ControlSlycot ("Can't find slycot module 'sb02md'" )
618-
619- try :
620- from slycot import sb02mt
621- except ImportError :
622- raise ControlSlycot ("Can't find slycot module 'sb02mt'" )
623-
624- # Make sure we can find the required slycot routine
625602 try :
626603 from slycot import sg02ad
627604 except ImportError :
628605 raise ControlSlycot ("Can't find slycot module 'sg02ad'" )
629606
630- # Reshape input arrays
631- A = np .array (A , ndmin = 2 )
632- B = np .array (B , ndmin = 2 )
633- Q = np .array (Q , ndmin = 2 )
634- R = np .eye (B .shape [1 ]) if R is None else np .array (R , ndmin = 2 )
635-
636- # Determine main dimensions
637- n = A .shape [0 ]
638- m = B .shape [1 ]
639-
640607 # Initialize optional matrices
641608 S = np .zeros ((n , m )) if S is None else np .array (S , ndmin = 2 )
642609 E = np .eye (A .shape [0 ]) if E is None else np .array (E , ndmin = 2 )
643610
644- # Check to make sure input matrices are the right shape and type
645- _check_shape ("A" , A , n , n , square = True )
646- _check_shape ("B" , B , n , m )
647- _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
648- _check_shape ("R" , R , m , m , square = True , symmetric = True )
649- _check_shape ("E" , E , n , n , square = True )
650- _check_shape ("S" , S , n , m )
651-
652- # Create back-up of arrays needed for later computations
653- A_b = copy (A )
654- R_b = copy (R )
655- B_b = copy (B )
656- E_b = copy (E )
657- S_b = copy (S )
658-
659611 # Solve the generalized algebraic Riccati equation by calling the
660612 # Slycot function sg02ad
661613 sort = 'S' if stabilizing else 'U'
@@ -669,7 +621,7 @@ def _dare_slycot(A, B, Q, R, S=None, E=None, stabilizing=True):
669621 L = np .array ([(alfar [i ] + alfai [i ]* 1j ) / beta [i ] for i in range (n )])
670622
671623 # Calculate the gain matrix G
672- G = solve (B_b .T @ X @ B_b + R_b , B_b .T @ X @ A_b + S_b .T )
624+ G = solve (B .T @ X @ B + R , B .T @ X @ A + S .T )
673625
674626 # Return the solution X, the closed-loop eigenvalues L and
675627 # the gain matrix G
0 commit comments