@@ -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 testinput_output_response
825
+ # For InputOutputSystems, also testinput/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 )