Skip to content

Commit beb7bbe

Browse files
committed
try least-squares if regular solve returns nan
1 parent b61f58d commit beb7bbe

File tree

1 file changed

+15
-11
lines changed

1 file changed

+15
-11
lines changed

control/statesp.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@
5353

5454
import math
5555
import numpy as np
56-
from numpy import any, array, asarray, concatenate, cos, delete, \
57-
dot, empty, exp, eye, isinf, ones, pad, sin, zeros, squeeze, pi
56+
from numpy import any, asarray, concatenate, cos, delete, \
57+
dot, empty, exp, eye, isinf, ones, pad, sin, zeros, squeeze
5858
from numpy.random import rand, randn
59-
from numpy.linalg import solve, eigvals, matrix_rank
59+
from numpy.linalg import eigvals, lstsq, matrix_rank, solve
6060
from numpy.linalg.linalg import LinAlgError
6161
import scipy as sp
6262
from scipy.signal import cont2discrete
@@ -887,6 +887,8 @@ def horner(self, x, warn_infinite=True):
887887
"""
888888
# Make sure the argument is a 1D array of complex numbers
889889
x_arr = np.atleast_1d(x).astype(complex, copy=False)
890+
if len(x_arr.shape) > 1:
891+
raise ValueError("input list must be 1D")
890892

891893
# return fast on systems with 0 or 1 state
892894
if not config.defaults['statesp.use_numpy_matrix']:
@@ -908,21 +910,21 @@ def horner(self, x, warn_infinite=True):
908910
# Fall back because either Slycot unavailable or cannot handle
909911
# certain cases.
910912

911-
# Make sure that we are operating on a simple list
912-
if len(x_arr.shape) > 1:
913-
raise ValueError("input list must be 1D")
914-
915913
# Preallocate
916914
out = empty((self.noutputs, self.ninputs, len(x_arr)),
917915
dtype=complex)
918916

919917
# TODO: can this be vectorized?
920918
for idx, x_idx in enumerate(x_arr):
921919
try:
922-
out[:, :, idx] = np.dot(
923-
self.C,
924-
solve(x_idx * eye(self.nstates) - self.A, self.B)) \
925-
+ self.D
920+
xI_A = x_idx * eye(self.nstates) - self.A
921+
xI_A_inv = solve(xI_A, self.B)
922+
# gh-664: not singular but the underlying LAPACK routine
923+
# was not satisfied with the condition. Try least-squares
924+
# solver.
925+
if np.any(np.isnan(xI_A_inv)): # pragma: no cover
926+
xI_A_inv, _, _, _ = lstsq(xI_A, self.B, rcond=None)
927+
out[:, :, idx] = np.dot(self.C, xI_A_inv) + self.D
926928
except LinAlgError:
927929
# Issue a warning messsage, for consistency with xferfcn
928930
if warn_infinite:
@@ -936,6 +938,8 @@ def horner(self, x, warn_infinite=True):
936938
else:
937939
out[:, :, idx] = complex(np.inf, np.nan)
938940

941+
942+
939943
return out
940944

941945
def freqresp(self, omega):

0 commit comments

Comments
 (0)