@@ -122,34 +122,35 @@ def __init__(self, *args):
122122 else :
123123 raise ValueError ("Needs 1 or 4 arguments; received %i." % len (args ))
124124
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 )
135129 self .A = A
136130 self .B = B
137131 self .C = C
138132 self .D = D
139133
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 )
141142
142143 # Check that the matrix sizes are consistent.
143- if self .states != A .shape [1 ]:
144+ if self .states != A .shape [0 ]:
144145 raise ValueError ("A must be square." )
145146 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 ." )
147148 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 ." )
153154
154155 # Check for states that don't do anything, and remove them.
155156 self ._remove_useless_states ()
@@ -179,17 +180,10 @@ def _remove_useless_states(self):
179180 useless .append (i )
180181
181182 # 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- 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 )
193187
194188 self .states = self .A .shape [0 ]
195189 self .inputs = self .B .shape [1 ]
@@ -405,7 +399,7 @@ def freqresp(self, omega):
405399 def pole (self ):
406400 """Compute the poles of a state space system."""
407401
408- return eigvals (self .A )
402+ return eigvals (self .A ) if self . states else np . array ([])
409403
410404 def zero (self ):
411405 """Compute the zeros of a state space system."""
@@ -477,18 +471,22 @@ def feedback(self, other=1, sign=-1):
477471 def minreal (self , tol = 0.0 ):
478472 """Calculate a minimal realization, removes unobservable and
479473 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+
492490
493491 # TODO: add discrete time check
494492 def returnScipySignalLTI (self ):
0 commit comments