@@ -343,7 +343,8 @@ def dlyap(A, Q, C=None, E=None, method=None):
343
343
# Riccati equation solvers care and dare
344
344
#
345
345
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" ):
347
348
"""X, L, G = care(A, B, Q, R=None) solves the continuous-time
348
349
algebraic Riccati equation
349
350
@@ -428,10 +429,10 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
428
429
m = B .shape [1 ]
429
430
430
431
# 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 )
435
436
436
437
# Solve the standard algebraic Riccati equation
437
438
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):
471
472
E = np .eye (A .shape [0 ]) if E is None else np .array (E , ndmin = 2 )
472
473
473
474
# 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 )
476
477
477
478
# See if we should solve this using SciPy
478
479
if method == 'scipy' :
@@ -510,8 +511,9 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None):
510
511
# the gain matrix G
511
512
return _ssmatrix (X ), L , _ssmatrix (G )
512
513
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
515
517
equation
516
518
517
519
: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):
521
523
matrix G = (B^T X B + R)^-1 B^T X A and the closed loop eigenvalues L,
522
524
i.e., the eigenvalues of A - B G.
523
525
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
525
527
algebraic Riccati equation
526
528
527
529
: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`
528
530
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).
534
537
535
538
Parameters
536
539
----------
@@ -576,7 +579,14 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None):
576
579
m = B .shape [1 ]
577
580
578
581
# 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 )
580
590
581
591
# Figure out how to solve the problem
582
592
if method == 'scipy' and not stabilizing :
@@ -587,21 +597,11 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None):
587
597
return _dare_slycot (A , B , Q , R , S , E , stabilizing )
588
598
589
599
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 )
601
601
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 )
603
603
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 )
605
605
if E is None :
606
606
L = eigvals (A - B @ G )
607
607
else :
@@ -611,7 +611,7 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None):
611
611
612
612
613
613
def _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
615
615
try :
616
616
from slycot import sb02md
617
617
except ImportError :
@@ -622,18 +622,11 @@ def _dare_slycot(A, B, Q, R, S=None, E=None, stabilizing=True):
622
622
except ImportError :
623
623
raise ControlSlycot ("Can't find slycot module 'sb02mt'" )
624
624
625
- # Make sure we can find the required slycot routine
626
625
try :
627
626
from slycot import sg02ad
628
627
except ImportError :
629
628
raise ControlSlycot ("Can't find slycot module 'sg02ad'" )
630
629
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
-
637
630
# Determine main dimensions
638
631
n = A .shape [0 ]
639
632
m = B .shape [1 ]
@@ -642,21 +635,6 @@ def _dare_slycot(A, B, Q, R, S=None, E=None, stabilizing=True):
642
635
S = np .zeros ((n , m )) if S is None else np .array (S , ndmin = 2 )
643
636
E = np .eye (A .shape [0 ]) if E is None else np .array (E , ndmin = 2 )
644
637
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
-
660
638
# Solve the generalized algebraic Riccati equation by calling the
661
639
# Slycot function sg02ad
662
640
sort = 'S' if stabilizing else 'U'
@@ -670,7 +648,7 @@ def _dare_slycot(A, B, Q, R, S=None, E=None, stabilizing=True):
670
648
L = np .array ([(alfar [i ] + alfai [i ]* 1j ) / beta [i ] for i in range (n )])
671
649
672
650
# 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 )
674
652
675
653
# Return the solution X, the closed-loop eigenvalues L and
676
654
# the gain matrix G
0 commit comments