Skip to content

Commit 4955691

Browse files
authored
Merge pull request #916 from murrayrm/iosys_classes-19Jun2023
I/O system class restructuring
2 parents bfdfbd5 + 39404c4 commit 4955691

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2243
-2492
lines changed

control/__init__.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,32 +68,35 @@
6868

6969
# Import functions from within the control system library
7070
# Note: the functions we use are specified as __all__ variables in the modules
71+
72+
# Input/output system modules
73+
from .iosys import *
74+
from .nlsys import *
75+
from .lti import *
76+
from .statesp import *
77+
from .xferfcn import *
78+
from .frdata import *
79+
7180
from .bdalg import *
7281
from .delay import *
7382
from .descfcn import *
7483
from .dtime import *
7584
from .freqplot import *
76-
from .lti import *
7785
from .margins import *
7886
from .mateqn import *
7987
from .modelsimp import *
80-
from .iosys import *
8188
from .nichols import *
8289
from .phaseplot import *
8390
from .pzmap import *
8491
from .rlocus import *
8592
from .statefbk import *
86-
from .statesp import *
8793
from .stochsys import *
8894
from .timeresp import *
89-
from .xferfcn import *
9095
from .ctrlutil import *
91-
from .frdata import *
9296
from .canonical import *
9397
from .robust import *
9498
from .config import *
9599
from .sisotool import *
96-
from .nlsys import *
97100
from .passivity import *
98101

99102
# Exceptions

control/bdalg.py

Lines changed: 85 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,13 @@
5353
5454
"""
5555

56+
from functools import reduce
5657
import numpy as np
58+
from warnings import warn
5759
from . import xferfcn as tf
5860
from . import statesp as ss
5961
from . import frdata as frd
62+
from .iosys import InputOutputSystem
6063

6164
__all__ = ['series', 'parallel', 'negate', 'feedback', 'append', 'connect']
6265

@@ -68,12 +71,13 @@ def series(sys1, *sysn):
6871
6972
Parameters
7073
----------
71-
sys1 : scalar, StateSpace, TransferFunction, or FRD
72-
*sysn : other scalars, StateSpaces, TransferFunctions, or FRDs
74+
sys1, sys2, ..., sysn: scalar, array, or :class:`InputOutputSystem`
75+
I/O systems to combine.
7376
7477
Returns
7578
-------
76-
out : scalar, StateSpace, or TransferFunction
79+
out : scalar, array, or :class:`InputOutputSystem`
80+
Series interconnection of the systems.
7781
7882
Raises
7983
------
@@ -83,14 +87,15 @@ def series(sys1, *sysn):
8387
8488
See Also
8589
--------
86-
parallel
87-
feedback
90+
append, feedback, interconnect, negate, parallel
8891
8992
Notes
9093
-----
91-
This function is a wrapper for the __mul__ function in the StateSpace and
92-
TransferFunction classes. The output type is usually the type of `sys2`.
93-
If `sys2` is a scalar, then the output type is the type of `sys1`.
94+
This function is a wrapper for the __mul__ function in the appropriate
95+
:class:`NonlinearIOSystem`, :class:`StateSpace`,
96+
:class:`TransferFunction`, or other I/O system class. The output type
97+
is the type of `sys1` unless a more general type is required based on
98+
type type of `sys2`.
9499
95100
If both systems have a defined timebase (dt = 0 for continuous time,
96101
dt > 0 for discrete time), then the timebase for both systems must
@@ -112,8 +117,7 @@ def series(sys1, *sysn):
112117
(2, 1, 5)
113118
114119
"""
115-
from functools import reduce
116-
return reduce(lambda x, y:y*x, sysn, sys1)
120+
return reduce(lambda x, y: y * x, sysn, sys1)
117121

118122

119123
def parallel(sys1, *sysn):
@@ -123,12 +127,13 @@ def parallel(sys1, *sysn):
123127
124128
Parameters
125129
----------
126-
sys1 : scalar, StateSpace, TransferFunction, or FRD
127-
*sysn : other scalars, StateSpaces, TransferFunctions, or FRDs
130+
sys1, sys2, ..., sysn: scalar, array, or :class:`InputOutputSystem`
131+
I/O systems to combine.
128132
129133
Returns
130134
-------
131-
out : scalar, StateSpace, or TransferFunction
135+
out : scalar, array, or :class:`InputOutputSystem`
136+
Parallel interconnection of the systems.
132137
133138
Raises
134139
------
@@ -137,8 +142,7 @@ def parallel(sys1, *sysn):
137142
138143
See Also
139144
--------
140-
series
141-
feedback
145+
append, feedback, interconnect, negate, series
142146
143147
Notes
144148
-----
@@ -167,8 +171,7 @@ def parallel(sys1, *sysn):
167171
(3, 4, 7)
168172
169173
"""
170-
from functools import reduce
171-
return reduce(lambda x, y:x+y, sysn, sys1)
174+
return reduce(lambda x, y: x + y, sysn, sys1)
172175

173176

174177
def negate(sys):
@@ -177,17 +180,23 @@ def negate(sys):
177180
178181
Parameters
179182
----------
180-
sys : StateSpace, TransferFunction or FRD
183+
sys: scalar, array, or :class:`InputOutputSystem`
184+
I/O systems to negate.
181185
182186
Returns
183187
-------
184-
out : StateSpace or TransferFunction
188+
out : scalar, array, or :class:`InputOutputSystem`
189+
Negated system.
185190
186191
Notes
187192
-----
188193
This function is a wrapper for the __neg__ function in the StateSpace and
189194
TransferFunction classes. The output type is the same as the input type.
190195
196+
See Also
197+
--------
198+
append, feedback, interconnect, parallel, series
199+
191200
Examples
192201
--------
193202
>>> G = ct.tf([2], [1, 1])
@@ -202,24 +211,23 @@ def negate(sys):
202211
return -sys
203212

204213
#! TODO: expand to allow sys2 default to work in MIMO case?
214+
#! TODO: allow renaming of signals (for all bdalg operations)
205215
def feedback(sys1, sys2=1, sign=-1):
206-
"""
207-
Feedback interconnection between two I/O systems.
216+
"""Feedback interconnection between two I/O systems.
208217
209218
Parameters
210219
----------
211-
sys1 : scalar, StateSpace, TransferFunction, FRD
212-
The primary process.
213-
sys2 : scalar, StateSpace, TransferFunction, FRD
214-
The feedback process (often a feedback controller).
220+
sys1, sys2: scalar, array, or :class:`InputOutputSystem`
221+
I/O systems to combine.
215222
sign: scalar
216223
The sign of feedback. `sign` = -1 indicates negative feedback, and
217224
`sign` = 1 indicates positive feedback. `sign` is an optional
218225
argument; it assumes a value of -1 if not specified.
219226
220227
Returns
221228
-------
222-
out : StateSpace or TransferFunction
229+
out : scalar, array, or :class:`InputOutputSystem`
230+
Feedback interconnection of the systems.
223231
224232
Raises
225233
------
@@ -232,17 +240,14 @@ def feedback(sys1, sys2=1, sign=-1):
232240
233241
See Also
234242
--------
235-
series
236-
parallel
243+
append, interconnect, negate, parallel, series
237244
238245
Notes
239246
-----
240-
This function is a wrapper for the feedback function in the StateSpace and
241-
TransferFunction classes. It calls TransferFunction.feedback if `sys1` is a
242-
TransferFunction object, and StateSpace.feedback if `sys1` is a StateSpace
243-
object. If `sys1` is a scalar, then it is converted to `sys2`'s type, and
244-
the corresponding feedback function is used. If `sys1` and `sys2` are both
245-
scalars, then TransferFunction.feedback is used.
247+
This function is a wrapper for the `feedback` function in the I/O
248+
system classes. It calls sys1.feedback if `sys1` is an I/O system
249+
object. If `sys1` is a scalar, then it is converted to `sys2`'s type,
250+
and the corresponding feedback function is used.
246251
247252
Examples
248253
--------
@@ -254,57 +259,55 @@ def feedback(sys1, sys2=1, sign=-1):
254259
255260
"""
256261
# Allow anything with a feedback function to call that function
262+
# TODO: rewrite to allow __rfeedback__
257263
try:
258264
return sys1.feedback(sys2, sign)
259-
except AttributeError:
265+
except (AttributeError, TypeError):
260266
pass
261267

262-
# Check for correct input types.
263-
if not isinstance(sys1, (int, float, complex, np.number,
264-
tf.TransferFunction, ss.StateSpace, frd.FRD)):
265-
raise TypeError("sys1 must be a TransferFunction, StateSpace " +
266-
"or FRD object, or a scalar.")
267-
if not isinstance(sys2, (int, float, complex, np.number,
268-
tf.TransferFunction, ss.StateSpace, frd.FRD)):
269-
raise TypeError("sys2 must be a TransferFunction, StateSpace " +
270-
"or FRD object, or a scalar.")
271-
272-
# If sys1 is a scalar, convert it to the appropriate LTI type so that we can
273-
# its feedback member function.
274-
if isinstance(sys1, (int, float, complex, np.number)):
275-
if isinstance(sys2, tf.TransferFunction):
268+
# Check for correct input types
269+
if not isinstance(sys1, (int, float, complex, np.number, np.ndarray,
270+
InputOutputSystem)):
271+
raise TypeError("sys1 must be an I/O system, scalar, or array")
272+
elif not isinstance(sys2, (int, float, complex, np.number, np.ndarray,
273+
InputOutputSystem)):
274+
raise TypeError("sys2 must be an I/O system, scalar, or array")
275+
276+
# If sys1 is a scalar or ndarray, use the type of sys2 to figure
277+
# out how to convert sys1, using transfer functions whenever possible.
278+
if isinstance(sys1, (int, float, complex, np.number, np.ndarray)):
279+
if isinstance(sys2, (int, float, complex, np.number, np.ndarray,
280+
tf.TransferFunction)):
276281
sys1 = tf._convert_to_transfer_function(sys1)
277-
elif isinstance(sys2, ss.StateSpace):
278-
sys1 = ss._convert_to_statespace(sys1)
279282
elif isinstance(sys2, frd.FRD):
280283
sys1 = frd._convert_to_FRD(sys1, sys2.omega)
281-
else: # sys2 is a scalar.
282-
sys1 = tf._convert_to_transfer_function(sys1)
283-
sys2 = tf._convert_to_transfer_function(sys2)
284+
else:
285+
sys1 = ss._convert_to_statespace(sys1)
284286

285287
return sys1.feedback(sys2, sign)
286288

287289
def append(*sys):
288290
"""append(sys1, sys2, [..., sysn])
289291
290-
Group models by appending their inputs and outputs.
292+
Group LTI state space models by appending their inputs and outputs.
291293
292294
Forms an augmented system model, and appends the inputs and
293-
outputs together. The system type will be the type of the first
294-
system given; if you mix state-space systems and gain matrices,
295-
make sure the gain matrices are not first.
295+
outputs together.
296296
297297
Parameters
298298
----------
299-
sys1, sys2, ..., sysn: StateSpace or TransferFunction
300-
LTI systems to combine
301-
299+
sys1, sys2, ..., sysn: scalar, array, or :class:`StateSpace`
300+
I/O systems to combine.
302301
303302
Returns
304303
-------
305-
sys: LTI system
306-
Combined LTI system, with input/output vectors consisting of all
307-
input/output vectors appended
304+
out: :class:`StateSpace`
305+
Combined system, with input/output vectors consisting of all
306+
input/output vectors appended.
307+
308+
See Also
309+
--------
310+
interconnect, feedback, negate, parallel, series
308311
309312
Examples
310313
--------
@@ -329,6 +332,10 @@ def append(*sys):
329332
def connect(sys, Q, inputv, outputv):
330333
"""Index-based interconnection of an LTI system.
331334
335+
.. deprecated:: 0.10.0
336+
`connect` will be removed in a future version of python-control in
337+
favor of `interconnect`, which works with named signals.
338+
332339
The system `sys` is a system typically constructed with `append`, with
333340
multiple inputs and outputs. The inputs and outputs are connected
334341
according to the interconnection matrix `Q`, and then the final inputs and
@@ -340,8 +347,8 @@ def connect(sys, Q, inputv, outputv):
340347
341348
Parameters
342349
----------
343-
sys : StateSpace or TransferFunction
344-
System to be connected
350+
sys : :class:`InputOutputSystem`
351+
System to be connected.
345352
Q : 2D array
346353
Interconnection matrix. First column gives the input to be connected.
347354
The second column gives the index of an output that is to be fed into
@@ -356,8 +363,12 @@ def connect(sys, Q, inputv, outputv):
356363
357364
Returns
358365
-------
359-
sys: LTI system
360-
Connected and trimmed LTI system
366+
out : :class:`InputOutputSystem`
367+
Connected and trimmed I/O system.
368+
369+
See Also
370+
--------
371+
append, feedback, interconnect, negate, parallel, series
361372
362373
Examples
363374
--------
@@ -369,12 +380,14 @@ def connect(sys, Q, inputv, outputv):
369380
370381
Notes
371382
-----
372-
The :func:`~control.interconnect` function in the
373-
:ref:`input/output systems <iosys-module>` module allows the use
374-
of named signals and provides an alternative method for
375-
interconnecting multiple systems.
383+
The :func:`~control.interconnect` function in the :ref:`input/output
384+
systems <iosys-module>` module allows the use of named signals and
385+
provides an alternative method for interconnecting multiple systems.
376386
377387
"""
388+
# TODO: maintain `connect` for use in MATLAB submodule (?)
389+
warn("`connect` is deprecated; use `interconnect`", DeprecationWarning)
390+
378391
inputv, outputv, Q = \
379392
np.atleast_1d(inputv), np.atleast_1d(outputv), np.atleast_1d(Q)
380393
# check indices

control/config.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,18 +123,15 @@ def reset_defaults():
123123
from .sisotool import _sisotool_defaults
124124
defaults.update(_sisotool_defaults)
125125

126-
from .iosys import _namedio_defaults
127-
defaults.update(_namedio_defaults)
126+
from .iosys import _iosys_defaults
127+
defaults.update(_iosys_defaults)
128128

129129
from .xferfcn import _xferfcn_defaults
130130
defaults.update(_xferfcn_defaults)
131131

132132
from .statesp import _statesp_defaults
133133
defaults.update(_statesp_defaults)
134134

135-
from .nlsys import _iosys_defaults
136-
defaults.update(_iosys_defaults)
137-
138135
from .optimal import _optimal_defaults
139136
defaults.update(_optimal_defaults)
140137

@@ -300,7 +297,7 @@ def use_legacy_defaults(version):
300297
set_defaults('control', default_dt=None)
301298

302299
# changed iosys naming conventions
303-
set_defaults('namedio', state_name_delim='.',
300+
set_defaults('iosys', state_name_delim='.',
304301
duplicate_system_name_prefix='copy of ',
305302
duplicate_system_name_suffix='',
306303
linearized_system_name_prefix='',

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