1414
1515import numpy as np
1616import scipy as sp
17- from numpy import eye , finfo , inexact
17+ from numpy import eye , finfo
1818from scipy .linalg import eigvals , solve
1919
2020from .exception import ControlArgument , ControlDimension , ControlSlycot , \
@@ -81,7 +81,7 @@ def _warn_ill_conditioned_E(E):
8181#
8282
8383
84- def lyap (A , Q , C = None , E = None , method = None ):
84+ def lyap (A , Q , C = None , E = None , method = None , symmetric_kwargs = None ):
8585 """Solves the continuous-time Lyapunov equation.
8686
8787 X = lyap(A, Q) solves
@@ -117,6 +117,9 @@ def lyap(A, Q, C=None, E=None, method=None):
117117 Set the method used for computing the result. Current methods are
118118 'slycot' and 'scipy'. If set to None (default), try 'slycot' first
119119 and then 'scipy'.
120+ symmetric_kwargs : dict, optional
121+ Keyword arguments passed to `scipy.linalg.issymmetric` or
122+ `scipy.linalg.ishermitian`.
120123
121124 Returns
122125 -------
@@ -169,7 +172,7 @@ def lyap(A, Q, C=None, E=None, method=None):
169172 # Solve standard Lyapunov equation
170173 if C is None and E is None :
171174 # Check to make sure input matrices are the right shape and type
172- _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" )
175+ _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" , symmetric_kwargs = symmetric_kwargs )
173176
174177 if method == 'scipy' :
175178 # Solve the Lyapunov equation using SciPy
@@ -197,7 +200,7 @@ def lyap(A, Q, C=None, E=None, method=None):
197200 # Solve the generalized Lyapunov equation
198201 elif C is None and E is not None :
199202 # Check to make sure input matrices are the right shape and type
200- _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" )
203+ _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" , symmetric_kwargs = symmetric_kwargs )
201204 _check_shape (E , n , n , square = True , name = "E" )
202205
203206 if method == 'scipy' :
@@ -243,7 +246,7 @@ def lyap(A, Q, C=None, E=None, method=None):
243246 return X
244247
245248
246- def dlyap (A , Q , C = None , E = None , method = None ):
249+ def dlyap (A , Q , C = None , E = None , method = None , symmetric_kwargs = None ):
247250 """Solves the discrete-time Lyapunov equation.
248251
249252 X = dlyap(A, Q) solves
@@ -279,6 +282,9 @@ def dlyap(A, Q, C=None, E=None, method=None):
279282 Set the method used for computing the result. Current methods are
280283 'slycot' and 'scipy'. If set to None (default), try 'slycot' first
281284 and then 'scipy'.
285+ symmetric_kwargs : dict, optional
286+ Keyword arguments passed to `scipy.linalg.issymmetric` or
287+ `scipy.linalg.ishermitian`.
282288
283289 Returns
284290 -------
@@ -346,7 +352,7 @@ def dlyap(A, Q, C=None, E=None, method=None):
346352 # Solve standard Lyapunov equation
347353 if C is None and E is None :
348354 # Check to make sure input matrices are the right shape and type
349- _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" )
355+ _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" , symmetric_kwargs = symmetric_kwargs )
350356
351357 if method == 'scipy' :
352358 # Solve the Lyapunov equation using SciPy
@@ -406,7 +412,7 @@ def dlyap(A, Q, C=None, E=None, method=None):
406412 # Solve the generalized Lyapunov equation
407413 elif C is None and E is not None :
408414 # Check to make sure input matrices are the right shape and type
409- _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" )
415+ _check_shape (Q , n , n , square = True , symmetric = True , name = "Q" , symmetric_kwargs = symmetric_kwargs )
410416 _check_shape (E , n , n , square = True , name = "E" )
411417
412418 if method == 'scipy' :
@@ -449,8 +455,8 @@ def dlyap(A, Q, C=None, E=None, method=None):
449455# Riccati equation solvers care and dare
450456#
451457
452- def care (A , B , Q , R = None , S = None , E = None , stabilizing = True , method = None ,
453- _As = "A" , _Bs = "B" , _Qs = "Q" , _Rs = "R" , _Ss = "S" , _Es = "E" ):
458+ def care (A , B , Q , R = None , S = None , E = None , stabilizing = True , method = None , symmetric_kwargs = None ,
459+ _As = "A" , _Bs = "B" , _Qs = "Q" , _Rs = "R" , _Ss = "S" , _Es = "E" , ):
454460 """Solves the continuous-time algebraic Riccati equation.
455461
456462 X, L, G = care(A, B, Q, R=None) solves
@@ -484,6 +490,9 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
484490 Set the method used for computing the result. Current methods are
485491 'slycot' and 'scipy'. If set to None (default), try 'slycot' first
486492 and then 'scipy'.
493+ symmetric_kwargs : dict, optional
494+ Keyword arguments passed to `scipy.linalg.issymmetric` or
495+ `scipy.linalg.ishermitian`.
487496 stabilizing : bool, optional
488497 If `method` is 'slycot', unstabilized eigenvalues will be returned
489498 in the initial elements of `L`. Not supported for 'scipy'.
@@ -518,8 +527,8 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
518527 # Check to make sure input matrices are the right shape and type
519528 _check_shape (A , n , n , square = True , name = _As )
520529 _check_shape (B , n , m , name = _Bs )
521- _check_shape (Q , n , n , square = True , symmetric = True , name = _Qs )
522- _check_shape (R , m , m , square = True , symmetric = True , name = _Rs )
530+ _check_shape (Q , n , n , square = True , symmetric = True , name = _Qs , symmetric_kwargs = symmetric_kwargs )
531+ _check_shape (R , m , m , square = True , symmetric = True , name = _Rs , symmetric_kwargs = symmetric_kwargs )
523532
524533 # Solve the standard algebraic Riccati equation
525534 if S is None and E is None :
@@ -605,7 +614,7 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
605614 # the gain matrix G
606615 return X , L , G
607616
608- def dare (A , B , Q , R , S = None , E = None , stabilizing = True , method = None ,
617+ def dare (A , B , Q , R , S = None , E = None , stabilizing = True , method = None , symmetric_kwargs = None ,
609618 _As = "A" , _Bs = "B" , _Qs = "Q" , _Rs = "R" , _Ss = "S" , _Es = "E" ):
610619 """Solves the discrete-time algebraic Riccati equation.
611620
@@ -640,6 +649,9 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None,
640649 Set the method used for computing the result. Current methods are
641650 'slycot' and 'scipy'. If set to None (default), try 'slycot' first
642651 and then 'scipy'.
652+ symmetric_kwargs : dict, optional
653+ Keyword arguments passed to `scipy.linalg.issymmetric` or
654+ `scipy.linalg.ishermitian`.
643655 stabilizing : bool, optional
644656 If `method` is 'slycot', unstabilized eigenvalues will be returned
645657 in the initial elements of `L`. Not supported for 'scipy'.
@@ -674,8 +686,8 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None,
674686 # Check to make sure input matrices are the right shape and type
675687 _check_shape (A , n , n , square = True , name = _As )
676688 _check_shape (B , n , m , name = _Bs )
677- _check_shape (Q , n , n , square = True , symmetric = True , name = _Qs )
678- _check_shape (R , m , m , square = True , symmetric = True , name = _Rs )
689+ _check_shape (Q , n , n , square = True , symmetric = True , name = _Qs , symmetric_kwargs = symmetric_kwargs )
690+ _check_shape (R , m , m , square = True , symmetric = True , name = _Rs , symmetric_kwargs = symmetric_kwargs )
679691 if E is not None :
680692 _check_shape (E , n , n , square = True , name = _Es )
681693 if S is not None :
@@ -740,7 +752,7 @@ def _slycot_or_scipy(method):
740752
741753
742754# Utility function to check matrix dimensions
743- def _check_shape (M , n , m , square = False , symmetric = False , name = "??" ):
755+ def _check_shape (M , n , m , square = False , symmetric = False , name = "??" , symmetric_kwargs = None ):
744756 """Check the shape and properties of a 2D array.
745757
746758 This function can be used to check to make sure a 2D array_like has the
@@ -773,7 +785,7 @@ def _check_shape(M, n, m, square=False, symmetric=False, name="??"):
773785 if (square or symmetric ) and M .shape [0 ] != M .shape [1 ]:
774786 raise ControlDimension ("%s must be a square matrix" % name )
775787
776- if symmetric and not _is_symmetric (M ):
788+ if symmetric and not _is_symmetric (M , symmetric_kwargs = symmetric_kwargs ):
777789 raise ControlArgument ("%s must be a symmetric matrix" % name )
778790
779791 if M .shape [0 ] != n or M .shape [1 ] != m :
@@ -785,10 +797,10 @@ def _check_shape(M, n, m, square=False, symmetric=False, name="??"):
785797
786798
787799# Utility function to check if a matrix is symmetric
788- def _is_symmetric (M ):
789- M = np . atleast_2d ( M )
790- if isinstance ( M [ 0 , 0 ], inexact ):
791- eps = finfo ( M . dtype ). eps
792- return (( M - M . T ) < eps ). all ()
793- else :
794- return ( M == M . T ). all ( )
800+ def _is_symmetric (M , symmetric_kwargs = None ):
801+ if symmetric_kwargs is None :
802+ symmetric_kwargs = {}
803+
804+ if np . iscomplexobj ( M ):
805+ return sp . linalg . ishermitian ( M , ** symmetric_kwargs )
806+ return sp . linalg . issymmetric ( M , ** symmetric_kwargs )
0 commit comments