Skip to content

Commit 0c1e983

Browse files
committed
allow np.array or np.matrix for state space matrices + unit tests
1 parent e2346cd commit 0c1e983

13 files changed

+1857
-166
lines changed

control/bdalg.py

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,25 @@
5353
5454
"""
5555

56-
import scipy as sp
5756
import numpy as np
5857
from . import xferfcn as tf
5958
from . import statesp as ss
6059
from . import frdata as frd
6160

6261
__all__ = ['series', 'parallel', 'negate', 'feedback', 'append', 'connect']
6362

63+
6464
def series(sys1, *sysn):
6565
"""Return the series connection (... \* sys3 \*) sys2 \* sys1
6666
6767
Parameters
6868
----------
69-
sys1: scalar, StateSpace, TransferFunction, or FRD
70-
sysn: other scalars, StateSpaces, TransferFunctions, or FRDs
69+
sys1 : scalar, StateSpace, TransferFunction, or FRD
70+
sysn : other scalars, StateSpaces, TransferFunctions, or FRDs
7171
7272
Returns
7373
-------
74-
out: scalar, StateSpace, or TransferFunction
74+
out : scalar, StateSpace, or TransferFunction
7575
7676
Raises
7777
------
@@ -105,18 +105,19 @@ def series(sys1, *sysn):
105105
from functools import reduce
106106
return reduce(lambda x, y:y*x, sysn, sys1)
107107

108+
108109
def parallel(sys1, *sysn):
109110
"""
110111
Return the parallel connection sys1 + sys2 (+ sys3 + ...)
111112
112113
Parameters
113114
----------
114-
sys1: scalar, StateSpace, TransferFunction, or FRD
115-
*sysn: other scalars, StateSpaces, TransferFunctions, or FRDs
115+
sys1 : scalar, StateSpace, TransferFunction, or FRD
116+
*sysn : other scalars, StateSpaces, TransferFunctions, or FRDs
116117
117118
Returns
118119
-------
119-
out: scalar, StateSpace, or TransferFunction
120+
out : scalar, StateSpace, or TransferFunction
120121
121122
Raises
122123
------
@@ -150,17 +151,18 @@ def parallel(sys1, *sysn):
150151
from functools import reduce
151152
return reduce(lambda x, y:x+y, sysn, sys1)
152153

154+
153155
def negate(sys):
154156
"""
155157
Return the negative of a system.
156158
157159
Parameters
158160
----------
159-
sys: StateSpace, TransferFunction or FRD
161+
sys : StateSpace, TransferFunction or FRD
160162
161163
Returns
162164
-------
163-
out: StateSpace or TransferFunction
165+
out : StateSpace or TransferFunction
164166
165167
Notes
166168
-----
@@ -177,7 +179,6 @@ def negate(sys):
177179
>>> sys2 = negate(sys1) # Same as sys2 = -sys1.
178180
179181
"""
180-
181182
return -sys;
182183

183184
#! TODO: expand to allow sys2 default to work in MIMO case?
@@ -187,18 +188,18 @@ def feedback(sys1, sys2=1, sign=-1):
187188
188189
Parameters
189190
----------
190-
sys1: scalar, StateSpace, TransferFunction, FRD
191-
The primary plant.
192-
sys2: scalar, StateSpace, TransferFunction, FRD
193-
The feedback plant (often a feedback controller).
191+
sys1 : scalar, StateSpace, TransferFunction, FRD
192+
The primary process.
193+
sys2 : scalar, StateSpace, TransferFunction, FRD
194+
The feedback process (often a feedback controller).
194195
sign: scalar
195196
The sign of feedback. `sign` = -1 indicates negative feedback, and
196197
`sign` = 1 indicates positive feedback. `sign` is an optional
197198
argument; it assumes a value of -1 if not specified.
198199
199200
Returns
200201
-------
201-
out: StateSpace or TransferFunction
202+
out : StateSpace or TransferFunction
202203
203204
Raises
204205
------
@@ -256,7 +257,7 @@ def feedback(sys1, sys2=1, sign=-1):
256257
return sys1.feedback(sys2, sign)
257258

258259
def append(*sys):
259-
'''append(sys1, sys2, ..., sysn)
260+
"""append(sys1, sys2, ..., sysn)
260261
261262
Group models by appending their inputs and outputs
262263
@@ -279,42 +280,40 @@ def append(*sys):
279280
280281
Examples
281282
--------
282-
>>> sys1 = ss("1. -2; 3. -4", "5.; 7", "6. 8", "9.")
283-
>>> sys2 = ss("-1.", "1.", "1.", "0.")
283+
>>> sys1 = ss([[1., -2], [3., -4]], [[5.], [7]]", [[6., 8]], [[9.]])
284+
>>> sys2 = ss([[-1.]], [[1.]], [[1.]], [[0.]])
284285
>>> sys = append(sys1, sys2)
285286
286-
.. todo::
287-
also implement for transfer function, zpk, etc.
288-
'''
287+
"""
289288
s1 = sys[0]
290289
for s in sys[1:]:
291290
s1 = s1.append(s)
292291
return s1
293292

294293
def connect(sys, Q, inputv, outputv):
295-
'''
296-
Index-base interconnection of system
294+
"""Index-based interconnection of an LTI system.
297295
298-
The system sys is a system typically constructed with append, with
299-
multiple inputs and outputs. The inputs and outputs are connected
300-
according to the interconnection matrix Q, and then the final
301-
inputs and outputs are trimmed according to the inputs and outputs
302-
listed in inputv and outputv.
296+
The system `sys` is a system typically constructed with `append`, with
297+
multiple inputs and outputs. The inputs and outputs are connected
298+
according to the interconnection matrix `Q`, and then the final inputs and
299+
outputs are trimmed according to the inputs and outputs listed in `inputv`
300+
and `outputv`.
303301
304-
Note: to have this work, inputs and outputs start counting at 1!!!!
302+
NOTE: Inputs and outputs are indexed starting at 1 and negative values
303+
correspond to a negative feedback interconnection.
305304
306305
Parameters
307306
----------
308-
sys: StateSpace Transferfunction
307+
sys : StateSpace Transferfunction
309308
System to be connected
310-
Q: 2d array
309+
Q : 2D array
311310
Interconnection matrix. First column gives the input to be connected
312-
second column gives the output to be fed into this input. Negative
311+
second column gives the output to be fed into this input. Negative
313312
values for the second column mean the feedback is negative, 0 means
314-
no connection is made
315-
inputv: 1d array
313+
no connection is made. Inputs and outputs are indexed starting at 1.
314+
inputv : 1D array
316315
list of final external inputs
317-
outputv: 1d array
316+
outputv : 1D array
318317
list of final external outputs
319318
320319
Returns
@@ -324,28 +323,30 @@ def connect(sys, Q, inputv, outputv):
324323
325324
Examples
326325
--------
327-
>>> sys1 = ss("1. -2; 3. -4", "5.; 7", "6, 8", "9.")
328-
>>> sys2 = ss("-1.", "1.", "1.", "0.")
326+
>>> sys1 = ss([[1., -2], [3., -4]], [[5.], [7]], [[6, 8]], [[9.]])
327+
>>> sys2 = ss([[-1.]], [[1.]], [[1.]], [[0.]])
329328
>>> sys = append(sys1, sys2)
330-
>>> Q = sp.mat([ [ 1, 2], [2, -1] ]) # basically feedback, output 2 in 1
329+
>>> Q = [[1, 2], [2, -1]]) # negative feedback interconnection
331330
>>> sysc = connect(sys, Q, [2], [1, 2])
332-
'''
331+
332+
"""
333333
# first connect
334-
K = sp.zeros( (sys.inputs, sys.outputs) )
335-
for r in sp.array(Q).astype(int):
334+
K = np.zeros((sys.inputs, sys.outputs))
335+
for r in np.array(Q).astype(int):
336336
inp = r[0]-1
337337
for outp in r[1:]:
338338
if outp > 0 and outp <= sys.outputs:
339339
K[inp,outp-1] = 1.
340340
elif outp < 0 and -outp >= -sys.outputs:
341341
K[inp,-outp-1] = -1.
342-
sys = sys.feedback(sp.matrix(K), sign=1)
342+
sys = sys.feedback(np.array(K), sign=1)
343343

344344
# now trim
345-
Ytrim = sp.zeros( (len(outputv), sys.outputs) )
346-
Utrim = sp.zeros( (sys.inputs, len(inputv)) )
345+
Ytrim = np.zeros((len(outputv), sys.outputs))
346+
Utrim = np.zeros((sys.inputs, len(inputv)))
347347
for i,u in enumerate(inputv):
348348
Utrim[u-1,i] = 1.
349349
for i,y in enumerate(outputv):
350350
Ytrim[i,y-1] = 1.
351-
return sp.matrix(Ytrim)*sys*sp.matrix(Utrim)
351+
352+
return Ytrim * sys * Utrim

control/config.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
# files. For now, you can just choose between MATLAB and FBS default
88
# values.
99

10+
import warnings
11+
1012
# Bode plot defaults
1113
bode_dB = False # Bode plot magnitude units
1214
bode_deg = True # Bode Plot phase units
1315
bode_Hz = False # Bode plot frequency units
1416
bode_number_of_samples = None # Bode plot number of samples
1517
bode_feature_periphery_decade = 1.0 # Bode plot feature periphery in decades
1618

19+
# State space module variables
20+
_use_numpy_matrix = True # Decide whether to use numpy.marix
1721

1822
def reset_defaults():
1923
"""Reset configuration values to their default values."""
@@ -22,6 +26,7 @@ def reset_defaults():
2226
global bode_Hz; bode_Hz = False
2327
global bode_number_of_samples; bode_number_of_samples = None
2428
global bode_feature_periphery_decade; bode_feature_periphery_decade = 1.0
29+
global _use_numpy_matrix; _use_numpy_matrix = True
2530

2631

2732
# Set defaults to match MATLAB
@@ -36,6 +41,7 @@ def use_matlab_defaults():
3641
global bode_dB; bode_dB = True
3742
global bode_deg; bode_deg = True
3843
global bode_Hz; bode_Hz = True
44+
global _use_numpy_matrix; _use_numpy_matrix = True
3945

4046

4147
# Set defaults to match FBS (Astrom and Murray)
@@ -52,3 +58,10 @@ def use_fbs_defaults():
5258
global bode_deg; bode_deg = True
5359
global bode_Hz; bode_Hz = False
5460

61+
62+
# Decide whether to use numpy.matrix for state space operations
63+
def use_numpy_matrix(flag=True, warn=True):
64+
if flag and warn:
65+
warnings.warn("Return type numpy.matrix is soon to be deprecated.",
66+
stacklevel=2)
67+
global _use_numpy_matrix; _use_numpy_matrix = flag

control/frdata.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
from warnings import warn
5353
import numpy as np
5454
from numpy import angle, array, empty, ones, \
55-
real, imag, matrix, absolute, eye, linalg, where, dot
55+
real, imag, absolute, eye, linalg, where, dot
5656
from scipy.interpolate import splprep, splev
5757
from .lti import LTI
5858

@@ -81,6 +81,10 @@ class FRD(LTI):
8181
8282
"""
8383

84+
# Allow NDarray * StateSpace to give StateSpace._rmul_() priority
85+
# https://docs.scipy.org/doc/numpy/reference/arrays.classes.html
86+
__array_priority__ = 11 # override ndarray and matrix types
87+
8488
epsw = 1e-8
8589

8690
def __init__(self, *args, **kwargs):
@@ -436,12 +440,13 @@ def feedback(self, other=1, sign=-1):
436440
# TODO: vectorize this
437441
# TODO: handle omega re-mapping
438442
for k, w in enumerate(other.omega):
439-
fresp[:, :, k] = self.fresp[:, :, k].view(type=matrix)* \
443+
fresp[:, :, k] = np.dot(
444+
self.fresp[:, :, k],
440445
linalg.solve(
441-
eye(self.inputs) +
442-
other.fresp[:, :, k].view(type=matrix) *
443-
self.fresp[:, :, k].view(type=matrix),
444-
eye(self.inputs))
446+
eye(self.inputs)
447+
+ np.dot(other.fresp[:, :, k], self.fresp[:, :, k]),
448+
eye(self.inputs))
449+
)
445450

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

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