8000 Merge pull request #913 from murrayrm/remove_matrix-10Jun2023 · python-control/python-control@8c49b09 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8c49b09

Browse files
authored
Merge pull request #913 from murrayrm/remove_matrix-10Jun2023
Remove NumPy matrix class
2 parents 71bbce2 + 77a75a6 commit 8c49b09

21 files changed

+217
-451
lines changed

.github/workflows/doctest.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ jobs:
3333
- name: Run doctest
3434
shell: bash -l {0}
3535
env:
36-
PYTHON_CONTROL_ARRAY_AND_MATRIX: ${{ matrix.array-and-matrix }}
3736
MPLBACKEND: ${{ matrix.mplbackend }}
3837
working-directory: doc
3938
run: |

.github/workflows/python-package-conda.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ jobs:
99
${{ matrix.slycot || 'no' }} Slycot;
1010
${{ matrix.pandas || 'no' }} Pandas;
1111
${{ matrix.cvxopt || 'no' }} CVXOPT
12-
${{ matrix.array-and-matrix == 1 && '; array and matrix' || '' }}
1312
${{ matrix.mplbackend && format('; {0}', matrix.mplbackend) }}
1413
runs-on: ubuntu-latest
1514

@@ -22,14 +21,12 @@ jobs:
2221
pandas: [""]
2322
cvxopt: ["", "conda"]
2423
mplbackend: [""]
25-
array-and-matrix: [0]
2624
include:
2725
- python-version: '3.11'
2826
slycot: conda
2927
pandas: conda
3028
cvxopt: conda
3129
mplbackend: QtAgg
32-
array-and-matrix: 1
3330

3431
steps:
3532
- uses: actions/checkout@v3
@@ -63,7 +60,6 @@ jobs:
6360
- name: Test with pytest
6461
shell: bash -l {0}
6562
env:
66-
PYTHON_CONTROL_ARRAY_AND_MATRIX: ${{ matrix.array-and-matrix }}
6763
MPLBACKEND: ${{ matrix.mplbackend }}
6864
run: pytest -v --cov=control --cov-config=.coveragerc control/tests
6965

control/config.py

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
__all__ = ['defaults', 'set_defaults', 'reset_defaults',
1616
'use_matlab_defaults', 'use_fbs_defaults',
17-
'use_legacy_defaults', 'use_numpy_matrix']
17+
'use_legacy_defaults']
1818

1919
# Package level default values
2020
_control_defaults = {
@@ -202,7 +202,6 @@ def use_matlab_defaults():
202202
The following conventions are used:
203203
* Bode plots plot gain in dB, phase in degrees, frequency in
204204
rad/sec, with grids
205-
* State space class and functions use Numpy matrix objects
206205
207206
Examples
208207
--------
@@ -211,7 +210,6 @@ def use_matlab_defaults():
211210
212211
"""
213212
set_defaults('freqplot', dB=True, deg=True, Hz=False, grid=True)
214-
set_defaults('statesp', use_numpy_matrix=True)
215213

216214

217215
# Set defaults to match FBS (Astrom and Murray)
@@ -233,41 +231,6 @@ def use_fbs_defaults():
233231
set_defaults('nyquist', mirror_style='--')
234232

235233

236-
# Decide whether to use numpy.matrix for state space operations
237-
def use_numpy_matrix(flag=True, warn=True):
238-
"""Turn on/off use of Numpy `matrix` class for state space operations.
239-
240-
Parameters
241-
----------
242-
flag : bool
243-
If flag is `True` (default), use the deprecated Numpy
244-
`matrix` class to represent matrices in the `~control.StateSpace`
245-
class and functions. If flat is `False`, then matrices are
246-
represented by a 2D `ndarray` object.
247-
248-
warn : bool
249-
If flag is `True` (default), issue a warning when turning on the use
250-
of the Numpy `matrix` class. Set `warn` to false to omit display of
251-
the warning message.
252-
253-
Notes
254-
-----
255-
Prior to release 0.9.x, the default type for 2D arrays is the Numpy
256-
`matrix` class. Starting in release 0.9.0, the default type for state
257-
space operations is a 2D array.
258-
259-
Examples
260-
--------
261-
>>> ct.use_numpy_matrix(True, False)
262-
>>> # do some legacy calculations using np.matrix
263-
264-
"""
265-
if flag and warn:
266-
warnings.warn("Return type numpy.matrix is deprecated.",
267-
stacklevel=2, category=DeprecationWarning)
268-
set_defaults('statesp', use_numpy_matrix=flag)
269-
270-
271234
def use_legacy_defaults(version):
272235
""" Sets the defaults to whatever they were in a given release.
273236
@@ -331,7 +294,7 @@ def use_legacy_defaults(version):
331294
# Version 0.9.0:
332295
if major == 0 and minor < 9:
333296
# switched to 'array' as default for state space objects
334-
set_defaults('statesp', use_numpy_matrix=True)
297+
warnings.warn("NumPy matrix class no longer supported")
335298

336299
# switched to 0 (=continuous) as default timestep
337300
set_defaults('control', default_dt=None)

control/iosys.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2227,8 +2227,6 @@ def ss(*args, **kwargs):
22272227
y[k] &= C x[k] + D u[k]
22282228
22292229
The matrices can be given as *array like* data types or strings.
2230-
Everything that the constructor of :class:`numpy.matrix` accepts is
2231-
permissible here too.
22322230
22332231
``ss(args, inputs=['u1', ..., 'up'], outputs=['y1', ..., 'yq'], states=['x1', ..., 'xn'])``
22342232
Create a system with named input, output, and state signals.

control/mateqn.py

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,9 @@ def lyap(A, Q, C=None, E=None, method=None):
126126
127127
Returns
128128
-------
129-
X : 2D array (or matrix)
129+
X : 2D array
130130
Solution to the Lyapunov or Sylvester equation
131131
132-
Notes
133-
-----
134-
The return type for 2D arrays depends on the default class set for
135-
state space operations. See :func:`~control.use_numpy_matrix`.
136-
137132
"""
138133
# Decide what method to use
139134
method = _slycot_or_scipy(method)
@@ -260,11 +255,6 @@ def dlyap(A, Q, C=None, E=None, method=None):
260255
X : 2D array (or matrix)
261256
Solution to the Lyapunov or Sylvester equation
262257
263-
Notes
264-
-----
265-
The return type for 2D arrays depends on the default class set for
266-
state space operations. See :func:`~control.use_numpy_matrix`.
267-
268258
"""
269259
# Decide what method to use
270260
method = _slycot_or_scipy(method)
@@ -395,11 +385,6 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True, method=None,
395385
G : 2D array (or matrix)
396386
Gain matrix
397387
398-
Notes
399-
-----
400-
The return type for 2D arrays depends on the default class set for
401-
state space operations. See :func:`~control.use_numpy_matrix`.
402-
403388
"""
404389
# Decide what method to use
405390
method = _slycot_or_scipy(method)
@@ -554,11 +539,6 @@ def dare(A, B, Q, R, S=None, E=None, stabilizing=True, method=None,
554539
G : 2D array (or matrix)
555540
Gain matrix
556541
557-
Notes
558-
-----
559-
The return type for 2D arrays depends on the default class set for
560-
state space operations. See :func:`~control.use_numpy_matrix`.
561-
562542
"""
563543
# Decide what method to use
564544
method = _slycot_or_scipy(method)

control/matlab/wrappers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def bode(*args, **kwargs):
4848
--------
4949
>>> from control.matlab import ss, bode
5050
51-
>>> sys = ss("1. -2; 3. -4", "5.; 7", "6. 8", "9.")
51+
>>> sys = ss([[1, -2], [3, -4]], [[5], [7]], [[6, 8]], 9)
5252
>>> mag, phase, omega = bode(sys)
5353
5454
.. todo::

control/statefbk.py

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,6 @@ def place(A, B, p):
110110
The algorithm will not place poles at the same location more
111111
than rank(B) times.
112112
113-
The return type for 2D arrays depends on the default class set for
114-
state space operations. See :func:`~control.use_numpy_matrix`.
115-
116113
References
117114
----------
118115
.. [1] A.L. Tits and Y. Yang, "Globally convergent algorithms for robust
@@ -193,11 +190,6 @@ def place_varga(A, B, p, dtime=False, alpha=None):
193190
[1] Varga A. "A Schur method for pole assignment." IEEE Trans. Automatic
194191
Control, Vol. AC-26, pp. 517-519, 1981.
195192
196-
Notes
197-
-----
198-
The return type for 2D arrays depends on the default class set for
199-
state space operations. See :func:`~control.use_numpy_matrix`.
200-
201193
Examples
202194
--------
203195
>>> A = [[-1, -1], [0, 1]]
@@ -279,10 +271,6 @@ def acker(A, B, poles):
279271
K : 2D array (or matrix)
280272
Gains such that A - B K has given eigenvalues
281273
282-
Notes
283-
-----
284-
The return type for 2D arrays depends on the default class set for
285-
state space operations. See :func:`~control.use_numpy_matrix`.
286274
"""
287275
# Convert the inputs to matrices
288276
a = _ssmatrix(A)
@@ -366,13 +354,10 @@ def lqr(*args, **kwargs):
366354
367355
Notes
368356
-----
369-
1. If the first argument is an LTI object, then this object will be used
370-
to define the dynamics and input matrices. Furthermore, if the LTI
371-
object corresponds to a discrete time system, the ``dlqr()`` function
372-
will be called.
373-
374-
2. The return type for 2D arrays depends on the default class set for
375-
state space operations. See :func:`~control.use_numpy_matrix`.
357+
If the first argument is an LTI object, then this object will be used
358+
to define the dynamics and input matrices. Furthermore, if the LTI
359+
object corresponds to a discrete time system, the ``dlqr()`` function
360+
will be called.
376361
377362
Examples
378363
--------
@@ -514,11 +499,6 @@ def dlqr(*args, **kwargs):
514499
--------
515500
lqr, lqe, dlqe
516501
517-
Notes
518-
-----
519-
The return type for 2D arrays depends on the default class set for
520-
state space operations. See :func:`~control.use_numpy_matrix`.
521-
522502
Examples
523503
--------
524504
>>> K, S, E = dlqr(dsys, Q, R, [N]) # doctest: +SKIP
@@ -971,11 +951,6 @@ def ctrb(A, B):
971951
C : 2D array (or matrix)
972952
Controllability matrix
973953
974-
Notes
975-
-----
976-
The return type for 2D arrays depends on the default class set for
977-
state space operations. See :func:`~control.use_numpy_matrix`.
978-
979954
Examples
< D96B code>980955
--------
981956
>>> G = ct.tf2ss([1], [1, 2, 3])
@@ -1010,11 +985,6 @@ def obsv(A, C):
1010985
O : 2D array (or matrix)
1011986
Observability matrix
1012987
1013-
Notes
1014-
-----
1015-
The return type for 2D arrays depends on the default class set for
1016-
state space operations. See :func:`~control.use_numpy_matrix`.
1017-
1018988
Examples
1019989
--------
1020990
>>> G = ct.tf2ss([1], [1, 2, 3])
@@ -1063,11 +1033,6 @@ def gram(sys, type):
10631033
if slycot routine sb03md cannot be found
10641034
if slycot routine sb03od cannot be found
10651035
1066-
Notes
1067-
-----
1068-
The return type for 2D arrays depends on the default class set for
1069-
state space operations. See :func:`~control.use_numpy_matrix`.
1070-
10711036
Examples
10721037
--------
10731038
>>> G = ct.rss(4)

control/statesp.py

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@
7777

7878
# Define module default parameter values
7979
_statesp_defaults = {
80-
'statesp.use_numpy_matrix': False, # False is default in 0.9.0 and above
8180
'statesp.remove_useless_states': False,
8281
'statesp.latex_num_format': '.3g',
8382
'statesp.latex_repr_type': 'partitioned',
@@ -104,14 +103,8 @@ def _ssmatrix(data, axis=1):
104103
arr : 2D array, with shape (0, 0) if a is empty
105104
106105
"""
107-
# Convert the data into an array or matrix, as configured
108-
# If data is passed as a string, use (deprecated?) matrix constructor
109-
if config.defaults['statesp.use_numpy_matrix']:
110-
arr = np.matrix(data, dtype=float)
111-
elif isinstance(data, str):
112-
arr = np.array(np.matrix(data, dtype=float))
113-
else:
114-
arr = np.array(data, dtype=float)
106+
# Convert the data into an array
107+
arr = np.array(data, dtype=float)
115108
ndim = arr.ndim
116109
shape = arr.shape
117110

@@ -205,12 +198,7 @@ class StateSpace(LTI):
205198
-----
206199
The main data members in the ``StateSpace`` class are the A, B, C, and D
207200
matrices. The class also keeps track of the number of states (i.e.,
208-
the size of A). The data format used to store state space matrices is
209-
set using the value of `config.defaults['use_numpy_matrix']`. If True
210-
(default), the state space elements are stored as `numpy.matrix` objects;
211-
otherwise they are `numpy.ndarray` objects. The
212-
:func:`~control.use_numpy_matrix` function can be used to set the storage
213-
type.
201+
the size of A).
214202
215203
A discrete time system is created by specifying a nonzero 'timebase', dt
216204
when the system is constructed:
@@ -358,10 +346,8 @@ def __init__(self, *args, init_namedio=True, **kwargs):
358346
elif kwargs:
359347
raise TypeError("unrecognized keyword(s): ", str(kwargs))
360348

361-
# Reset shapes (may not be needed once np.matrix support is removed)
349+
# Reset shape if system is static
362350
if self._isstatic():
363-
# static gain
364-
# matrix's default "empty" shape is 1x0
365351
A.shape = (0, 0)
366352
B.shape = (0, self.ninputs)
367353
C.shape = (self.noutputs, 0)
@@ -467,10 +453,6 @@ def _remove_useless_states(self):
467453
"""
468454

469455
# Search for useless states and get indices of these states.
470-
#
471-
# Note: shape from np.where depends on whether we are storing state
472-
# space objects as np.matrix or np.array. Code below will work
473-
# correctly in either case.
474456
ax1_A = np.where(~self.A.any(axis=1))[0]
475457
ax1_B = np.where(~self.B.any(axis=1))[0]
476458
ax0_A = np.where(~self.A.any(axis=0))[-1]
@@ -502,12 +484,11 @@ def __str__(self):
502484
return string
503485

504486
# represent to implement a re-loadable version
505-
# TODO: remove the conversion to array when matrix is no longer used
506487
def __repr__(self):
507488
"""Print state-space system in loadable form."""
508489
return "StateSpace({A}, {B}, {C}, {D}{dt})".format(
509-
A=asarray(self.A).__repr__(), B=asarray(self.B).__repr__(),
510-
C=asarray(self.C).__repr__(), D=asarray(self.D).__repr__(),
490+
A=self.A.__repr__(), B=self.B.__repr__(),
491+
C=self.C.__repr__(), D=self.D.__repr__(),
511492
dt=(isdtime(self, strict=True) and ", {}".format(self.dt)) or '')
512493

513494
def _latex_partitioned_stateless(self):
@@ -930,18 +911,17 @@ def horner(self, x, warn_infinite=True):
930911
x_arr = np.atleast_1d(x).astype(complex, copy=False)
931912

932913
# return fast on systems with 0 or 1 state
933-
if not config.defaults['statesp.use_numpy_matrix']:
934-
if self.nstates == 0:
935-
return self.D[:, :, np.newaxis] \
936-
* np.ones_like(x_arr, dtype=complex)
937-
if self.nstates == 1:
938-
with np.errstate(divide='ignore', invalid='ignore'):
939-
out = self.C[:, :, np.newaxis] \
940-
/ (x_arr - self.A[0, 0]) \
941-
* self.B[:, :, np.newaxis] \
942-
+ self.D[:, :, np.newaxis]
943-
out[np.isnan(out)] = complex(np.inf, np.nan)
944-
return out
914+
if self.nstates == 0:
915+
return self.D[:, :, np.newaxis] \
916+
* np.ones_like(x_arr, dtype=complex)
917+
elif self.nstates == 1:
918+
with np.errstate(divide='ignore', invalid='ignore'):
919+
out = self.C[:, :, np.newaxis] \
920+
/ (x_arr - self.A[0, 0]) \
921+
* self.B[:, :, np.newaxis] \
922+
+ self.D[:, :, np.newaxis]
923+
out[np.isnan(out)] = complex(np.inf, np.nan)
924+
return out
945925

946926
try:
947927
out = self.slycot_laub(x_arr)

0 commit comments

Comments
 (0)
0