From: <kk...@us...> - 2011-02-08 22:14:23
|
Revision: 56 http://python-control.svn.sourceforge.net/python-control/?rev=56&view=rev Author: kkchen Date: 2011-02-08 22:14:17 +0000 (Tue, 08 Feb 2011) Log Message: ----------- Changes to xferfcn.py: - Check that we don't have any zero denominators. - Bug fix in _addSISO - Exceptions more specific Also added a simple SISO multiplication test to TestXferFcn.py. Kevin K. Chen <kk...@pr...> Modified Paths: -------------- branches/control-0.4a/src/TestXferFcn.py branches/control-0.4a/src/xferfcn.py Modified: branches/control-0.4a/src/TestXferFcn.py =================================================================== --- branches/control-0.4a/src/TestXferFcn.py 2011-02-08 22:14:13 UTC (rev 55) +++ branches/control-0.4a/src/TestXferFcn.py 2011-02-08 22:14:17 UTC (rev 56) @@ -57,13 +57,22 @@ sys1 = xTransferFunction(num1, den1) sys2 = xTransferFunction(num2, den2) - sys3 = sys1 + sys2 for i in range(sys3.outputs): for j in range(sys3.inputs): np.testing.assert_array_equal(sys3.num[i][j], num3[i][j]) np.testing.assert_array_equal(sys3.den[i][j], den3[i][j]) + + def testMulSISO1(self): + """Multiply two direct feedthrough systems.""" + + sys1 = xTransferFunction(2., [1.]) + sys2 = xTransferFunction(1., 4.) + sys3 = sys1 * sys2 + + np.testing.assert_array_equal(sys3.num, [[[2.]]]) + np.testing.assert_array_equal(sys3.den, [[[4.]]]) if __name__ == "__main__": unittest.main() \ No newline at end of file Modified: branches/control-0.4a/src/xferfcn.py =================================================================== --- branches/control-0.4a/src/xferfcn.py 2011-02-08 22:14:13 UTC (rev 55) +++ branches/control-0.4a/src/xferfcn.py 2011-02-08 22:14:17 UTC (rev 56) @@ -67,7 +67,7 @@ """This is the constructor. The default transfer function is 1 (unit gain direct feedthrough).""" - # Make num and den into 3-d numpy arrays, if necessary. + # Make num and den into lists of lists of arrays, if necessary. data = [num, den] for i in range(len(data)): if isinstance(data[i], (int, float, long, complex)): @@ -93,19 +93,34 @@ inputs = len(num[0]) outputs = len(num) + # Make sure the numerator and denominator matrices have consistent + # sizes. if inputs != len(den[0]): - raise ValueError("The numerator and denominator matrices must have \ -the same column\n(input) size.") + raise ValueError("The numerator has %i input(s), but the \ +denominator has %i\ninput(s)." % (inputs, len(den[0]))) if outputs != len(den): - raise ValueError("The numerator and denominator matrices must have \ -the same row\n(output) size.") - for i in range(1, outputs): + raise ValueError("The numerator has %i output(s), but the \ +denominator has %i\noutput(s)." % (outputs, len(den))) + + # Make sure that each row has the same number of columns. + for i in range(outputs): if len(num[i]) != inputs: - raise ValueError("Each row of the numerator matrix must have \ -the same number of\nelements.") + raise ValueError("Row 0 of the numerator matrix has %i \ +elements, but row %i\nhas %i." % (inputs, i, len(num[i]))) if len(den[i]) != inputs: - raise ValueError("Each row of the denominator matrix must have \ -the same number of\nelements.") + raise ValueError("Row 0 of the denominator matrix has %i \ +elements, but row %i\nhas %i." % (inputs, i, len(den[i]))) + + # Check that we don't have any zero denominators. + for j in range(inputs): + iszero = True + for k in den[i][j]: + if k: + iszero = False + break + if iszero: + raise ValueError("Input %i, output %i has a zero \ +denominator." % (j + 1, i + 1)) self.num = num self.den = den @@ -180,11 +195,11 @@ # Check that the input-output sizes are consistent. if self.inputs != other.inputs: - raise ValueError("The two systems to be added must have the same \ -input size.") + raise ValueError("The first summand has %i input(s), but the second \ +has %i." % (self.inputs, other.inputs)) if self.outputs != other.outputs: - raise ValueError("The two systems to be added must have the same \ -output size.") + raise ValueError("The first summand has %i output(s), but the second \ +has %i." % (self.outputs, other.outputs)) # Preallocate the numerator and denominator of the sum. num = [[[] for j in range(self.inputs)] for i in range(self.outputs)] @@ -192,7 +207,7 @@ for i in range(self.outputs): for j in range(self.inputs): - num[i][j], den[i][j] = addSISO(self.num[i][j], self.den[i][j], + num[i][j], den[i][j] = _addSISO(self.num[i][j], self.den[i][j], other.num[i][j], other.den[i][j]) return xTransferFunction(num, den) @@ -221,15 +236,15 @@ # Check that the input-output sizes are consistent. if self.inputs != other.outputs: - raise ValueError("C = A * B: A must have the same number of \ -columns (inputs) as B has\nrows (outputs).") + raise ValueError("C = A * B: A has %i column(s) (input(s)), but B \ +has %i row(s)\n(output(s))." % (self.inputs, other.outputs)) inputs = other.inputs outputs = self.outputs # Preallocate the numerator and denominator of the sum. num = [[[0] for j in range(inputs)] for i in range(outputs)] - den = [[[0] for j in range(inputs)] for i in range(outputs)] + den = [[[1] for j in range(inputs)] for i in range(outputs)] # Temporary storage for the summands needed to find the (i, j)th element # of the product. @@ -241,7 +256,7 @@ for k in range(self.inputs): # Multiply & add. num_summand[k] = sp.polymul(self.num[i][k], other.num[k][j]) den_summand[k] = sp.polymul(self.den[i][k], other.den[k][j]) - num[i][j], den[i][j] = addSISO(num[i][j], den[i][j], + num[i][j], den[i][j] = _addSISO(num[i][j], den[i][j], num_summand[k], den_summand[k]) return xTransferFunction(num, den) @@ -251,16 +266,42 @@ return self * other - def __div__(self, sys): + # TODO: Division of MIMO transfer function objects is quite difficult. + def __div__(self, other): """Divide two transfer functions""" - pass + if self.inputs > 1 or self.outputs > 1 or \ + other.inputs > 1 or other.outputs > 1: + raise NotImplementedError("xTransferFunction.__div__ is currently \ +implemented only for SISO systems.") + + # Convert the second argument to a transfer function. + if not isinstance(other, xTransferFunction): + other = ss2tf(other) + + num = sp.polymul(self.num[0][0], other.den[0][0]) + den = sp.polymul(self.den[0][0], other.num[0][0]) + return xTransferFunction(num, den) + + # TODO: Division of MIMO transfer function objects is quite difficult. def __rdiv__(self, sys): """Reverse divide two transfer functions""" - pass + if self.inputs > 1 or self.outputs > 1 or \ + other.inputs > 1 or other.outputs > 1: + raise NotImplementedError("xTransferFunction.__rdiv__ is currently \ +implemented only for SISO systems.") + + # Convert the second argument to a transfer function. + if not isinstance(other, xTransferFunction): + other = ss2tf(other) + + num = sp.polymul(self.den[0][0], other.num[0][0]) + den = sp.polymul(self.num[0][0], other.den[0][0]) + return xTransferFunction(num, den) + def evalfr(self, freq): """Evaluate a transfer function at a single frequency""" @@ -354,6 +395,7 @@ def __sub__(self, other): """Subtract two transfer functions""" return self + (-other) + def __rsub__(self, other): """Subtract two transfer functions""" return other + (-self) @@ -505,7 +547,7 @@ thestr = newstr return thestr -def addSISO(num1, den1, num2, den2): +def _addSISO(num1, den1, num2, den2): """Return num/den = num1/den1 + num2/den2, where each numerator and denominator is a list of polynomial coefficients.""" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |