44
44
45
45
from .exception import ControlSlycot , ControlArgument , ControlDimension , \
46
46
slycot_check
47
- from .statesp import _ssmatrix
48
47
49
48
# Make sure we have access to the right slycot routines
50
49
try :
@@ -151,12 +150,12 @@ def lyap(A, Q, C=None, E=None, method=None):
151
150
m = Q .shape [0 ]
152
151
153
152
# Check to make sure input matrices are the right shape and type
154
- _check_shape ("A" , A , n , n , square = True )
153
+ _check_shape (A , n , n , square = True , name = "A" )
155
154
156
155
# Solve standard Lyapunov equation
157
156
if C is None and E is None :
158
157
# Check to make sure input matrices are the right shape and type
159
- _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
158
+ _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" )
160
159
161
160
if method == 'scipy' :
162
161
# Solve the Lyapunov equation using SciPy
@@ -171,8 +170,8 @@ def lyap(A, Q, C=None, E=None, method=None):
171
170
# Solve the Sylvester equation
172
171
elif C is not None and E is None :
173
172
# Check to make sure input matrices are the right shape and type
174
- _check_shape ("Q" , Q , m , m , square = True )
175
- _check_shape ("C" , C , n , m )
173
+ _check_shape (Q , m , m , square = True , name = "Q" )
174
+ _check_shape (C , n , m , name = "C" )
176
175
177
176
if method == 'scipy' :
178
177
# Solve the Sylvester equation using SciPy
@@ -184,8 +183,8 @@ def lyap(A, Q, C=None, E=None, method=None):
184
183
# Solve the generalized Lyapunov equation
185
184
elif C is None and E is not None :
186
185
# Check to make sure input matrices are the right shape and type
187
- _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
188
- _check_shape ("E" , E , n , n , square = True )
186
+ _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" )
187
+ _check_shape (E , n , n , square = True , name = "E" )
189
188
190
189
if method == 'scipy' :
191
190
raise ControlArgument (
@@ -210,7 +209,7 @@ def lyap(A, Q, C=None, E=None, method=None):
210
209
else :
211
210
raise ControlArgument ("Invalid set of input parameters" )
212
211
213
- return _ssmatrix ( X )
212
+ return X
214
213
215
214
216
215
def dlyap (A , Q , C = None , E = None , method = None ):
@@ -281,12 +280,12 @@ def dlyap(A, Q, C=None, E=None, method=None):
281
280
m = Q .shape [0 ]
282
281
283
282
# Check to make sure input matrices are the right shape and type
284
- _check_shape ("A" , A , n , n , square = True )
283
+ _check_shape (A , n , n , square = True , name = "A" )
285
284
286
285
# Solve standard Lyapunov equation
287
286
if C is None and E is None :
288
287
# Check to make sure input matrices are the right shape and type
289
- _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
288
+ _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" )
290
289
291
290
if method == 'scipy' :
292
291
# Solve the Lyapunov equation using SciPy
@@ -301,8 +300,8 @@ def dlyap(A, Q, C=None, E=None, method=None):
301
300
# Solve the Sylvester equation
302
301
elif C is not None and E is None :
303
302
# Check to make sure input matrices are the right shape and type
304
- _check_shape ("Q" , Q , m , m , square = True )
305
- _check_shape ("C" , C , n , m )
303
+ _check_shape (Q , m , m , square = True , name = "Q" )
304
+ _check_shape (C , n , m , name = "C" )
306
305
307
306
if method == 'scipy' :
308
307
raise ControlArgument (
@@ -314,8 +313,8 @@ def dlyap(A, Q, C=None, E=None, method=None):
314
313
# Solve the generalized Lyapunov equation
315
314
elif C is None and E is not None :
316
315
# Check to make sure input matrices are the right shape and type
317
- _check_shape ("Q" , Q , n , n , square = True , symmetric = True )
318
- _check_shape ("E" , E , n , n , square = True )
316
+ _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" )
317
+ _check_shape (E , n , n , square = True , name = "E" )
319
318
320
319
if method == 'scipy' :
321
320
raise ControlArgument (
@@ -333,7 +332,7 @@ def dlyap(A, Q, C=None, E=None, method=None):
333
332
else :
334
333
raise ControlArgument ("Invalid set of input parameters" )
335
334
336
- return _ssmatrix ( X )
335
+ return X
337
336
338
337
339
338
#
@@ -407,10 +406,10 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
407
406
m = B .shape [1 ]
408
407
409
408
# Check to make sure input matrices are the right shape and type
410
- _check_shape (_As , A , n , n , square = True )
411
- _check_shape (_Bs , B , n , m )
412
- _check_shape (_Qs , Q , n , n , square = True , symmetric = True )
413
- _check_shape (_Rs , R , m , m , square = True , symmetric = True )
409
+ _check_shape (A , n , n , square = True , name = _As )
410
+ _check_shape (B , n , m , name = _Bs )
411
+ _check_shape (Q , n , n , square = True , symmetric = True , name = _Qs )
412
+ _check_shape (R , m , m , square = True , symmetric = True , name = _Rs )
414
413
415
414
# Solve the standard algebraic Riccati equation
416
415
if S is None and E is None :
@@ -423,7 +422,7 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
423
422
X = sp .linalg .solve_continuous_are (A , B , Q , R )
424
423
K = np .linalg .solve (R , B .T @ X )
425
424
E , _ = np .linalg .eig (A - B @ K )
426
- return _ssmatrix ( X ) , E , _ssmatrix ( K )
425
+ return X , E , K
427
426
428
427
# Make sure we can import required slycot routines
429
428
try :
@@ -448,7 +447,7 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
448
447
449
448
# Return the solution X, the closed-loop eigenvalues L and
450
449
# the gain matrix G
451
- return _ssmatrix ( X ) , w [:n ], _ssmatrix ( G )
450
+ return X , w [:n ], G
452
451
453
452
# Solve the generalized algebraic Riccati equation
454
453
else :
@@ -457,8 +456,8 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
457
456
E = np .eye (A .shape [0 ]) if E is None else np .array (E , ndmin = 2 )
458
457
459
458
# Check to make sure input matrices are the right shape and type
460
- _check_shape (_Es , E , n , n , square = True )
461
- _check_shape (_Ss , S , n , m )
459
+ _check_shape (E , n , n , square = True , name = _Es )
460
+ _check_shape (S , n , m , name = _Ss )
462
461
463
462
# See if we should solve this using SciPy
464
463
if method == 'scipy' :
@@ -469,7 +468,7 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
469
468
X = sp .linalg .solve_continuous_are (A , B , Q , R , s = S , e = E )
470
469
K = np .linalg .solve (R , B .T @ X @ E + S .T )
471
470
eigs , _ = sp .linalg .eig (A - B @ K , E )
472
- return _ssmatrix ( X ) , eigs , _ssmatrix ( K )
471
+ return X , eigs , K
473
472
474
473
# Make sure we can find the required slycot routine
475
474
try :
@@ -494,7 +493,7 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
494
493
495
494
# Return the solution X, the closed-loop eigenvalues L and
496
495
# the gain matrix G
497
- return _ssmatrix ( X ) , L , _ssmatrix ( G )
496
+ return X , L , G
498
497
499
498
def dare (A , B , Q , R , S = None , E = None , stabilizing = True , method = None ,
500
499
_As = "A" , _Bs = "B" , _Qs = "Q" , _Rs = "R" , _Ss = "S" , _Es = "E" ):
@@ -564,14 +563,14 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None,
564
563
m = B .shape [1 ]
565
564
566
565
# Check to make sure input matrices are the right shape and type
567
- _check_shape (_As , A , n , n , square = True )
568
- _check_shape (_Bs , B , n , m )
569
- _check_shape (_Qs , Q , n , n , square = True , symmetric = True )
570
- _check_shape (_Rs , R , m , m , square = True , symmetric = True )
566
+ _check_shape (A , n , n , square = True , name = _As )
567
+ _check_shape (B , n , m , name = _Bs )
568
+ _check_shape (Q , n , n , square = True , symmetric = True , name = _Qs )
569
+ _check_shape (R , m , m , square = True , symmetric = True , name = _Rs )
571
570
if E is not None :
572
- _check_shape (_Es , E , n , n , square = True )
571
+ _check_shape (E , n , n , square = True , name = _Es )
573
572
if S is not None :
574
- _check_shape (_Ss , S , n , m )
573
+ _check_shape (S , n , m , name = _Ss )
575
574
576
575
# Figure out how to solve the problem
577
576
if method == 'scipy' :
@@ -589,7 +588,7 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None,
589
588
else :
590
589
L , _ = sp .linalg .eig (A - B @ G , E )
591
590
592
- return _ssmatrix ( X ) , L , _ssmatrix ( G )
591
+ return X , L , G
593
592
594
593
# Make sure we can import required slycot routine
595
594
try :
@@ -618,7 +617,7 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None,
618
617
619
618
# Return the solution X, the closed-loop eigenvalues L and
620
619
# the gain matrix G
621
- return _ssmatrix ( X ) , L , _ssmatrix ( G )
620
+ return X , L , G
622
621
623
622
624
623
# Utility function to decide on method to use
@@ -632,15 +631,17 @@ def _slycot_or_scipy(method):
632
631
633
632
634
633
# Utility function to check matrix dimensions
635
- def _check_shape (name , M , n , m , square = False , symmetric = False ):
634
+ def _check_shape (M , n , m , square = False , symmetric = False , name = "??" ):
636
635
if square and M .shape [0 ] != M .shape [1 ]:
637
636
raise ControlDimension ("%s must be a square matrix" % name )
638
637
639
638
if symmetric and not _is_symmetric (M ):
640
639
raise ControlArgument ("%s must be a symmetric matrix" % name )
641
640
642
641
if M .shape [0 ] != n or M .shape [1 ] != m :
643
- raise ControlDimension ("Incompatible dimensions of %s matrix" % name )
642
+ raise ControlDimension (
643
+ f"Incompatible dimensions of { name } matrix; "
644
+ f"expected ({ n } , { m } ) but found { M .shape } " )
644
645
645
646
646
647
# Utility function to check if a matrix is symmetric
0 commit comments