Skip to content

Commit eb146a6

Browse files
authored
Merge pull request #515 from murrayrm/input_output_to_ninput_noutput
Switch LTI class and subclasses to use ninputs, noutputs, nstates
2 parents 1502d38 + eb401a9 commit eb146a6

21 files changed

+457
-385
lines changed

control/bdalg.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def series(sys1, *sysn):
7676
Raises
7777
------
7878
ValueError
79-
if `sys2.inputs` does not equal `sys1.outputs`
79+
if `sys2.ninputs` does not equal `sys1.noutputs`
8080
if `sys1.dt` is not compatible with `sys2.dt`
8181
8282
See Also
@@ -336,25 +336,25 @@ def connect(sys, Q, inputv, outputv):
336336
"""
337337
inputv, outputv, Q = np.asarray(inputv), np.asarray(outputv), np.asarray(Q)
338338
# check indices
339-
index_errors = (inputv - 1 > sys.inputs) | (inputv < 1)
339+
index_errors = (inputv - 1 > sys.ninputs) | (inputv < 1)
340340
if np.any(index_errors):
341341
raise IndexError(
342342
"inputv index %s out of bounds" % inputv[np.where(index_errors)])
343-
index_errors = (outputv - 1 > sys.outputs) | (outputv < 1)
343+
index_errors = (outputv - 1 > sys.noutputs) | (outputv < 1)
344344
if np.any(index_errors):
345345
raise IndexError(
346346
"outputv index %s out of bounds" % outputv[np.where(index_errors)])
347-
index_errors = (Q[:,0:1] - 1 > sys.inputs) | (Q[:,0:1] < 1)
347+
index_errors = (Q[:,0:1] - 1 > sys.ninputs) | (Q[:,0:1] < 1)
348348
if np.any(index_errors):
349349
raise IndexError(
350350
"Q input index %s out of bounds" % Q[np.where(index_errors)])
351-
index_errors = (np.abs(Q[:,1:]) - 1 > sys.outputs)
351+
index_errors = (np.abs(Q[:,1:]) - 1 > sys.noutputs)
352352
if np.any(index_errors):
353353
raise IndexError(
354354
"Q output index %s out of bounds" % Q[np.where(index_errors)])
355355

356356
# first connect
357-
K = np.zeros((sys.inputs, sys.outputs))
357+
K = np.zeros((sys.ninputs, sys.noutputs))
358358
for r in np.array(Q).astype(int):
359359
inp = r[0]-1
360360
for outp in r[1:]:
@@ -365,8 +365,8 @@ def connect(sys, Q, inputv, outputv):
365365
sys = sys.feedback(np.array(K), sign=1)
366366

367367
# now trim
368-
Ytrim = np.zeros((len(outputv), sys.outputs))
369-
Utrim = np.zeros((sys.inputs, len(inputv)))
368+
Ytrim = np.zeros((len(outputv), sys.noutputs))
369+
Utrim = np.zeros((sys.ninputs, len(inputv)))
370370
for i,u in enumerate(inputv):
371371
Utrim[u-1,i] = 1.
372372
for i,y in enumerate(outputv):

control/canonical.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,24 +79,24 @@ def reachable_form(xsys):
7979
zsys.B[0, 0] = 1.0
8080
zsys.A = zeros_like(xsys.A)
8181
Apoly = poly(xsys.A) # characteristic polynomial
82-
for i in range(0, xsys.states):
82+
for i in range(0, xsys.nstates):
8383
zsys.A[0, i] = -Apoly[i+1] / Apoly[0]
84-
if (i+1 < xsys.states):
84+
if (i+1 < xsys.nstates):
8585
zsys.A[i+1, i] = 1.0
8686

8787
# Compute the reachability matrices for each set of states
8888
Wrx = ctrb(xsys.A, xsys.B)
8989
Wrz = ctrb(zsys.A, zsys.B)
9090

91-
if matrix_rank(Wrx) != xsys.states:
91+
if matrix_rank(Wrx) != xsys.nstates:
9292
raise ValueError("System not controllable to working precision.")
9393

9494
# Transformation from one form to another
9595
Tzx = solve(Wrx.T, Wrz.T).T # matrix right division, Tzx = Wrz * inv(Wrx)
9696

9797
# Check to make sure inversion was OK. Note that since we are inverting
9898
# Wrx and we already checked its rank, this exception should never occur
99-
if matrix_rank(Tzx) != xsys.states: # pragma: no cover
99+
if matrix_rank(Tzx) != xsys.nstates: # pragma: no cover
100100
raise ValueError("Transformation matrix singular to working precision.")
101101

102102
# Finally, compute the output matrix
@@ -133,9 +133,9 @@ def observable_form(xsys):
133133
zsys.C[0, 0] = 1
134134
zsys.A = zeros_like(xsys.A)
135135
Apoly = poly(xsys.A) # characteristic polynomial
136-
for i in range(0, xsys.states):
136+
for i in range(0, xsys.nstates):
137137
zsys.A[i, 0] = -Apoly[i+1] / Apoly[0]
138-
if (i+1 < xsys.states):
138+
if (i+1 < xsys.nstates):
139139
zsys.A[i, i+1] = 1
140140

141141
# Compute the observability matrices for each set of states
@@ -145,7 +145,7 @@ def observable_form(xsys):
145145
# Transformation from one form to another
146146
Tzx = solve(Wrz, Wrx) # matrix left division, Tzx = inv(Wrz) * Wrx
147147

148-
if matrix_rank(Tzx) != xsys.states:
148+
if matrix_rank(Tzx) != xsys.nstates:
149149
raise ValueError("Transformation matrix singular to working precision.")
150150

151151
# Finally, compute the output matrix

control/frdata.py

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,11 @@ def __init__(self, *args, **kwargs):
155155
def __str__(self):
156156
"""String representation of the transfer function."""
157157

158-
mimo = self.inputs > 1 or self.outputs > 1
158+
mimo = self.ninputs > 1 or self.noutputs > 1
159159
outstr = ['Frequency response data']
160160

161-
for i in range(self.inputs):
162-
for j in range(self.outputs):
161+
for i in range(self.ninputs):
162+
for j in range(self.noutputs):
163163
if mimo:
164164
outstr.append("Input %i to output %i:" % (i + 1, j + 1))
165165
outstr.append('Freq [rad/s] Response')
@@ -201,12 +201,12 @@ def __add__(self, other):
201201
other = _convert_to_FRD(other, omega=self.omega)
202202

203203
# Check that the input-output sizes are consistent.
204-
if self.inputs != other.inputs:
204+
if self.ninputs != other.ninputs:
205205
raise ValueError("The first summand has %i input(s), but the \
206-
second has %i." % (self.inputs, other.inputs))
207-
if self.outputs != other.outputs:
206+
second has %i." % (self.ninputs, other.ninputs))
207+
if self.noutputs != other.noutputs:
208208
raise ValueError("The first summand has %i output(s), but the \
209-
second has %i." % (self.outputs, other.outputs))
209+
second has %i." % (self.noutputs, other.noutputs))
210210

211211
return FRD(self.fresp + other.fresp, other.omega)
212212

@@ -236,14 +236,14 @@ def __mul__(self, other):
236236
other = _convert_to_FRD(other, omega=self.omega)
237237

238238
# Check that the input-output sizes are consistent.
239-
if self.inputs != other.outputs:
239+
if self.ninputs != other.noutputs:
240240
raise ValueError(
241241
"H = G1*G2: input-output size mismatch: "
242242
"G1 has %i input(s), G2 has %i output(s)." %
243-
(self.inputs, other.outputs))
243+
(self.ninputs, other.noutputs))
244244

245-
inputs = other.inputs
246-
outputs = self.outputs
245+
inputs = other.ninputs
246+
outputs = self.noutputs
247247
fresp = empty((outputs, inputs, len(self.omega)),
248248
dtype=self.fresp.dtype)
249249
for i in range(len(self.omega)):
@@ -263,14 +263,14 @@ def __rmul__(self, other):
263263
other = _convert_to_FRD(other, omega=self.omega)
264264

265265
# Check that the input-output sizes are consistent.
266-
if self.outputs != other.inputs:
266+
if self.noutputs != other.ninputs:
267267
raise ValueError(
268268
"H = G1*G2: input-output size mismatch: "
269269
"G1 has %i input(s), G2 has %i output(s)." %
270-
(other.inputs, self.outputs))
270+
(other.ninputs, self.noutputs))
271271

272-
inputs = self.inputs
273-
outputs = other.outputs
272+
inputs = self.ninputs
273+
outputs = other.noutputs
274274

275275
fresp = empty((outputs, inputs, len(self.omega)),
276276
dtype=self.fresp.dtype)
@@ -290,8 +290,8 @@ def __truediv__(self, other):
290290
else:
291291
other = _convert_to_FRD(other, omega=self.omega)
292292

293-
if (self.inputs > 1 or self.outputs > 1 or
294-
other.inputs > 1 or other.outputs > 1):
293+
if (self.ninputs > 1 or self.noutputs > 1 or
294+
other.ninputs > 1 or other.noutputs > 1):
295295
raise NotImplementedError(
296296
"FRD.__truediv__ is currently only implemented for SISO "
297297
"systems.")
@@ -313,8 +313,8 @@ def __rtruediv__(self, other):
313313
else:
314314
other = _convert_to_FRD(other, omega=self.omega)
315315

316-
if (self.inputs > 1 or self.outputs > 1 or
317-
other.inputs > 1 or other.outputs > 1):
316+
if (self.ninputs > 1 or self.noutputs > 1 or
317+
other.ninputs > 1 or other.noutputs > 1):
318318
raise NotImplementedError(
319319
"FRD.__rtruediv__ is currently only implemented for "
320320
"SISO systems.")
@@ -392,10 +392,10 @@ def eval(self, omega, squeeze=None):
392392
else:
393393
out = self.fresp[:, :, elements]
394394
else:
395-
out = empty((self.outputs, self.inputs, len(omega_array)),
395+
out = empty((self.noutputs, self.ninputs, len(omega_array)),
396396
dtype=complex)
397-
for i in range(self.outputs):
398-
for j in range(self.inputs):
397+
for i in range(self.noutputs):
398+
for j in range(self.ninputs):
399399
for k, w in enumerate(omega_array):
400400
frraw = splev(w, self.ifunc[i, j], der=0)
401401
out[i, j, k] = frraw[0] + 1.0j * frraw[1]
@@ -406,7 +406,7 @@ def __call__(self, s, squeeze=None):
406406
"""Evaluate system's transfer function at complex frequencies.
407407
408408
Returns the complex frequency response `sys(s)` of system `sys` with
409-
`m = sys.inputs` number of inputs and `p = sys.outputs` number of
409+
`m = sys.ninputs` number of inputs and `p = sys.noutputs` number of
410410
outputs.
411411
412412
To evaluate at a frequency omega in radians per second, enter
@@ -474,10 +474,10 @@ def feedback(self, other=1, sign=-1):
474474

475475
other = _convert_to_FRD(other, omega=self.omega)
476476

477-
if (self.outputs != other.inputs or self.inputs != other.outputs):
477+
if (self.noutputs != other.ninputs or self.ninputs != other.noutputs):
478478
raise ValueError(
479479
"FRD.feedback, inputs/outputs mismatch")
480-
fresp = empty((self.outputs, self.inputs, len(other.omega)),
480+
fresp = empty((self.noutputs, self.ninputs, len(other.omega)),
481481
dtype=complex)
482482
# TODO: vectorize this
483483
# TODO: handle omega re-mapping
@@ -487,9 +487,9 @@ def feedback(self, other=1, sign=-1):
487487
fresp[:, :, k] = np.dot(
488488
self.fresp[:, :, k],
489489
linalg.solve(
490-
eye(self.inputs)
490+
eye(self.ninputs)
491491
+ np.dot(other.fresp[:, :, k], self.fresp[:, :, k]),
492-
eye(self.inputs))
492+
eye(self.ninputs))
493493
)
494494

495495
return FRD(fresp, other.omega, smooth=(self.ifunc is not None))

control/freqplot.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ def bode_plot(syslist, omega=None,
214214

215215
mags, phases, omegas, nyquistfrqs = [], [], [], []
216216
for sys in syslist:
217-
if sys.inputs > 1 or sys.outputs > 1:
217+
if sys.ninputs > 1 or sys.noutputs > 1:
218218
# TODO: Add MIMO bode plots.
219219
raise NotImplementedError(
220220
"Bode is currently only implemented for SISO systems.")
@@ -582,7 +582,7 @@ def nyquist_plot(syslist, omega=None, plot=True, label_freq=0,
582582
num=50, endpoint=True, base=10.0)
583583

584584
for sys in syslist:
585-
if sys.inputs > 1 or sys.outputs > 1:
585+
if sys.ninputs > 1 or sys.noutputs > 1:
586586
# TODO: Add MIMO nyquist plots.
587587
raise NotImplementedError(
588588
"Nyquist is currently only implemented for SISO systems.")
@@ -672,7 +672,7 @@ def gangof4_plot(P, C, omega=None, **kwargs):
672672
-------
673673
None
674674
"""
675-
if P.inputs > 1 or P.outputs > 1 or C.inputs > 1 or C.outputs > 1:
675+
if P.ninputs > 1 or P.noutputs > 1 or C.ninputs > 1 or C.noutputs > 1:
676676
# TODO: Add MIMO go4 plots.
677677
raise NotImplementedError(
678678
"Gang of four is currently only implemented for SISO systems.")

control/iosys.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -659,25 +659,25 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None,
659659

660660
# Create the I/O system object
661661
super(LinearIOSystem, self).__init__(
662-
inputs=linsys.inputs, outputs=linsys.outputs,
663-
states=linsys.states, params={}, dt=linsys.dt, name=name)
662+
inputs=linsys.ninputs, outputs=linsys.noutputs,
663+
states=linsys.nstates, params={}, dt=linsys.dt, name=name)
664664

665665
# Initalize additional state space variables
666666
StateSpace.__init__(self, linsys, remove_useless=False)
667667

668668
# Process input, output, state lists, if given
669669
# Make sure they match the size of the linear system
670670
ninputs, self.input_index = self._process_signal_list(
671-
inputs if inputs is not None else linsys.inputs, prefix='u')
672-
if ninputs is not None and linsys.inputs != ninputs:
671+
inputs if inputs is not None else linsys.ninputs, prefix='u')
672+
if ninputs is not None and linsys.ninputs != ninputs:
673673
raise ValueError("Wrong number/type of inputs given.")
674674
noutputs, self.output_index = self._process_signal_list(
675-
outputs if outputs is not None else linsys.outputs, prefix='y')
676-
if noutputs is not None and linsys.outputs != noutputs:
675+
outputs if outputs is not None else linsys.noutputs, prefix='y')
676+
if noutputs is not None and linsys.noutputs != noutputs:
677677
raise ValueError("Wrong number/type of outputs given.")
678678
nstates, self.state_index = self._process_signal_list(
679-
states if states is not None else linsys.states, prefix='x')
680-
if nstates is not None and linsys.states != nstates:
679+
states if states is not None else linsys.nstates, prefix='x')
680+
if nstates is not None and linsys.nstates != nstates:
681681
raise ValueError("Wrong number/type of states given.")
682682

683683
def _update_params(self, params={}, warning=True):
@@ -1345,9 +1345,9 @@ def __init__(self, io_sys, ss_sys=None):
13451345
# Initialize the state space attributes
13461346
if isinstance(ss_sys, StateSpace):
13471347
# Make sure the dimension match
1348-
if io_sys.ninputs != ss_sys.inputs or \
1349-
io_sys.noutputs != ss_sys.outputs or \
1350-
io_sys.nstates != ss_sys.states:
1348+
if io_sys.ninputs != ss_sys.ninputs or \
1349+
io_sys.noutputs != ss_sys.noutputs or \
1350+
io_sys.nstates != ss_sys.nstates:
13511351
raise ValueError("System dimensions for first and second "
13521352
"arguments must match.")
13531353
StateSpace.__init__(self, ss_sys, remove_useless=False)

control/lti.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,46 @@ def __init__(self, inputs=1, outputs=1, dt=None):
4747
"""Assign the LTI object's numbers of inputs and ouputs."""
4848

4949
# Data members common to StateSpace and TransferFunction.
50-
self.inputs = inputs
51-
self.outputs = outputs
50+
self.ninputs = inputs
51+
self.noutputs = outputs
5252
self.dt = dt
5353

54+
#
55+
# Getter and setter functions for legacy state attributes
56+
#
57+
# For this iteration, generate a deprecation warning whenever the
58+
# getter/setter is called. For a future iteration, turn it into a
59+
# future warning, so that users will see it.
60+
#
61+
62+
@property
63+
def inputs(self):
64+
warn("The LTI `inputs` attribute will be deprecated in a future "
65+
"release. Use `ninputs` instead.",
66+
DeprecationWarning, stacklevel=2)
67+
return self.ninputs
68+
69+
@inputs.setter
70+
def inputs(self, value):
71+
warn("The LTI `inputs` attribute will be deprecated in a future "
72+
"release. Use `ninputs` instead.",
73+
DeprecationWarning, stacklevel=2)
74+
self.ninputs = value
75+
76+
@property
77+
def outputs(self):
78+
warn("The LTI `outputs` attribute will be deprecated in a future "
79+
"release. Use `noutputs` instead.",
80+
DeprecationWarning, stacklevel=2)
81+
return self.noutputs
82+
83+
@outputs.setter
84+
def outputs(self, value):
85+
warn("The LTI `outputs` attribute will be deprecated in a future "
86+
"release. Use `noutputs` instead.",
87+
DeprecationWarning, stacklevel=2)
88+
self.noutputs = value
89+
5490
def isdtime(self, strict=False):
5591
"""
5692
Check to see if a system is a discrete-time system
@@ -88,7 +124,7 @@ def isctime(self, strict=False):
88124

89125
def issiso(self):
90126
'''Check to see if a system is single input, single output'''
91-
return self.inputs == 1 and self.outputs == 1
127+
return self.ninputs == 1 and self.noutputs == 1
92128

93129
def damp(self):
94130
'''Natural frequency, damping ratio of system poles
@@ -126,7 +162,7 @@ def frequency_response(self, omega, squeeze=None):
126162
G(exp(j*omega*dt)) = mag*exp(j*phase).
127163
128164
In general the system may be multiple input, multiple output (MIMO),
129-
where `m = self.inputs` number of inputs and `p = self.outputs` number
165+
where `m = self.ninputs` number of inputs and `p = self.noutputs` number
130166
of outputs.
131167
132168
Parameters
@@ -475,7 +511,7 @@ def evalfr(sys, x, squeeze=None):
475511
476512
Returns the complex frequency response `sys(x)` where `x` is `s` for
477513
continuous-time systems and `z` for discrete-time systems, with
478-
`m = sys.inputs` number of inputs and `p = sys.outputs` number of
514+
`m = sys.ninputs` number of inputs and `p = sys.noutputs` number of
479515
outputs.
480516
481517
To evaluate at a frequency omega in radians per second, enter
@@ -532,7 +568,7 @@ def freqresp(sys, omega, squeeze=None):
532568
"""Frequency response of an LTI system at multiple angular frequencies.
533569
534570
In general the system may be multiple input, multiple output (MIMO), where
535-
`m = sys.inputs` number of inputs and `p = sys.outputs` number of
571+
`m = sys.ninputs` number of inputs and `p = sys.noutputs` number of
536572
outputs.
537573
538574
Parameters

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy