From: <kk...@us...> - 2011-02-08 22:16:37
|
Revision: 83 http://python-control.svn.sourceforge.net/python-control/?rev=83&view=rev Author: kkchen Date: 2011-02-08 22:16:31 +0000 (Tue, 08 Feb 2011) Log Message: ----------- Begun work on tf2ss wrapping. Wrote StateSpace._remove_useless_states. TransferFunction.pole and convertToStateSpace have been edited to use TransferFunction._common_den, which returns a common denominator formulation of a MIMO transfer function. _common_den is not completed yet. Also, StateSpace._remove_useless_states was written to remove states marked by entire rows or columns of zeros in the A, B, or C matrix. Now, TestBDAlg.py passes. Kevin K. Chen <kk...@pr...> Modified Paths: -------------- branches/control-0.4a/src/statesp.py branches/control-0.4a/src/xferfcn.py Modified: branches/control-0.4a/src/statesp.py =================================================================== --- branches/control-0.4a/src/statesp.py 2011-02-08 22:16:26 UTC (rev 82) +++ branches/control-0.4a/src/statesp.py 2011-02-08 22:16:31 UTC (rev 83) @@ -68,12 +68,13 @@ """ -from numpy import angle, any, array, concatenate, cos, dot, empty, exp, eye, \ - ones, pi, poly, poly1d, matrix, roots, sin, zeros +from numpy import all, angle, any, array, concatenate, cos, delete, dot, \ + empty, exp, eye, matrix, ones, pi, poly, poly1d, roots, sin, zeros from numpy.random import rand, randn from numpy.linalg import inv, det, solve from numpy.linalg.linalg import LinAlgError from scipy.signal import lti +from slycot import td04ad from lti import Lti import xferfcn @@ -121,6 +122,46 @@ if self.outputs != D.shape[0]: raise ValueError("D must have the same row size as C.") + # Check for states that don't do anything, and remove them. + self._remove_useless_states() + + def _remove_useless_states(self): + """Check for states that don't do anything, and remove them. + + Scan the A, B, and C matrices for rows or columns of zeros. If the + zeros are such that a particular state has no effect on the input-output + dynamics, then remove that state from the A, B, and C matrices. + + """ + + # Indices of useless states. + useless = [] + + # Search for useless states. + for i in range(self.states): + if (all(self.A[i, :] == zeros((1, self.states))) and + all(self.B[i, :] == zeros((1, self.inputs)))): + useless.append(i) + # To avoid duplucate indices in useless, jump to the next + # iteration. + continue + if (all(self.A[:, i] == zeros((self.states, 1))) and + all(self.C[:, i] == zeros((self.outputs, 1)))): + useless.append(i) + + # Remove the useless states. + if all(useless == range(self.states)): + # All the states were useless. + self.A = 0 + self.B = zeros((1, self.inputs)) + self.C = zeros((self.outputs, 1)) + else: + # A more typical scenario. + self.A = delete(self.A, useless, 0) + self.A = delete(self.A, useless, 1) + self.B = delete(self.B, useless, 0) + self.C = delete(self.C, useless, 1) + def __str__(self): """String representation of the state space.""" @@ -374,15 +415,23 @@ # Already a state space system; just return it return sys elif isinstance(sys, xferfcn.TransferFunction): - # TODO: Wrap SLICOT to do transfer function to state space conversion. - raise NotImplementedError("Transfer function to state space conversion \ -is not implemented yet.") - elif (isinstance(sys, (int, long, float, complex))): + # Change the numerator and denominator arrays so that the transfer + # function matrix has a common denominator. + num, den = sys._common_den() + # Make a list of the orders of the denominator polynomials. + index = [len(den) for i in range(sys.outputs)] + # Repeat the common denominator along the rows. + den = array([den for i in range(sys.outputs)]) + + ssout = td04ad(sys.inputs, sys.outputs, index, num, den) + + return StateSpace(ssout[1], ssout[2], ssout[3], ssout[4]) + elif isinstance(sys, (int, long, float, complex)): # Generate a simple state space system of the desired dimension # The following Doesn't work due to inconsistencies in ltisys: # return StateSpace([[]], [[]], [[]], eye(outputs, inputs)) return StateSpace(0., zeros((1, inputs)), zeros((outputs, 1)), - sys * ones(outputs, inputs)) + sys * ones((outputs, inputs))) else: raise TypeError("Can't convert given type to StateSpace system.") Modified: branches/control-0.4a/src/xferfcn.py =================================================================== --- branches/control-0.4a/src/xferfcn.py 2011-02-08 22:16:26 UTC (rev 82) +++ branches/control-0.4a/src/xferfcn.py 2011-02-08 22:16:31 UTC (rev 83) @@ -26,6 +26,7 @@ TransferFunction.zero TransferFunction.feedback TransferFunction.returnScipySignalLti +TransferFunction._common_den _tfpolyToString _addSISO convertToTransferFunction @@ -72,7 +73,7 @@ # External function declarations from numpy import angle, array, empty, ndarray, ones, polyadd, polymul, \ - polyval, zeros + polyval, roots, zeros from scipy.signal import lti from copy import deepcopy from slycot import tb04ad @@ -406,9 +407,9 @@ def pole(self): """Compute the poles of a transfer function.""" - raise NotImplementedError("TransferFunction.pole is not implemented \ -yet.") - + num, den = self._common_den() + return roots(den) + def zero(self): """Compute the zeros of a transfer function.""" @@ -463,6 +464,39 @@ return out + def _common_den(self): + """Compute MIMO common denominator; return it and an adjusted numerator. + + >>> n, d = sys._common_den() + + computes the single denominator containing all the poles of sys.den, and + reports it as the array d. + + The output numerator array n is modified to use the common denominator. + It is an sys.outputs-by-sys.inputs-by-[something] array. + + """ + + # Preallocate some variables. Start by figuring out the maximum number + # of numerator coefficients. + numcoeffs = 0 + for i in range(self.outputs): + for j in range(self.inputs): + numcoeffs = max(numcoeffs, len(self.num[i][j])) + # The output 3-D adjusted numerator array. + num = empty((i, j, numcoeffs)) + # A list to keep track of roots found as we scan self.den. + poles = [] + # A 3-D list to keep track of common denominator roots not present in + # the self.den[i][j]. + missingpoles = [[[] for j in range(self.inputs)] for i in + range(self.outputs)] + + for i in range(sys.outputs): + for j in range(sys.inputs): + currentpoles = roots(self.den[i][j]) + #TODO: finish this + # Utility function to convert a transfer function polynomial to a string # Borrowed from poly1d library def _tfpolyToString(coeffs, var='s'): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |