GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

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.

Sign up for GitHub

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
murrayrm merged 12 commits into python-control:master from murrayrm:array_priority+system_composition
Jan 8, 2021

Uh oh!

There was an error while loading. Please reload this page.

Merged

TransferFunction array priority plus system type conversion checking #498

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

Clear filters
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
Diff view

There are no files selected for viewing

4 changes: 2 additions & 2 deletions 4 control/bdalg.py
Show comments
View file
Open in desktop
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
2 changes: 1 addition & 1 deletion 2 control/dtime.py
Show comments
View file
Open in desktop
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
18 changes: 9 additions & 9 deletions 18 control/frdata.py
Show comments
View file
Open in desktop
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
18 changes: 11 additions & 7 deletions 18 control/iosys.py
Show comments
View file
Open in desktop
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
37 changes: 17 additions & 20 deletions 37 control/statesp.py
Show comments
View file
Open in desktop
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
2 changes: 1 addition & 1 deletion 2 control/tests/bdalg_test.py
Show comments
View file
Open in desktop
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
8 changes: 4 additions & 4 deletions 8 control/tests/frd_test.py
Show comments
View file
Open in desktop
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
35 changes: 29 additions & 6 deletions 35 control/tests/statesp_test.py
Show comments
View file
Open in desktop
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.