From 4792163bde887b2e4bc1f5716299d14985a5ef7b Mon Sep 17 00:00:00 2001 From: mark-yeatman Date: Sun, 11 Dec 2022 22:09:19 -0500 Subject: [PATCH 1/3] Removed epsilon perturbation value in solve_passivity_LMI. Change unit test to reflect scaling values from empirical testing. --- control/passivity.py | 34 +++++++++++++++++++-------------- control/tests/passivity_test.py | 17 ++++++++--------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/control/passivity.py b/control/passivity.py index 3777b3d92..3d48f34f6 100644 --- a/control/passivity.py +++ b/control/passivity.py @@ -14,7 +14,6 @@ except ImportError: cvx = None -eps = np.nextafter(0, 1) __all__ = ["get_output_fb_index", "get_input_ff_index", "ispassive", "solve_passivity_LMI"] @@ -28,8 +27,8 @@ def solve_passivity_LMI(sys, rho=None, nu=None): passive. Inputs of None for `rho` or `nu` indicate that the function should solve for that index (they are mutually exclusive, they can't both be None, otherwise you're trying to solve a nonconvex bilinear matrix - inequality.) The last element of `solution` is either the output or input - passivity index, for `rho` = None and `nu` = None respectively. + inequality.) The last element of the output `solution` is either the output or input + passivity index, for `rho` = None and `nu` = None respectively. The sources for the algorithm are: @@ -74,11 +73,8 @@ def solve_passivity_LMI(sys, rho=None, nu=None): D = sys.D # account for strictly proper systems - [n, m] = D.shape - D = D + eps * np.eye(n, m) - + [_, m] = D.shape [n, _] = A.shape - A = A - eps*np.eye(n) def make_LMI_matrix(P, rho, nu, one): q = sys.noutputs @@ -113,7 +109,7 @@ def make_P_basis_matrices(n, rho, nu): P[i, j] = 1 P[j, i] = 1 matrix_list.append(make_LMI_matrix(P, 0, 0, 0).flatten()) - zeros = eps*np.eye(n) + zeros = 0.0*np.eye(n) if rho is None: matrix_list.append(make_LMI_matrix(zeros, 1, 0, 0).flatten()) elif nu is None: @@ -149,9 +145,9 @@ def P_pos_def_constraint(n): if rho is not None and nu is not None: sys_constants = -make_LMI_matrix(np.zeros_like(A), rho, nu, 1.0) elif rho is not None: - sys_constants = -make_LMI_matrix(np.zeros_like(A), rho, eps, 1.0) + sys_constants = -make_LMI_matrix(np.zeros_like(A), rho, 0.0, 1.0) elif nu is not None: - sys_constants = -make_LMI_matrix(np.zeros_like(A), eps, nu, 1.0) + sys_constants = -make_LMI_matrix(np.zeros_like(A), 0.0, nu, 1.0) sys_coefficents = np.vstack(sys_matrix_list).T @@ -174,8 +170,18 @@ def P_pos_def_constraint(n): # crunch feasibility solution cvx.solvers.options['show_progress'] = False - sol = cvx.solvers.sdp(c, Gs=Gs, hs=hs) - return sol["x"] + try: + sol = cvx.solvers.sdp(c, Gs=Gs, hs=hs) + return sol["x"] + + except ZeroDivisionError as e: + print(e) + print( + """The system is probably ill conditioned. + Consider perturbing the system matrices a small amount.""" + ) + raise(ZeroDivisionError) + def get_output_fb_index(sys): @@ -194,7 +200,7 @@ def get_output_fb_index(sys): float The OFP index """ - sol = solve_passivity_LMI(sys, nu=eps) + sol = solve_passivity_LMI(sys, nu=0.0) if sol is None: raise RuntimeError("LMI passivity problem is infeasible") else: @@ -218,7 +224,7 @@ def get_input_ff_index(sys): float The IFP index """ - sol = solve_passivity_LMI(sys, rho=eps) + sol = solve_passivity_LMI(sys, rho=0.0) if sol is None: raise RuntimeError("LMI passivity problem is infeasible") else: diff --git a/control/tests/passivity_test.py b/control/tests/passivity_test.py index 6cee1bdb6..bc1ddb871 100644 --- a/control/tests/passivity_test.py +++ b/control/tests/passivity_test.py @@ -99,16 +99,15 @@ def test_system_dimension(): @pytest.mark.parametrize( - "systemmatrices, expected", - [((A, B, C, D*0.0), True), + "system_matrices, expected", + [((A, B, C, D*1e-8), True), ((A_d, B, C, D), True), - pytest.param((A*1e12, B, C, D*0), True, - marks=pytest.mark.xfail(reason="gh-761")), - ((A, B*0, C*0, D), True), - ((A*0, B, C, D), True), - ((A*0, B*0, C*0, D*0), True)]) -def test_ispassive_edge_cases(systemmatrices, expected): - sys = ss(*systemmatrices) + pytest.param((A*1e8, B, C, D*1e-8), True), + ((A, B*1e-8, C*1e-8, D), True), + ((A*1e-8, B, C, D), True), + ((A*1e-8, B*1e-8, C*1e-8, D*1e-8), True)]) +def test_ispassive_edge_cases(system_matrices, expected): + sys = ss(*system_matrices) assert passivity.ispassive(sys) == expected From a17e3c823e733f20a428f79ea91f9383612f79b8 Mon Sep 17 00:00:00 2001 From: mark-yeatman Date: Thu, 15 Dec 2022 02:50:00 -0500 Subject: [PATCH 2/3] Remove test case. --- control/tests/passivity_test.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/control/tests/passivity_test.py b/control/tests/passivity_test.py index bc1ddb871..4d7c8e6eb 100644 --- a/control/tests/passivity_test.py +++ b/control/tests/passivity_test.py @@ -100,12 +100,11 @@ def test_system_dimension(): @pytest.mark.parametrize( "system_matrices, expected", - [((A, B, C, D*1e-8), True), + [((A, B, C, D*0), True), ((A_d, B, C, D), True), - pytest.param((A*1e8, B, C, D*1e-8), True), - ((A, B*1e-8, C*1e-8, D), True), - ((A*1e-8, B, C, D), True), - ((A*1e-8, B*1e-8, C*1e-8, D*1e-8), True)]) + ((A, B*0, C*0, D), True), + ((A*0, B, C, D), True), + ((A*0, B*0, C*0, D*0), True)]) def test_ispassive_edge_cases(system_matrices, expected): sys = ss(*system_matrices) assert passivity.ispassive(sys) == expected From e6b837d4ff463547708e8a155862473adb34290b Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 16 Dec 2022 15:44:58 -0500 Subject: [PATCH 3/3] Update control/passivity.py Co-authored-by: Ben Greiner --- control/passivity.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/control/passivity.py b/control/passivity.py index 3d48f34f6..0f4104186 100644 --- a/control/passivity.py +++ b/control/passivity.py @@ -175,12 +175,9 @@ def P_pos_def_constraint(n): return sol["x"] except ZeroDivisionError as e: - print(e) - print( - """The system is probably ill conditioned. - Consider perturbing the system matrices a small amount.""" - ) - raise(ZeroDivisionError) + raise ValueError("The system is probably ill conditioned. " + "Consider perturbing the system matrices by a small amount." + ) from e 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