Skip to content

Commit 8607fb0

Browse files
committed
StateSpace.zero() now supports MIMO systems
scipy.linalg.eigvals() is used to solve the generalized eigenvalue problem. The zero locations used in the unit test were obtained via the zero() function in MATLAB R2017b with two more digits of precision added based on the results of StateSpace.zero().
1 parent 601b581 commit 8607fb0

File tree

2 files changed

+20
-18
lines changed

2 files changed

+20
-18
lines changed

control/statesp.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@
5454
import math
5555
import numpy as np
5656
from numpy import all, angle, any, array, asarray, concatenate, cos, delete, \
57-
dot, empty, exp, eye, matrix, ones, poly, poly1d, roots, shape, sin, \
58-
zeros, squeeze
57+
dot, empty, exp, eye, isinf, matrix, ones, pad, shape, sin, zeros, squeeze
5958
from numpy.random import rand, randn
6059
from numpy.linalg import solve, eigvals, matrix_rank
6160
from numpy.linalg.linalg import LinAlgError
@@ -516,17 +515,22 @@ def pole(self):
516515
def zero(self):
517516
"""Compute the zeros of a state space system."""
518517

519-
if self.inputs > 1 or self.outputs > 1:
520-
raise NotImplementedError("StateSpace.zeros is currently \
521-
implemented only for SISO systems.")
518+
# This implements the QZ algorithm for finding transmission zeros from
519+
# https://dspace.mit.edu/bitstream/handle/1721.1/841/P-0802-06587335.pdf.
520+
# The QZ algorithm solves the generalized eigenvalue problem: given
521+
# `L = [A, B; C, D]` and `M = [I_nxn 0]`, find all finite λ for which
522+
# there exist nontrivial solutions of the equation `Lz - λMz`.
523+
L = concatenate((concatenate((self.A, self.B), axis=1),
524+
concatenate((self.C, self.D), axis=1)), axis=0)
525+
M = pad(eye(self.A.shape[0]), ((0, self.C.shape[0]),
526+
(0, self.B.shape[1])), "constant")
522527

523-
den = poly1d(poly(self.A))
524-
# Compute the numerator based on zeros
525-
#! TODO: This is currently limited to SISO systems
526-
num = poly1d(poly(self.A - dot(self.B, self.C)) + ((self.D[0, 0] - 1) *
527-
den))
528-
529-
return roots(num)
528+
if self.states:
529+
return np.array([x for x in sp.linalg.eigvals(L, M,
530+
overwrite_a=True)
531+
if not isinf(x)])
532+
else:
533+
return np.array([])
530534

531535
# Feedback around a state space system
532536
def feedback(self, other=1, sign=-1):

control/tests/statesp_test.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,12 @@ def testPole(self):
4343
np.testing.assert_array_almost_equal(p, true_p)
4444

4545
def testZero(self):
46-
"""Evaluate the zeros of a SISO system."""
46+
"""Evaluate the zeros of a MIMO system."""
4747

48-
sys = StateSpace(self.sys1.A, [[3.], [-2.], [4.]], [[-1., 3., 2.]], [[-4.]])
49-
z = sys.zero()
48+
z = np.sort(self.sys1.zero())
49+
true_z = np.sort([44.41465, -0.490252, -5.924398])
5050

51-
np.testing.assert_array_almost_equal(z, [4.26864638637134,
52-
-3.75932319318567 + 1.10087776649554j,
53-
-3.75932319318567 - 1.10087776649554j])
51+
np.testing.assert_array_almost_equal(z, true_z)
5452

5553
def testAdd(self):
5654
"""Add two MIMO systems."""

0 commit comments

Comments
 (0)