@@ -298,7 +298,6 @@ def testPlace_varga_discrete_partial_eigs(self, matarrayin):
298298 P_placed = np .linalg .eigvals (A - B @ K )
299299 self .checkPlaced (P_expected , P_placed )
300300
301-
302301 def check_LQR (self , K , S , poles , Q , R ):
303302 S_expected = asmatarrayout (np .sqrt (Q @ R ))
304303 K_expected = asmatarrayout (S_expected / R )
@@ -334,6 +333,8 @@ def test_LQR_3args(self, matarrayin, matarrayout, method):
334333
335334 @pytest .mark .parametrize ("method" , [None , 'slycot' , 'scipy' ])
336335 def test_DLQR_3args (self , matarrayin , matarrayout , method ):
336+ if method == 'slycot' and not slycot_check ():
337+ return
337338 dsys = ss (0. , 1. , 1. , 0. , .1 )
338339 Q , R = (matarrayin ([[X ]]) for X in [10. , 2. ])
339340 K , S , poles = dlqr (dsys , Q , R , method = method )
@@ -433,9 +434,13 @@ def check_LQE(self, L, P, poles, G, QN, RN):
433434 np .testing .assert_array_almost_equal (L , L_expected )
434435 np .testing .assert_array_almost_equal (poles , poles_expected )
435436
436- def test_LQE (self , matarrayin ):
437+ @pytest .mark .parametrize ("method" , [None , 'slycot' , 'scipy' ])
438+ def test_LQE (self , matarrayin , method ):
439+ if method == 'slycot' and not slycot_check ():
440+ return
441+
437442 A , G , C , QN , RN = (matarrayin ([[X ]]) for X in [0. , .1 , 1. , 10. , 2. ])
438- L , P , poles = lqe (A , G , C , QN , RN )
443+ L , P , poles = lqe (A , G , C , QN , RN , method = method )
439444 self .check_LQE (L , P , poles , G , QN , RN )
440445
441446 def test_lqe_call_format (self ):
@@ -482,9 +487,13 @@ def check_DLQE(self, L, P, poles, G, QN, RN):
482487 np .testing .assert_array_almost_equal (L , L_expected )
483488 np .testing .assert_array_almost_equal (poles , poles_expected )
484489
485- def test_DLQE (self , matarrayin ):
490+ @pytest .mark .parametrize ("method" , [None , 'slycot' , 'scipy' ])
491+ def test_DLQE (self , matarrayin , method ):
492+ if method == 'slycot' and not slycot_check ():
493+ return
494+
486495 A , G , C , QN , RN = (matarrayin ([[X ]]) for X in [0. , .1 , 1. , 10. , 2. ])
487- L , P , poles = dlqe (A , G , C , QN , RN )
496+ L , P , poles = dlqe (A , G , C , QN , RN , method = method )
488497 self .check_DLQE (L , P , poles , G , QN , RN )
489498
490499 def test_care (self , matarrayin ):
@@ -522,3 +531,68 @@ def test_dare(self, matarrayin, stabilizing):
522531 sgn = {True : - 1 , False : 1 }[stabilizing ]
523532 assert np .all (sgn * (np .abs (L ) - 1 ) > 0 )
524533
534+ def test_lqr_discrete (self ):
535+ """Test overloading of lqr operator for discrete time systems"""
536+ csys = ct .rss (2 , 1 , 1 )
537+ dsys = ct .drss (2 , 1 , 1 )
538+ Q = np .eye (2 )
539+ R = np .eye (1 )
540+
541+ # Calling with a system versus explicit A, B should be the sam
542+ K_csys , S_csys , E_csys = ct .lqr (csys , Q , R )
543+ K_expl , S_expl , E_expl = ct .lqr (csys .A , csys .B , Q , R )
544+ np .testing .assert_almost_equal (K_csys , K_expl )
545+ np .testing .assert_almost_equal (S_csys , S_expl )
546+ np .testing .assert_almost_equal (E_csys , E_expl )
547+
548+ # Calling lqr() with a discrete time system should call dlqr()
549+ K_lqr , S_lqr , E_lqr = ct .lqr (dsys , Q , R )
550+ K_dlqr , S_dlqr , E_dlqr = ct .dlqr (dsys , Q , R )
551+ np .testing .assert_almost_equal (K_lqr , K_dlqr )
552+ np .testing .assert_almost_equal (S_lqr , S_dlqr )
553+ np .testing .assert_almost_equal (E_lqr , E_dlqr )
554+
555+ # Calling lqr() with no timebase should call lqr()
556+ asys = ct .ss (csys .A , csys .B , csys .C , csys .D , dt = None )
557+ K_asys , S_asys , E_asys = ct .lqr (asys , Q , R )
558+ K_expl , S_expl , E_expl = ct .lqr (csys .A , csys .B , Q , R )
559+ np .testing .assert_almost_equal (K_asys , K_expl )
560+ np .testing .assert_almost_equal (S_asys , S_expl )
561+ np .testing .assert_almost_equal (E_asys , E_expl )
562+
563+ # Calling dlqr() with a continuous time system should raise an error
564+ with pytest .raises (ControlArgument , match = "dsys must be discrete" ):
565+ K , S , E = ct .dlqr (csys , Q , R )
566+
567+ def test_lqe_discrete (self ):
568+ """Test overloading of lqe operator for discrete time systems"""
569+ csys = ct .rss (2 , 1 , 1 )
570+ dsys = ct .drss (2 , 1 , 1 )
571+ Q = np .eye (1 )
572+ R = np .eye (1 )
573+
574+ # Calling with a system versus explicit A, B should be the sam
575+ K_csys , S_csys , E_csys = ct .lqe (csys , Q , R )
576+ K_expl , S_expl , E_expl = ct .lqe (csys .A , csys .B , csys .C , Q , R )
577+ np .testing .assert_almost_equal (K_csys , K_expl )
578+ np .testing .assert_almost_equal (S_csys , S_expl )
579+ np .testing .assert_almost_equal (E_csys , E_expl )
580+
581+ # Calling lqe() with a discrete time system should call dlqe()
582+ K_lqe , S_lqe , E_lqe = ct .lqe (dsys , Q , R )
583+ K_dlqe , S_dlqe , E_dlqe = ct .dlqe (dsys , Q , R )
584+ np .testing .assert_almost_equal (K_lqe , K_dlqe )
585+ np .testing .assert_almost_equal (S_lqe , S_dlqe )
586+ np .testing .assert_almost_equal (E_lqe , E_dlqe )
587+
588+ # Calling lqe() with no timebase should call lqe()
589+ asys = ct .ss (csys .A , csys .B , csys .C , csys .D , dt = None )
590+ K_asys , S_asys , E_asys = ct .lqe (asys , Q , R )
591+ K_expl , S_expl , E_expl = ct .lqe (csys .A , csys .B , csys .C , Q , R )
592+ np .testing .assert_almost_equal (K_asys , K_expl )
593+ np .testing .assert_almost_equal (S_asys , S_expl )
594+ np .testing .assert_almost_equal (E_asys , E_expl )
595+
596+ # Calling dlqe() with a continuous time system should raise an error
597+ with pytest .raises (ControlArgument , match = "called with a continuous" ):
598+ K , S , E = ct .dlqe (csys , Q , R )
0 commit comments