@@ -122,34 +122,35 @@ def __init__(self, *args):
122
122
else :
123
123
raise ValueError ("Needs 1 or 4 arguments; received %i." % len (args ))
124
124
125
- # Here we're going to convert inputs to matrices, if the user gave a
126
- # non-matrix type.
127
- #! TODO: [A, B, C, D] = map(matrix, [A, B, C, D])?
128
- matrices = [A , B , C , D ]
129
- for i in range (len (matrices )):
130
- # Convert to matrix first, if necessary.
131
- matrices [i ] = matrix (matrices [i ])
132
- [A , B , C , D ] = matrices
133
-
134
- LTI .__init__ (self , B .shape [1 ], C .shape [0 ], dt )
125
+ A , B , C , D = [matrix (M ) for M in (A , B , C , D )]
126
+
127
+ # TODO: use super here?
128
+ LTI .__init__ (self , inputs = D .shape [1 ], outputs = D .shape [0 ], dt = dt )
135
129
self .A = A
136
130
self .B = B
137
131
self .C = C
138
132
self .D = D
139
133
140
- self .states = A .shape [0 ]
134
+ self .states = A .shape [1 ]
135
+
136
+ if 0 == self .states :
137
+ # static gain
138
+ # matrix's default "empty" shape is 1x0
139
+ A .shape = (0 ,0 )
140
+ B .shape = (0 ,self .inputs )
141
+ C .shape = (self .outputs ,0 )
141
142
142
143
# Check that the matrix sizes are consistent.
143
- if self .states != A .shape [1 ]:
144
+ if self .states != A .shape [0 ]:
144
145
raise ValueError ("A must be square." )
145
146
if self .states != B .shape [0 ]:
146
- raise ValueError ("B must have the same row size as A ." )
147
+ raise ValueError ("A and B must have the same number of rows ." )
147
148
if self .states != C .shape [1 ]:
148
- raise ValueError ("C must have the same column size as A ." )
149
- if self .inputs != D .shape [1 ]:
150
- raise ValueError ("D must have the same column size as B ." )
151
- if self .outputs != D .shape [0 ]:
152
- raise ValueError ("D must have the same row size as C ." )
149
+ raise ValueError ("A and C must have the same number of columns ." )
150
+ if self .inputs != B .shape [1 ]:
151
+ raise ValueError ("B and D must have the same number of columns ." )
152
+ if self .outputs != C .shape [0 ]:
153
+ raise ValueError ("C and D must have the same number of rows ." )
153
154
154
155
# Check for states that don't do anything, and remove them.
155
156
self ._remove_useless_states ()
@@ -179,17 +180,10 @@ def _remove_useless_states(self):
179
180
useless .append (i )
180
181
181
182
# Remove the useless states.
182
- if all (useless == range (self .states )):
183
- # All the states were useless.
184
- self .A = zeros ((1 , 1 ))
185
- self .B = zeros ((1 , self .inputs ))
186
- self .C = zeros ((self .outputs , 1 ))
187
- else :
188
- # A more typical scenario.
189
- self .A = delete (self .A , useless , 0 )
190
- self .A = delete (self .A , useless , 1 )
191
-
57AE
self .B = delete (self .B , useless , 0 )
192
- self .C = delete (self .C , useless , 1 )
183
+ self .A = delete (self .A , useless , 0 )
184
+ self .A = delete (self .A , useless , 1 )
185
+ self .B = delete (self .B , useless , 0 )
186
+ self .C = delete (self .C , useless , 1 )
193
187
194
188
self .states = self .A .shape [0 ]
195
189
self .inputs = self .B .shape [1 ]
@@ -405,7 +399,7 @@ def freqresp(self, omega):
405
399
def pole (self ):
406
400
"""Compute the poles of a state space system."""
407
401
408
- return eigvals (self .A )
402
+ return eigvals (self .A ) if self . states else np . array ([])
409
403
410
404
def zero (self ):
411
405
"""Compute the zeros of a state space system."""
@@ -477,18 +471,22 @@ def feedback(self, other=1, sign=-1):
477
471
def minreal (self , tol = 0.0 ):
478
472
"""Calculate a minimal realization, removes unobservable and
479
473
uncontrollable states"""
480
- try :
481
- from slycot import tb01pd
482
- B = empty ((self .states , max (self .inputs , self .outputs )))
483
- B [:,:self .inputs ] = self .B
484
- C = empty ((max (self .outputs , self .inputs ), self .states ))
485
- C [:self .outputs ,:] = self .C
486
- A , B , C , nr = tb01pd (self .states , self .inputs , self .outputs ,
487
- self .A , B , C , tol = tol )
488
- return StateSpace (A [:nr ,:nr ], B [:nr ,:self .inputs ],
489
- C [:self .outputs ,:nr ], self .D )
490
- except ImportError :
491
- raise TypeError ("minreal requires slycot tb01pd" )
474
+ if self .states :
475
+ try :
476
+ from slycot import tb01pd
477
+ B = empty ((self .states , max (self .inputs , self .outputs )))
478
+ B [:,:self .inputs ] = self .B
479
+ C = empty ((max (self .outputs , self .inputs ), self .states ))
480
+ C [:self .outputs ,:] = self .C
481
+ A , B , C , nr = tb01pd (self .states , self .inputs , self .outputs ,
482
+ self .A , B , C , tol = tol )
483
+ return StateSpace (A [:nr ,:nr ], B [:nr ,:self .inputs ],
484
+ C [:self .outputs ,:nr ], self .D )
485
+ except ImportError :
486
+ raise TypeError ("minreal requires slycot tb01pd" )
487
+ else :
488
+ return StateSpace (self )
489
+
492
490
493
491
# TODO: add discrete time check
494
492
def returnScipySignalLTI (self ):
0 commit comments