Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
fixes in canonical and canonical_test that were assuming numpy.matrix
  • Loading branch information
sawyerbfuller committed Jul 24, 2020
commit ebd73c1d22efd121dc908498478bf9a94d0ca92d
30 changes: 13 additions & 17 deletions control/canonical.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from .statesp import StateSpace
from .statefbk import ctrb, obsv

from numpy import zeros, shape, poly, iscomplex, hstack, dot, transpose
from numpy import zeros, zeros_like, shape, poly, iscomplex, vstack, hstack, dot, \
transpose, empty
from numpy.linalg import solve, matrix_rank, eig

__all__ = ['canonical_form', 'reachable_form', 'observable_form', 'modal_form',
Expand Down Expand Up @@ -70,9 +71,9 @@ def reachable_form(xsys):
zsys = StateSpace(xsys)

# Generate the system matrices for the desired canonical form
zsys.B = zeros(shape(xsys.B))
zsys.B = zeros_like(xsys.B)
zsys.B[0, 0] = 1.0
zsys.A = zeros(shape(xsys.A))
zsys.A = zeros_like(xsys.A)
Apoly = poly(xsys.A) # characteristic polynomial
for i in range(0, xsys.states):
zsys.A[0, i] = -Apoly[i+1] / Apoly[0]
Expand Down Expand Up @@ -124,9 +125,9 @@ def observable_form(xsys):
zsys = StateSpace(xsys)

# Generate the system matrices for the desired canonical form
zsys.C = zeros(shape(xsys.C))
zsys.C = zeros_like(xsys.C)
zsys.C[0, 0] = 1
zsys.A = zeros(shape(xsys.A))
zsys.A = zeros_like(xsys.A)
Apoly = poly(xsys.A) # characteristic polynomial
for i in range(0, xsys.states):
zsys.A[i, 0] = -Apoly[i+1] / Apoly[0]
Expand All @@ -144,7 +145,7 @@ def observable_form(xsys):
raise ValueError("Transformation matrix singular to working precision.")

# Finally, compute the output matrix
zsys.B = Tzx * xsys.B
zsys.B = Tzx.dot(xsys.B)

return zsys, Tzx

Expand Down Expand Up @@ -174,9 +175,9 @@ def modal_form(xsys):
# Calculate eigenvalues and matrix of eigenvectors Tzx,
eigval, eigvec = eig(xsys.A)

# Eigenvalues and according eigenvectors are not sorted,
# Eigenvalues and corresponding eigenvectors are not sorted,
# thus modal transformation is ambiguous
# Sorting eigenvalues and respective vectors by largest to smallest eigenvalue
# Sort eigenvalues and vectors from largest to smallest eigenvalue
idx = eigval.argsort()[::-1]
eigval = eigval[idx]
eigvec = eigvec[:,idx]
Expand All @@ -189,23 +190,18 @@ def modal_form(xsys):

# Keep track of complex conjugates (need only one)
lst_conjugates = []
Tzx = None
Tzx = empty((0, xsys.A.shape[0])) # empty zero-height row matrix
for val, vec in zip(eigval, eigvec.T):
if iscomplex(val):
if val not in lst_conjugates:
lst_conjugates.append(val.conjugate())
if Tzx is not None:
Tzx = hstack((Tzx, hstack((vec.real.T, vec.imag.T))))
else:
Tzx = hstack((vec.real.T, vec.imag.T))
Tzx = vstack((Tzx, vec.real, vec.imag))
else:
# if conjugate has already been seen, skip this eigenvalue
lst_conjugates.remove(val)
else:
if Tzx is not None:
Tzx = hstack((Tzx, vec.real.T))
else:
Tzx = vec.real.T
Tzx = vstack((Tzx, vec.real))
Tzx = Tzx.T

# Generate the system matrices for the desired canonical form
zsys.A = solve(Tzx, xsys.A).dot(Tzx)
Expand Down
24 changes: 12 additions & 12 deletions control/tests/canonical_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ def test_reachable_form(self):
D_true = 42.0

# Perform a coordinate transform with a random invertible matrix
T_true = np.matrix([[-0.27144004, -0.39933167, 0.75634684, 0.44135471],
T_true = np.array([[-0.27144004, -0.39933167, 0.75634684, 0.44135471],
[-0.74855725, -0.39136285, -0.18142339, -0.50356997],
[-0.40688007, 0.81416369, 0.38002113, -0.16483334],
[-0.44769516, 0.15654653, -0.50060858, 0.72419146]])
A = np.linalg.solve(T_true, A_true)*T_true
A = np.linalg.solve(T_true, A_true).dot(T_true)
B = np.linalg.solve(T_true, B_true)
C = C_true*T_true
C = C_true.dot(T_true)
D = D_true

# Create a state space system and convert it to the reachable canonical form
Expand Down Expand Up @@ -69,11 +69,11 @@ def test_modal_form(self):
D_true = 42.0

# Perform a coordinate transform with a random invertible matrix
T_true = np.matrix([[-0.27144004, -0.39933167, 0.75634684, 0.44135471],
T_true = np.array([[-0.27144004, -0.39933167, 0.75634684, 0.44135471],
[-0.74855725, -0.39136285, -0.18142339, -0.50356997],
[-0.40688007, 0.81416369, 0.38002113, -0.16483334],
[-0.44769516, 0.15654653, -0.50060858, 0.72419146]])
A = np.linalg.solve(T_true, A_true)*T_true
A = np.linalg.solve(T_true, A_true).dot(T_true)
B = np.linalg.solve(T_true, B_true)
C = C_true*T_true
D = D_true
Expand All @@ -98,9 +98,9 @@ def test_modal_form(self):
C_true = np.array([[1, 0, 0, 1]])
D_true = np.array([[0]])

A = np.linalg.solve(T_true, A_true) * T_true
A = np.linalg.solve(T_true, A_true).dot(T_true)
B = np.linalg.solve(T_true, B_true)
C = C_true * T_true
C = C_true.dot(T_true)
D = D_true

# Create state space system and convert to modal canonical form
Expand Down Expand Up @@ -132,9 +132,9 @@ def test_modal_form(self):
C_true = np.array([[0, 1, 0, 1]])
D_true = np.array([[0]])

A = np.linalg.solve(T_true, A_true) * T_true
A = np.linalg.solve(T_true, A_true).dot(T_true)
B = np.linalg.solve(T_true, B_true)
C = C_true * T_true
C = C_true.dot(T_true)
D = D_true

# Create state space system and convert to modal canonical form
Expand Down Expand Up @@ -173,13 +173,13 @@ def test_observable_form(self):
D_true = 42.0

# Perform a coordinate transform with a random invertible matrix
T_true = np.matrix([[-0.27144004, -0.39933167, 0.75634684, 0.44135471],
T_true = np.array([[-0.27144004, -0.39933167, 0.75634684, 0.44135471],
[-0.74855725, -0.39136285, -0.18142339, -0.50356997],
[-0.40688007, 0.81416369, 0.38002113, -0.16483334],
[-0.44769516, 0.15654653, -0.50060858, 0.72419146]])
A = np.linalg.solve(T_true, A_true)*T_true
A = np.linalg.solve(T_true, A_true).dot(T_true)
B = np.linalg.solve(T_true, B_true)
C = C_true*T_true
C = C_true.dot(T_true)
D = D_true

# Create a state space system and convert it to the observable canonical form
Expand Down