Skip to content

Move _tf_close_coeff back to testing realm and make better use of assertion messages #1109

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 5 commits into from
Feb 3, 2025
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
5 changes: 1 addition & 4 deletions .github/scripts/set-conda-test-matrix.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
""" set-conda-test-matrix.py
"""Create test matrix for conda packages in OS/BLAS test matrix workflow."""

Create test matrix for conda packages
"""
import json, re
from pathlib import Path

osmap = {'linux': 'ubuntu',
Expand Down
4 changes: 1 addition & 3 deletions .github/scripts/set-pip-test-matrix.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
""" set-pip-test-matrix.py
"""Create test matrix for pip wheels in OS/BLAS test matrix workflow."""

Create test matrix for pip wheels
"""
import json
from pathlib import Path

Expand Down
35 changes: 16 additions & 19 deletions control/tests/bdalg_test.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
"""bdalg_test.py - test suite for block diagram algebra
"""bdalg_test.py - test suite for block diagram algebra.

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

import control as ctrl
import numpy as np
from numpy import sort
import pytest

import control as ctrl
from control.xferfcn import TransferFunction, _tf_close_coeff
from control.bdalg import _ensure_tf, append, connect, feedback
from control.lti import poles, zeros
from control.statesp import StateSpace
from control.bdalg import feedback, append, connect
from control.lti import zeros, poles
from control.bdalg import _ensure_tf
from control.tests.conftest import assert_tf_close_coeff
from control.xferfcn import TransferFunction
from numpy import sort


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."""
"""Tests for the feedback function in bdalg.py."""

@pytest.fixture
def tsys(self):
Expand Down Expand Up @@ -180,7 +177,7 @@ def testTFTF(self, tsys):
[[[1., 4., 9., 8., 5.]]])

def testLists(self, tsys):
"""Make sure that lists of various lengths work for operations"""
"""Make sure that lists of various lengths work for operations."""
sys1 = ctrl.tf([1, 1], [1, 2])
sys2 = ctrl.tf([1, 3], [1, 4])
sys3 = ctrl.tf([1, 5], [1, 6])
Expand Down Expand Up @@ -237,7 +234,7 @@ def testLists(self, tsys):
sort(zeros(sys1 + sys2 + sys3 + sys4 + sys5)))

def testMimoSeries(self, tsys):
"""regression: bdalg.series reverses order of arguments"""
"""regression: bdalg.series reverses order of arguments."""
g1 = ctrl.ss([], [], [], [[1, 2], [0, 3]])
g2 = ctrl.ss([], [], [], [[1, 0], [2, 3]])
ref = g2 * g1
Expand Down Expand Up @@ -430,9 +427,9 @@ class TestEnsureTf:
],
)
def test_ensure(self, arraylike_or_tf, dt, tf):
"""Test nominal cases"""
"""Test nominal cases."""
ensured_tf = _ensure_tf(arraylike_or_tf, dt)
assert _tf_close_coeff(tf, ensured_tf)
assert_tf_close_coeff(tf, ensured_tf)

@pytest.mark.parametrize(
"arraylike_or_tf, dt, exception",
Expand Down Expand Up @@ -460,7 +457,7 @@ def test_ensure(self, arraylike_or_tf, dt, tf):
],
)
def test_error_ensure(self, arraylike_or_tf, dt, exception):
"""Test error cases"""
"""Test error cases."""
with pytest.raises(exception):
_ensure_tf(arraylike_or_tf, dt)

Expand Down Expand Up @@ -624,7 +621,7 @@ class TestTfCombineSplit:
def test_combine_tf(self, tf_array, tf):
"""Test combining transfer functions."""
tf_combined = ctrl.combine_tf(tf_array)
assert _tf_close_coeff(tf_combined, tf)
assert_tf_close_coeff(tf_combined, tf)

@pytest.mark.parametrize(
"tf_array, tf",
Expand Down Expand Up @@ -712,12 +709,12 @@ def test_split_tf(self, tf_array, tf):
# Test entry-by-entry
for i in range(tf_split.shape[0]):
for j in range(tf_split.shape[1]):
assert _tf_close_coeff(
assert_tf_close_coeff(
tf_split[i, j],
tf_array[i, j],
)
# Test combined
assert _tf_close_coeff(
assert_tf_close_coeff(
ctrl.combine_tf(tf_split),
ctrl.combine_tf(tf_array),
)
Expand Down
48 changes: 43 additions & 5 deletions control/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""conftest.py - pytest local plugins and fixtures"""
"""conftest.py - pytest local plugins, fixtures, marks and functions."""

import os
from contextlib import contextmanager
Expand All @@ -9,6 +9,7 @@

import control


# some common pytest marks. These can be used as test decorators or in
# pytest.param(marks=)
slycotonly = pytest.mark.skipif(
Expand Down Expand Up @@ -61,7 +62,7 @@ def mplcleanup():

@pytest.fixture(scope="function")
def legacy_plot_signature():
"""Turn off warnings for calls to plotting functions with old signatures"""
"""Turn off warnings for calls to plotting functions with old signatures."""
import warnings
warnings.filterwarnings(
'ignore', message='passing systems .* is deprecated',
Expand All @@ -75,14 +76,51 @@ def legacy_plot_signature():

@pytest.fixture(scope="function")
def ignore_future_warning():
"""Turn off warnings for functions that generate FutureWarning"""
"""Turn off warnings for functions that generate FutureWarning."""
import warnings
warnings.filterwarnings(
'ignore', message='.*deprecated', category=FutureWarning)
yield
warnings.resetwarnings()


# Allow pytest.mark.slow to mark slow tests (skip with pytest -m "not slow")

def pytest_configure(config):
"""Allow pytest.mark.slow to mark slow tests.

skip with pytest -m "not slow"
"""
config.addinivalue_line("markers", "slow: mark test as slow to run")


def assert_tf_close_coeff(actual, desired, rtol=1e-5, atol=1e-8):
"""Check if two transfer functions have close coefficients.

Parameters
----------
actual, desired : TransferFunction
Transfer functions to compare.
rtol : float
Relative tolerance for ``np.testing.assert_allclose``.
atol : float
Absolute tolerance for ``np.testing.assert_allclose``.

Raises
------
AssertionError
"""
# Check number of outputs and inputs
assert actual.noutputs == desired.noutputs
assert actual.ninputs == desired.ninputs
# Check timestep
assert actual.dt == desired.dt
# Check coefficient arrays
for i in range(actual.noutputs):
for j in range(actual.ninputs):
np.testing.assert_allclose(
actual.num[i][j],
desired.num[i][j],
rtol=rtol, atol=atol)
np.testing.assert_allclose(
actual.den[i][j],
desired.den[i][j],
rtol=rtol, atol=atol)
Loading
Loading