Skip to content

Commit e539c72

Browse files
committed
scipy-based implementation of ss2tf
1 parent ebff125 commit e539c72

File tree

7 files changed

+16
-44
lines changed

7 files changed

+16
-44
lines changed

control/tests/frd_test.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,6 @@ def testNyquist(self, frd_fcn):
258258
freqplot.nyquist(f1)
259259
# plt.savefig('/dev/null', format='svg')
260260

261-
@slycotonly
262261
@pytest.mark.parametrize(
263262
"frd_fcn", [ct.frd, ct.FRD, ct.FrequencyResponseData])
264263
def testMIMO(self, frd_fcn):
@@ -276,7 +275,6 @@ def testMIMO(self, frd_fcn):
276275
sys.frequency_response(chkpts)[1],
277276
f1.frequency_response(chkpts)[1])
278277

279-
@slycotonly
280278
@pytest.mark.parametrize(
281279
"frd_fcn", [ct.frd, ct.FRD, ct.FrequencyResponseData])
282280
def testMIMOfb(self, frd_fcn):
@@ -295,7 +293,6 @@ def testMIMOfb(self, frd_fcn):
295293
f1.frequency_response(chkpts)[1],
296294
f2.frequency_response(chkpts)[1])
297295

298-
@slycotonly
299296
@pytest.mark.parametrize(
300297
"frd_fcn", [ct.frd, ct.FRD, ct.FrequencyResponseData])
301298
def testMIMOfb2(self, frd_fcn):
@@ -316,7 +313,6 @@ def testMIMOfb2(self, frd_fcn):
316313
f1.frequency_response(chkpts)[1],
317314
f2.frequency_response(chkpts)[1])
318315

319-
@slycotonly
320316
@pytest.mark.parametrize(
321317
"frd_fcn", [ct.frd, ct.FRD, ct.FrequencyResponseData])
322318
def testMIMOMult(self, frd_fcn):
@@ -335,7 +331,6 @@ def testMIMOMult(self, frd_fcn):
335331
(f1*f2).frequency_response(chkpts)[1],
336332
(sys*sys).frequency_response(chkpts)[1])
337333

338-
@slycotonly
339334
@pytest.mark.parametrize(
340335
"frd_fcn", [ct.frd, ct.FRD, ct.FrequencyResponseData])
341336
def testMIMOSmooth(self, frd_fcn):

control/tests/iosys_test.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2288,7 +2288,6 @@ def test_signal_indexing():
22882288
resp.outputs['y[0]', 'u[0]']
22892289

22902290

2291-
@slycotonly
22922291
@pytest.mark.parametrize("fcn, spec, expected, missing", [
22932292
(ct.ss, {}, "states=4, outputs=3, inputs=2", r"dt|name"),
22942293
(ct.tf, {}, "outputs=3, inputs=2", r"dt|states|name"),

control/tests/lti_test.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,6 @@ def test_squeeze_exceptions(self, fcn):
309309
evalfr(sys, [[0.1j, 1j], [1j, 10j]])
310310

311311

312-
@slycotonly
313312
@pytest.mark.parametrize(
314313
"outdx, inpdx, key",
315314
[('y[0]', 'u[1]', (0, 1)),
@@ -356,7 +355,6 @@ def test_subsys_indexing(fcn, outdx, inpdx, key):
356355
subsys_chk.frequency_response(omega).response)
357356

358357

359-
@slycotonly
360358
@pytest.mark.parametrize("op", [
361359
'__mul__', '__rmul__', '__add__', '__radd__', '__sub__', '__rsub__'])
362360
@pytest.mark.parametrize("fcn", [ct.ss, ct.tf, ct.frd])

control/tests/matlab2_test.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ def MIMO_mats(self):
4949
D = zeros((2, 2))
5050
return A, B, C, D
5151

52-
@slycotonly
5352
def test_dcgain_mimo(self, MIMO_mats):
5453
"""Test function dcgain with MIMO systems"""
5554
#Test MIMO systems

control/tests/statesp_test.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,13 @@ def test_zero_siso(self, sys222):
248248

249249
np.testing.assert_almost_equal(true_z, z)
250250

251-
@slycotonly
252251
def test_zero_mimo_sys322_square(self, sys322):
253252
"""Evaluate the zeros of a square MIMO system."""
254253

255254
z = np.sort(sys322.zeros())
256255
true_z = np.sort([44.41465, -0.490252, -5.924398])
257256
np.testing.assert_array_almost_equal(z, true_z)
258257

259-
@slycotonly
260258
def test_zero_mimo_sys222_square(self, sys222):
261259
"""Evaluate the zeros of a square MIMO system."""
262260

@@ -320,7 +318,6 @@ def test_multiply_ss(self, sys222, sys322):
320318
np.testing.assert_array_almost_equal(sys.C, C)
321319
np.testing.assert_array_almost_equal(sys.D, D)
322320

323-
@slycotonly
324321
def test_add_sub_mimo_siso(self):
325322
# Test SS with SS
326323
ss_siso = StateSpace(
@@ -702,8 +699,6 @@ def test_call(self, dt, omega, resp):
702699
with pytest.raises(AttributeError):
703700
sys.evalfr(omega)
704701

705-
706-
@slycotonly
707702
def test_freq_resp(self):
708703
"""Evaluate the frequency response at multiple frequencies."""
709704

control/tests/xferfcn_test.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
import pytest
1111

1212
import control as ct
13-
from control import (StateSpace, TransferFunction, defaults, evalfr, isctime,
14-
isdtime, reset_defaults, rss, sample_system, set_defaults,
15-
ss, ss2tf, tf, tf2ss, zpk)
13+
from control import StateSpace, TransferFunction, defaults, evalfr, isctime, \
14+
isdtime, reset_defaults, rss, sample_system, set_defaults, ss, ss2tf, tf, \
15+
tf2ss, zpk
1616
from control.statesp import _convert_to_statespace
1717
from control.tests.conftest import slycotonly
1818
from control.xferfcn import _convert_to_transfer_function, _tf_close_coeff
@@ -186,7 +186,6 @@ def test_reverse_sign_siso(self):
186186
np.testing.assert_allclose(sys2.num, [[[-1., -3., -5.]]])
187187
np.testing.assert_allclose(sys2.den, [[[1., 6., 2., -1.]]])
188188

189-
@slycotonly
190189
def test_reverse_sign_mimo(self):
191190
"""Negate a MIMO system."""
192191
num1 = [[[1., 2.], [0., 3.], [2., -1.]],
@@ -228,7 +227,6 @@ def test_add_siso(self):
228227
np.testing.assert_allclose(sys3.num, [[[20., 4., -8]]])
229228
np.testing.assert_allclose(sys3.den, [[[1., 6., 1., -7., -2., 1.]]])
230229

231-
@slycotonly
232230
def test_add_mimo(self):
233231
"""Add two MIMO systems."""
234232
num1 = [[[1., 2.], [0., 3.], [2., -1.]],
@@ -276,7 +274,6 @@ def test_subtract_siso(self):
276274
np.testing.assert_allclose(sys4.num, [[[-2., -6., 12., 10., 2.]]])
277275
np.testing.assert_allclose(sys4.den, [[[1., 6., 1., -7., -2., 1.]]])
278276

279-
@slycotonly
280277
def test_subtract_mimo(self):
281278
"""Subtract two MIMO systems."""
282279
num1 = [[[1., 2.], [0., 3.], [2., -1.]],
@@ -327,7 +324,6 @@ def test_multiply_siso(self):
327324
np.testing.assert_allclose(sys3.num, sys4.num)
328325
np.testing.assert_allclose(sys3.den, sys4.den)
329326

330-
@slycotonly
331327
def test_multiply_mimo(self):
332328
"""Multiply two MIMO systems."""
333329
num1 = [[[1., 2.], [0., 3.], [2., -1.]],
@@ -714,7 +710,6 @@ def test_call_dtime(self):
714710
sys = TransferFunction([1., 3., 5], [1., 6., 2., -1], 0.1)
715711
np.testing.assert_array_almost_equal(sys(1j), -0.5 - 0.5j)
716712

717-
@slycotonly
718713
def test_call_mimo(self):
719714
"""Evaluate the frequency response of a MIMO system at one frequency."""
720715

@@ -755,7 +750,6 @@ def test_frequency_response_siso(self):
755750
np.testing.assert_array_almost_equal(phase, truephase)
756751
np.testing.assert_array_almost_equal(omega, trueomega)
757752

758-
@slycotonly
759753
def test_freqresp_mimo(self):
760754
"""Evaluate the MIMO magnitude and phase at multiple frequencies."""
761755
num = [[[1., 2.], [0., 3.], [2., -1.]],
@@ -852,7 +846,6 @@ def test_common_den_nonproper(self):
852846
_, den2, _ = tf2._common_den(allow_nonproper=True)
853847
np.testing.assert_array_almost_equal(den2, common_den_ref)
854848

855-
@slycotonly
856849
def test_pole_mimo(self):
857850
"""Test for correct MIMO poles."""
858851
sys = TransferFunction(
@@ -936,7 +929,6 @@ def test_append(self):
936929
tf_appended_2 = tf1.append(tf2).append(tf3)
937930
assert _tf_close_coeff(tf_exp_2, tf_appended_2)
938931

939-
@slycotonly
940932
def test_convert_to_transfer_function(self):
941933
"""Test for correct state space to transfer function conversion."""
942934
A = [[1., -2.], [-3., 4.]]
@@ -1023,7 +1015,6 @@ def test_state_space_conversion_mimo(self):
10231015
np.testing.assert_array_almost_equal(H.num[1][0], H2.num[1][0])
10241016
np.testing.assert_array_almost_equal(H.den[1][0], H2.den[1][0])
10251017

1026-
@slycotonly
10271018
def test_indexing(self):
10281019
"""Test TF scalar indexing and slice"""
10291020
tm = ss2tf(rss(5, 3, 3))
@@ -1213,7 +1204,6 @@ def test_printing_polynomial(self, args, outputfmt, var, dt, dtstring):
12131204
assert len(polystr[0].split('\n')) == 4
12141205
assert polystr[2] == outputfmt.format(var=var)
12151206

1216-
@slycotonly
12171207
def test_printing_mimo(self):
12181208
"""Print MIMO, continuous time"""
12191209
sys = ss2tf(rss(4, 2, 3))
@@ -1332,7 +1322,6 @@ def test_printing_zpk_mimo(self, num, den, output):
13321322
res = str(G)
13331323
assert res.partition('\n\n')[2] == output
13341324

1335-
@slycotonly
13361325
def test_size_mismatch(self):
13371326
"""Test size mismacht"""
13381327
sys1 = ss2tf(rss(2, 2, 2))

control/xferfcn.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,12 @@ def _convert_to_transfer_function(
15161516
den = [[[1.] for j in range(sys.ninputs)]
15171517
for i in range(sys.noutputs)]
15181518
else:
1519+
# Preallocate numerator and denominator arrays
1520+
num = [[[] for j in range(sys.ninputs)]
1521+
for i in range(sys.noutputs)]
1522+
den = [[[] for j in range(sys.ninputs)]
1523+
for i in range(sys.noutputs)]
1524+
15191525
try:
15201526
# Use Slycot to make the transformation
15211527
# Make sure to convert system matrices to numpy arrays
@@ -1524,12 +1530,6 @@ def _convert_to_transfer_function(
15241530
sys.nstates, sys.ninputs, sys.noutputs, array(sys.A),
15251531
array(sys.B), array(sys.C), array(sys.D), tol1=0.0)
15261532

1527-
# Preallocate outputs.
1528-
num = [[[] for j in range(sys.ninputs)]
1529-
for i in range(sys.noutputs)]
1530-
den = [[[] for j in range(sys.ninputs)]
1531-
for i in range(sys.noutputs)]
1532-
15331533
for i in range(sys.noutputs):
15341534
for j in range(sys.ninputs):
15351535
num[i][j] = list(tfout[6][i, j, :])
@@ -1538,16 +1538,13 @@ def _convert_to_transfer_function(
15381538
den[i][j] = list(tfout[5][i, :])
15391539

15401540
except ImportError:
1541-
# If slycot is not available, use signal.lti (SISO only)
1542-
if sys.ninputs != 1 or sys.noutputs != 1:
1543-
raise ControlMIMONotImplemented("Not implemented for " +
1544-
"MIMO systems without slycot.")
1545-
1546-
# Do the conversion using sp.signal.ss2tf
1547-
# Note that this returns a 2D array for the numerator
1548-
num, den = sp.signal.ss2tf(sys.A, sys.B, sys.C, sys.D)
1549-
num = squeeze(num) # Convert to 1D array
1550-
den = squeeze(den) # Probably not needed
1541+
# If slycot not available, do conversion using sp.signal.ss2tf
1542+
for j in range(sys.ninputs):
1543+
num_j, den_j = sp.signal.ss2tf(
1544+
sys.A, sys.B, sys.C, sys.D, input=j)
1545+
for i in range(sys.noutputs):
1546+
num[i][j] = num_j[i]
1547+
den[i][j] = den_j
15511548

15521549
newsys = TransferFunction(num, den, sys.dt)
15531550
if use_prefix_suffix:

0 commit comments

Comments
 (0)