Skip to content

Enable scalar division of state-space objects #811

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions control/frdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,10 +408,6 @@ def __truediv__(self, other):
smooth=(self.ifunc is not None) and
(other.ifunc is not None))

# TODO: Remove when transition to python3 complete
def __div__(self, other):
return self.__truediv__(other)

# TODO: Division of MIMO transfer function objects is not written yet.
def __rtruediv__(self, other):
"""Right divide two LTI objects."""
Expand All @@ -429,10 +425,6 @@ def __rtruediv__(self, other):

return other / self

# TODO: Remove when transition to python3 complete
def __rdiv__(self, other):
return self.__rtruediv__(other)

def __pow__(self, other):
if not type(other) == int:
raise ValueError("Exponent must be an integer")
Expand Down
13 changes: 13 additions & 0 deletions control/iosys.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,19 @@ def __neg__(sys):
# Return the newly created system
return newsys

def __truediv__(sys2, sys1):
"""Division of input/output systems

Only division by scalars and arrays of scalars is supported"""
# Note: order of arguments is flipped so that self = sys2,
# corresponding to the ordering convention of sys2 * sys1

if not isinstance(sys1, (LTI, NamedIOSystem)):
return sys2 * (1/sys1)
else:
return NotImplemented


# Update parameters used for _rhs, _out (used by subclasses)
def _update_params(self, params, warning=False):
if warning:
Expand Down
21 changes: 11 additions & 10 deletions control/statesp.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
from .frdata import FrequencyResponseData
from .lti import LTI, _process_frequency_response
from .namedio import common_timebase, isdtime, _process_namedio_keywords, \
_process_dt_keyword
_process_dt_keyword, NamedIOSystem
from . import config
from copy import deepcopy

Expand Down Expand Up @@ -794,17 +794,18 @@ def __rmul__(self, other):
pass
raise TypeError("can't interconnect systems")

# TODO: __div__ and __rdiv__ are not written yet.
def __div__(self, other):
"""Divide two LTI systems."""
# TODO: general __truediv__, and __rtruediv__; requires descriptor system support
def __truediv__(self, other):
"""Division of StateSpace systems

raise NotImplementedError("StateSpace.__div__ is not implemented yet.")

def __rdiv__(self, other):
"""Right divide two LTI systems."""
Only division by TFs, FRDs, scalars, and arrays of scalars is
supported.
"""
if not isinstance(other, (LTI, NamedIOSystem)):
return self * (1/other)
else:
return NotImplemented

raise NotImplementedError(
"StateSpace.__rdiv__ is not implemented yet.")

def __call__(self, x, squeeze=None, warn_infinite=True):
"""Evaluate system's transfer function at complex frequency.
Expand Down
11 changes: 11 additions & 0 deletions control/tests/statesp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,17 @@ def test_multiply_ss(self, sys222, sys322):
np.testing.assert_array_almost_equal(sys.C, C)
np.testing.assert_array_almost_equal(sys.D, D)

@pytest.mark.parametrize("k", [2, -3.141, np.float32(2.718), np.array([[4.321], [5.678]])])
def test_truediv_ss_scalar(self, sys322, k):
"""Divide SS by scalar."""
sys = sys322 / k
syscheck = sys322 * (1/k)

np.testing.assert_array_almost_equal(sys.A, syscheck.A)
np.testing.assert_array_almost_equal(sys.B, syscheck.B)
np.testing.assert_array_almost_equal(sys.C, syscheck.C)
np.testing.assert_array_almost_equal(sys.D, syscheck.D)

@pytest.mark.parametrize("omega, resp",
[(1.,
np.array([[ 4.37636761e-05-0.01522976j,
Expand Down
6 changes: 3 additions & 3 deletions control/tests/type_conversion_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@ def sys_dict():
('mul', 'flt', ['ss', 'tf', 'frd', 'lio', 'ios', 'arr', 'flt']),

# op left ss tf frd lio ios arr flt
('truediv', 'ss', ['xs', 'tf', 'frd', 'xio', 'xos', 'xs', 'xs' ]),
('truediv', 'ss', ['xs', 'tf', 'frd', 'xio', 'xos', 'ss', 'ss' ]),
('truediv', 'tf', ['tf', 'tf', 'xrd', 'tf', 'xos', 'tf', 'tf' ]),
('truediv', 'frd', ['frd', 'frd', 'frd', 'frd', 'E', 'frd', 'frd']),
('truediv', 'lio', ['xio', 'tf', 'frd', 'xio', 'xio', 'xio', 'xio']),
('truediv', 'ios', ['xos', 'xos', 'E', 'xos', 'xos' 'xos', 'xos']),
('truediv', 'lio', ['xio', 'tf', 'frd', 'xio', 'xio', 'lio', 'lio']),
('truediv', 'ios', ['xos', 'xos', 'E', 'xos', 'xos', 'ios', 'ios']),
('truediv', 'arr', ['xs', 'tf', 'frd', 'xio', 'xos', 'arr', 'arr']),
('truediv', 'flt', ['xs', 'tf', 'frd', 'xio', 'xos', 'arr', 'flt'])]

Expand Down
8 changes: 0 additions & 8 deletions control/xferfcn.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,10 +702,6 @@ def __truediv__(self, other):

return TransferFunction(num, den, dt)

# TODO: Remove when transition to python3 complete
def __div__(self, other):
return TransferFunction.__truediv__(self, other)

# TODO: Division of MIMO transfer function objects is not written yet.
def __rtruediv__(self, other):
"""Right divide two LTI objects."""
Expand All @@ -724,10 +720,6 @@ def __rtruediv__(self, other):

return other / self

# TODO: Remove when transition to python3 complete
def __rdiv__(self, other):
return TransferFunction.__rtruediv__(self, other)

def __pow__(self, other):
if not type(other) == int:
raise ValueError("Exponent must be an integer")
Expand Down