Skip to content

Commit 8b6d041

Browse files
authored
fix issue with multiplying MIMO LTI system by scalar (#1078)
1 parent e2c0ff8 commit 8b6d041

File tree

3 files changed

+39
-11
lines changed

3 files changed

+39
-11
lines changed

control/frdata.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,14 +261,20 @@ def __init__(self, *args, **kwargs):
261261

262262
# create interpolation functions
263263
if smooth:
264+
# Set the order of the fit
265+
if self.omega.size < 2:
266+
raise ValueError("can't smooth with only 1 frequency")
267+
degree = 3 if self.omega.size > 3 else self.omega.size - 1
268+
264269
self.ifunc = empty((self.fresp.shape[0], self.fresp.shape[1]),
265270
dtype=tuple)
266271
for i in range(self.fresp.shape[0]):
267272
for j in range(self.fresp.shape[1]):
268273
self.ifunc[i, j], u = splprep(
269274
u=self.omega, x=[real(self.fresp[i, j, :]),
270275
imag(self.fresp[i, j, :])],
271-
w=1.0/(absolute(self.fresp[i, j, :]) + 0.001), s=0.0)
276+
w=1.0/(absolute(self.fresp[i, j, :]) + 0.001),
277+
s=0.0, k=degree)
272278
else:
273279
self.ifunc = None
274280

@@ -392,7 +398,12 @@ def __add__(self, other):
392398

393399
# Convert the second argument to a frequency response function.
394400
# or re-base the frd to the current omega (if needed)
395-
other = _convert_to_frd(other, omega=self.omega)
401+
if isinstance(other, (int, float, complex, np.number)):
402+
other = _convert_to_frd(
403+
other, omega=self.omega,
404+
inputs=self.ninputs, outputs=self.noutputs)
405+
else:
406+
other = _convert_to_frd(other, omega=self.omega)
396407

397408
# Check that the input-output sizes are consistent.
398409
if self.ninputs != other.ninputs:

control/tests/lti_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,21 @@ def test_subsys_indexing(fcn, outdx, inpdx, key):
350350
np.testing.assert_almost_equal(
351351
subsys_fcn.frequency_response(omega).response,
352352
subsys_chk.frequency_response(omega).response)
353+
354+
355+
@slycotonly
356+
@pytest.mark.parametrize("op", [
357+
'__mul__', '__rmul__', '__add__', '__radd__', '__sub__', '__rsub__'])
358+
@pytest.mark.parametrize("fcn", [ct.ss, ct.tf, ct.frd])
359+
def test_scalar_algebra(op, fcn):
360+
sys_ss = ct.rss(4, 2, 2)
361+
match fcn:
362+
case ct.ss:
363+
sys = sys_ss
364+
case ct.tf:
365+
sys = ct.tf(sys_ss)
366+
case ct.frd:
367+
sys = ct.frd(sys_ss, [0.1, 1, 10])
368+
369+
scaled = getattr(sys, op)(2)
370+
np.testing.assert_almost_equal(getattr(sys(1j), op)(2), scaled(1j))

control/xferfcn.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -634,11 +634,11 @@ def __mul__(self, other):
634634
from .statesp import StateSpace
635635

636636
# Convert the second argument to a transfer function.
637-
if isinstance(other, StateSpace):
637+
if isinstance(other, (StateSpace, np.ndarray)):
638638
other = _convert_to_transfer_function(other)
639-
elif isinstance(other, (int, float, complex, np.number, np.ndarray)):
640-
other = _convert_to_transfer_function(other, inputs=self.ninputs,
641-
outputs=self.noutputs)
639+
elif isinstance(other, (int, float, complex, np.number)):
640+
# Multiply by a scaled identity matrix (transfer function)
641+
other = _convert_to_transfer_function(np.eye(self.ninputs) * other)
642642
if not isinstance(other, TransferFunction):
643643
return NotImplemented
644644

@@ -681,8 +681,8 @@ def __rmul__(self, other):
681681

682682
# Convert the second argument to a transfer function.
683683
if isinstance(other, (int, float, complex, np.number)):
684-
other = _convert_to_transfer_function(other, inputs=self.ninputs,
685-
outputs=self.ninputs)
684+
# Multiply by a scaled identity matrix (transfer function)
685+
other = _convert_to_transfer_function(np.eye(self.noutputs) * other)
686686
else:
687687
other = _convert_to_transfer_function(other)
688688

@@ -723,9 +723,8 @@ def __truediv__(self, other):
723723
"""Divide two LTI objects."""
724724

725725
if isinstance(other, (int, float, complex, np.number)):
726-
other = _convert_to_transfer_function(
727-
other, inputs=self.ninputs,
728-
outputs=self.ninputs)
726+
# Multiply by a scaled identity matrix (transfer function)
727+
other = _convert_to_transfer_function(np.eye(self.ninputs) * other)
729728
else:
730729
other = _convert_to_transfer_function(other)
731730

0 commit comments

Comments
 (0)