8000 Merge pull request #515 from murrayrm/input_output_to_ninput_noutput · python-control/python-control@eb146a6 · GitHub
[go: up one dir, main page]

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.&qu 10000 ot;)
@@ -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)
0