Skip to content

Commit

Permalink
make tests work with pre python-control#431 source code state
Browse files Browse the repository at this point in the history
revert this commit when merging into/with python-control#431

(remove statesp_test.py::test_copy_constructor_nodt if not applicable)
  • Loading branch information
bnavigator committed Dec 29, 2020
1 parent ca9476f commit deeb0c7
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 181 deletions.
1 change: 1 addition & 0 deletions control/tests/config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ def test_change_default_dt(self, dt):
# lambda t, x, u: x, inputs=1, outputs=1)
# assert nlsys.dt == dt

@pytest.mark.skip("implemented in gh-431")
def test_change_default_dt_static(self):
"""Test that static gain systems always have dt=None"""
ct.set_defaults('control', default_dt=0)
Expand Down
75 changes: 22 additions & 53 deletions control/tests/discrete_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
import numpy as np
import pytest

from control import (StateSpace, TransferFunction, bode, common_timebase,
evalfr, feedback, forced_response, impulse_response,
isctime, isdtime, rss, sample_system, step_response,
timebase)
from control import StateSpace, TransferFunction, feedback, step_response, \
isdtime, timebase, isctime, sample_system, bode, impulse_response, \
evalfr, timebaseEqual, forced_response, rss


class TestDiscrete:
Expand Down Expand Up @@ -52,21 +51,13 @@ class Tsys:

return T

def testCompatibleTimebases(self, tsys):
"""test that compatible timebases don't throw errors and vice versa"""
common_timebase(tsys.siso_ss1.dt, tsys.siso_tf1.dt)
common_timebase(tsys.siso_ss1.dt, tsys.siso_ss1c.dt)
common_timebase(tsys.siso_ss1d.dt, tsys.siso_ss1.dt)
common_timebase(tsys.siso_ss1.dt, tsys.siso_ss1d.dt)
common_timebase(tsys.siso_ss1.dt, tsys.siso_ss1d.dt)
common_timebase(tsys.siso_ss1d.dt, tsys.siso_ss3d.dt)
common_timebase(tsys.siso_ss3d.dt, tsys.siso_ss1d.dt)
with pytest.raises(ValueError):
# cont + discrete
common_timebase(tsys.siso_ss1d.dt, tsys.siso_ss1c.dt)
with pytest.raises(ValueError):
# incompatible discrete
common_timebase(tsys.siso_ss1d.dt, tsys.siso_ss2d.dt)
def testTimebaseEqual(self, tsys):
"""Test for equal timebases and not so equal ones"""
assert timebaseEqual(tsys.siso_ss1, tsys.siso_tf1)
assert timebaseEqual(tsys.siso_ss1, tsys.siso_ss1c)
assert not timebaseEqual(tsys.siso_ss1d, tsys.siso_ss1c)
assert not timebaseEqual(tsys.siso_ss1d, tsys.siso_ss2d)
assert not timebaseEqual(tsys.siso_ss1d, tsys.siso_ss3d)

def testSystemInitialization(self, tsys):
# Check to make sure systems are discrete time with proper variables
Expand All @@ -84,18 +75,6 @@ def testSystemInitialization(self, tsys):
assert tsys.siso_tf2d.dt == 0.2
assert tsys.siso_tf3d.dt is True

# keyword argument check
# dynamic systems
assert TransferFunction(1, [1, 1], dt=0.1).dt == 0.1
assert TransferFunction(1, [1, 1], 0.1).dt == 0.1
assert StateSpace(1,1,1,1, dt=0.1).dt == 0.1
assert StateSpace(1,1,1,1, 0.1).dt == 0.1
# static gain system, dt argument should still override default dt
assert TransferFunction(1, [1,], dt=0.1).dt == 0.1
assert TransferFunction(1, [1,], 0.1).dt == 0.1
assert StateSpace(0,0,1,1, dt=0.1).dt == 0.1
assert StateSpace(0,0,1,1, 0.1).dt == 0.1

def testCopyConstructor(self, tsys):
for sys in (tsys.siso_ss1, tsys.siso_ss1c, tsys.siso_ss1d):
newsys = StateSpace(sys)
Expand Down Expand Up @@ -135,7 +114,6 @@ def test_timebase_conversions(self, tsys):
assert timebase(tf1*tf2) == timebase(tf2)
assert timebase(tf1*tf3) == timebase(tf3)
assert timebase(tf1*tf4) == timebase(tf4)
assert timebase(tf3*tf4) == timebase(tf4)
assert timebase(tf2*tf1) == timebase(tf2)
assert timebase(tf3*tf1) == timebase(tf3)
assert timebase(tf4*tf1) == timebase(tf4)
Expand All @@ -150,36 +128,33 @@ def test_timebase_conversions(self, tsys):

# Make sure discrete time without sampling is converted correctly
assert timebase(tf3*tf3) == timebase(tf3)
assert timebase(tf3*tf4) == timebase(tf4)
assert timebase(tf3+tf3) == timebase(tf3)
assert timebase(tf3+tf4) == timebase(tf4)
assert timebase(feedback(tf3, tf3)) == timebase(tf3)
assert timebase(feedback(tf3, tf4)) == timebase(tf4)

# Make sure all other combinations are errors
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
tf2 * tf3
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
tf3 * tf2
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
tf2 * tf4
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
tf4 * tf2
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
tf2 + tf3
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
tf3 + tf2
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
tf2 + tf4
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
tf4 + tf2
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
feedback(tf2, tf3)
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
feedback(tf3, tf2)
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
feedback(tf2, tf4)
with pytest.raises(ValueError, match="incompatible timebases"):
with pytest.raises(ValueError, match="different sampling times"):
feedback(tf4, tf2)

def testisdtime(self, tsys):
Expand Down Expand Up @@ -237,7 +212,6 @@ def testAddition(self, tsys):
sys = tsys.siso_ss1c + tsys.siso_ss1c
sys = tsys.siso_ss1d + tsys.siso_ss1d
sys = tsys.siso_ss3d + tsys.siso_ss3d
sys = tsys.siso_ss1d + tsys.siso_ss3d

with pytest.raises(ValueError):
StateSpace.__add__(tsys.mimo_ss1c, tsys.mimo_ss1d)
Expand All @@ -254,7 +228,6 @@ def testAddition(self, tsys):
sys = tsys.siso_tf1c + tsys.siso_tf1c
sys = tsys.siso_tf1d + tsys.siso_tf1d
sys = tsys.siso_tf2d + tsys.siso_tf2d
sys = tsys.siso_tf1d + tsys.siso_tf3d

with pytest.raises(ValueError):
TransferFunction.__add__(tsys.siso_tf1c, tsys.siso_tf1d)
Expand All @@ -279,7 +252,6 @@ def testMultiplication(self, tsys):
sys = tsys.siso_ss1d * tsys.siso_ss1
sys = tsys.siso_ss1c * tsys.siso_ss1c
sys = tsys.siso_ss1d * tsys.siso_ss1d
sys = tsys.siso_ss1d * tsys.siso_ss3d

with pytest.raises(ValueError):
StateSpace.__mul__(tsys.mimo_ss1c, tsys.mimo_ss1d)
Expand All @@ -295,7 +267,6 @@ def testMultiplication(self, tsys):
sys = tsys.siso_tf1d * tsys.siso_tf1
sys = tsys.siso_tf1c * tsys.siso_tf1c
sys = tsys.siso_tf1d * tsys.siso_tf1d
sys = tsys.siso_tf1d * tsys.siso_tf3d

with pytest.raises(ValueError):
TransferFunction.__mul__(tsys.siso_tf1c, tsys.siso_tf1d)
Expand All @@ -322,7 +293,6 @@ def testFeedback(self, tsys):
sys = feedback(tsys.siso_ss1d, tsys.siso_ss1)
sys = feedback(tsys.siso_ss1c, tsys.siso_ss1c)
sys = feedback(tsys.siso_ss1d, tsys.siso_ss1d)
sys = feedback(tsys.siso_ss1d, tsys.siso_ss3d)

with pytest.raises(ValueError):
feedback(tsys.mimo_ss1c, tsys.mimo_ss1d)
Expand All @@ -338,7 +308,6 @@ def testFeedback(self, tsys):
sys = feedback(tsys.siso_tf1d, tsys.siso_tf1)
sys = feedback(tsys.siso_tf1c, tsys.siso_tf1c)
sys = feedback(tsys.siso_tf1d, tsys.siso_tf1d)
sys = feedback(tsys.siso_tf1d, tsys.siso_tf3d)

with pytest.raises(ValueError):
feedback(tsys.siso_tf1c, tsys.siso_tf1d)
Expand Down
30 changes: 15 additions & 15 deletions control/tests/iosys_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ class TSys:
"""Return some test systems"""
# Create a single input/single output linear system
T.siso_linsys = ct.StateSpace(
[[-1, 1], [0, -2]], [[0], [1]], [[1, 0]], [[0]])
[[-1, 1], [0, -2]], [[0], [1]], [[1, 0]], [[0]], 0)

# Create a multi input/multi output linear system
T.mimo_linsys1 = ct.StateSpace(
[[-1, 1], [0, -2]], [[1, 0], [0, 1]],
[[1, 0], [0, 1]], np.zeros((2, 2)))
[[1, 0], [0, 1]], np.zeros((2, 2)), 0)

# Create a multi input/multi output linear system
T.mimo_linsys2 = ct.StateSpace(
[[-1, 1], [0, -2]], [[0, 1], [1, 0]],
[[1, 0], [0, 1]], np.zeros((2, 2)))
[[1, 0], [0, 1]], np.zeros((2, 2)), 0)

# Create simulation parameters
T.T = np.linspace(0, 10, 100)
Expand Down Expand Up @@ -281,7 +281,7 @@ def test_algebraic_loop(self, tsys):
linsys = tsys.siso_linsys
lnios = ios.LinearIOSystem(linsys)
nlios = ios.NonlinearIOSystem(None, \
lambda t, x, u, params: u*u, inputs=1, outputs=1)
lambda t, x, u, params: u*u, inputs=1, outputs=1, dt=0)
nlios1 = nlios.copy()
nlios2 = nlios.copy()

Expand Down Expand Up @@ -310,7 +310,7 @@ def test_algebraic_loop(self, tsys):
iosys = ios.InterconnectedSystem(
(lnios, nlios), # linear system w/ nonlinear feedback
((1,), # feedback interconnection (sig to 0)
(0, (1, 0, -1))),
(0, (1, 0, -1))),
0, # input to linear system
0 # output from linear system
)
Expand All @@ -331,7 +331,7 @@ def test_algebraic_loop(self, tsys):

# Algebraic loop due to feedthrough term
linsys = ct.StateSpace(
[[-1, 1], [0, -2]], [[0], [1]], [[1, 0]], [[1]])
[[-1, 1], [0, -2]], [[0], [1]], [[1, 0]], [[1]], 0)
lnios = ios.LinearIOSystem(linsys)
iosys = ios.InterconnectedSystem(
(nlios, lnios), # linear system w/ nonlinear feedback
Expand Down Expand Up @@ -374,7 +374,7 @@ def test_rmul(self, tsys):
# Also creates a nested interconnected system
ioslin = ios.LinearIOSystem(tsys.siso_linsys)
nlios = ios.NonlinearIOSystem(None, \
lambda t, x, u, params: u*u, inputs=1, outputs=1)
lambda t, x, u, params: u*u, inputs=1, outputs=1, dt=0)
sys1 = nlios * ioslin
sys2 = ios.InputOutputSystem.__rmul__(nlios, sys1)

Expand Down Expand Up @@ -414,7 +414,7 @@ def test_feedback(self, tsys):
# Linear system with constant feedback (via "nonlinear" mapping)
ioslin = ios.LinearIOSystem(tsys.siso_linsys)
nlios = ios.NonlinearIOSystem(None, \
lambda t, x, u, params: u, inputs=1, outputs=1)
lambda t, x, u, params: u, inputs=1, outputs=1, dt=0)
iosys = ct.feedback(ioslin, nlios)
linsys = ct.feedback(tsys.siso_linsys, 1)

Expand Down Expand Up @@ -740,7 +740,7 @@ def test_named_signals(self, tsys):
inputs = ('u[0]', 'u[1]'),
outputs = ('y[0]', 'y[1]'),
states = tsys.mimo_linsys1.states,
name = 'sys1')
name = 'sys1', dt=0)
sys2 = ios.LinearIOSystem(tsys.mimo_linsys2,
inputs = ('u[0]', 'u[1]'),
outputs = ('y[0]', 'y[1]'),
Expand Down Expand Up @@ -1015,7 +1015,7 @@ def test_duplicates(self, tsys):
nlios = ios.NonlinearIOSystem(lambda t, x, u, params: x,
lambda t, x, u, params: u * u,
inputs=1, outputs=1, states=1,
name="sys")
name="sys", dt=0)

# Duplicate objects
with pytest.warns(UserWarning, match="Duplicate object"):
Expand All @@ -1024,7 +1024,7 @@ def test_duplicates(self, tsys):
# Nonduplicate objects
nlios1 = nlios.copy()
nlios2 = nlios.copy()
with pytest.warns(UserWarning, match="copy of sys") as record:
with pytest.warns(UserWarning, match="Duplicate name"):
ios_series = nlios1 * nlios2
assert "copy of sys_1.x[0]" in ios_series.state_index.keys()
assert "copy of sys.x[0]" in ios_series.state_index.keys()
Expand All @@ -1033,10 +1033,10 @@ def test_duplicates(self, tsys):
iosys_siso = ct.LinearIOSystem(tsys.siso_linsys)
nlios1 = ios.NonlinearIOSystem(None,
lambda t, x, u, params: u * u,
inputs=1, outputs=1, name="sys")
inputs=1, outputs=1, name="sys", dt=0)
nlios2 = ios.NonlinearIOSystem(None,
lambda t, x, u, params: u * u,
inputs=1, outputs=1, name="sys")
inputs=1, outputs=1, name="sys", dt=0)

with pytest.warns(UserWarning, match="Duplicate name"):
ct.InterconnectedSystem((nlios1, iosys_siso, nlios2),
Expand All @@ -1045,10 +1045,10 @@ def test_duplicates(self, tsys):
# Same system, different names => everything should be OK
nlios1 = ios.NonlinearIOSystem(None,
lambda t, x, u, params: u * u,
inputs=1, outputs=1, name="nlios1")
inputs=1, outputs=1, name="nlios1", dt=0)
nlios2 = ios.NonlinearIOSystem(None,
lambda t, x, u, params: u * u,
inputs=1, outputs=1, name="nlios2")
inputs=1, outputs=1, name="nlios2", dt=0)
with pytest.warns(None) as record:
ct.InterconnectedSystem((nlios1, iosys_siso, nlios2),
inputs=0, outputs=0, states=0)
Expand Down
83 changes: 1 addition & 82 deletions control/tests/lti_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest

from control import c2d, tf, tf2ss, NonlinearIOSystem
from control.lti import (LTI, common_timebase, damp, dcgain, isctime, isdtime,
from control.lti import (LTI, damp, dcgain, isctime, isdtime,
issiso, pole, timebaseEqual, zero)
from control.tests.conftest import slycotonly

Expand Down Expand Up @@ -72,84 +72,3 @@ def test_dcgain(self):
sys = tf(84, [1, 2])
np.testing.assert_equal(sys.dcgain(), 42)
np.testing.assert_equal(dcgain(sys), 42)

@pytest.mark.parametrize("dt1, dt2, expected",
[(None, None, True),
(None, 0, True),
(None, 1, True),
pytest.param(None, True, True,
marks=pytest.mark.xfail(
reason="returns false")),
(0, 0, True),
(0, 1, False),
(0, True, False),
(1, 1, True),
(1, 2, False),
(1, True, False),
(True, True, True)])
def test_timebaseEqual_deprecated(self, dt1, dt2, expected):
"""Test that timbaseEqual throws a warning and returns as documented"""
sys1 = tf([1], [1, 2, 3], dt1)
sys2 = tf([1], [1, 4, 5], dt2)

print(sys1.dt)
print(sys2.dt)

with pytest.deprecated_call():
assert timebaseEqual(sys1, sys2) is expected
# Make sure behaviour is symmetric
with pytest.deprecated_call():
assert timebaseEqual(sys2, sys1) is expected

@pytest.mark.parametrize("dt1, dt2, expected",
[(None, None, None),
(None, 0, 0),
(None, 1, 1),
(None, True, True),
(True, True, True),
(True, 1, 1),
(1, 1, 1),
(0, 0, 0),
])
@pytest.mark.parametrize("sys1", [True, False])
@pytest.mark.parametrize("sys2", [True, False])
def test_common_timebase(self, dt1, dt2, expected, sys1, sys2):
"""Test that common_timbase adheres to :ref:`conventions-ref`"""
i1 = tf([1], [1, 2, 3], dt1) if sys1 else dt1
i2 = tf([1], [1, 4, 5], dt2) if sys2 else dt2
assert common_timebase(i1, i2) == expected
# Make sure behaviour is symmetric
assert common_timebase(i2, i1) == expected

@pytest.mark.parametrize("i1, i2",
[(True, 0),
(0, 1),
(1, 2)])
def test_common_timebase_errors(self, i1, i2):
"""Test that common_timbase throws errors on invalid combinations"""
with pytest.raises(ValueError):
common_timebase(i1, i2)
# Make sure behaviour is symmetric
with pytest.raises(ValueError):
common_timebase(i2, i1)

@pytest.mark.parametrize("dt, ref, strictref",
[(None, True, False),
(0, False, False),
(1, True, True),
(True, True, True)])
@pytest.mark.parametrize("objfun, arg",
[(LTI, ()),
(NonlinearIOSystem, (lambda x: x, ))])
def test_isdtime(self, objfun, arg, dt, ref, strictref):
"""Test isdtime and isctime functions to follow convention"""
obj = objfun(*arg, dt=dt)

assert isdtime(obj) == ref
assert isdtime(obj, strict=True) == strictref

if dt is not None:
ref = not ref
strictref = not strictref
assert isctime(obj) == ref
assert isctime(obj, strict=True) == strictref

0 comments on commit deeb0c7

Please sign in to comment.