diff --git a/control/bdalg.py b/control/bdalg.py index 9d5aac654..b92496477 100644 --- a/control/bdalg.py +++ b/control/bdalg.py @@ -54,6 +54,7 @@ """ import scipy as sp +import numpy as np from . import xferfcn as tf from . import statesp as ss from . import frdata as frd @@ -221,18 +222,18 @@ def feedback(sys1, sys2=1, sign=-1): """ # Check for correct input types. - if not isinstance(sys1, (int, float, complex, tf.TransferFunction, - ss.StateSpace, frd.FRD)): + if not isinstance(sys1, (int, float, complex, np.number, + tf.TransferFunction, ss.StateSpace, frd.FRD)): raise TypeError("sys1 must be a TransferFunction, StateSpace " + "or FRD object, or a scalar.") - if not isinstance(sys2, (int, float, complex, tf.TransferFunction, - ss.StateSpace, frd.FRD)): + if not isinstance(sys2, (int, float, complex, np.number, + tf.TransferFunction, ss.StateSpace, frd.FRD)): raise TypeError("sys2 must be a TransferFunction, StateSpace " + "or FRD object, or a scalar.") # If sys1 is a scalar, convert it to the appropriate LTI type so that we can # its feedback member function. - if isinstance(sys1, (int, float, complex)): + if isinstance(sys1, (int, float, complex, np.number)): if isinstance(sys2, tf.TransferFunction): sys1 = tf._convertToTransferFunction(sys1) elif isinstance(sys2, ss.StateSpace): diff --git a/control/frdata.py b/control/frdata.py index ff1663b15..313da12b2 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -49,6 +49,7 @@ """ # External function declarations +import numpy as np from numpy import angle, array, empty, ones, \ real, imag, matrix, absolute, eye, linalg, where, dot from scipy.interpolate import splprep, splev @@ -219,7 +220,7 @@ def __mul__(self, other): """Multiply two LTI objects (serial connection).""" # Convert the second argument to a transfer function. - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): return FRD(self.fresp * other, self.omega, smooth=(self.ifunc is not None)) else: @@ -245,7 +246,7 @@ def __rmul__(self, other): """Right Multiply two LTI objects (serial connection).""" # Convert the second argument to an frd function. - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): return FRD(self.fresp * other, self.omega, smooth=(self.ifunc is not None)) else: @@ -272,7 +273,7 @@ def __rmul__(self, other): def __truediv__(self, other): """Divide two LTI objects.""" - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): return FRD(self.fresp * (1/other), self.omega, smooth=(self.ifunc is not None)) else: @@ -295,7 +296,7 @@ def __div__(self, other): # TODO: Division of MIMO transfer function objects is not written yet. def __rtruediv__(self, other): """Right divide two LTI objects.""" - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): return FRD(other / self.fresp, self.omega, smooth=(self.ifunc is not None)) else: @@ -449,7 +450,7 @@ def _convertToFRD(sys, omega, inputs=1, outputs=1): return FRD(fresp, omega, smooth=True) - elif isinstance(sys, (int, float, complex)): + elif isinstance(sys, (int, float, complex, np.number)): fresp = ones((outputs, inputs, len(omega)), dtype=float)*sys return FRD(fresp, omega, smooth=True) diff --git a/control/lti.py b/control/lti.py index ab7672d51..aeef5cbb1 100644 --- a/control/lti.py +++ b/control/lti.py @@ -12,6 +12,7 @@ timebaseEqual() """ +import numpy as np from numpy import absolute, real __all__ = ['issiso', 'timebase', 'timebaseEqual', 'isdtime', 'isctime', @@ -96,7 +97,7 @@ def dcgain(self): # Test to see if a system is SISO def issiso(sys, strict=False): - if isinstance(sys, (int, float, complex)) and not strict: + if isinstance(sys, (int, float, complex, np.number)) and not strict: return True elif not isinstance(sys, LTI): raise ValueError("Object is not an LTI system") @@ -114,7 +115,7 @@ def timebase(sys, strict=True): set to False, dt = True will be returned as 1. """ # System needs to be either a constant or an LTI system - if isinstance(sys, (int, float, complex)): + if isinstance(sys, (int, float, complex, np.number)): return None elif not isinstance(sys, LTI): raise ValueError("Timebase not defined") @@ -162,7 +163,7 @@ def isdtime(sys, strict=False): """ # Check to see if this is a constant - if isinstance(sys, (int, float, complex)): + if isinstance(sys, (int, float, complex, np.number)): # OK as long as strict checking is off return True if not strict else False @@ -187,7 +188,7 @@ def isctime(sys, strict=False): """ # Check to see if this is a constant - if isinstance(sys, (int, float, complex)): + if isinstance(sys, (int, float, complex, np.number)): # OK as long as strict checking is off return True if not strict else False diff --git a/control/statesp.py b/control/statesp.py index 13e662f03..816485f77 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -58,6 +58,7 @@ from numpy.random import rand, randn from numpy.linalg import solve, eigvals, matrix_rank from numpy.linalg.linalg import LinAlgError +import scipy as sp from scipy.signal import lti, cont2discrete # from exceptions import Exception import warnings @@ -218,7 +219,7 @@ def __add__(self, other): """Add two LTI systems (parallel connection).""" # Check for a couple of special cases - if (isinstance(other, (int, float, complex))): + if (isinstance(other, (int, float, complex, np.number))): # Just adding a scalar; put it in the D matrix A, B, C = self.A, self.B, self.C; D = self.D + other; @@ -275,7 +276,7 @@ def __mul__(self, other): """Multiply two LTI objects (serial connection).""" # Check for a couple of special cases - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): # Just multiplying by a scalar; change the output A, B = self.A, self.B C = self.C * other @@ -316,7 +317,7 @@ def __rmul__(self, other): """Right multiply two LTI objects (serial connection).""" # Check for a couple of special cases - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): # Just multiplying by a scalar; change the input A, C = self.A, self.C; B = self.B * other; @@ -699,11 +700,10 @@ def _convertToStateSpace(sys, **kw): # TODO: do we want to squeeze first and check dimenations? # I think this will fail if num and den aren't 1-D after # the squeeze - lti_sys = lti(squeeze(sys.num), squeeze(sys.den)) - return StateSpace(lti_sys.A, lti_sys.B, lti_sys.C, lti_sys.D, - sys.dt) + A, B, C, D = sp.signal.tf2ss(squeeze(sys.num), squeeze(sys.den)) + return StateSpace(A, B, C, D, sys.dt) - elif isinstance(sys, (int, float, complex)): + elif isinstance(sys, (int, float, complex, np.number)): if "inputs" in kw: inputs = kw["inputs"] else: diff --git a/control/xferfcn.py b/control/xferfcn.py index be21f8509..0d7d2f417 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -52,10 +52,11 @@ """ # External function declarations +import numpy as np from numpy import angle, any, array, empty, finfo, insert, ndarray, ones, \ polyadd, polymul, polyval, roots, sort, sqrt, zeros, squeeze, exp, pi, \ where, delete, real, poly, poly1d -import numpy as np +import scipy as sp from scipy.signal import lti, tf2zpk, zpk2tf, cont2discrete from copy import deepcopy from warnings import warn @@ -126,7 +127,7 @@ def __init__(self, *args): data = [num, den] for i in range(len(data)): # Check for a scalar (including 0d ndarray) - if (isinstance(data[i], (int, float, complex)) or + if (isinstance(data[i], (int, float, complex, np.number)) or (isinstance(data[i], ndarray) and data[i].ndim == 0)): # Convert scalar to list of list of array. if (isinstance(data[i], int)): @@ -135,7 +136,7 @@ def __init__(self, *args): else: data[i] = [[array([data[i]])]] elif (isinstance(data[i], (list, tuple, ndarray)) and - isinstance(data[i][0], (int, float, complex))): + isinstance(data[i][0], (int, float, complex, np.number))): # Convert array to list of list of array. if (isinstance(data[i][0], int)): # Convert integers to floats at this point @@ -146,7 +147,8 @@ def __init__(self, *args): elif (isinstance(data[i], list) and isinstance(data[i][0], list) and isinstance(data[i][0][0], (list, tuple, ndarray)) and - isinstance(data[i][0][0][0], (int, float, complex))): + isinstance(data[i][0][0][0], (int, float, complex, + np.number))): # We might already have the right format. Convert the # coefficient vectors to arrays, if necessary. for j in range(len(data[i])): @@ -363,7 +365,7 @@ def __rsub__(self, other): def __mul__(self, other): """Multiply two LTI objects (serial connection).""" # Convert the second argument to a transfer function. - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): other = _convertToTransferFunction(other, inputs=self.inputs, outputs=self.inputs) else: @@ -410,7 +412,7 @@ def __rmul__(self, other): """Right multiply two LTI objects (serial connection).""" # Convert the second argument to a transfer function. - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): other = _convertToTransferFunction(other, inputs=self.inputs, outputs=self.inputs) else: @@ -458,7 +460,7 @@ def __rmul__(self, other): def __truediv__(self, other): """Divide two LTI objects.""" - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): other = _convertToTransferFunction( other, inputs=self.inputs, outputs=self.inputs) @@ -492,7 +494,7 @@ def __div__(self, other): # TODO: Division of MIMO transfer function objects is not written yet. def __rtruediv__(self, other): """Right divide two LTI objects.""" - if isinstance(other, (int, float, complex)): + if isinstance(other, (int, float, complex, np.number)): other = _convertToTransferFunction( other, inputs=self.inputs, outputs=self.inputs) @@ -1128,22 +1130,21 @@ def _convertToTransferFunction(sys, **kw): # Each transfer function matrix row # has a common denominator. den[i][j] = list(tfout[5][i, :]) - # print(num) - # print(den) + except ImportError: # If slycot is not available, use signal.lti (SISO only) if (sys.inputs != 1 or sys.outputs != 1): raise TypeError("No support for MIMO without slycot") - lti_sys = lti(sys.A, sys.B, sys.C, sys.D) - num = squeeze(lti_sys.num) - den = squeeze(lti_sys.den) - # print(num) - # print(den) + # Do the conversion using sp.signal.ss2tf + # Note that this returns a 2D array for the numerator + num, den = sp.signal.ss2tf(sys.A, sys.B, sys.C, sys.D) + num = squeeze(num) # Convert to 1D array + den = squeeze(den) # Probably not needed return TransferFunction(num, den, sys.dt) - elif isinstance(sys, (int, float, complex)): + elif isinstance(sys, (int, float, complex, np.number)): if "inputs" in kw: inputs = kw["inputs"] else: