Skip to content

Commit b1c6670

Browse files
committed
fix issue #240: _common_den was not padding properly
1 parent 4b0101c commit b1c6670

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
@@ -800,9 +800,13 @@ def _common_den(self, imag_tol=None):
800800
num[i,j,0] = poleset[i][j][2]
801801
else:
802802
# create the denominator matching this input
803+
# polyfromroots gives coeffs in opposite order from what we use
804+
# coefficients should be padded on right, ending at np
803805
np = len(poles[j])
804806
den[j,np::-1] = polyfromroots(poles[j]).real
805807
denorder[j] = np
808+
809+
# now create the numerator, also padded on the right
806810
for i in range(self.outputs):
807811
# start with the current set of zeros for this output
808812
nwzeros = list(poleset[i][j][0])
@@ -811,14 +815,15 @@ def _common_den(self, imag_tol=None):
811815
for ip in chain(poleset[i][j][3],
812816
range(poleset[i][j][4],np)):
813817
nwzeros.append(poles[j][ip])
814-
818+
815819
numpoly = poleset[i][j][2] * polyfromroots(nwzeros).real
816-
m = npmax - len(numpoly)
817-
#print(j,i,m,len(numpoly),len(poles[j]))
818-
if m < 0:
819-
num[i,j,::-1] = numpoly
820-
else:
821-
num[i,j,:m:-1] = numpoly
820+
# print(numpoly, den[j])
821+
# polyfromroots gives coeffs in opposite order => invert
822+
# numerator polynomial should be padded on left and right
823+
# ending at np to line up with what td04ad expects...
824+
num[i, j, np+1-len(numpoly):np+1] = numpoly[::-1]
825+
# print(num[i, j])
826+
822827
if (abs(den.imag) > epsnm).any():
823828
print("Warning: The denominator has a nontrivial imaginary part: %f"
824829
% abs(den.imag).max())

0 commit comments

Comments
 (0)