GeezerVison is showing github.com — TransferFunction array priority plus system type conversion checking by murrayrm · Pull Request #498 · python-control/python-control
TransferFunction array priority plus system type conversion checking
#498
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
Uh oh!
There was an error while loading. Please reload this page.
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
bb8132e
dev instructions
sawyerbfuller Dec 21, 2020
51612df
Merge branch 'master' of https://github.com/sawyerbfuller/python-control
murrayrm Jan 6, 2021
4ad565f
Merge branch 'call-method' of https://github.com/sawyerbfuller/python…
murrayrm Jan 7, 2021
85c9e7f
Merge branch 'master' of https://github.com/python-control/python-con…
murrayrm Jan 8, 2021
8793d78
set array_priority=11 for TransferFunction, to match StateSpace
murrayrm Jan 3, 2021
bd77d71
update _convert_to_transfer_function to allow 0D and 1D arrays
murrayrm Jan 3, 2021
4b7bf8a
rename _convertToX to _convert_to_x + statesp/ndarray unit tests
murrayrm Jan 3, 2021
94b6209
fix converstion exceptions to be TypeError
murrayrm Jan 4, 2021
67a0561
add unit tests for checking type converstions
murrayrm Jan 4, 2021
4a24c84
update LinearIOSystem.__rmul__ for Python2/Python3 consistency
murrayrm Jan 4, 2021
f0593fa
add (skipped) function for desired binary operator conversions
murrayrm Jan 5, 2021
e910c22
Update control/tests/type_conversion_test.py
murrayrm Jan 5, 2021
File filter
Filter by extension
.py
(12)
.rst
(1)
All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Hide whitespace
Diff view
Hide whitespace
There are no files selected for viewing
Original file line number
Diff line number
Diff line change
Expand Up
@@ -242,9 +242,9 @@ def feedback(sys1, sys2=1, sign=-1):
if isinstance(sys2, tf.TransferFunction):
sys1 = tf._convert_to_transfer_function(sys1)
elif isinstance(sys2, ss.StateSpace):
sys1 = ss._convertToStateSpace (sys1)
sys1 = ss._convert_to_statespace (sys1)
elif isinstance(sys2, frd.FRD):
sys1 = frd._convertToFRD (sys1, sys2.omega)
sys1 = frd._convert_to_FRD (sys1, sys2.omega)
else: # sys2 is a scalar.
sys1 = tf._convert_to_transfer_function(sys1)
sys2 = tf._convert_to_transfer_function(sys2)
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -47,7 +47,7 @@
"""
from .lti import isctime
from .statesp import StateSpace, _convertToStateSpace
from .statesp import StateSpace
__all__ = ['sample_system', 'c2d']
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -197,7 +197,7 @@ def __add__(self, other):
# Convert the second argument to a frequency response function.
# or re-base the frd to the current omega (if needed)
other = _convertToFRD (other, omega=self.omega)
other = _convert_to_FRD (other, omega=self.omega)
# Check that the input-output sizes are consistent.
if self.inputs != other.inputs:
Expand Down
Expand Up
@@ -232,7 +232,7 @@ def __mul__(self, other):
return FRD(self.fresp * other, self.omega,
smooth=(self.ifunc is not None))
else:
other = _convertToFRD (other, omega=self.omega)
other = _convert_to_FRD (other, omega=self.omega)
# Check that the input-output sizes are consistent.
if self.inputs != other.outputs:
Expand All
@@ -259,7 +259,7 @@ def __rmul__(self, other):
return FRD(self.fresp * other, self.omega,
smooth=(self.ifunc is not None))
else:
other = _convertToFRD (other, omega=self.omega)
other = _convert_to_FRD (other, omega=self.omega)
# Check that the input-output sizes are consistent.
if self.outputs != other.inputs:
Expand Down
Expand Up
@@ -287,7 +287,7 @@ def __truediv__(self, other):
return FRD(self.fresp * (1/other), self.omega,
smooth=(self.ifunc is not None))
else:
other = _convertToFRD (other, omega=self.omega)
other = _convert_to_FRD (other, omega=self.omega)
if (self.inputs > 1 or self.outputs > 1 or
other.inputs > 1 or other.outputs > 1):
Expand All
@@ -310,7 +310,7 @@ def __rtruediv__(self, other):
return FRD(other / self.fresp, self.omega,
smooth=(self.ifunc is not None))
else:
other = _convertToFRD (other, omega=self.omega)
other = _convert_to_FRD (other, omega=self.omega)
if (self.inputs > 1 or self.outputs > 1 or
other.inputs > 1 or other.outputs > 1):
Expand Down
Expand Up
@@ -450,7 +450,7 @@ def freqresp(self, omega):
def feedback(self, other=1, sign=-1):
"""Feedback interconnection between two FRD objects."""
other = _convertToFRD (other, omega=self.omega)
other = _convert_to_FRD (other, omega=self.omega)
if (self.outputs != other.inputs or self.inputs != other.outputs):
raise ValueError(
Expand Down
Expand Up
@@ -486,7 +486,7 @@ def feedback(self, other=1, sign=-1):
FRD = FrequencyResponseData
def _convertToFRD (sys, omega, inputs=1, outputs=1):
def _convert_to_FRD (sys, omega, inputs=1, outputs=1):
"""Convert a system to frequency response data form (if needed).
If sys is already an frd, and its frequency range matches or
Expand All
@@ -496,8 +496,8 @@ def _convertToFRD(sys, omega, inputs=1, outputs=1):
scalar, then the number of inputs and outputs can be specified
manually, as in:
>>> frd = _convertToFRD (3., omega) # Assumes inputs = outputs = 1
>>> frd = _convertToFRD (1., omegs, inputs=3, outputs=2)
>>> frd = _convert_to_FRD (3., omega) # Assumes inputs = outputs = 1
>>> frd = _convert_to_FRD (1., omegs, inputs=3, outputs=2)
In the latter example, sys's matrix transfer function is [[1., 1., 1.]
[1., 1., 1.]].
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -220,7 +220,7 @@ def __mul__(sys2, sys1):
raise NotImplemented("Matrix multiplication not yet implemented")
elif not isinstance(sys1, InputOutputSystem):
raise ValueError ("Unknown I/O system object ", sys1)
raise TypeError ("Unknown I/O system object ", sys1)
# Make sure systems can be interconnected
if sys1.noutputs != sys2.ninputs:
Expand Down
Expand Up
@@ -254,20 +254,24 @@ def __mul__(sys2, sys1):
def __rmul__(sys1, sys2):
"""Pre-multiply an input/output systems by a scalar/matrix"""
if isinstance(sys2, (int, float, np.number)):
if isinstance(sys2, InputOutputSystem):
# Both systems are InputOutputSystems => use __mul__
return InputOutputSystem.__mul__(sys2, sys1)
elif isinstance(sys2, (int, float, np.number)):
# TODO: Scale the output
raise NotImplemented("Scalar multiplication not yet implemented")
elif isinstance(sys2, np.ndarray):
# TODO: Post-multiply by a matrix
raise NotImplemented("Matrix multiplication not yet implemented")
elif not isinstance(sys2, InputOutputSystem):
raise ValueError("Unknown I/O system object ", sys1)
elif isinstance(sys2, StateSpace):
# TODO: Should eventuall preserve LinearIOSystem structure
return StateSpace.__mul__(sys2, sys1)
else:
# Both systems are InputOutputSystems => use __mul__
return InputOutputSystem.__mul__(sys2, sys1)
raise TypeError("Unknown I/O system object ", sys1)
def __add__(sys1, sys2):
"""Add two input/output systems (parallel interconnection)"""
Expand All
@@ -281,7 +285,7 @@ def __add__(sys1, sys2):
raise NotImplemented("Matrix addition not yet implemented")
elif not isinstance(sys2, InputOutputSystem):
raise ValueError ("Unknown I/O system object ", sys2)
raise TypeError ("Unknown I/O system object ", sys2)
# Make sure number of input and outputs match
if sys1.ninputs != sys2.ninputs or sys1.noutputs != sys2.noutputs:
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -10,7 +10,7 @@
# Python 3 compatibility (needs to go here)
from __future__ import print_function
from __future__ import division # for _convertToStateSpace
from __future__ import division # for _convert_to_statespace
"""Copyright (c) 2010 by California Institute of Technology
All rights reserved.
Expand Down
Expand Up
@@ -527,7 +527,7 @@ def __add__(self, other):
D = self.D + other
dt = self.dt
else:
other = _convertToStateSpace (other)
other = _convert_to_statespace (other)
# Check to make sure the dimensions are OK
if ((self.inputs != other.inputs) or
Expand Down
Expand Up
@@ -577,7 +577,7 @@ def __mul__(self, other):
D = self.D * other
dt = self.dt
else:
other = _convertToStateSpace (other)
other = _convert_to_statespace (other)
# Check to make sure the dimensions are OK
if self.inputs != other.outputs:
Expand Down
Expand Up
@@ -614,7 +614,7 @@ def __rmul__(self, other):
# is lti, and convertible?
if isinstance(other, LTI):
return _convertToStateSpace (other) * self
return _convert_to_statespace (other) * self
# try to treat this as a matrix
try:
Expand Down
Expand Up
@@ -839,7 +839,7 @@ def zero(self):
def feedback(self, other=1, sign=-1):
"""Feedback interconnection between two LTI systems."""
other = _convertToStateSpace (other)
other = _convert_to_statespace (other)
# Check to make sure the dimensions are OK
if (self.inputs != other.outputs) or (self.outputs != other.inputs):
Expand Down
Expand Up
@@ -907,7 +907,7 @@ def lft(self, other, nu=-1, ny=-1):
Dimension of (plant) control input.
"""
other = _convertToStateSpace (other)
other = _convert_to_statespace (other)
# maximal values for nu, ny
if ny == -1:
ny = min(other.inputs, self.outputs)
Expand Down
Expand Up
@@ -1061,7 +1061,7 @@ def append(self, other):
The second model is converted to state-space if necessary, inputs and
outputs are appended and their order is preserved"""
if not isinstance(other, StateSpace):
other = _convertToStateSpace (other)
other = _convert_to_statespace (other)
self.dt = common_timebase(self.dt, other.dt)
Expand Down
Expand Up
@@ -1186,16 +1186,16 @@ def is_static_gain(self):
# TODO: add discrete time check
def _convertToStateSpace (sys, **kw):
def _convert_to_statespace (sys, **kw):
"""Convert a system to state space form (if needed).
If sys is already a state space, then it is returned. If sys is a
transfer function object, then it is converted to a state space and
returned. If sys is a scalar, then the number of inputs and outputs can
be specified manually, as in:
>>> sys = _convertToStateSpace (3.) # Assumes inputs = outputs = 1
>>> sys = _convertToStateSpace (1., inputs=3, outputs=2)
>>> sys = _convert_to_statespace (3.) # Assumes inputs = outputs = 1
>>> sys = _convert_to_statespace (1., inputs=3, outputs=2)
In the latter example, A = B = C = 0 and D = [[1., 1., 1.]
[1., 1., 1.]].
Expand All
@@ -1205,7 +1205,7 @@ def _convertToStateSpace(sys, **kw):
if isinstance(sys, StateSpace):
if len(kw):
raise TypeError("If sys is a StateSpace, _convertToStateSpace "
raise TypeError("If sys is a StateSpace, _convert_to_statespace "
"cannot take keywords.")
# Already a state space system; just return it
Expand All
@@ -1221,7 +1221,7 @@ def _convertToStateSpace(sys, **kw):
from slycot import td04ad
if len(kw):
raise TypeError("If sys is a TransferFunction, "
"_convertToStateSpace cannot take keywords.")
"_convert_to_statespace cannot take keywords.")
# Change the numerator and denominator arrays so that the transfer
# function matrix has a common denominator.
Expand Down
Expand Up
@@ -1281,11 +1281,8 @@ def _convertToStateSpace(sys, **kw):
try:
D = _ssmatrix(sys)
return StateSpace([], [], [], D)
except Exception as e:
print("Failure to assume argument is matrix-like in"
" _convertToStateSpace, result %s" % e)
raise TypeError("Can't convert given type to StateSpace system.")
except:
raise TypeError("Can't convert given type to StateSpace system.")
# TODO: add discrete time option
Expand Down
Expand Up
@@ -1662,14 +1659,14 @@ def tf2ss(*args):
from .xferfcn import TransferFunction
if len(args) == 2 or len(args) == 3:
# Assume we were given the num, den
return _convertToStateSpace (TransferFunction(*args))
return _convert_to_statespace (TransferFunction(*args))
elif len(args) == 1:
sys = args[0]
if not isinstance(sys, TransferFunction):
raise TypeError("tf2ss(sys): sys must be a TransferFunction "
"object.")
return _convertToStateSpace (sys)
return _convert_to_statespace (sys)
else:
raise ValueError("Needs 1 or 2 arguments; received %i." % len(args))
Expand Down
Expand Up
@@ -1769,5 +1766,5 @@ def ssdata(sys):
(A, B, C, D): list of matrices
State space data for the system
"""
ss = _convertToStateSpace (sys)
ss = _convert_to_statespace (sys)
return ss.A, ss.B, ss.C, ss.D
Original file line number
Diff line number
Diff line change
Expand Up
@@ -255,7 +255,7 @@ def test_feedback_args(self, tsys):
ctrl.feedback(*args)
# If second argument is not LTI or convertable, generate an exception
args = (tsys.sys1, np.array([1]) )
args = (tsys.sys1, 'hello world' )
with pytest.raises(TypeError):
ctrl.feedback(*args)
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -12,7 +12,7 @@
import control as ct
from control.statesp import StateSpace
from control.xferfcn import TransferFunction
from control.frdata import FRD, _convertToFRD , FrequencyResponseData
from control.frdata import FRD, _convert_to_FRD , FrequencyResponseData
from control import bdalg, evalfr, freqplot
from control.tests.conftest import slycotonly
Expand Down
Expand Up
@@ -174,9 +174,9 @@ def testFeedback2(self):
def testAuto(self):
omega = np.logspace(-1, 2, 10)
f1 = _convertToFRD (1, omega)
f2 = _convertToFRD (np.array([[1, 0], [0.1, -1]]), omega)
f2 = _convertToFRD ([[1, 0], [0.1, -1]], omega)
f1 = _convert_to_FRD (1, omega)
f2 = _convert_to_FRD (np.array([[1, 0], [0.1, -1]]), omega)
f2 = _convert_to_FRD ([[1, 0], [0.1, -1]], omega)
f1, f2 # reference to avoid pyflakes error
def testNyquist(self):
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -9,14 +9,16 @@
import numpy as np
import pytest
import operator
from numpy.linalg import solve
from scipy.linalg import block_diag, eigvals
import control as ct
from control.config import defaults
from control.dtime import sample_system
from control.lti import evalfr
from control.statesp import (StateSpace, _convertToStateSpace , drss, rss, ss ,
tf2ss, _statesp_defaults)
from control.statesp import (StateSpace, _convert_to_statespace , drss,
rss, ss, tf2ss, _statesp_defaults)
from control.tests.conftest import ismatarrayout, slycotonly
from control.xferfcn import TransferFunction, ss2tf
Expand Down
Expand Up
@@ -224,7 +226,7 @@ def test_pole(self, sys322):
def test_zero_empty(self):
"""Test to make sure zero() works with no zeros in system."""
sys = _convertToStateSpace (TransferFunction([1], [1, 2, 1]))
sys = _convert_to_statespace (TransferFunction([1], [1, 2, 1]))
np.testing.assert_array_equal(sys.zero(), np.array([]))
@slycotonly
Expand Down
Expand Up
@@ -456,7 +458,7 @@ def test_append_tf(self):
s = TransferFunction([1, 0], [1])
h = 1 / (s + 1) / (s + 2)
sys1 = StateSpace(A1, B1, C1, D1)
sys2 = _convertToStateSpace (h)
sys2 = _convert_to_statespace (h)
sys3c = sys1.append(sys2)
np.testing.assert_array_almost_equal(sys1.A, sys3c.A[:3, :3])
np.testing.assert_array_almost_equal(sys1.B, sys3c.B[:3, :2])
Expand Down
Expand Up
@@ -625,10 +627,10 @@ def test_empty(self):
assert 0 == g1.outputs
def test_matrix_to_state_space(self):
"""_convertToStateSpace (matrix) gives ss([],[],[],D)"""
"""_convert_to_statespace (matrix) gives ss([],[],[],D)"""
with pytest.deprecated_call():
D = np.matrix([[1, 2, 3], [4, 5, 6]])
g = _convertToStateSpace (D)
g = _convert_to_statespace (D)
np.testing.assert_array_equal(np.empty((0, 0)), g.A)
np.testing.assert_array_equal(np.empty((0, D.shape[1])), g.B)
Expand Down
Expand Up
@@ -927,3 +929,24 @@ def test_latex_repr(gmats, ref, repr_type, num_format, editsdefaults):
g = StateSpace(*gmats)
refkey = "{}_{}".format(refkey_n[num_format], refkey_r[repr_type])
assert g._repr_latex_() == ref[refkey]
@pytest.mark.parametrize(
"op",
[pytest.param(getattr(operator, s), id=s) for s in ('add', 'sub', 'mul')])
@pytest.mark.parametrize(
"tf, arr",
[pytest.param(ct.tf([1], [0.5, 1]), np.array(2.), id="0D scalar"),
pytest.param(ct.tf([1], [0.5, 1]), np.array([2.]), id="1D scalar"),
pytest.param(ct.tf([1], [0.5, 1]), np.array([[2.]]), id="2D scalar")])
def test_xferfcn_ndarray_precedence(op, tf, arr):
# Apply the operator to the transfer function and array
ss = ct.tf2ss(tf)
result = op(ss, arr)
assert isinstance(result, ct.StateSpace)
# Apply the operator to the array and transfer function
ss = ct.tf2ss(tf)
result = op(arr, ss)
assert isinstance(result, ct.StateSpace)
Loading
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.