|
54 | 54 | import math
|
55 | 55 | import numpy as np
|
56 | 56 | 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 |
59 | 58 | from numpy.random import rand, randn
|
60 | 59 | from numpy.linalg import solve, eigvals, matrix_rank
|
61 | 60 | from numpy.linalg.linalg import LinAlgError
|
@@ -516,17 +515,40 @@ def pole(self):
|
516 | 515 | def zero(self):
|
517 | 516 | """Compute the zeros of a state space system."""
|
518 | 517 |
|
519 |
| - if self.inputs > 1 or self.outputs > 1: |
520 |
| - raise NotImplementedError("StateSpace.zeros is currently \ |
521 |
| -implemented only for SISO systems.") |
| 518 | + if not self.states: |
| 519 | + return np.array([]) |
522 | 520 |
|
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) |
| 521 | + # Use AB08ND from Slycot if it's available, otherwise use |
| 522 | + # scipy.lingalg.eigvals(). |
| 523 | + try: |
| 524 | + from slycot import ab08nd |
| 525 | + |
| 526 | + out = ab08nd(self.A.shape[0], self.B.shape[1], self.C.shape[0], |
| 527 | + self.A, self.B, self.C, self.D) |
| 528 | + nu = out[0] |
| 529 | + return sp.linalg.eigvals(out[8][0:nu,0:nu], out[9][0:nu,0:nu]) |
| 530 | + except ImportError: # Slycot unavailable. Fall back to scipy. |
| 531 | + if self.C.shape[0] != self.D.shape[1]: |
| 532 | + raise NotImplementedError("StateSpace.zero only supports " |
| 533 | + "systems with the same number of " |
| 534 | + "inputs as outputs.") |
| 535 | + |
| 536 | + # This implements the QZ algorithm for finding transmission zeros |
| 537 | + # from |
| 538 | + # https://dspace.mit.edu/bitstream/handle/1721.1/841/P-0802-06587335.pdf. |
| 539 | + # The QZ algorithm solves the generalized eigenvalue problem: given |
| 540 | + # `L = [A, B; C, D]` and `M = [I_nxn 0]`, find all finite λ for |
| 541 | + # which there exist nontrivial solutions of the equation `Lz - λMz`. |
| 542 | + # |
| 543 | + # The generalized eigenvalue problem is only solvable if its |
| 544 | + # arguments are square matrices. |
| 545 | + L = concatenate((concatenate((self.A, self.B), axis=1), |
| 546 | + concatenate((self.C, self.D), axis=1)), axis=0) |
| 547 | + M = pad(eye(self.A.shape[0]), ((0, self.C.shape[0]), |
| 548 | + (0, self.B.shape[1])), "constant") |
| 549 | + return np.array([x for x in sp.linalg.eigvals(L, M, |
| 550 | + overwrite_a=True) |
| 551 | + if not isinf(x)]) |
530 | 552 |
|
531 | 553 | # Feedback around a state space system
|
532 | 554 | def feedback(self, other=1, sign=-1):
|
|
0 commit comments