Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5e16344
reorganize travis matrix, extend conftest.py
bnavigator Dec 29, 2020
c220a98
pytestify bdalg_test
bnavigator Dec 29, 2020
90a0059
pytestify canonical_test
bnavigator Jul 26, 2020
79b5bfe
pytestify config_test
bnavigator Dec 29, 2020
74d563c
pytestify convert_test.py
bnavigator Jul 24, 2020
c7cc828
pytestify ctrlutil_test
bnavigator Jul 24, 2020
d899140
pytestify delay_test.py
bnavigator Jul 24, 2020
53520e9
pytestify discrete_test
bnavigator Aug 19, 2020
659aa2d
pytestify flatsys_test
bnavigator Dec 29, 2020
1da4c5c
pytestify frd_test
bnavigator Jul 24, 2020
96f0911
pytestify freqresp_test.py
bnavigator Dec 29, 2020
caed712
pytestify input_element_int_test
bnavigator Jul 26, 2020
55a6119
pytestify iosys_test
bnavigator Dec 29, 2020
521e927
pytestify lti_test.py
bnavigator Jul 26, 2020
2c31029
pytestify mateqn_test
bnavigator Dec 29, 2020
fc2f770
pytestify matlab tests
bnavigator Jul 26, 2020
5880473
pytestify minreal_test
bnavigator Jul 26, 2020
97eedaf
pytestify modelsimp
bnavigator Dec 29, 2020
3eb0a92
pytestify nichols_test
bnavigator Jul 26, 2020
000998f
pytestify phaseplot_test
bnavigator Jul 28, 2020
3177796
pytestify rlocus_test.py
bnavigator Jul 28, 2020
f745bd2
pytestify robust tests
bnavigator Dec 29, 2020
2ee0773
pytestify sisotool_test.py
bnavigator Dec 29, 2020
e660363
pytestify slycot_convert_test.py
bnavigator Jul 28, 2020
952c511
pytestify statefbk tests
bnavigator Dec 29, 2020
6386832
pytestify statesp tests
bnavigator Dec 29, 2020
1dd2525
pytestify timeresp_test.py
bnavigator Dec 29, 2020
29883e8
pytestify xferfcn_input_test.py
bnavigator Jul 31, 2020
ca9476f
pytestify xferfcn tests
bnavigator Dec 29, 2020
2b98769
make tests work with pre #431 source code state
bnavigator Dec 29, 2020
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
Prev Previous commit
Next Next commit
pytestify bdalg_test
  • Loading branch information
bnavigator committed Dec 29, 2020
commit c220a98e7fd4445c10b96ab3c779ac3fce9eb91b
214 changes: 102 additions & 112 deletions control/tests/bdalg_test.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,53 @@
#!/usr/bin/env python
#
# bdalg_test.py - test suite for block diagram algebra
# RMM, 30 Mar 2011 (based on TestBDAlg from v0.4a)
"""bdalg_test.py - test suite for block diagram algebra

RMM, 30 Mar 2011 (based on TestBDAlg from v0.4a)
"""

import unittest
import numpy as np
from numpy import sort
import pytest

import control as ctrl
from control.xferfcn import TransferFunction
from control.statesp import StateSpace
from control.bdalg import feedback, append, connect
from control.lti import zero, pole

class TestFeedback(unittest.TestCase):

class TestFeedback:
"""These are tests for the feedback function in bdalg.py. Currently, some
of the tests are not implemented, or are not working properly. TODO: these
need to be fixed."""

def setUp(self):
"""This contains some random LTI systems and scalars for testing."""

# Two random SISO systems.
self.sys1 = TransferFunction([1, 2], [1, 2, 3])
self.sys2 = StateSpace([[1., 4.], [3., 2.]], [[1.], [-4.]],
[[1., 0.]], [[0.]]) # 2 states, SISO
self.sys3 = StateSpace([[-1.]], [[1.]], [[1.]], [[0.]]) # 1 state, SISO
@pytest.fixture
def tsys(self):
class T:
pass
# Three SISO systems.
T.sys1 = TransferFunction([1, 2], [1, 2, 3])
T.sys2 = StateSpace([[1., 4.], [3., 2.]], [[1.], [-4.]],
[[1., 0.]], [[0.]])
T.sys3 = StateSpace([[-1.]], [[1.]], [[1.]], [[0.]]) # 1 state, SISO

# Two random scalars.
self.x1 = 2.5
self.x2 = -3.
T.x1 = 2.5
T.x2 = -3.
return T

def testScalarScalar(self):
def testScalarScalar(self, tsys):
"""Scalar system with scalar feedback block."""
ans1 = feedback(tsys.x1, tsys.x2)
ans2 = feedback(tsys.x1, tsys.x2, 1.)

ans1 = feedback(self.x1, self.x2)
ans2 = feedback(self.x1, self.x2, 1.)

self.assertAlmostEqual(ans1.num[0][0][0] / ans1.den[0][0][0],
-2.5 / 6.5)
self.assertAlmostEqual(ans2.num[0][0][0] / ans2.den[0][0][0], 2.5 / 8.5)
np.testing.assert_almost_equal(
ans1.num[0][0][0] / ans1.den[0][0][0], -2.5 / 6.5)
np.testing.assert_almost_equal(
ans2.num[0][0][0] / ans2.den[0][0][0], 2.5 / 8.5)

def testScalarSS(self):
def testScalarSS(self, tsys):
"""Scalar system with state space feedback block."""

ans1 = feedback(self.x1, self.sys2)
ans2 = feedback(self.x1, self.sys2, 1.)
ans1 = feedback(tsys.x1, tsys.sys2)
ans2 = feedback(tsys.x1, tsys.sys2, 1.)

np.testing.assert_array_almost_equal(ans1.A, [[-1.5, 4.], [13., 2.]])
np.testing.assert_array_almost_equal(ans1.B, [[2.5], [-10.]])
Expand All @@ -56,35 +59,33 @@ def testScalarSS(self):
np.testing.assert_array_almost_equal(ans2.D, [[2.5]])

# Make sure default arugments work as well
ans3 = feedback(self.sys2, 1)
ans4 = feedback(self.sys2)
ans3 = feedback(tsys.sys2, 1)
ans4 = feedback(tsys.sys2)
np.testing.assert_array_almost_equal(ans3.A, ans4.A)
np.testing.assert_array_almost_equal(ans3.B, ans4.B)
np.testing.assert_array_almost_equal(ans3.C, ans4.C)
np.testing.assert_array_almost_equal(ans3.D, ans4.D)

def testScalarTF(self):
def testScalarTF(self, tsys):
"""Scalar system with transfer function feedback block."""

ans1 = feedback(self.x1, self.sys1)
ans2 = feedback(self.x1, self.sys1, 1.)
ans1 = feedback(tsys.x1, tsys.sys1)
ans2 = feedback(tsys.x1, tsys.sys1, 1.)

np.testing.assert_array_almost_equal(ans1.num, [[[2.5, 5., 7.5]]])
np.testing.assert_array_almost_equal(ans1.den, [[[1., 4.5, 8.]]])
np.testing.assert_array_almost_equal(ans2.num, [[[2.5, 5., 7.5]]])
np.testing.assert_array_almost_equal(ans2.den, [[[1., -0.5, -2.]]])

# Make sure default arugments work as well
ans3 = feedback(self.sys1, 1)
ans4 = feedback(self.sys1)
ans3 = feedback(tsys.sys1, 1)
ans4 = feedback(tsys.sys1)
np.testing.assert_array_almost_equal(ans3.num, ans4.num)
np.testing.assert_array_almost_equal(ans3.den, ans4.den)

def testSSScalar(self):
def testSSScalar(self, tsys):
"""State space system with scalar feedback block."""

ans1 = feedback(self.sys2, self.x1)
ans2 = feedback(self.sys2, self.x1, 1.)
ans1 = feedback(tsys.sys2, tsys.x1)
ans2 = feedback(tsys.sys2, tsys.x1, 1.)

np.testing.assert_array_almost_equal(ans1.A, [[-1.5, 4.], [13., 2.]])
np.testing.assert_array_almost_equal(ans1.B, [[1.], [-4.]])
Expand All @@ -95,11 +96,10 @@ def testSSScalar(self):
np.testing.assert_array_almost_equal(ans2.C, [[1., 0.]])
np.testing.assert_array_almost_equal(ans2.D, [[0.]])

def testSSSS1(self):
def testSSSS1(self, tsys):
"""State space system with state space feedback block."""

ans1 = feedback(self.sys2, self.sys2)
ans2 = feedback(self.sys2, self.sys2, 1.)
ans1 = feedback(tsys.sys2, tsys.sys2)
ans2 = feedback(tsys.sys2, tsys.sys2, 1.)

np.testing.assert_array_almost_equal(ans1.A, [[1., 4., -1., 0.],
[3., 2., 4., 0.], [1., 0., 1., 4.], [-4., 0., 3., 2]])
Expand All @@ -112,10 +112,9 @@ def testSSSS1(self):
np.testing.assert_array_almost_equal(ans2.C, [[1., 0., 0., 0.]])
np.testing.assert_array_almost_equal(ans2.D, [[0.]])

def testSSSS2(self):
def testSSSS2(self, tsys):
"""State space system with state space feedback block, including a
direct feedthrough term."""

sys3 = StateSpace([[-1., 4.], [2., -3]], [[2.], [3.]], [[-3., 1.]],
[[-2.]])
sys4 = StateSpace([[-3., -2.], [1., 4.]], [[-2.], [-6.]], [[2., -3.]],
Expand Down Expand Up @@ -147,42 +146,39 @@ def testSSSS2(self):
np.testing.assert_array_almost_equal(ans2.D, [[-0.285714285714286]])


def testSSTF(self):
def testSSTF(self, tsys):
"""State space system with transfer function feedback block."""

# This functionality is not implemented yet.
pass

def testTFScalar(self):
def testTFScalar(self, tsys):
"""Transfer function system with scalar feedback block."""

ans1 = feedback(self.sys1, self.x1)
ans2 = feedback(self.sys1, self.x1, 1.)
ans1 = feedback(tsys.sys1, tsys.x1)
ans2 = feedback(tsys.sys1, tsys.x1, 1.)

np.testing.assert_array_almost_equal(ans1.num, [[[1., 2.]]])
np.testing.assert_array_almost_equal(ans1.den, [[[1., 4.5, 8.]]])
np.testing.assert_array_almost_equal(ans2.num, [[[1., 2.]]])
np.testing.assert_array_almost_equal(ans2.den, [[[1., -0.5, -2.]]])

def testTFSS(self):
def testTFSS(self, tsys):
"""Transfer function system with state space feedback block."""

# This functionality is not implemented yet.
pass

def testTFTF(self):
def testTFTF(self, tsys):
"""Transfer function system with transfer function feedback block."""

ans1 = feedback(self.sys1, self.sys1)
ans2 = feedback(self.sys1, self.sys1, 1.)
ans1 = feedback(tsys.sys1, tsys.sys1)
ans2 = feedback(tsys.sys1, tsys.sys1, 1.)

np.testing.assert_array_almost_equal(ans1.num, [[[1., 4., 7., 6.]]])
np.testing.assert_array_almost_equal(ans1.den,
[[[1., 4., 11., 16., 13.]]])
[[[1., 4., 11., 16., 13.]]])
np.testing.assert_array_almost_equal(ans2.num, [[[1., 4., 7., 6.]]])
np.testing.assert_array_almost_equal(ans2.den, [[[1., 4., 9., 8., 5.]]])
np.testing.assert_array_almost_equal(ans2.den,
[[[1., 4., 9., 8., 5.]]])

def testLists(self):
def testLists(self, tsys):
"""Make sure that lists of various lengths work for operations"""
sys1 = ctrl.tf([1, 1], [1, 2])
sys2 = ctrl.tf([1, 3], [1, 4])
Expand All @@ -195,19 +191,19 @@ def testLists(self):
np.testing.assert_array_almost_equal(sort(pole(sys1_2)), [-4., -2.])
np.testing.assert_array_almost_equal(sort(zero(sys1_2)), [-3., -1.])

sys1_3 = ctrl.series(sys1, sys2, sys3);
sys1_3 = ctrl.series(sys1, sys2, sys3)
np.testing.assert_array_almost_equal(sort(pole(sys1_3)),
[-6., -4., -2.])
np.testing.assert_array_almost_equal(sort(zero(sys1_3)),
[-5., -3., -1.])

sys1_4 = ctrl.series(sys1, sys2, sys3, sys4);
sys1_4 = ctrl.series(sys1, sys2, sys3, sys4)
np.testing.assert_array_almost_equal(sort(pole(sys1_4)),
[-8., -6., -4., -2.])
np.testing.assert_array_almost_equal(sort(zero(sys1_4)),
[-7., -5., -3., -1.])

sys1_5 = ctrl.series(sys1, sys2, sys3, sys4, sys5);
sys1_5 = ctrl.series(sys1, sys2, sys3, sys4, sys5)
np.testing.assert_array_almost_equal(sort(pole(sys1_5)),
[-8., -6., -4., -2., -0.])
np.testing.assert_array_almost_equal(sort(zero(sys1_5)),
Expand All @@ -219,109 +215,103 @@ def testLists(self):
np.testing.assert_array_almost_equal(sort(zero(sys1_2)),
sort(zero(sys1 + sys2)))

sys1_3 = ctrl.parallel(sys1, sys2, sys3);
sys1_3 = ctrl.parallel(sys1, sys2, sys3)
np.testing.assert_array_almost_equal(sort(pole(sys1_3)),
[-6., -4., -2.])
np.testing.assert_array_almost_equal(sort(zero(sys1_3)),
sort(zero(sys1 + sys2 + sys3)))

sys1_4 = ctrl.parallel(sys1, sys2, sys3, sys4);
sys1_4 = ctrl.parallel(sys1, sys2, sys3, sys4)
np.testing.assert_array_almost_equal(sort(pole(sys1_4)),
[-8., -6., -4., -2.])
np.testing.assert_array_almost_equal(sort(zero(sys1_4)),
sort(zero(sys1 + sys2 +
sys3 + sys4)))

np.testing.assert_array_almost_equal(
sort(zero(sys1_4)),
sort(zero(sys1 + sys2 + sys3 + sys4)))

sys1_5 = ctrl.parallel(sys1, sys2, sys3, sys4, sys5);
sys1_5 = ctrl.parallel(sys1, sys2, sys3, sys4, sys5)
np.testing.assert_array_almost_equal(sort(pole(sys1_5)),
[-8., -6., -4., -2., -0.])
np.testing.assert_array_almost_equal(sort(zero(sys1_5)),
sort(zero(sys1 + sys2 +
sys3 + sys4 + sys5)))
def testMimoSeries(self):
np.testing.assert_array_almost_equal(
sort(zero(sys1_5)),
sort(zero(sys1 + sys2 + sys3 + sys4 + sys5)))

def testMimoSeries(self, tsys):
"""regression: bdalg.series reverses order of arguments"""
g1 = ctrl.ss([],[],[],[[1,2],[0,3]])
g2 = ctrl.ss([],[],[],[[1,0],[2,3]])
ref = g2*g1
tst = ctrl.series(g1,g2)
# assert_array_equal on mismatched matrices gives
# "repr failed for <matrix>: ..."
def assert_equal(x,y):
np.testing.assert_array_equal(np.asarray(x),
np.asarray(y))
assert_equal(ref.A, tst.A)
assert_equal(ref.B, tst.B)
assert_equal(ref.C, tst.C)
assert_equal(ref.D, tst.D)

def test_feedback_args(self):
g1 = ctrl.ss([], [], [], [[1, 2], [0, 3]])
g2 = ctrl.ss([], [], [], [[1, 0], [2, 3]])
ref = g2 * g1
tst = ctrl.series(g1, g2)

np.testing.assert_array_equal(ref.A, tst.A)
np.testing.assert_array_equal(ref.B, tst.B)
np.testing.assert_array_equal(ref.C, tst.C)
np.testing.assert_array_equal(ref.D, tst.D)

def test_feedback_args(self, tsys):
# Added 25 May 2019 to cover missing exception handling in feedback()
# If first argument is not LTI or convertable, generate an exception
args = ([1], self.sys2)
self.assertRaises(TypeError, ctrl.feedback, *args)
args = ([1], tsys.sys2)
with pytest.raises(TypeError):
ctrl.feedback(*args)

# If second argument is not LTI or convertable, generate an exception
args = (self.sys1, np.array([1]))
self.assertRaises(TypeError, ctrl.feedback, *args)
args = (tsys.sys1, np.array([1]))
with pytest.raises(TypeError):
ctrl.feedback(*args)

# Convert first argument to FRD, if needed
h = TransferFunction([1], [1, 2, 2])
omega = np.logspace(-1, 2, 10)
frd = ctrl.FRD(h, omega)
sys = ctrl.feedback(1, frd)
self.assertTrue(isinstance(sys, ctrl.FRD))
assert isinstance(sys, ctrl.FRD)

def testConnect(self):
sys = append(self.sys2, self.sys3) # two siso systems
def testConnect(self, tsys):
sys = append(tsys.sys2, tsys.sys3) # two siso systems

# should not raise error
connect(sys, [[1, 2], [2, -2]], [2], [1, 2])
connect(sys, [[1, 2], [2, 0]], [2], [1, 2])
connect(sys, [[1, 2, 0], [2, -2, 1]], [2], [1, 2])
connect(sys, [[1, 2], [2, -2]], [2, 1], [1])
sys3x3 = append(sys, self.sys3) # 3x3 mimo
sys3x3 = append(sys, tsys.sys3) # 3x3 mimo
connect(sys3x3, [[1, 2, 0], [2, -2, 1], [3, -3, 0]], [2], [1, 2])
connect(sys3x3, [[1, 2, 0], [2, -2, 1], [3, -3, 0]], [1, 2, 3], [3])
connect(sys3x3, [[1, 2, 0], [2, -2, 1], [3, -3, 0]], [2, 3], [2, 1])

# feedback interconnection out of bounds: input too high
Q = [[1, 3], [2, -2]]
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [2], [1, 2])
# feedback interconnection out of bounds: input too low
Q = [[0, 2], [2, -2]]
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [2], [1, 2])

# feedback interconnection out of bounds: output too high
Q = [[1, 2], [2, -3]]
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [2], [1, 2])
Q = [[1, 2], [2, 4]]
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [2], [1, 2])

# input/output index testing
Q = [[1, 2], [2, -2]] # OK interconnection
Q = [[1, 2], [2, -2]] # OK interconnection

# input index is out of bounds: too high
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [3], [1, 2])
# input index is out of bounds: too low
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [0], [1, 2])
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [-2], [1, 2])
# output index is out of bounds: too high
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [2], [1, 3])
# output index is out of bounds: too low
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [2], [1, 0])
with self.assertRaises(IndexError):
with pytest.raises(IndexError):
connect(sys, Q, [2], [1, -1])


if __name__ == "__main__":
unittest.main()