Skip to content

Commit e2877a1

Browse files
bnavigatormurrayrm
authored andcommitted
Check for symmetric matrices with machine precision (#348)
* check for symmetric matrices with machine precision * mateqn_test array instead of deprecated matrix * test mateqn parameter checks * check for int first
1 parent cb25633 commit e2877a1

File tree

2 files changed

+180
-106
lines changed

2 files changed

+180
-106
lines changed

control/mateqn.py

+23-13
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
Author: Bjorn Olofsson
4242
"""
4343

44-
from numpy import shape, size, array, asarray, copy, zeros, eye, dot
44+
from numpy import shape, size, asarray, copy, zeros, eye, dot, \
45+
finfo, inexact, atleast_2d
4546
from scipy.linalg import eigvals, solve_discrete_are, solve
4647
from .exception import ControlSlycot, ControlArgument
4748
from .statesp import _ssmatrix
@@ -122,7 +123,7 @@ def lyap(A, Q, C=None, E=None):
122123
if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
123124
raise ControlArgument("Q must be a quadratic matrix.")
124125

125-
if not (asarray(Q) == asarray(Q).T).all():
126+
if not _is_symmetric(Q):
126127
raise ControlArgument("Q must be a symmetric matrix.")
127128

128129
# Solve the Lyapunov equation by calling Slycot function sb03md
@@ -188,7 +189,7 @@ def lyap(A, Q, C=None, E=None):
188189
raise ControlArgument("E must be a square matrix with the same \
189190
dimension as A.")
190191

191-
if not (asarray(Q) == asarray(Q).T).all():
192+
if not _is_symmetric(Q):
192193
raise ControlArgument("Q must be a symmetric matrix.")
193194

194195
# Make sure we have access to the write slicot routine
@@ -309,7 +310,7 @@ def dlyap(A,Q,C=None,E=None):
309310
if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
310311
raise ControlArgument("Q must be a quadratic matrix.")
311312

312-
if not (asarray(Q) == asarray(Q).T).all():
313+
if not _is_symmetric(Q):
313314
raise ControlArgument("Q must be a symmetric matrix.")
314315

315316
# Solve the Lyapunov equation by calling the Slycot function sb03md
@@ -371,7 +372,7 @@ def dlyap(A,Q,C=None,E=None):
371372
raise ControlArgument("E must be a square matrix with the same \
372373
dimension as A.")
373374

374-
if not (asarray(Q) == asarray(Q).T).all():
375+
if not _is_symmetric(Q):
375376
raise ControlArgument("Q must be a symmetric matrix.")
376377

377378
# Solve the generalized Lyapunov equation by calling Slycot
@@ -500,10 +501,10 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True):
500501
size(B) == 1 and n > 1:
501502
raise ControlArgument("Incompatible dimensions of B matrix.")
502503

503-
if not (asarray(Q) == asarray(Q).T).all():
504+
if not _is_symmetric(Q):
504505
raise ControlArgument("Q must be a symmetric matrix.")
505506

506-
if not (asarray(R) == asarray(R).T).all():
507+
if not _is_symmetric(R):
507508
raise ControlArgument("R must be a symmetric matrix.")
508509

509510
# Create back-up of arrays needed for later computations
@@ -603,10 +604,10 @@ def care(A, B, Q, R=None, S=None, E=None, stabilizing=True):
603604
size(S) == 1 and m > 1:
604605
raise ControlArgument("Incompatible dimensions of S matrix.")
605606

606-
if not (asarray(Q) == asarray(Q).T).all():
607+
if not _is_symmetric(Q):
607608
raise ControlArgument("Q must be a symmetric matrix.")
608609

609-
if not (asarray(R) == asarray(R).T).all():
610+
if not _is_symmetric(R):
610611
raise ControlArgument("R must be a symmetric matrix.")
611612

612613
# Create back-up of arrays needed for later computations
@@ -775,10 +776,10 @@ def dare_old(A, B, Q, R, S=None, E=None, stabilizing=True):
775776
size(B) == 1 and n > 1:
776777
raise ControlArgument("Incompatible dimensions of B matrix.")
777778

778-
if not (asarray(Q) == asarray(Q).T).all():
779+
if not _is_symmetric(Q):
779780
raise ControlArgument("Q must be a symmetric matrix.")
780781

781-
if not (asarray(R) == asarray(R).T).all():
782+
if not _is_symmetric(R):
782783
raise ControlArgument("R must be a symmetric matrix.")
783784

784785
# Create back-up of arrays needed for later computations
@@ -882,10 +883,10 @@ def dare_old(A, B, Q, R, S=None, E=None, stabilizing=True):
882883
size(S) == 1 and m > 1:
883884
raise ControlArgument("Incompatible dimensions of S matrix.")
884885

885-
if not (asarray(Q) == asarray(Q).T).all():
886+
if not _is_symmetric(Q):
886887
raise ControlArgument("Q must be a symmetric matrix.")
887888

888-
if not (asarray(R) == asarray(R).T).all():
889+
if not _is_symmetric(R):
889890
raise ControlArgument("R must be a symmetric matrix.")
890891

891892
# Create back-up of arrays needed for later computations
@@ -960,3 +961,12 @@ def dare_old(A, B, Q, R, S=None, E=None, stabilizing=True):
960961
# Invalid set of input parameters
961962
else:
962963
raise ControlArgument("Invalid set of input parameters.")
964+
965+
966+
def _is_symmetric(M):
967+
M = atleast_2d(M)
968+
if isinstance(M[0, 0], inexact):
969+
eps = finfo(M.dtype).eps
970+
return ((M - M.T) < eps).all()
971+
else:
972+
return (M == M.T).all()

0 commit comments

Comments
 (0)