Skip to content

Commit 72f427d

Browse files
authored
Merge pull request python-control#242 from murrayrm/fix_common_den
fix issue python-control#240: _common_den was not padding properly
2 parents 0434508 + b1c6670 commit 72f427d

File tree

3 files changed

+53
-7
lines changed

3 files changed

+53
-7
lines changed

control/tests/convert_test.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,29 @@ def testTf2SsDuplicatePoles(self):
245245
except ImportError:
246246
print("Slycot not present, skipping")
247247

248+
@unittest.skipIf(not slycot_check(), "slycot not installed")
249+
def test_tf2ss_robustness(self):
250+
"""Unit test to make sure that tf2ss is working correctly.
251+
Source: https://github.com/python-control/python-control/issues/240
252+
"""
253+
import control
254+
255+
num = [ [[0], [1]], [[1], [0]] ]
256+
den1 = [ [[1], [1,1]], [[1,4], [1]] ]
257+
sys1tf = control.tf(num, den1)
258+
sys1ss = control.tf2ss(sys1tf)
259+
260+
# slight perturbation
261+
den2 = [ [[1], [1e-10, 1, 1]], [[1,4], [1]] ]
262+
sys2tf = control.tf(num, den2)
263+
sys2ss = control.tf2ss(sys2tf)
264+
265+
# Make sure that the poles match for StateSpace and TransferFunction
266+
np.testing.assert_array_almost_equal(np.sort(sys1tf.pole()),
267+
np.sort(sys1ss.pole()))
268+
np.testing.assert_array_almost_equal(np.sort(sys2tf.pole()),
269+
np.sort(sys2ss.pole()))
270+
248271
def suite():
249272
return unittest.TestLoader().loadTestsFromTestCase(TestConvert)
250273

control/tests/timeresp_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from control.statesp import *
1616
from control.xferfcn import TransferFunction, _convertToTransferFunction
1717
from control.dtime import c2d
18+
from control.exception import slycot_check
1819

1920
class TestTimeresp(unittest.TestCase):
2021
def setUp(self):
@@ -233,6 +234,23 @@ def test_discrete_initial(self):
233234
t, yout = impulse_response(h1, np.arange(4))
234235
np.testing.assert_array_equal(yout[0], [0., 1., 0., 0.])
235236

237+
@unittest.skipIf(not slycot_check(), "slycot not installed")
238+
def test_step_robustness(self):
239+
"Unit test: https://github.com/python-control/python-control/issues/240"
240+
# Create 2 input, 2 output system
241+
num = [ [[0], [1]], [[1], [0]] ]
242+
243+
den1 = [ [[1], [1,1]], [[1,4], [1]] ]
244+
sys1 = TransferFunction(num, den1)
245+
246+
den2 = [ [[1], [1e-10, 1, 1]], [[1,4], [1]] ] # slight perturbation
247+
sys2 = TransferFunction(num, den2)
248+
249+
# Compute step response from input 1 to output 1, 2
250+
t1, y1 = step_response(sys1, input=0)
251+
t2, y2 = step_response(sys2, input=0)
252+
np.testing.assert_array_almost_equal(y1, y2)
253+
236254
def suite():
237255
return unittest.TestLoader().loadTestsFromTestCase(TestTimeresp)
238256

control/xferfcn.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -840,9 +840,13 @@ def _common_den(self, imag_tol=None):
840840
num[i,j,0] = poleset[i][j][2]
841841
else:
842842
# create the denominator matching this input
843+
# polyfromroots gives coeffs in opposite order from what we use
844+
# coefficients should be padded on right, ending at np
843845
np = len(poles[j])
844846
den[j,np::-1] = polyfromroots(poles[j]).real
845847
denorder[j] = np
848+
849+
# now create the numerator, also padded on the right
846850
for i in range(self.outputs):
847851
# start with the current set of zeros for this output
848852
nwzeros = list(poleset[i][j][0])
@@ -851,14 +855,15 @@ def _common_den(self, imag_tol=None):
851855
for ip in chain(poleset[i][j][3],
852856
range(poleset[i][j][4],np)):
853857
nwzeros.append(poles[j][ip])
854-
858+
855859
numpoly = poleset[i][j][2] * polyfromroots(nwzeros).real
856-
m = npmax - len(numpoly)
857-
#print(j,i,m,len(numpoly),len(poles[j]))
858-
if m < 0:
859-
num[i,j,::-1] = numpoly
860-
else:
861-
num[i,j,:m:-1] = numpoly
860+
# print(numpoly, den[j])
861+
# polyfromroots gives coeffs in opposite order => invert
862+
# numerator polynomial should be padded on left and right
863+
# ending at np to line up with what td04ad expects...
864+
num[i, j, np+1-len(numpoly):np+1] = numpoly[::-1]
865+
# print(num[i, j])
866+
862867
if (abs(den.imag) > epsnm).any():
863868
print("Warning: The denominator has a nontrivial imaginary part: %f"
864869
% abs(den.imag).max())

0 commit comments

Comments
 (0)