Skip to content
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
16 changes: 7 additions & 9 deletions control/tests/canonical_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import pytest
import scipy.linalg

from control.tests.conftest import slycotonly

from control import ss, tf, tf2ss
from control.canonical import canonical_form, reachable_form, \
observable_form, modal_form, similarity_transform, bdschur
Expand Down Expand Up @@ -244,7 +242,7 @@ def block_diag_from_eig(eigvals):
return scipy.linalg.block_diag(*blocks)


@slycotonly
@pytest.mark.slycot
@pytest.mark.parametrize(
"eigvals, condmax, blksizes",
[
Expand All @@ -269,7 +267,7 @@ def test_bdschur_ref(eigvals, condmax, blksizes):
np.testing.assert_array_almost_equal(solve(t, a) @ t, b)


@slycotonly
@pytest.mark.slycot
@pytest.mark.parametrize(
"eigvals, sorted_blk_eigvals, sort",
[
Expand Down Expand Up @@ -300,7 +298,7 @@ def test_bdschur_sort(eigvals, sorted_blk_eigvals, sort):
blk_eigval.imag)


@slycotonly
@pytest.mark.slycot
def test_bdschur_defective():
# the eigenvalues of this simple defective matrix cannot be separated
# a previous version of the bdschur would fail on this
Expand All @@ -323,14 +321,14 @@ def test_bdschur_condmax_lt_1():
bdschur(1, condmax=np.nextafter(1, 0))


@slycotonly
@pytest.mark.slycot
def test_bdschur_invalid_sort():
# sort must be in ('continuous', 'discrete')
with pytest.raises(ValueError):
bdschur(1, sort='no-such-sort')


@slycotonly
@pytest.mark.slycot
@pytest.mark.parametrize(
"A_true, B_true, C_true, D_true",
[(np.diag([4.0, 3.0, 2.0, 1.0]), # order from largest to smallest
Expand Down Expand Up @@ -390,7 +388,7 @@ def test_modal_form(A_true, B_true, C_true, D_true):
C @ np.linalg.matrix_power(A, i) @ B)


@slycotonly
@pytest.mark.slycot
@pytest.mark.parametrize(
"condmax, len_blksizes",
[(1.1, 1),
Expand All @@ -409,7 +407,7 @@ def test_modal_form_condmax(condmax, len_blksizes):
np.testing.assert_array_almost_equal(zsys.D, xsys.D)


@slycotonly
@pytest.mark.slycot
@pytest.mark.parametrize(
"sys_type",
['continuous',
Expand Down
21 changes: 15 additions & 6 deletions control/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@

import control

def pytest_runtest_setup(item):
if (not control.exception.slycot_check()
and any(mark.name == 'slycot'
for mark in item.iter_markers())):
pytest.skip("slycot not installed")

if (not control.exception.cvxopt_check()
and any(mark.name == 'cvxopt'
for mark in item.iter_markers())):
pytest.skip("cvxopt not installed")

if (not control.exception.pandas_check()
and any(mark.name == 'pandas'
for mark in item.iter_markers())):
pytest.skip("pandas not installed")

# some common pytest marks. These can be used as test decorators or in
# pytest.param(marks=)
slycotonly = pytest.mark.skipif(
not control.exception.slycot_check(), reason="slycot not installed")
cvxoptonly = pytest.mark.skipif(
not control.exception.cvxopt_check(), reason="cvxopt not installed")


@pytest.fixture(scope="session", autouse=True)
Expand Down
5 changes: 2 additions & 3 deletions control/tests/convert_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from control.statefbk import ctrb, obsv
from control.freqplot import bode
from control.exception import slycot_check, ControlMIMONotImplemented
from control.tests.conftest import slycotonly


# Set to True to print systems to the output.
Expand Down Expand Up @@ -214,7 +213,7 @@ def testSs2tfStaticMimo(self):
np.testing.assert_allclose(numref,
np.array(gtf.num) / np.array(gtf.den))

@slycotonly
@pytest.mark.slycot
def testTf2SsDuplicatePoles(self):
"""Tests for 'too few poles for MIMO tf gh-111'"""
num = [[[1], [0]],
Expand All @@ -225,7 +224,7 @@ def testTf2SsDuplicatePoles(self):
s = ss(g)
np.testing.assert_allclose(g.poles(), s.poles())

@slycotonly
@pytest.mark.slycot
def test_tf2ss_robustness(self):
"""Unit test to make sure that tf2ss is working correctly. gh-240"""
num = [ [[0], [1]], [[1], [0]] ]
Expand Down
8 changes: 3 additions & 5 deletions control/tests/frd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
from control.xferfcn import TransferFunction
from control.frdata import frd, _convert_to_frd, FrequencyResponseData
from control import bdalg, freqplot
from control.tests.conftest import slycotonly
from control.exception import pandas_check


class TestFRD:
Expand Down Expand Up @@ -567,7 +565,7 @@ def test_mul_mimo_siso(self, left, right, expected):
np.testing.assert_array_almost_equal(expected_frd.omega, result.omega)
np.testing.assert_array_almost_equal(expected_frd.frdata, result.frdata)

@slycotonly
@pytest.mark.slycot
def test_truediv_mimo_siso(self):
omega = np.logspace(-1, 1, 10)
tf_mimo = TransferFunction([1], [1, 0]) * np.eye(2)
Expand All @@ -592,7 +590,7 @@ def test_truediv_mimo_siso(self):
np.testing.assert_array_almost_equal(expected.omega, result.omega)
np.testing.assert_array_almost_equal(expected.frdata, result.frdata)

@slycotonly
@pytest.mark.slycot
def test_rtruediv_mimo_siso(self):
omega = np.logspace(-1, 1, 10)
tf_mimo = TransferFunction([1], [1, 0]) * np.eye(2)
Expand Down Expand Up @@ -821,7 +819,7 @@ def test_named_signals():
assert f1.output_labels == ['y0']


@pytest.mark.skipif(not pandas_check(), reason="pandas not installed")
@pytest.mark.pandas
def test_to_pandas():
# Create a SISO frequency response
h1 = TransferFunction([1], [1, 2, 2])
Expand Down
5 changes: 2 additions & 3 deletions control/tests/freqresp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
singular_values_plot, singular_values_response)
from control.matlab import bode, rss, ss, tf
from control.statesp import StateSpace
from control.tests.conftest import slycotonly
from control.xferfcn import TransferFunction

pytestmark = pytest.mark.usefixtures("mplcleanup")
Expand Down Expand Up @@ -61,7 +60,7 @@ def test_freqresp_siso(ss_siso):


@pytest.mark.filterwarnings(r"ignore:freqresp\(\) is deprecated")
@slycotonly
@pytest.mark.slycot
def test_freqresp_mimo_legacy(ss_mimo):
"""Test MIMO frequency response calls"""
omega = np.linspace(10e-2, 10e2, 1000)
Expand All @@ -70,7 +69,7 @@ def test_freqresp_mimo_legacy(ss_mimo):
ctrl.freqresp(tf_mimo, omega)


@slycotonly
@pytest.mark.slycot
def test_freqresp_mimo(ss_mimo):
"""Test MIMO frequency response calls"""
omega = np.linspace(10e-2, 10e2, 1000)
Expand Down
3 changes: 1 addition & 2 deletions control/tests/lti_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
isdtime, issiso, ss, tf, tf2ss
from control.exception import slycot_check
from control.lti import LTI, bandwidth, damp, dcgain, evalfr, poles, zeros
from control.tests.conftest import slycotonly


class TestLTI:
Expand Down Expand Up @@ -59,7 +58,7 @@ def test_issiso(self):
assert issiso(sys)
assert issiso(sys, strict=True)

@slycotonly
@pytest.mark.slycot
def test_issiso_mimo(self):
# MIMO transfer function
sys = tf([[[-1, 41], [1]], [[1, 2], [3, 4]]],
Expand Down
7 changes: 3 additions & 4 deletions control/tests/mateqn_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import control as ct
from control.mateqn import lyap, dlyap, care, dare
from control.exception import ControlArgument, ControlDimension, slycot_check
from control.tests.conftest import slycotonly


class TestMatrixEquations:
Expand Down Expand Up @@ -88,7 +87,7 @@ def test_lyap_sylvester(self):
X_slycot = lyap(A, B, C, method='slycot')
assert_array_almost_equal(X_scipy, X_slycot)

@slycotonly
@pytest.mark.slycot
def test_lyap_g(self):
A = array([[-1, 2], [-3, -4]])
Q = array([[3, 1], [1, 1]])
Expand All @@ -115,7 +114,7 @@ def test_dlyap(self):
# print("The solution obtained is ", X)
assert_array_almost_equal(A @ X @ A.T - X + Q, zeros((2,2)))

@slycotonly
@pytest.mark.slycot
def test_dlyap_g(self):
A = array([[-0.6, 0],[-0.1, -0.4]])
Q = array([[3, 1],[1, 1]])
Expand All @@ -129,7 +128,7 @@ def test_dlyap_g(self):
with pytest.raises(ControlArgument, match="'scipy' not valid"):
X = dlyap(A, Q, None, E, method='scipy')

@slycotonly
@pytest.mark.slycot
def test_dlyap_sylvester(self):
A = 5
B = array([[4, 3], [4, 3]])
Expand Down
13 changes: 6 additions & 7 deletions control/tests/matlab_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
from control.exception import ControlArgument

from control.frdata import FRD
from control.tests.conftest import slycotonly

# for running these through Matlab or Octave
'''
Expand Down Expand Up @@ -487,29 +486,29 @@ def testEvalfr_mimo(self, mimo):
ref = np.array([[44.8 - 21.4j, 0.], [0., 44.8 - 21.4j]])
np.testing.assert_array_almost_equal(fr, ref)

@slycotonly
@pytest.mark.slycot
def testHsvd(self, siso):
"""Call hsvd()"""
hsvd(siso.ss1)
hsvd(siso.ss2)
hsvd(siso.ss3)

@slycotonly
@pytest.mark.slycot
def testBalred(self, siso):
"""Call balred()"""
balred(siso.ss1, 1)
balred(siso.ss2, 2)
balred(siso.ss3, [2, 2])

@slycotonly
@pytest.mark.slycot
def testModred(self, siso):
"""Call modred()"""
modred(siso.ss1, [1])
modred(siso.ss2 * siso.ss1, [0, 1])
modred(siso.ss1, [1], 'matchdc')
modred(siso.ss1, [1], 'truncate')

@slycotonly
@pytest.mark.slycot
def testPlace_varga(self, siso):
"""Call place_varga()"""
place_varga(siso.ss1.A, siso.ss1.B, [-2, -2])
Expand Down Expand Up @@ -552,7 +551,7 @@ def testObsv(self, siso):
obsv(siso.ss1.A, siso.ss1.C)
obsv(siso.ss2.A, siso.ss2.C)

@slycotonly
@pytest.mark.slycot
def testGram(self, siso):
"""Call gram()"""
gram(siso.ss1, 'c')
Expand Down Expand Up @@ -696,7 +695,7 @@ def testFRD(self):
frd2 = frd(frd1.frdata[0, 0, :], omega)
assert isinstance(frd2, FRD)

@slycotonly
@pytest.mark.slycot
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
3 changes: 1 addition & 2 deletions control/tests/minreal_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@
from control.statesp import StateSpace
from control.xferfcn import TransferFunction
from itertools import permutations
from control.tests.conftest import slycotonly


@pytest.fixture
def fixedseed(scope="class"):
np.random.seed(5)


@slycotonly
@pytest.mark.slycot
@pytest.mark.usefixtures("fixedseed")
class TestMinreal:
"""Tests for the StateSpace class."""
Expand Down
7 changes: 3 additions & 4 deletions control/tests/modelsimp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@
from control.exception import ControlArgument, ControlDimension
from control.modelsimp import balred, eigensys_realization, hsvd, markov, \
modred
from control.tests.conftest import slycotonly


class TestModelsimp:
"""Test model reduction functions"""

@slycotonly
@pytest.mark.slycot
def testHSVD(self):
A = np.array([[1., -2.], [3., -4.]])
B = np.array([[5.], [7.]])
Expand Down Expand Up @@ -390,7 +389,7 @@ def testModredTruncate(self):
np.testing.assert_array_almost_equal(rsys.D, Drtrue)


@slycotonly
@pytest.mark.slycot
def testBalredTruncate(self):
# controlable canonical realization computed in matlab for the transfer
# function:
Expand Down Expand Up @@ -431,7 +430,7 @@ def testBalredTruncate(self):
np.testing.assert_array_almost_equal(Cr, Crtrue, decimal=4)
np.testing.assert_array_almost_equal(Dr, Drtrue, decimal=4)

@slycotonly
@pytest.mark.slycot
def testBalredMatchDC(self):
# controlable canonical realization computed in matlab for the transfer
# function:
Expand Down
3 changes: 1 addition & 2 deletions control/tests/optimal_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import control as ct
import control.optimal as opt
import control.flatsys as flat
from control.tests.conftest import slycotonly
from numpy.lib import NumpyVersion


Expand Down Expand Up @@ -103,7 +102,7 @@ def test_finite_horizon_simple(method):
# optimal control problem with terminal cost set to LQR "cost to go"
# gives the same answer as LQR.
#
@slycotonly
@pytest.mark.slycot
def test_discrete_lqr():
# oscillator model defined in 2D
# Source: https://www.mpt3.org/UI/RegulationProblem
Expand Down
3 changes: 1 addition & 2 deletions control/tests/passivity_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
import pytest
import numpy
from control import ss, passivity, tf, sample_system, parallel, feedback
from control.tests.conftest import cvxoptonly
from control.exception import ControlArgument, ControlDimension

pytestmark = cvxoptonly
pytestmark = pytest.mark.cvxopt


def test_ispassive_ctime():
Expand Down
Loading
Loading