Skip to content

Allow unit tests to run without slycot #122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions control/statesp.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,10 @@ def _convertToStateSpace(sys, **kw):
ssout[3][:sys.outputs, :states],
ssout[4], sys.dt)
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")

# 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
Expand Down
28 changes: 26 additions & 2 deletions control/tests/convert_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from control.statefbk import ctrb, obsv
from control.freqplot import bode
from control.matlab import tf

from control.exception import slycot_check

class TestConvert(unittest.TestCase):
"""Test state space and transfer function conversions."""
Expand All @@ -35,7 +35,8 @@ def setUp(self):
# Maximum number of states to test + 1
self.maxStates = 4
# Maximum number of inputs and outputs to test + 1
self.maxIO = 5
# If slycot is not installed, just check SISO
self.maxIO = 5 if slycot_check() else 2
# Set to True to print systems to the output.
self.debug = False
# get consistent results
Expand Down Expand Up @@ -161,6 +162,29 @@ def testConvert(self):
np.testing.assert_array_almost_equal( \
ssorig_imag, tfxfrm_imag)

def testConvertMIMO(self):
"""Test state space to transfer function conversion."""
verbose = self.debug

# Do a MIMO conversation and make sure that it is processed
# correctly both with and without slycot
#
# Example from issue #120, jgoppert
import control

# Set up a transfer function (should always work)
tfcn = control.tf([[[-235, 1.146e4],
[-235, 1.146E4],
[-235, 1.146E4, 0]]],
[[[1, 48.78, 0],
[1, 48.78, 0, 0],
[0.008, 1.39, 48.78]]])

# Convert to state space and look for an error
if (not slycot_check()):
self.assertRaises(TypeError, control.tf2ss, tfcn)


def suite():
return unittest.TestLoader().loadTestsFromTestCase(TestConvert)

Expand Down
6 changes: 6 additions & 0 deletions control/tests/frd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from control.frdata import FRD, _convertToFRD
from control import bdalg
from control import freqplot
from control.exception import slycot_check
import matplotlib.pyplot as plt


Expand Down Expand Up @@ -179,6 +180,7 @@ def testNyquist(self):
freqplot.nyquist(f1, f1.omega)
# plt.savefig('/dev/null', format='svg')

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testMIMO(self):
sys = StateSpace([[-0.5, 0.0], [0.0, -1.0]],
[[1.0, 0.0], [0.0, 1.0]],
Expand All @@ -193,6 +195,7 @@ def testMIMO(self):
sys.freqresp([0.1, 1.0, 10])[1],
f1.freqresp([0.1, 1.0, 10])[1])

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testMIMOfb(self):
sys = StateSpace([[-0.5, 0.0], [0.0, -1.0]],
[[1.0, 0.0], [0.0, 1.0]],
Expand All @@ -208,6 +211,7 @@ def testMIMOfb(self):
f1.freqresp([0.1, 1.0, 10])[1],
f2.freqresp([0.1, 1.0, 10])[1])

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testMIMOfb2(self):
sys = StateSpace(np.matrix('-2.0 0 0; 0 -1 1; 0 0 -3'),
np.matrix('1.0 0; 0 0; 0 1'),
Expand All @@ -223,6 +227,7 @@ def testMIMOfb2(self):
f1.freqresp([0.1, 1.0, 10])[1],
f2.freqresp([0.1, 1.0, 10])[1])

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testMIMOMult(self):
sys = StateSpace([[-0.5, 0.0], [0.0, -1.0]],
[[1.0, 0.0], [0.0, 1.0]],
Expand All @@ -238,6 +243,7 @@ def testMIMOMult(self):
(f1*f2).freqresp([0.1, 1.0, 10])[1],
(sys*sys).freqresp([0.1, 1.0, 10])[1])

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testMIMOSmooth(self):
sys = StateSpace([[-0.5, 0.0], [0.0, -1.0]],
[[1.0, 0.0], [0.0, 1.0]],
Expand Down
2 changes: 2 additions & 0 deletions control/tests/freqresp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import numpy as np
from control.statesp import StateSpace
from control.matlab import ss, tf, bode
from control.exception import slycot_check
import matplotlib.pyplot as plt

class TestFreqresp(unittest.TestCase):
Expand Down Expand Up @@ -42,6 +43,7 @@ def test_doubleint(self):
sys = ss(A, B, C, D);
bode(sys);

@unittest.skipIf(not slycot_check(), "slycot not installed")
def test_mimo(self):
# MIMO
B = np.matrix('1,0;0,1')
Expand Down
93 changes: 54 additions & 39 deletions control/tests/matlab_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import scipy as sp
from control.matlab import *
from control.frdata import FRD
from control.exception import slycot_check
import warnings

# for running these through Matlab or Octave
Expand Down Expand Up @@ -165,12 +166,13 @@ def testStep(self):
np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
np.testing.assert_array_almost_equal(tout, t)

#Test MIMO system, which contains ``siso_ss1`` twice
sys = self.mimo_ss1
y_00, _t = step(sys, T=t, input=0, output=0)
y_11, _t = step(sys, T=t, input=1, output=1)
np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)
if slycot_check():
# Test MIMO system, which contains ``siso_ss1`` twice
sys = self.mimo_ss1
y_00, _t = step(sys, T=t, input=0, output=0)
y_11, _t = step(sys, T=t, input=1, output=1)
np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)

def testImpulse(self):
t = np.linspace(0, 1, 10)
Expand Down Expand Up @@ -206,12 +208,13 @@ def testImpulse(self):
np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
np.testing.assert_array_almost_equal(tout, t)

#Test MIMO system, which contains ``siso_ss1`` twice
sys = self.mimo_ss1
y_00, _t = impulse(sys, T=t, input=0, output=0)
y_11, _t = impulse(sys, T=t, input=1, output=1)
np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)
if slycot_check():
#Test MIMO system, which contains ``siso_ss1`` twice
sys = self.mimo_ss1
y_00, _t = impulse(sys, T=t, input=0, output=0)
y_11, _t = impulse(sys, T=t, input=1, output=1)
np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)

def testInitial(self):
#Test SISO system
Expand All @@ -229,13 +232,14 @@ def testInitial(self):
np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
np.testing.assert_array_almost_equal(tout, t)

#Test MIMO system, which contains ``siso_ss1`` twice
sys = self.mimo_ss1
x0 = np.matrix(".5; 1.; .5; 1.")
y_00, _t = initial(sys, T=t, X0=x0, input=0, output=0)
y_11, _t = initial(sys, T=t, X0=x0, input=1, output=1)
np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)
if slycot_check():
#Test MIMO system, which contains ``siso_ss1`` twice
sys = self.mimo_ss1
x0 = np.matrix(".5; 1.; .5; 1.")
y_00, _t = initial(sys, T=t, X0=x0, input=0, output=0)
y_11, _t = initial(sys, T=t, X0=x0, input=1, output=1)
np.testing.assert_array_almost_equal(y_00, youttrue, decimal=4)
np.testing.assert_array_almost_equal(y_11, youttrue, decimal=4)

def testLsim(self):
t = np.linspace(0, 1, 10)
Expand All @@ -259,18 +263,19 @@ def testLsim(self):
yout, _t, _xout = lsim(self.siso_ss1, u, t, x0)
np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)

#Test MIMO system, which contains ``siso_ss1`` twice
#first system: initial value, second system: step response
u = np.array([[0., 1.], [0, 1], [0, 1], [0, 1], [0, 1],
[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]])
x0 = np.matrix(".5; 1; 0; 0")
youttrue = np.array([[11., 9.], [8.1494, 17.6457], [5.9361, 24.7072],
[4.2258, 30.4855], [2.9118, 35.2234],
[1.9092, 39.1165], [1.1508, 42.3227],
[0.5833, 44.9694], [0.1645, 47.1599],
[-0.1391, 48.9776]])
yout, _t, _xout = lsim(self.mimo_ss1, u, t, x0)
np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)
if slycot_check():
#Test MIMO system, which contains ``siso_ss1`` twice
#first system: initial value, second system: step response
u = np.array([[0., 1.], [0, 1], [0, 1], [0, 1], [0, 1],
[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]])
x0 = np.matrix(".5; 1; 0; 0")
youttrue = np.array([[11., 9.], [8.1494, 17.6457],
[5.9361, 24.7072], [4.2258, 30.4855],
[2.9118, 35.2234], [1.9092, 39.1165],
[1.1508, 42.3227], [0.5833, 44.9694],
[0.1645, 47.1599], [-0.1391, 48.9776]])
yout, _t, _xout = lsim(self.mimo_ss1, u, t, x0)
np.testing.assert_array_almost_equal(yout, youttrue, decimal=4)

def testMargin(self):
#! TODO: check results to make sure they are OK
Expand Down Expand Up @@ -310,11 +315,12 @@ def testDcgain(self):
gain_sim],
[59, 59, 59, 59, 59])

# Test with MIMO system, which contains ``siso_ss1`` twice
gain_mimo = dcgain(self.mimo_ss1)
# print('gain_mimo: \n', gain_mimo)
np.testing.assert_array_almost_equal(gain_mimo, [[59., 0 ],
[0, 59.]])
if slycot_check():
# Test with MIMO system, which contains ``siso_ss1`` twice
gain_mimo = dcgain(self.mimo_ss1)
# print('gain_mimo: \n', gain_mimo)
np.testing.assert_array_almost_equal(gain_mimo, [[59., 0 ],
[0, 59.]])

def testBode(self):
bode(self.siso_ss1)
Expand Down Expand Up @@ -370,29 +376,35 @@ def testEvalfr(self):
evalfr(self.siso_tf1, w)
evalfr(self.siso_tf2, w)
evalfr(self.siso_tf3, w)
np.testing.assert_array_almost_equal(
evalfr(self.mimo_ss1, w),
np.array( [[44.8-21.4j, 0.], [0., 44.8-21.4j]]))
if slycot_check():
np.testing.assert_array_almost_equal(
evalfr(self.mimo_ss1, w),
np.array( [[44.8-21.4j, 0.], [0., 44.8-21.4j]]))

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testHsvd(self):
hsvd(self.siso_ss1)
hsvd(self.siso_ss2)
hsvd(self.siso_ss3)

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testBalred(self):
balred(self.siso_ss1, 1)
balred(self.siso_ss2, 2)
balred(self.siso_ss3, [2, 2])

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testModred(self):
modred(self.siso_ss1, [1])
modred(self.siso_ss2 * self.siso_ss3, [1, 2])
modred(self.siso_ss3, [1], 'matchdc')
modred(self.siso_ss3, [1], 'truncate')

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testPlace(self):
place(self.siso_ss1.A, self.siso_ss1.B, [-2, -2])

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testLQR(self):
(K, S, E) = lqr(self.siso_ss1.A, self.siso_ss1.B, np.eye(2), np.eye(1))
(K, S, E) = lqr(self.siso_ss2.A, self.siso_ss2.B, np.eye(3), \
Expand All @@ -416,6 +428,7 @@ def testObsv(self):
obsv(self.siso_ss1.A, self.siso_ss1.C)
obsv(self.siso_ss2.A, self.siso_ss2.C)

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testGram(self):
gram(self.siso_ss1, 'c')
gram(self.siso_ss2, 'c')
Expand Down Expand Up @@ -452,6 +465,7 @@ def testSISOssdata(self):
for i in range(len(ssdata_1)):
np.testing.assert_array_almost_equal(ssdata_1[i], ssdata_2[i])

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testMIMOssdata(self):
m = (self.mimo_ss1.A, self.mimo_ss1.B, self.mimo_ss1.C, self.mimo_ss1.D)
ssdata_1 = ssdata(self.mimo_ss1);
Expand Down Expand Up @@ -532,6 +546,7 @@ def testFRD(self):
frd2 = frd(frd1.fresp[0,0,:], omega)
assert isinstance(frd2, FRD)

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testMinreal(self, verbose=False):
"""Test a minreal model reduction"""
#A = [-2, 0.5, 0; 0.5, -0.3, 0; 0, 0, -0.1]
Expand Down
2 changes: 2 additions & 0 deletions control/tests/minreal_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
from control.statesp import StateSpace
from control.xferfcn import TransferFunction
from itertools import permutations
from control.exception import slycot_check

@unittest.skipIf(not slycot_check(), "slycot not installed")
class TestMinreal(unittest.TestCase):
"""Tests for the StateSpace class."""

Expand Down
2 changes: 2 additions & 0 deletions control/tests/statefbk_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,13 @@ def check_LQR(self, K, S, poles, Q, R):
np.testing.assert_array_almost_equal(poles, poles_expected)


@unittest.skipIf(not slycot_check(), "slycot not installed")
def test_LQR_integrator(self):
A, B, Q, R = 0., 1., 10., 2.
K, S, poles = lqr(A, B, Q, R)
self.check_LQR(K, S, poles, Q, R)

@unittest.skipIf(not slycot_check(), "slycot not installed")
def test_LQR_3args(self):
sys = ss(0., 1., 1., 0.)
Q, R = 10., 2.
Expand Down
3 changes: 3 additions & 0 deletions control/tests/statesp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from control import matlab
from control.statesp import StateSpace, _convertToStateSpace
from control.xferfcn import TransferFunction
from control.exception import slycot_check

class TestStateSpace(unittest.TestCase):
"""Tests for the StateSpace class."""
Expand Down Expand Up @@ -113,6 +114,7 @@ def testEvalFr(self):

np.testing.assert_almost_equal(sys.evalfr(1.), resp)

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testFreqResp(self):
"""Evaluate the frequency response at multiple frequencies."""

Expand All @@ -138,6 +140,7 @@ def testFreqResp(self):
np.testing.assert_almost_equal(phase, truephase)
np.testing.assert_equal(omega, trueomega)

@unittest.skipIf(not slycot_check(), "slycot not installed")
def testMinreal(self):
"""Test a minreal model reduction"""
#A = [-2, 0.5, 0; 0.5, -0.3, 0; 0, 0, -0.1]
Expand Down
Loading