25
25
[2 , 1 , None ],
26
26
[2 , 1 , True ],
27
27
[2 , 1 , False ],
28
- [2 , 2 , None ],
29
- [2 , 2 , True ],
30
- [2 , 2 , False ],
28
+ [2 , 3 , None ],
29
+ [2 , 3 , True ],
30
+ [2 , 3 , False ],
31
31
])
32
32
def test_trdata_shapes (nin , nout , squeeze ):
33
33
# SISO, single trace
@@ -48,6 +48,12 @@ def test_trdata_shapes(nin, nout, squeeze):
48
48
assert res .x .shape == (sys .nstates , ntimes )
49
49
assert res .u is None
50
50
51
+ # Check dimensions of the response
52
+ assert res .ntraces == 0 # single trace
53
+ assert res .ninputs == 0 # no input for initial response
54
+ assert res .noutputs == sys .noutputs
55
+ assert res .nstates == sys .nstates
56
+
51
57
# Check shape of class properties
52
58
if sys .issiso ():
53
59
assert res .outputs .shape == (ntimes ,)
@@ -78,6 +84,12 @@ def test_trdata_shapes(nin, nout, squeeze):
78
84
assert res .x .shape == (sys .nstates , sys .ninputs , ntimes )
79
85
assert res .u .shape == (sys .ninputs , sys .ninputs , ntimes )
80
86
87
+ # Check shape of class members
88
+ assert res .ntraces == sys .ninputs
89
+ assert res .ninputs == sys .ninputs
90
+ assert res .noutputs == sys .noutputs
91
+ assert res .nstates == sys .nstates
92
+
81
93
# Check shape of inputs and outputs
82
94
if sys .issiso () and squeeze is not False :
83
95
assert res .outputs .shape == (ntimes , )
@@ -108,11 +120,19 @@ def test_trdata_shapes(nin, nout, squeeze):
108
120
res = ct .forced_response (sys , T , U , X0 , squeeze = squeeze )
109
121
ntimes = res .time .shape [0 ]
110
122
123
+ # Check shape of class members
111
124
assert len (res .time .shape ) == 1
112
125
assert res .y .shape == (sys .noutputs , ntimes )
113
126
assert res .x .shape == (sys .nstates , ntimes )
114
127
assert res .u .shape == (sys .ninputs , ntimes )
115
128
129
+ # Check dimensions of the response
130
+ assert res .ntraces == 0 # single trace
131
+ assert res .ninputs == sys .ninputs
132
+ assert res .noutputs == sys .noutputs
133
+ assert res .nstates == sys .nstates
134
+
135
+ # Check shape of inputs and outputs
116
136
if sys .issiso () and squeeze is not False :
117
137
assert res .outputs .shape == (ntimes ,)
118
138
assert res .states .shape == (sys .nstates , ntimes )
@@ -176,6 +196,167 @@ def test_response_copy():
176
196
with pytest .raises (ValueError , match = "not enough" ):
177
197
t , y , x = response_mimo
178
198
199
+ # Labels
200
+ assert response_mimo .output_labels is None
201
+ assert response_mimo .state_labels is None
202
+ assert response_mimo .input_labels is None
203
+ response = response_mimo (
204
+ output_labels = ['y1' , 'y2' ], input_labels = 'u' ,
205
+ state_labels = ["x[%d]" % i for i in range (4 )])
206
+ assert response .output_labels == ['y1' , 'y2' ]
207
+ assert response .state_labels == ['x[0]' , 'x[1]' , 'x[2]' , 'x[3]' ]
208
+ assert response .input_labels == ['u' ]
209
+
179
210
# Unknown keyword
180
- with pytest .raises (ValueError , match = "unknown " ):
211
+ with pytest .raises (ValueError , match = "Unknown parameter(s)* " ):
181
212
response_bad_kw = response_mimo (input = 0 )
213
+
214
+
215
+ def test_trdata_labels ():
216
+ # Create an I/O system with labels
217
+ sys = ct .rss (4 , 3 , 2 )
218
+ iosys = ct .LinearIOSystem (sys )
219
+
220
+ T = np .linspace (1 , 10 , 10 )
221
+ U = [np .sin (T ), np .cos (T )]
222
+
223
+ # Create a response
224
+ response = ct .input_output_response (iosys , T , U )
225
+
226
+ # Make sure the labels got created
227
+ np .testing .assert_equal (
228
+ response .output_labels , ["y[%d]" % i for i in range (sys .noutputs )])
229
+ np .testing .assert_equal (
230
+ response .state_labels , ["x[%d]" % i for i in range (sys .nstates )])
231
+ np .testing .assert_equal (
232
+ response .input_labels , ["u[%d]" % i for i in range (sys .ninputs )])
233
+
234
+
235
+ def test_trdata_multitrace ():
236
+ #
237
+ # Output signal processing
238
+ #
239
+
240
+ # Proper call of multi-trace data w/ ambiguous 2D output
241
+ response = ct .TimeResponseData (
242
+ np .zeros (5 ), np .ones ((2 , 5 )), np .zeros ((3 , 2 , 5 )),
243
+ np .ones ((4 , 2 , 5 )), multi_trace = True )
244
+ assert response .ntraces == 2
245
+ assert response .noutputs == 1
246
+ assert response .nstates == 3
247
+ assert response .ninputs == 4
248
+
249
+ # Proper call of single trace w/ ambiguous 2D output
250
+ response = ct .TimeResponseData (
251
+ np .zeros (5 ), np .ones ((2 , 5 )), np .zeros ((3 , 5 )),
252
+ np .ones ((4 , 5 )), multi_trace = False )
253
+ assert response .ntraces == 0
254
+ assert response .noutputs == 2
255
+ assert response .nstates == 3
256
+ assert response .ninputs == 4
257
+
258
+ # Proper call of multi-trace data w/ ambiguous 1D output
259
+ response = ct .TimeResponseData (
260
+ np .zeros (5 ), np .ones (5 ), np .zeros ((3 , 5 )),
261
+ np .ones ((4 , 5 )), multi_trace = False )
262
+ assert response .ntraces == 0
263
+ assert response .noutputs == 1
264
+ assert response .nstates == 3
265
+ assert response .ninputs == 4
266
+ assert response .y .shape == (1 , 5 ) # Make sure reshape occured
267
+
268
+ # Output vector not the right shape
269
+ with pytest .raises (ValueError , match = "Output vector is the wrong shape" ):
270
+ response = ct .TimeResponseData (
271
+ np .zeros (5 ), np .ones ((1 , 2 , 3 , 5 )), None , None )
272
+
273
+ # Inconsistent output vector: different number of time points
274
+ with pytest .raises (ValueError , match = "Output vector does not match time" ):
275
+ response = ct .TimeResponseData (
276
+ np .zeros (5 ), np .ones (6 ), np .zeros (5 ), np .zeros (5 ))
277
+
278
+ #
279
+ # State signal processing
280
+ #
281
+
282
+ # For multi-trace, state must be 3D
283
+ with pytest .raises (ValueError , match = "State vector is the wrong shape" ):
284
+ response = ct .TimeResponseData (
285
+ np .zeros (5 ), np .ones ((1 , 5 )), np .zeros ((3 , 5 )), multi_trace = True )
286
+
287
+ # If not multi-trace, state must be 2D
288
+ with pytest .raises (ValueError , match = "State vector is the wrong shape" ):
289
+ response = ct .TimeResponseData (
290
+ np .zeros (5 ), np .ones (5 ), np .zeros ((3 , 1 , 5 )), multi_trace = False )
291
+
292
+ # State vector in the wrong shape
293
+ with pytest .raises (ValueError , match = "State vector is the wrong shape" ):
294
+ response = ct .TimeResponseData (
295
+ np .zeros (5 ), np .ones ((1 , 2 , 5 )), np .zeros ((2 , 1 , 5 )))
296
+
297
+ # Inconsistent state vector: different number of time points
298
+ with pytest .raises (ValueError , match = "State vector does not match time" ):
299
+ response = ct .TimeResponseData (
300
+ np .zeros (5 ), np .ones (5 ), np .zeros ((1 , 6 )), np .zeros (5 ))
301
+
302
+ #
303
+ # Input signal processing
304
+ #
305
+
306
+ # Proper call of multi-trace data with 2D input
307
+ response = ct .TimeResponseData (
308
+ np .zeros (5 ), np .ones ((2 , 5 )), np .zeros ((3 , 2 , 5 )),
309
+ np .ones ((2 , 5 )), multi_trace = True )
310
+ assert response .ntraces == 2
311
+ assert response .noutputs == 1
312
+ assert response .nstates == 3
313
+ assert response .ninputs == 1
314
+
315
+ # Input vector in the wrong shape
316
+ with pytest .raises (ValueError , match = "Input vector is the wrong shape" ):
317
+ response = ct .TimeResponseData (
318
+ np .zeros (5 ), np .ones ((1 , 2 , 5 )), None , np .zeros ((2 , 1 , 5 )))
319
+
320
+ # Inconsistent input vector: different number of time points
321
+ with pytest .raises (ValueError , match = "Input vector does not match time" ):
322
+ response = ct .TimeResponseData (
323
+ np .zeros (5 ), np .ones (5 ), np .zeros ((1 , 5 )), np .zeros (6 ))
324
+
325
+
326
+ def test_trdata_exceptions ():
327
+ # Incorrect dimension for time vector
328
+ with pytest .raises (ValueError , match = "Time vector must be 1D" ):
329
+ ct .TimeResponseData (np .zeros ((2 ,2 )), np .zeros (2 ), None )
330
+
331
+ # Infer SISO system from inputs and outputs
332
+ response = ct .TimeResponseData (
333
+ np .zeros (5 ), np .ones (5 ), None , np .ones (5 ))
334
+ assert response .issiso
335
+
336
+ response = ct .TimeResponseData (
337
+ np .zeros (5 ), np .ones ((1 , 5 )), None , np .ones ((1 , 5 )))
338
+ assert response .issiso
339
+
340
+ response = ct .TimeResponseData (
341
+ np .zeros (5 ), np .ones ((1 , 2 , 5 )), None , np .ones ((1 , 2 , 5 )))
342
+ assert response .issiso
343
+
344
+ # Not enough input to infer whether SISO
345
+ with pytest .raises (ValueError , match = "Can't determine if system is SISO" ):
346
+ response = ct .TimeResponseData (
347
+ np .zeros (5 ), np .ones ((1 , 2 , 5 )), np .ones ((4 , 2 , 5 )), None )
348
+
349
+ # Not enough input to infer whether SISO
350
+ with pytest .raises (ValueError , match = "Keyword `issiso` does not match" ):
351
+ response = ct .TimeResponseData (
352
+ np .zeros (5 ), np .ones ((2 , 5 )), None , np .ones ((1 , 5 )), issiso = True )
353
+
354
+ # Unknown squeeze keyword value
355
+ with pytest .raises (ValueError , match = "Unknown squeeze value" ):
356
+ response = ct .TimeResponseData (
357
+ np .zeros (5 ), np .ones (5 ), None , np .ones (5 ), squeeze = 1 )
358
+
359
+ # Legacy interface index error
360
+ response [0 ], response [1 ], response [2 ]
361
+ with pytest .raises (IndexError ):
362
+ response [3 ]
0 commit comments