Skip to content

Commit 63ce37d

Browse files
author
Rory Yorke
committed
pytest-parametrize and slycot-mark tests in mateqn_test.py
Test solvers dependent on Slycot when "slycot" test marker is specified. These tests are now parametrized by method.
1 parent 2435a6a commit 63ce37d

1 file changed

Lines changed: 95 additions & 58 deletions

File tree

control/tests/mateqn_test.py

Lines changed: 95 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -39,53 +39,56 @@
3939
import pytest
4040
from scipy.linalg import eigvals, solve
4141

42-
import control as ct
4342
from control.mateqn import lyap, dlyap, care, dare
44-
from control.exception import ControlArgument, ControlDimension, slycot_check
43+
from control.exception import ControlArgument, ControlDimension
4544

4645

4746
class TestMatrixEquations:
4847
"""These are tests for the matrix equation solvers in mateqn.py"""
4948

50-
def test_lyap(self):
49+
@pytest.mark.parametrize('method',
50+
['scipy',
51+
pytest.param('slycot', marks=pytest.mark.slycot)])
52+
def test_lyap(self, method):
5153
A = array([[-1, 1], [-1, 0]])
5254
Q = array([[1, 0], [0, 1]])
53-
X = lyap(A, Q)
55+
X = lyap(A, Q, method=method)
5456
# print("The solution obtained is ", X)
5557
assert_array_almost_equal(A @ X + X @ A.T + Q, zeros((2,2)))
5658

5759
A = array([[1, 2], [-3, -4]])
5860
Q = array([[3, 1], [1, 1]])
59-
X = lyap(A,Q)
61+
X = lyap(A,Q, method=method)
6062
# print("The solution obtained is ", X)
6163
assert_array_almost_equal(A @ X + X @ A.T + Q, zeros((2,2)))
6264

6365
# Compare methods
64-
if slycot_check():
66+
if method == 'slycot':
6567
X_scipy = lyap(A, Q, method='scipy')
66-
X_slycot = lyap(A, Q, method='slycot')
67-
assert_array_almost_equal(X_scipy, X_slycot)
68+
assert_array_almost_equal(X_scipy, X)
6869

69-
def test_lyap_sylvester(self):
70+
@pytest.mark.parametrize('method',
71+
['scipy',
72+
pytest.param('slycot', marks=pytest.mark.slycot)])
73+
def test_lyap_sylvester(self, method):
7074
A = 5
7175
B = array([[4, 3], [4, 3]])
7276
C = array([2, 1])
73-
X = lyap(A, B, C)
77+
X = lyap(A, B, C, method=method)
7478
# print("The solution obtained is ", X)
7579
assert_array_almost_equal(A * X + X @ B + C, zeros((1,2)))
7680

7781
A = array([[2, 1], [1, 2]])
7882
B = array([[1, 2], [0.5, 0.1]])
7983
C = array([[1, 0], [0, 1]])
80-
X = lyap(A, B, C)
84+
X = lyap(A, B, C, method=method)
8185
# print("The solution obtained is ", X)
8286
assert_array_almost_equal(A @ X + X @ B + C, zeros((2,2)))
8387

8488
# Compare methods
85-
if slycot_check():
89+
if method=='slycot':
8690
X_scipy = lyap(A, B, C, method='scipy')
87-
X_slycot = lyap(A, B, C, method='slycot')
88-
assert_array_almost_equal(X_scipy, X_slycot)
91+
assert_array_almost_equal(X_scipy, X)
8992

9093
@pytest.mark.slycot
9194
def test_lyap_g(self):
@@ -101,19 +104,27 @@ def test_lyap_g(self):
101104
with pytest.raises(ControlArgument, match="'scipy' not valid"):
102105
X = lyap(A, Q, None, E, method='scipy')
103106

104-
def test_dlyap(self):
107+
@pytest.mark.parametrize('method',
108+
['scipy',
109+
pytest.param('slycot', marks=pytest.mark.slycot)])
110+
def test_dlyap(self, method):
105111
A = array([[-0.6, 0],[-0.1, -0.4]])
106112
Q = array([[1,0],[0,1]])
107-
X = dlyap(A,Q)
113+
X = dlyap(A,Q,method=method)
108114
# print("The solution obtained is ", X)
109115
assert_array_almost_equal(A @ X @ A.T - X + Q, zeros((2,2)))
110116

111117
A = array([[-0.6, 0],[-0.1, -0.4]])
112118
Q = array([[3, 1],[1, 1]])
113-
X = dlyap(A,Q)
119+
X = dlyap(A,Q,method=method)
114120
# print("The solution obtained is ", X)
115121
assert_array_almost_equal(A @ X @ A.T - X + Q, zeros((2,2)))
116122

123+
# Compare methods
124+
if method=='slycot':
125+
X_scipy = dlyap(A,Q, method='scipy')
126+
assert_array_almost_equal(X_scipy, X)
127+
117128
@pytest.mark.slycot
118129
def test_dlyap_g(self):
119130
A = array([[-0.6, 0],[-0.1, -0.4]])
@@ -148,35 +159,40 @@ def test_dlyap_sylvester(self):
148159
with pytest.raises(ControlArgument, match="'scipy' not valid"):
149160
X = dlyap(A, B, C, method='scipy')
150161

151-
def test_care(self):
162+
@pytest.mark.parametrize('method',
163+
['scipy',
164+
pytest.param('slycot', marks=pytest.mark.slycot)])
165+
def test_care(self, method):
152166
A = array([[-2, -1],[-1, -1]])
153167
Q = array([[0, 0],[0, 1]])
154168
B = array([[1, 0],[0, 4]])
155169

156-
X, L, G = care(A, B, Q)
170+
X, L, G = care(A, B, Q, method=method)
157171
# print("The solution obtained is", X)
158172
M = A.T @ X + X @ A - X @ B @ B.T @ X + Q
159173
assert_array_almost_equal(M,
160174
zeros((2,2)))
161175
assert_array_almost_equal(B.T @ X, G)
162176

163177
# Compare methods
164-
if slycot_check():
178+
if method == 'slycot':
165179
X_scipy, L_scipy, G_scipy = care(A, B, Q, method='scipy')
166-
X_slycot, L_slycot, G_slycot = care(A, B, Q, method='slycot')
167-
assert_array_almost_equal(X_scipy, X_slycot)
168-
assert_array_almost_equal(np.sort(L_scipy), np.sort(L_slycot))
169-
assert_array_almost_equal(G_scipy, G_slycot)
170-
171-
def test_care_g(self):
180+
assert_array_almost_equal(X_scipy, X)
181+
assert_array_almost_equal(np.sort(L_scipy), np.sort(L))
182+
assert_array_almost_equal(G_scipy, G)
183+
184+
@pytest.mark.parametrize('method',
185+
['scipy',
186+
pytest.param('slycot', marks=pytest.mark.slycot)])
187+
def test_care_g(self, method):
172188
A = array([[-2, -1],[-1, -1]])
173189
Q = array([[0, 0],[0, 1]])
174190
B = array([[1, 0],[0, 4]])
175191
R = array([[2, 0],[0, 1]])
176192
S = array([[0, 0],[0, 0]])
177193
E = array([[2, 1],[1, 2]])
178194

179-
X,L,G = care(A,B,Q,R,S,E)
195+
X,L,G = care(A,B,Q,R,S,E,method=method)
180196
# print("The solution obtained is", X)
181197
Gref = solve(R, B.T @ X @ E + S.T)
182198
assert_array_almost_equal(Gref, G)
@@ -186,24 +202,25 @@ def test_care_g(self):
186202
zeros((2,2)))
187203

188204
# Compare methods
189-
if slycot_check():
205+
if method=='slycot':
190206
X_scipy, L_scipy, G_scipy = care(
191207
A, B, Q, R, S, E, method='scipy')
192-
X_slycot, L_slycot, G_slycot = care(
193-
A, B, Q, R, S, E, method='slycot')
194-
assert_array_almost_equal(X_scipy, X_slycot)
195-
assert_array_almost_equal(np.sort(L_scipy), np.sort(L_slycot))
196-
assert_array_almost_equal(G_scipy, G_slycot)
197-
198-
def test_care_g2(self):
208+
assert_array_almost_equal(X_scipy, X)
209+
assert_array_almost_equal(np.sort(L_scipy), np.sort(L))
210+
assert_array_almost_equal(G_scipy, G)
211+
212+
@pytest.mark.parametrize('method',
213+
['scipy',
214+
pytest.param('slycot', marks=pytest.mark.slycot)])
215+
def test_care_g2(self, method):
199216
A = array([[-2, -1],[-1, -1]])
200217
Q = array([[0, 0],[0, 1]])
201218
B = array([[1],[0]])
202219
R = 1
203220
S = array([[1],[0]])
204221
E = array([[2, 1],[1, 2]])
205222

206-
X,L,G = care(A,B,Q,R,S,E)
223+
X,L,G = care(A,B,Q,R,S,E,method=method)
207224
# print("The solution obtained is", X)
208225
Gref = 1/R * (B.T @ X @ E + S.T)
209226
assert_array_almost_equal(
@@ -213,22 +230,23 @@ def test_care_g2(self):
213230
assert_array_almost_equal(Gref , G)
214231

215232
# Compare methods
216-
if slycot_check():
233+
if method=='slycot':
217234
X_scipy, L_scipy, G_scipy = care(
218235
A, B, Q, R, S, E, method='scipy')
219-
X_slycot, L_slycot, G_slycot = care(
220-
A, B, Q, R, S, E, method='slycot')
221-
assert_array_almost_equal(X_scipy, X_slycot)
222-
assert_array_almost_equal(L_scipy, L_slycot)
223-
assert_array_almost_equal(G_scipy, G_slycot)
224-
225-
def test_dare(self):
236+
assert_array_almost_equal(X_scipy, X)
237+
assert_array_almost_equal(L_scipy, L)
238+
assert_array_almost_equal(G_scipy, G)
239+
240+
@pytest.mark.parametrize('method',
241+
['scipy',
242+
pytest.param('slycot', marks=pytest.mark.slycot)])
243+
def test_dare(self, method):
226244
A = array([[-0.6, 0],[-0.1, -0.4]])
227245
Q = array([[2, 1],[1, 0]])
228246
B = array([[2, 1],[0, 1]])
229247
R = array([[1, 0],[0, 1]])
230248

231-
X, L, G = dare(A, B, Q, R)
249+
X, L, G = dare(A, B, Q, R, method=method)
232250
# print("The solution obtained is", X)
233251
Gref = solve(B.T @ X @ B + R, B.T @ X @ A)
234252
assert_array_almost_equal(Gref, G)
@@ -243,7 +261,7 @@ def test_dare(self):
243261
B = array([[1],[0]])
244262
R = 2
245263

246-
X, L, G = dare(A, B, Q, R)
264+
X, L, G = dare(A, B, Q, R, method=method)
247265
# print("The solution obtained is", X)
248266
AtXA = A.T @ X @ A
249267
AtXB = A.T @ X @ B
@@ -256,6 +274,7 @@ def test_dare(self):
256274
lam = eigvals(A - B @ G)
257275
assert_array_less(abs(lam), 1.0)
258276

277+
@pytest.mark.slycot
259278
def test_dare_compare(self):
260279
A = np.array([[-0.6, 0], [-0.1, -0.4]])
261280
Q = np.array([[2, 1], [1, 0]])
@@ -267,23 +286,24 @@ def test_dare_compare(self):
267286
# Solve via scipy
268287
X_scipy, L_scipy, G_scipy = dare(A, B, Q, R, method='scipy')
269288

270-
# Solve via slycot
271-
if ct.slycot_check():
272-
X_slicot, L_slicot, G_slicot = dare(
273-
A, B, Q, R, S, E, method='scipy')
274-
np.testing.assert_almost_equal(X_scipy, X_slicot)
275-
np.testing.assert_almost_equal(L_scipy, L_slicot)
276-
np.testing.assert_almost_equal(G_scipy, G_slicot)
289+
X_slicot, L_slicot, G_slicot = dare(
290+
A, B, Q, R, S, E, method='scipy')
291+
np.testing.assert_almost_equal(X_scipy, X_slicot)
292+
np.testing.assert_almost_equal(L_scipy, L_slicot)
293+
np.testing.assert_almost_equal(G_scipy, G_slicot)
277294

278-
def test_dare_g(self):
295+
@pytest.mark.parametrize('method',
296+
['scipy',
297+
pytest.param('slycot', marks=pytest.mark.slycot)])
298+
def test_dare_g(self, method):
279299
A = array([[-0.6, 0],[-0.1, -0.4]])
280300
Q = array([[2, 1],[1, 3]])
281301
B = array([[1, 5],[2, 4]])
282302
R = array([[1, 0],[0, 1]])
283303
S = array([[1, 0],[2, 0]])
284304
E = array([[2, 1],[1, 2]])
285305

286-
X, L, G = dare(A, B, Q, R, S, E)
306+
X, L, G = dare(A, B, Q, R, S, E, method=method)
287307
# print("The solution obtained is", X)
288308
Gref = solve(B.T @ X @ B + R, B.T @ X @ A + S.T)
289309
assert_array_almost_equal(Gref, G)
@@ -293,16 +313,26 @@ def test_dare_g(self):
293313
# check for stable closed loop
294314
lam = eigvals(A - B @ G, E)
295315
assert_array_less(abs(lam), 1.0)
296-
297-
def test_dare_g2(self):
316+
# Compare methods
317+
if method=='slycot':
318+
X_scipy, L_scipy, G_scipy = dare(
319+
A, B, Q, R, S, E, method='scipy')
320+
assert_array_almost_equal(X_scipy, X)
321+
assert_array_almost_equal(L_scipy, L)
322+
assert_array_almost_equal(G_scipy, G)
323+
324+
@pytest.mark.parametrize('method',
325+
['scipy',
326+
pytest.param('slycot', marks=pytest.mark.slycot)])
327+
def test_dare_g2(self, method):
298328
A = array([[-0.6, 0], [-0.1, -0.4]])
299329
Q = array([[2, 1], [1, 3]])
300330
B = array([[1], [2]])
301331
R = 1
302332
S = array([[1], [2]])
303333
E = array([[2, 1], [1, 2]])
304334

305-
X, L, G = dare(A, B, Q, R, S, E)
335+
X, L, G = dare(A, B, Q, R, S, E, method=method)
306336
# print("The solution obtained is", X)
307337
AtXA = A.T @ X @ A
308338
AtXB = A.T @ X @ B
@@ -316,6 +346,13 @@ def test_dare_g2(self):
316346
lam = eigvals(A - B @ G, E)
317347
assert_array_less(abs(lam), 1.0)
318348

349+
if method=='slycot':
350+
X_scipy, L_scipy, G_scipy = dare(
351+
A, B, Q, R, S, E, method='scipy')
352+
assert_array_almost_equal(X_scipy, X)
353+
assert_array_almost_equal(L_scipy, L)
354+
assert_array_almost_equal(G_scipy, G)
355+
319356
def test_raise(self):
320357
""" Test exception raise for invalid inputs """
321358

0 commit comments

Comments
 (0)