GeezerVison is showing github.com — Switch LTI class and subclasses to use ninputs, noutputs, nstates by murrayrm · Pull Request #515 · python-control/python-control
Switch LTI class and subclasses to use ninputs, noutputs, nstates
#515
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
File filter
Filter by extension
.py
(21)
All 1 file type 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
@@ -76,7 +76,7 @@ def series(sys1, *sysn):
Raises
------
ValueError
if `sys2.inputs ` does not equal `sys1.outputs `
if `sys2.ninputs ` does not equal `sys1.noutputs `
if `sys1.dt` is not compatible with `sys2.dt`
See Also
Expand Down
Expand Up
@@ -336,25 +336,25 @@ def connect(sys, Q, inputv, outputv):
"""
inputv, outputv, Q = np.asarray(inputv), np.asarray(outputv), np.asarray(Q)
# check indices
index_errors = (inputv - 1 > sys.inputs ) | (inputv < 1)
index_errors = (inputv - 1 > sys.ninputs ) | (inputv < 1)
if np.any(index_errors):
raise IndexError(
"inputv index %s out of bounds" % inputv[np.where(index_errors)])
index_errors = (outputv - 1 > sys.outputs ) | (outputv < 1)
index_errors = (outputv - 1 > sys.noutputs ) | (outputv < 1)
if np.any(index_errors):
raise IndexError(
"outputv index %s out of bounds" % outputv[np.where(index_errors)])
index_errors = (Q[:,0:1] - 1 > sys.inputs ) | (Q[:,0:1] < 1)
index_errors = (Q[:,0:1] - 1 > sys.ninputs ) | (Q[:,0:1] < 1)
if np.any(index_errors):
raise IndexError(
"Q input index %s out of bounds" % Q[np.where(index_errors)])
index_errors = (np.abs(Q[:,1:]) - 1 > sys.outputs )
index_errors = (np.abs(Q[:,1:]) - 1 > sys.noutputs )
if np.any(index_errors):
raise IndexError(
"Q output index %s out of bounds" % Q[np.where(index_errors)])
# first connect
K = np.zeros((sys.inputs , sys.outputs ))
K = np.zeros((sys.ninputs , sys.noutputs ))
for r in np.array(Q).astype(int):
inp = r[0]-1
for outp in r[1:]:
Expand All
@@ -365,8 +365,8 @@ def connect(sys, Q, inputv, outputv):
sys = sys.feedback(np.array(K), sign=1)
# now trim
Ytrim = np.zeros((len(outputv), sys.outputs ))
Utrim = np.zeros((sys.inputs , len(inputv)))
Ytrim = np.zeros((len(outputv), sys.noutputs ))
Utrim = np.zeros((sys.ninputs , len(inputv)))
for i,u in enumerate(inputv):
Utrim[u-1,i] = 1.
for i,y in enumerate(outputv):
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -79,24 +79,24 @@ def reachable_form(xsys):
zsys.B[0, 0] = 1.0
zsys.A = zeros_like(xsys.A)
Apoly = poly(xsys.A) # characteristic polynomial
for i in range(0, xsys.states ):
for i in range(0, xsys.nstates ):
zsys.A[0, i] = -Apoly[i+1] / Apoly[0]
if (i+1 < xsys.states ):
if (i+1 < xsys.nstates ):
zsys.A[i+1, i] = 1.0
# Compute the reachability matrices for each set of states
Wrx = ctrb(xsys.A, xsys.B)
Wrz = ctrb(zsys.A, zsys.B)
if matrix_rank(Wrx) != xsys.states :
if matrix_rank(Wrx) != xsys.nstates :
raise ValueError("System not controllable to working precision.")
# Transformation from one form to another
Tzx = solve(Wrx.T, Wrz.T).T # matrix right division, Tzx = Wrz * inv(Wrx)
# Check to make sure inversion was OK. Note that since we are inverting
# Wrx and we already checked its rank, this exception should never occur
if matrix_rank(Tzx) != xsys.states : # pragma: no cover
if matrix_rank(Tzx) != xsys.nstates : # pragma: no cover
raise ValueError("Transformation matrix singular to working precision.")
# Finally, compute the output matrix
Expand Down
Expand Up
@@ -133,9 +133,9 @@ def observable_form(xsys):
zsys.C[0, 0] = 1
zsys.A = zeros_like(xsys.A)
Apoly = poly(xsys.A) # characteristic polynomial
for i in range(0, xsys.states ):
for i in range(0, xsys.nstates ):
zsys.A[i, 0] = -Apoly[i+1] / Apoly[0]
if (i+1 < xsys.states ):
if (i+1 < xsys.nstates ):
zsys.A[i, i+1] = 1
# Compute the observability matrices for each set of states
Expand All
@@ -145,7 +145,7 @@ def observable_form(xsys):
# Transformation from one form to another
Tzx = solve(Wrz, Wrx) # matrix left division, Tzx = inv(Wrz) * Wrx
if matrix_rank(Tzx) != xsys.states :
if matrix_rank(Tzx) != xsys.nstates :
raise ValueError("Transformation matrix singular to working precision.")
# Finally, compute the output matrix
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -155,11 +155,11 @@ def __init__(self, *args, **kwargs):
def __str__(self):
"""String representation of the transfer function."""
mimo = self.inputs > 1 or self.outputs > 1
mimo = self.ninputs > 1 or self.noutputs > 1
outstr = ['Frequency response data']
for i in range(self.inputs ):
for j in range(self.outputs ):
for i in range(self.ninputs ):
for j in range(self.noutputs ):
if mimo:
outstr.append("Input %i to output %i:" % (i + 1, j + 1))
outstr.append('Freq [rad/s] Response')
Expand Down
Expand Up
@@ -201,12 +201,12 @@ def __add__(self, other):
other = _convert_to_FRD(other, omega=self.omega)
# Check that the input-output sizes are consistent.
if self.inputs != other.inputs :
if self.ninputs != other.ninputs :
raise ValueError("The first summand has %i input(s), but the \
second has %i." % (self.inputs , other.inputs ))
if self.outputs != other.outputs :
second has %i." % (self.ninputs , other.ninputs ))
if self.noutputs != other.noutputs :
raise ValueError("The first summand has %i output(s), but the \
second has %i." % (self.outputs , other.outputs ))
second has %i." % (self.noutputs , other.noutputs ))
return FRD(self.fresp + other.fresp, other.omega)
Expand Down
Expand Up
@@ -236,14 +236,14 @@ def __mul__(self, other):
other = _convert_to_FRD(other, omega=self.omega)
# Check that the input-output sizes are consistent.
if self.inputs != other.outputs :
if self.ninputs != other.noutputs :
raise ValueError(
"H = G1*G2: input-output size mismatch: "
"G1 has %i input(s), G2 has %i output(s)." %
(self.inputs , other.outputs ))
(self.ninputs , other.noutputs ))
inputs = other.inputs
outputs = self.outputs
inputs = other.ninputs
outputs = self.noutputs
fresp = empty((outputs, inputs, len(self.omega)),
dtype=self.fresp.dtype)
for i in range(len(self.omega)):
Expand All
@@ -263,14 +263,14 @@ def __rmul__(self, other):
other = _convert_to_FRD(other, omega=self.omega)
# Check that the input-output sizes are consistent.
if self.outputs != other.inputs :
if self.noutputs != other.ninputs :
raise ValueError(
"H = G1*G2: input-output size mismatch: "
"G1 has %i input(s), G2 has %i output(s)." %
(other.inputs , self.outputs ))
(other.ninputs , self.noutputs ))
inputs = self.inputs
outputs = other.outputs
inputs = self.ninputs
outputs = other.noutputs
fresp = empty((outputs, inputs, len(self.omega)),
dtype=self.fresp.dtype)
Expand All
@@ -290,8 +290,8 @@ def __truediv__(self, other):
else:
other = _convert_to_FRD(other, omega=self.omega)
if (self.inputs > 1 or self.outputs > 1 or
other.inputs > 1 or other.outputs > 1):
if (self.ninputs > 1 or self.noutputs > 1 or
other.ninputs > 1 or other.noutputs > 1):
raise NotImplementedError(
"FRD.__truediv__ is currently only implemented for SISO "
"systems.")
Expand All
@@ -313,8 +313,8 @@ def __rtruediv__(self, other):
else:
other = _convert_to_FRD(other, omega=self.omega)
if (self.inputs > 1 or self.outputs > 1 or
other.inputs > 1 or other.outputs > 1):
if (self.ninputs > 1 or self.noutputs > 1 or
other.ninputs > 1 or other.noutputs > 1):
raise NotImplementedError(
"FRD.__rtruediv__ is currently only implemented for "
"SISO systems.")
Expand Down
Expand Up
@@ -392,10 +392,10 @@ def eval(self, omega, squeeze=None):
else:
out = self.fresp[:, :, elements]
else:
out = empty((self.outputs , self.inputs , len(omega_array)),
out = empty((self.noutputs , self.ninputs , len(omega_array)),
dtype=complex)
for i in range(self.outputs ):
for j in range(self.inputs ):
for i in range(self.noutputs ):
for j in range(self.ninputs ):
for k, w in enumerate(omega_array):
frraw = splev(w, self.ifunc[i, j], der=0)
out[i, j, k] = frraw[0] + 1.0j * frraw[1]
Expand All
@@ -406,7 +406,7 @@ def __call__(self, s, squeeze=None):
"""Evaluate system's transfer function at complex frequencies.
Returns the complex frequency response `sys(s)` of system `sys` with
`m = sys.inputs ` number of inputs and `p = sys.outputs ` number of
`m = sys.ninputs ` number of inputs and `p = sys.noutputs ` number of
outputs.
To evaluate at a frequency omega in radians per second, enter
Expand Down
Expand Up
@@ -474,10 +474,10 @@ def feedback(self, other=1, sign=-1):
other = _convert_to_FRD(other, omega=self.omega)
if (self.outputs != other.inputs or self.inputs != other.outputs ):
if (self.noutputs != other.ninputs or self.ninputs != other.noutputs ):
raise ValueError(
"FRD.feedback, inputs/outputs mismatch")
fresp = empty((self.outputs , self.inputs , len(other.omega)),
fresp = empty((self.noutputs , self.ninputs , len(other.omega)),
dtype=complex)
# TODO: vectorize this
# TODO: handle omega re-mapping
Expand All
@@ -487,9 +487,9 @@ def feedback(self, other=1, sign=-1):
fresp[:, :, k] = np.dot(
self.fresp[:, :, k],
linalg.solve(
eye(self.inputs )
eye(self.ninputs )
+ np.dot(other.fresp[:, :, k], self.fresp[:, :, k]),
eye(self.inputs ))
eye(self.ninputs ))
)
return FRD(fresp, other.omega, smooth=(self.ifunc is not None))
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -214,7 +214,7 @@ def bode_plot(syslist, omega=None,
mags, phases, omegas, nyquistfrqs = [], [], [], []
for sys in syslist:
if sys.inputs > 1 or sys.outputs > 1:
if sys.ninputs > 1 or sys.noutputs > 1:
# TODO: Add MIMO bode plots.
raise NotImplementedError(
"Bode is currently only implemented for SISO systems.")
Expand Down
Expand Up
@@ -582,7 +582,7 @@ def nyquist_plot(syslist, omega=None, plot=True, label_freq=0,
num=50, endpoint=True, base=10.0)
for sys in syslist:
if sys.inputs > 1 or sys.outputs > 1:
if sys.ninputs > 1 or sys.noutputs > 1:
# TODO: Add MIMO nyquist plots.
raise NotImplementedError(
"Nyquist is currently only implemented for SISO systems.")
Expand Down
Expand Up
@@ -672,7 +672,7 @@ def gangof4_plot(P, C, omega=None, **kwargs):
-------
None
"""
if P.inputs > 1 or P.outputs > 1 or C.inputs > 1 or C.outputs > 1:
if P.ninputs > 1 or P.noutputs > 1 or C.ninputs > 1 or C.noutputs > 1:
# TODO: Add MIMO go4 plots.
raise NotImplementedError(
"Gang of four is currently only implemented for SISO systems.")
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -659,25 +659,25 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None,
# Create the I/O system object
super(LinearIOSystem, self).__init__(
inputs=linsys.inputs , outputs=linsys.outputs ,
states=linsys.states , params={}, dt=linsys.dt, name=name)
inputs=linsys.ninputs , outputs=linsys.noutputs ,
states=linsys.nstates , params={}, dt=linsys.dt, name=name)
# Initalize additional state space variables
StateSpace.__init__(self, linsys, remove_useless=False)
# Process input, output, state lists, if given
# Make sure they match the size of the linear system
ninputs, self.input_index = self._process_signal_list(
inputs if inputs is not None else linsys.inputs , prefix='u')
if ninputs is not None and linsys.inputs != ninputs:
inputs if inputs is not None else linsys.ninputs , prefix='u')
if ninputs is not None and linsys.ninputs != ninputs:
raise ValueError("Wrong number/type of inputs given.")
noutputs, self.output_index = self._process_signal_list(
outputs if outputs is not None else linsys.outputs , prefix='y')
if noutputs is not None and linsys.outputs != noutputs:
outputs if outputs is not None else linsys.noutputs , prefix='y')
if noutputs is not None and linsys.noutputs != noutputs:
raise ValueError("Wrong number/type of outputs given.")
nstates, self.state_index = self._process_signal_list(
states if states is not None else linsys.states , prefix='x')
if nstates is not None and linsys.states != nstates:
states if states is not None else linsys.nstates , prefix='x')
if nstates is not None and linsys.nstates != nstates:
raise ValueError("Wrong number/type of states given.")
def _update_params(self, params={}, warning=True):
Expand Down
Expand Up
@@ -1345,9 +1345,9 @@ def __init__(self, io_sys, ss_sys=None):
# Initialize the state space attributes
if isinstance(ss_sys, StateSpace):
# Make sure the dimension match
if io_sys.ninputs != ss_sys.inputs or \
io_sys.noutputs != ss_sys.outputs or \
io_sys.nstates != ss_sys.states :
if io_sys.ninputs != ss_sys.ninputs or \
io_sys.noutputs != ss_sys.noutputs or \
io_sys.nstates != ss_sys.nstates :
raise ValueError("System dimensions for first and second "
"arguments must match.")
StateSpace.__init__(self, ss_sys, remove_useless=False)
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -47,10 +47,46 @@ def __init__(self, inputs=1, outputs=1, dt=None):
"""Assign the LTI object's numbers of inputs and ouputs."""
# Data members common to StateSpace and TransferFunction.
self.inputs = inputs
self.outputs = outputs
self.ninputs = inputs
self.noutputs = outputs
self.dt = dt
#
# Getter and setter functions for legacy state attributes
#
# For this iteration, generate a deprecation warning whenever the
# getter/setter is called. For a future iteration, turn it into a
# future warning, so that users will see it.
#
@property
def inputs(self):
warn("The LTI `inputs` attribute will be deprecated in a future "
"release. Use `ninputs` instead.",
DeprecationWarning, stacklevel=2)
return self.ninputs
@inputs.setter
def inputs(self, value):
warn("The LTI `inputs` attribute will be deprecated in a future "
"release. Use `ninputs` instead.",
DeprecationWarning, stacklevel=2)
self.ninputs = value
@property
def outputs(self):
warn("The LTI `outputs` attribute will be deprecated in a future "
"release. Use `noutputs` instead.",
DeprecationWarning, stacklevel=2)
return self.noutputs
@outputs.setter
def outputs(self, value):
warn("The LTI `outputs` attribute will be deprecated in a future "
"release. Use `noutputs` instead.",
DeprecationWarning, stacklevel=2)
self.noutputs = value
def isdtime(self, strict=False):
"""
Check to see if a system is a discrete-time system
Expand Down
Expand Up
@@ -88,7 +124,7 @@ def isctime(self, strict=False):
def issiso(self):
'''Check to see if a system is single input, single output'''
return self.inputs == 1 and self.outputs == 1
return self.ninputs == 1 and self.noutputs == 1
def damp(self):
'''Natural frequency, damping ratio of system poles
Expand Down
Expand Up
@@ -126,7 +162,7 @@ def frequency_response(self, omega, squeeze=None):
G(exp(j*omega*dt)) = mag*exp(j*phase).
In general the system may be multiple input, multiple output (MIMO),
where `m = self.inputs ` number of inputs and `p = self.outputs ` number
where `m = self.ninputs ` number of inputs and `p = self.noutputs ` number
of outputs.
Parameters
Expand Down
Expand Up
@@ -475,7 +511,7 @@ def evalfr(sys, x, squeeze=None):
Returns the complex frequency response `sys(x)` where `x` is `s` for
continuous-time systems and `z` for discrete-time systems, with
`m = sys.inputs ` number of inputs and `p = sys.outputs ` number of
`m = sys.ninputs ` number of inputs and `p = sys.noutputs ` number of
outputs.
To evaluate at a frequency omega in radians per second, enter
Expand Down
Expand Up
@@ -532,7 +568,7 @@ def freqresp(sys, omega, squeeze=None):
"""Frequency response of an LTI system at multiple angular frequencies.
In general the system may be multiple input, multiple output (MIMO), where
`m = sys.inputs ` number of inputs and `p = sys.outputs ` number of
`m = sys.ninputs ` number of inputs and `p = sys.noutputs ` number of
outputs.
Parameters
Expand Down
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.