Skip to content

Commit 2d0d513

Browse files
authored
Merge pull request #814 from Mark-Yeatman/main
Removed epsilon perturbation value in solve_passivity_LMI. Fix associated unit test.
2 parents b1d55a1 + e6b837d commit 2d0d513

2 files changed

Lines changed: 21 additions & 20 deletions

File tree

control/passivity.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
except ImportError:
1515
cvx = None
1616

17-
eps = np.nextafter(0, 1)
1817

1918
__all__ = ["get_output_fb_index", "get_input_ff_index", "ispassive",
2019
"solve_passivity_LMI"]
@@ -28,8 +27,8 @@ def solve_passivity_LMI(sys, rho=None, nu=None):
2827
passive. Inputs of None for `rho` or `nu` indicate that the function should
2928
solve for that index (they are mutually exclusive, they can't both be
3029
None, otherwise you're trying to solve a nonconvex bilinear matrix
31-
inequality.) The last element of `solution` is either the output or input
32-
passivity index, for `rho` = None and `nu` = None respectively.
30+
inequality.) The last element of the output `solution` is either the output or input
31+
passivity index, for `rho` = None and `nu` = None respectively.
3332
3433
The sources for the algorithm are:
3534
@@ -74,11 +73,8 @@ def solve_passivity_LMI(sys, rho=None, nu=None):
7473
D = sys.D
7574

7675
# account for strictly proper systems
77-
[n, m] = D.shape
78-
D = D + eps * np.eye(n, m)
79-
76+
[_, m] = D.shape
8077
[n, _] = A.shape
81-
A = A - eps*np.eye(n)
8278

8379
def make_LMI_matrix(P, rho, nu, one):
8480
q = sys.noutputs
@@ -113,7 +109,7 @@ def make_P_basis_matrices(n, rho, nu):
113109
P[i, j] = 1
114110
P[j, i] = 1
115111
matrix_list.append(make_LMI_matrix(P, 0, 0, 0).flatten())
116-
zeros = eps*np.eye(n)
112+
zeros = 0.0*np.eye(n)
117113
if rho is None:
118114
matrix_list.append(make_LMI_matrix(zeros, 1, 0, 0).flatten())
119115
elif nu is None:
@@ -149,9 +145,9 @@ def P_pos_def_constraint(n):
149145
if rho is not None and nu is not None:
150146
sys_constants = -make_LMI_matrix(np.zeros_like(A), rho, nu, 1.0)
151147
elif rho is not None:
152-
sys_constants = -make_LMI_matrix(np.zeros_like(A), rho, eps, 1.0)
148+
sys_constants = -make_LMI_matrix(np.zeros_like(A), rho, 0.0, 1.0)
153149
elif nu is not None:
154-
sys_constants = -make_LMI_matrix(np.zeros_like(A), eps, nu, 1.0)
150+
sys_constants = -make_LMI_matrix(np.zeros_like(A), 0.0, nu, 1.0)
155151

156152
sys_coefficents = np.vstack(sys_matrix_list).T
157153

@@ -174,8 +170,15 @@ def P_pos_def_constraint(n):
174170

175171
# crunch feasibility solution
176172
cvx.solvers.options['show_progress'] = False
177-
sol = cvx.solvers.sdp(c, Gs=Gs, hs=hs)
178-
return sol["x"]
173+
try:
174+
sol = cvx.solvers.sdp(c, Gs=Gs, hs=hs)
175+
return sol["x"]
176+
177+
except ZeroDivisionError as e:
178+
raise ValueError("The system is probably ill conditioned. "
179+
"Consider perturbing the system matrices by a small amount."
180+
) from e
181+
179182

180183

181184
def get_output_fb_index(sys):
@@ -194,7 +197,7 @@ def get_output_fb_index(sys):
194197
float
195198
The OFP index
196199
"""
197-
sol = solve_passivity_LMI(sys, nu=eps)
200+
sol = solve_passivity_LMI(sys, nu=0.0)
198201
if sol is None:
199202
raise RuntimeError("LMI passivity problem is infeasible")
200203
else:
@@ -218,7 +221,7 @@ def get_input_ff_index(sys):
218221
float
219222
The IFP index
220223
"""
221-
sol = solve_passivity_LMI(sys, rho=eps)
224+
sol = solve_passivity_LMI(sys, rho=0.0)
222225
if sol is None:
223226
raise RuntimeError("LMI passivity problem is infeasible")
224227
else:

control/tests/passivity_test.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,14 @@ def test_system_dimension():
9999

100100

101101
@pytest.mark.parametrize(
102-
"systemmatrices, expected",
103-
[((A, B, C, D*0.0), True),
102+
"system_matrices, expected",
103+
[((A, B, C, D*0), True),
104104
((A_d, B, C, D), True),
105-
pytest.param((A*1e12, B, C, D*0), True,
106-
marks=pytest.mark.xfail(reason="gh-761")),
107105
((A, B*0, C*0, D), True),
108106
((A*0, B, C, D), True),
109107
((A*0, B*0, C*0, D*0), True)])
110-
def test_ispassive_edge_cases(systemmatrices, expected):
111-
sys = ss(*systemmatrices)
108+
def test_ispassive_edge_cases(system_matrices, expected):
109+
sys = ss(*system_matrices)
112110
assert passivity.ispassive(sys) == expected
113111

114112

0 commit comments

Comments
 (0)