@@ -347,7 +347,7 @@ def test_impulse_response_mimo(self, mimo_ss2):
347
347
yref_notrim = np .zeros ((2 , len (t )))
348
348
yref_notrim [:1 , :] = yref
349
349
_t , yy = impulse_response (sys , T = t , input = 0 )
350
- np .testing .assert_array_almost_equal (yy , yref_notrim , decimal = 4 )
350
+ np .testing .assert_array_almost_equal (yy [:, 0 ,:] , yref_notrim , decimal = 4 )
351
351
352
352
@pytest .mark .skipif (StrictVersion (sp .__version__ ) < "1.3" ,
353
353
reason = "requires SciPy 1.3 or greater" )
@@ -639,9 +639,10 @@ def test_time_vector(self, tsystem, fun, squeeze, matarrayout):
639
639
if hasattr (tsystem , 't' ):
640
640
# tout should always match t, which has shape (n, )
641
641
np .testing .assert_allclose (tout , tsystem .t )
642
- if squeeze is False or sys .outputs > 1 :
642
+
643
+ if squeeze is False or not sys .issiso ():
643
644
assert yout .shape [0 ] == sys .outputs
644
- assert yout .shape [1 ] == tout .shape [0 ]
645
+ assert yout .shape [- 1 ] == tout .shape [0 ]
645
646
else :
646
647
assert yout .shape == tout .shape
647
648
@@ -725,21 +726,22 @@ def test_time_series_data_convention_2D(self, siso_ss1):
725
726
726
727
@pytest .mark .usefixtures ("editsdefaults" )
727
728
@pytest .mark .parametrize ("fcn" , [ct .ss , ct .tf , ct .ss2io ])
728
- @pytest .mark .parametrize ("nstate, nout, ninp, squeeze, shape" , [
729
- [1 , 1 , 1 , None , (8 ,)],
730
- [2 , 1 , 1 , True , (8 ,)],
731
- [3 , 1 , 1 , False , (1 , 8 )],
732
- [3 , 2 , 1 , None , (2 , 8 )],
733
- [4 , 2 , 1 , True , (2 , 8 )],
734
- [5 , 2 , 1 , False , (2 , 8 )],
735
- [3 , 1 , 2 , None , (1 , 8 )],
736
- [4 , 1 , 2 , True , (8 ,)],
737
- [5 , 1 , 2 , False , (1 , 8 )],
738
- [4 , 2 , 2 , None , (2 , 8 )],
739
- [5 , 2 , 2 , True , (2 , 8 )],
740
- [6 , 2 , 2 , False , (2 , 8 )],
729
+ @pytest .mark .parametrize ("nstate, nout, ninp, squeeze, shape1, shape2" , [
730
+ # state out in squeeze in/out out-only
731
+ [1 , 1 , 1 , None , (8 ,), (8 ,)],
732
+ [2 , 1 , 1 , True , (8 ,), (8 ,)],
733
+ [3 , 1 , 1 , False , (1 , 1 , 8 ), (1 , 8 )],
734
+ [3 , 2 , 1 , None , (2 , 1 , 8 ), (2 , 8 )],
735
+ [4 , 2 , 1 , True , (2 , 8 ), (2 , 8 )],
736
+ [5 , 2 , 1 , False , (2 , 1 , 8 ), (2 , 8 )],
737
+ [3 , 1 , 2 , None , (1 , 2 , 8 ), (1 , 8 )],
738
+ [4 , 1 , 2 , True , (2 , 8 ), (8 ,)],
739
+ [5 , 1 , 2 , False , (1 , 2 , 8 ), (1 , 8 )],
740
+ [4 , 2 , 2 , None , (2 , 2 , 8 ), (2 , 8 )],
741
+ [5 , 2 , 2 , True , (2 , 2 , 8 ), (2 , 8 )],
742
+ [6 , 2 , 2 , False , (2 , 2 , 8 ), (2 , 8 )],
741
743
])
742
- def test_squeeze (self , fcn , nstate , nout , ninp , squeeze , shape ):
744
+ def test_squeeze (self , fcn , nstate , nout , ninp , squeeze , shape1 , shape2 ):
743
745
# Figure out if we have SciPy 1+
744
746
scipy0 = StrictVersion (sp .__version__ ) < '1.0'
745
747
@@ -750,27 +752,56 @@ def test_squeeze(self, fcn, nstate, nout, ninp, squeeze, shape):
750
752
else :
751
753
sys = fcn (ct .rss (nstate , nout , ninp , strictly_proper = True ))
752
754
753
- # Keep track of expect users warnings
754
- warntype = UserWarning if sys .inputs > 1 else None
755
-
756
755
# Generate the time and input vectors
757
756
tvec = np .linspace (0 , 1 , 8 )
758
757
uvec = np .dot (
759
758
np .ones ((sys .inputs , 1 )),
760
759
np .reshape (np .sin (tvec ), (1 , 8 )))
761
760
761
+ #
762
762
# Pass squeeze argument and make sure the shape is correct
763
- with pytest .warns (warntype , match = "Converting MIMO system" ):
764
- _ , yvec = ct .impulse_response (sys , tvec , squeeze = squeeze )
765
- assert yvec .shape == shape
763
+ #
764
+ # For responses that are indexed by the input, check against shape1
765
+ # For responses that have no/fixed input, check against shape2
766
+ #
766
767
767
- _ , yvec = ct .initial_response (sys , tvec , 1 , squeeze = squeeze )
768
- assert yvec .shape == shape
768
+ # Impulse response
769
+ if isinstance (sys , StateSpace ):
770
+ # Check the states as well
771
+ _ , yvec , xvec = ct .impulse_response (
772
+ sys , tvec , squeeze = squeeze , return_x = True )
773
+ if sys .issiso ():
774
+ assert xvec .shape == (sys .states , 8 )
775
+ else :
776
+ assert xvec .shape == (sys .states , sys .inputs , 8 )
777
+ else :
778
+ _ , yvec = ct .impulse_response (sys , tvec , squeeze = squeeze )
779
+ assert yvec .shape == shape1
769
780
770
- with pytest .warns (warntype , match = "Converting MIMO system" ):
781
+ # Step response
782
+ if isinstance (sys , StateSpace ):
783
+ # Check the states as well
784
+ _ , yvec , xvec = ct .step_response (
785
+ sys , tvec , squeeze = squeeze , return_x = True )
786
+ if sys .issiso ():
787
+ assert xvec .shape == (sys .states , 8 )
788
+ else :
789
+ assert xvec .shape == (sys .states , sys .inputs , 8 )
790
+ else :
771
791
_ , yvec = ct .step_response (sys , tvec , squeeze = squeeze )
772
- assert yvec .shape == shape
792
+ assert yvec .shape == shape1
793
+
794
+ # Initial response (only indexed by output)
795
+ if isinstance (sys , StateSpace ):
796
+ # Check the states as well
797
+ _ , yvec , xvec = ct .initial_response (
798
+ sys , tvec , 1 , squeeze = squeeze , return_x = True )
799
+ assert xvec .shape == (sys .states , 8 )
800
+ else :
801
+ _ , yvec = ct .initial_response (sys , tvec , 1 , squeeze = squeeze )
802
+ assert yvec .shape == shape2
773
803
804
+ # Forced response (only indexed by output)
774
805
if isinstance (sys , StateSpace ):
775
806
# Check the states as well
776
807
_ , yvec , xvec = ct .forced_response (
@@ -779,52 +810,54 @@ def test_squeeze(self, fcn, nstate, nout, ninp, squeeze, shape):
779
810
else :
780
811
# Just check the input/output response
781
812
_ , yvec = ct .forced_response (sys , tvec , uvec , 0 , squeeze = squeeze )
782
- assert yvec .shape == shape
813
+ assert yvec .shape == shape2
783
814
784
815
# Test cases where we choose a subset of inputs and outputs
785
816
_ , yvec = ct .step_response (
786
817
sys , tvec , input = ninp - 1 , output = nout - 1 , squeeze = squeeze )
787
- # Possible code if we implemenet a squeeze='siso' option
788
818
if squeeze is False :
789
819
# Shape should be unsqueezed
790
- assert yvec .shape == (1 , 8 )
820
+ assert yvec .shape == (1 , 1 , 8 )
791
821
else :
792
822
# Shape should be squeezed
793
823
assert yvec .shape == (8 , )
794
824
795
- # For InputOutputSystems, also test input_output_response
825
+ # For InputOutputSystems, also test input/output response
796
826
if isinstance (sys , ct .InputOutputSystem ) and not scipy0 :
797
827
_ , yvec = ct .input_output_response (sys , tvec , uvec , squeeze = squeeze )
798
- assert yvec .shape == shape
828
+ assert yvec .shape == shape2
799
829
800
830
#
801
831
# Changing config.default to False should return 3D frequency response
802
832
#
803
833
ct .config .set_defaults ('control' , squeeze_time_response = False )
804
834
805
- with pytest . warns ( warntype , match = "Converting MIMO system" ):
806
- _ , yvec = ct . impulse_response ( sys , tvec )
807
- assert yvec .shape == (sys .outputs , 8 )
835
+ _ , yvec = ct . impulse_response ( sys , tvec )
836
+ if squeeze is not True or sys . inputs > 1 or sys . outputs > 1 :
837
+ assert yvec .shape == (sys .outputs , sys . inputs , 8 )
808
838
809
- _ , yvec = ct .initial_response (sys , tvec , 1 )
810
- assert yvec .shape == (sys .outputs , 8 )
839
+ _ , yvec = ct .step_response (sys , tvec )
840
+ if squeeze is not True or sys .inputs > 1 or sys .outputs > 1 :
841
+ assert yvec .shape == (sys .outputs , sys .inputs , 8 )
811
842
812
- with pytest . warns ( warntype , match = "Converting MIMO system" ):
813
- _ , yvec = ct . step_response ( sys , tvec )
814
- assert yvec .shape == (sys .outputs , 8 )
843
+ _ , yvec = ct . initial_response ( sys , tvec , 1 )
844
+ if squeeze is not True or sys . outputs > 1 :
845
+ assert yvec .shape == (sys .outputs , 8 )
815
846
816
847
if isinstance (sys , ct .StateSpace ):
817
848
_ , yvec , xvec = ct .forced_response (
818
849
sys , tvec , uvec , 0 , return_x = True )
819
850
assert xvec .shape == (sys .states , 8 )
820
851
else :
821
852
_ , yvec = ct .forced_response (sys , tvec , uvec , 0 )
822
- assert yvec .shape == (sys .outputs , 8 )
853
+ if squeeze is not True or sys .outputs > 1 :
854
+ assert yvec .shape == (sys .outputs , 8 )
823
855
824
856
# For InputOutputSystems, also test input_output_response
825
857
if isinstance (sys , ct .InputOutputSystem ) and not scipy0 :
826
858
_ , yvec = ct .input_output_response (sys , tvec , uvec )
827
- assert yvec .shape == (sys .noutputs , 8 )
859
+ if squeeze is not True or sys .outputs > 1 :
860
+ assert yvec .shape == (sys .outputs , 8 )
828
861
829
862
@pytest .mark .parametrize ("fcn" , [ct .ss , ct .tf , ct .ss2io ])
830
863
def test_squeeze_exception (self , fcn ):
@@ -861,3 +894,37 @@ def test_squeeze_0_8_4(self, nstate, nout, ninp, squeeze, shape):
861
894
862
895
_ , yvec = ct .initial_response (sys , tvec , 1 , squeeze = squeeze )
863
896
assert yvec .shape == shape
897
+
898
+ @pytest .mark .parametrize (
899
+ "nstate, nout, ninp, squeeze, ysh_in, ysh_no, xsh_in" , [
900
+ [4 , 1 , 1 , None , (8 ,), (8 ,), (8 , 4 )],
901
+ [4 , 1 , 1 , True , (8 ,), (8 ,), (8 , 4 )],
902
+ [4 , 1 , 1 , False , (8 , 1 , 1 ), (8 , 1 ), (8 , 4 )],
903
+ [4 , 2 , 1 , None , (8 , 2 , 1 ), (8 , 2 ), (8 , 4 , 1 )],
904
+ [4 , 2 , 1 , True , (8 , 2 ), (8 , 2 ), (8 , 4 , 1 )],
905
+ [4 , 2 , 1 , False , (8 , 2 , 1 ), (8 , 2 ), (8 , 4 , 1 )],
906
+ [4 , 1 , 2 , None , (8 , 1 , 2 ), (8 , 1 ), (8 , 4 , 2 )],
907
+ [4 , 1 , 2 , True , (8 , 2 ), (8 ,), (8 , 4 , 2 )],
908
+ [4 , 1 , 2 , False , (8 , 1 , 2 ), (8 , 1 ), (8 , 4 , 2 )],
909
+ [4 , 2 , 2 , None , (8 , 2 , 2 ), (8 , 2 ), (8 , 4 , 2 )],
910
+ [4 , 2 , 2 , True , (8 , 2 , 2 ), (8 , 2 ), (8 , 4 , 2 )],
911
+ [4 , 2 , 2 , False , (8 , 2 , 2 ), (8 , 2 ), (8 , 4 , 2 )],
912
+ ])
913
+ def test_response_transpose (
914
+ self , nstate , nout , ninp , squeeze , ysh_in , ysh_no , xsh_in ):
915
+ sys = ct .rss (nstate , nout , ninp )
916
+ T = np .linspace (0 , 1 , 8 )
917
+
918
+ # Step response - input indexed
919
+ t , y , x = ct .step_response (
920
+ sys , T , transpose = True , return_x = True , squeeze = squeeze )
921
+ assert t .shape == (T .size , )
922
+ assert y .shape == ysh_in
923
+ assert x .shape == xsh_in
924
+
925
+ # Initial response - no input indexing
926
+ t , y , x = ct .initial_response (
927
+ sys , T , 1 , transpose = True , return_x = True , squeeze = squeeze )
928
+ assert t .shape == (T .size , )
929
+ assert y .shape == ysh_no
930
+ assert x .shape == (T .size , sys .states )
0 commit comments