-
Notifications
You must be signed in to change notification settings - Fork 441
Add passivity module, ispassive function, and passivity_test. Introduces optional dependency cvxopt. #739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add passivity module, ispassive function, and passivity_test. Introduces optional dependency cvxopt. #739
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
e8e87a4
Add passivity module, is_passive function, and passivity_test.
2eef661
Remove dependancies on lmi-sdp and sympy for is_passive.
147a24e
Use sys.nstates in stead of using A.shape[0]
fdb2d4a
Attempt to setup cvxopt similar to slycot.
dc46f3c
Remove unused import.
Mark-Yeatman 0ecc135
Apply suggestions from code review
Mark-Yeatman 649b21e
Update passivity.py
Mark-Yeatman b73e6fe
Address some review comments.
cd7ec0f
Update control/passivity.py
Mark-Yeatman d65f1d2
Remove duplicate "print" statement.
7e79c82
Fix grammar in doc string.
c57b928
Another grammar in doc string fix.
27487a9
Address edge case of stricly proper systems.
d6916c6
Expand unit tests, add info to doc string for parameters and returns,…
bb16be0
Parameterize unit tests. Catch edge case of A=0.
Mark-Yeatman cf0eac3
Run autopep8.
Mark-Yeatman 7e47d80
Add wrapper like functionality for ispassive(), so that it can be cal…
Mark-Yeatman ce11d0b
mark the whole passivity_test module as skippable
bnavigator f7d74b2
Fix bug in tests and lti.py.
0c361df
Merge branch 'main' into passivity-tools
Mark-Yeatman c2255b0
Fix merge issue.
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
''' | ||
Author: Mark Yeatman | ||
Date: May 15, 2022 | ||
''' | ||
|
||
import numpy as np | ||
from control import statesp as ss | ||
|
||
try: | ||
import cvxopt as cvx | ||
except ImportError as e: | ||
cvx = None | ||
|
||
|
||
def ispassive(sys): | ||
''' | ||
Indicates if a linear time invariant (LTI) system is passive | ||
|
||
Constructs a linear matrix inequality and a feasibility optimization | ||
such that if a solution exists, the system is passive. | ||
|
||
The source for the algorithm is: | ||
McCourt, Michael J., and Panos J. Antsaklis. | ||
"Demonstrating passivity and dissipativity using computational methods." ISIS 8 (2013). | ||
|
||
Parameters | ||
---------- | ||
sys: A continuous LTI system | ||
System to be checked. | ||
|
||
Returns | ||
------- | ||
bool: | ||
The input system passive. | ||
''' | ||
if cvx is None: | ||
raise ModuleNotFoundError("cvxopt required for passivity module") | ||
|
||
sys = ss._convert_to_statespace(sys) | ||
|
||
A = sys.A | ||
B = sys.B | ||
C = sys.C | ||
D = sys.D | ||
|
||
# account for strictly proper systems | ||
[n, m] = D.shape | ||
D = D + np.nextafter(0, 1)*np.eye(n, m) | ||
|
||
[n, _] = A.shape | ||
A = A - np.nextafter(0, 1)*np.eye(n) | ||
|
||
def make_LMI_matrix(P): | ||
V = np.vstack(( | ||
np.hstack((A.T @ P + P@A, P@B)), | ||
np.hstack((B.T@P, np.zeros_like(D)))) | ||
) | ||
return V | ||
|
||
matrix_list = [] | ||
state_space_size = sys.nstates | ||
for i in range(0, state_space_size): | ||
for j in range(0, state_space_size): | ||
if j <= i: | ||
P = np.zeros_like(A) | ||
P[i, j] = 1.0 | ||
P[j, i] = 1.0 | ||
matrix_list.append(make_LMI_matrix(P).flatten()) | ||
|
||
coefficents = np.vstack(matrix_list).T | ||
|
||
constants = -np.vstack(( | ||
np.hstack((np.zeros_like(A), - C.T)), | ||
np.hstack((- C, -D - D.T))) | ||
) | ||
|
||
number_of_opt_vars = int( | ||
(state_space_size**2-state_space_size)/2 + state_space_size) | ||
c = cvx.matrix(0.0, (number_of_opt_vars, 1)) | ||
|
||
# crunch feasibility solution | ||
cvx.solvers.options['show_progress'] = False | ||
sol = cvx.solvers.sdp(c, | ||
Gs=[cvx.matrix(coefficents)], | ||
hs=[cvx.matrix(constants)]) | ||
|
||
return (sol["x"] is not None) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
''' | ||
Author: Mark Yeatman | ||
Date: May 30, 2022 | ||
''' | ||
import pytest | ||
import numpy | ||
from control import ss, passivity, tf | ||
from control.tests.conftest import cvxoptonly | ||
|
||
|
||
pytestmark = cvxoptonly | ||
|
||
|
||
def test_ispassive(): | ||
A = numpy.array([[0, 1], [-2, -2]]) | ||
B = numpy.array([[0], [1]]) | ||
C = numpy.array([[-1, 2]]) | ||
D = numpy.array([[1.5]]) | ||
sys = ss(A, B, C, D) | ||
|
||
# happy path is passive | ||
assert(passivity.ispassive(sys)) | ||
|
||
# happy path not passive | ||
D = -D | ||
sys = ss(A, B, C, D) | ||
|
||
assert(not passivity.ispassive(sys)) | ||
|
||
|
||
A_d = numpy.array([[-2, 0], [0, 0]]) | ||
A = numpy.array([[-3, 0], [0, -2]]) | ||
B = numpy.array([[0], [1]]) | ||
C = numpy.array([[-1, 2]]) | ||
D = numpy.array([[1.5]]) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"test_input,expected", | ||
[((A, B, C, D*0.0), True), | ||
((A_d, B, C, D), True), | ||
((A*1e12, B, C, D*0), 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(test_input, expected): | ||
|
||
# strictly proper | ||
A = test_input[0] | ||
B = test_input[1] | ||
C = test_input[2] | ||
D = test_input[3] | ||
sys = ss(A, B, C, D) | ||
assert(passivity.ispassive(sys) == expected) | ||
|
||
|
||
def test_transfer_function(): | ||
sys = tf([1], [1, 2]) | ||
assert(passivity.ispassive(sys)) | ||
|
||
sys = tf([1], [1, -2]) | ||
assert(not passivity.ispassive(sys)) | ||
|
||
|
||
def test_oo_style(): | ||
A = numpy.array([[0, 1], [-2, -2]]) | ||
B = numpy.array([[0], [1]]) | ||
C = numpy.array([[-1, 2]]) | ||
D = numpy.array([[1.5]]) | ||
sys = ss(A, B, C, D) | ||
assert(sys.ispassive()) | ||
|
||
sys = tf([1], [1, 2]) | ||
assert(sys.ispassive()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.