|
9 | 9 |
|
10 | 10 | import operator |
11 | 11 |
|
12 | | -import control as ct |
13 | 12 | import numpy as np |
14 | 13 | import pytest |
| 14 | +from numpy.linalg import solve |
| 15 | +from numpy.testing import assert_array_almost_equal |
| 16 | +from scipy.linalg import block_diag, eigvals |
| 17 | + |
| 18 | +import control as ct |
15 | 19 | from control.config import defaults |
16 | 20 | from control.dtime import sample_system |
17 | 21 | from control.lti import evalfr |
18 | 22 | from control.statesp import (StateSpace, _convert_to_statespace, _rss_generate, |
19 | 23 | _statesp_defaults, drss, linfnorm, rss, ss, tf2ss) |
| 24 | +from control.tests.conftest import (assert_tf_close_coeff, editsdefaults, |
| 25 | + slycotonly) |
20 | 26 | from control.xferfcn import TransferFunction, ss2tf |
21 | | -from numpy.linalg import solve |
22 | | -from numpy.testing import assert_array_almost_equal |
23 | | -from scipy.linalg import block_diag, eigvals |
24 | | - |
25 | | -from .conftest import assert_tf_close_coeff, editsdefaults, slycotonly |
26 | 27 |
|
27 | 28 |
|
28 | 29 | class TestStateSpace: |
@@ -579,25 +580,28 @@ def test_pow(self, request, sysname, power): |
579 | 580 | np.testing.assert_allclose(expected.D, result.D) |
580 | 581 |
|
581 | 582 | @slycotonly |
582 | | - @pytest.mark.parametrize("order", ["inv*sys", "sys*inv"]) |
583 | | - @pytest.mark.parametrize("sysname", ["sys222", "sys322"]) |
| 583 | + @pytest.mark.parametrize("order", ["left", "right"]) |
| 584 | + @pytest.mark.parametrize("sysname", ["sys121", "sys222", "sys322"]) |
584 | 585 | def test_pow_inv(self, request, sysname, order): |
585 | | - """Power of -1 (inverse of biproper system). |
| 586 | + """Check for identity when multiplying by inverse. |
586 | 587 |
|
587 | | - Testing transfer function representations to avoid the |
588 | | - non-uniqueness of the state-space representation. Once MIMO |
589 | | - canonical forms are supported, can check canonical state-space |
590 | | - matrices instead. |
| 588 | + This holds approximately true for a few steps but is very |
| 589 | + unstable due to numerical precision. Don't assume this in |
| 590 | + real life. For testing purposes only! |
591 | 591 | """ |
592 | 592 | sys = request.getfixturevalue(sysname) |
593 | | - if order == "inv*sys": |
594 | | - result = (sys**-1 * sys).minreal() |
| 593 | + if order == "left": |
| 594 | + combined = sys**-1 * sys |
595 | 595 | else: |
596 | | - result = (sys * sys**-1).minreal() |
597 | | - expected = StateSpace([], [], [], np.eye(sys.ninputs), dt=0) |
598 | | - assert_tf_close_coeff( |
599 | | - ss2tf(expected).minreal(), |
600 | | - ss2tf(result).minreal()) |
| 596 | + combined = sys * sys**-1 |
| 597 | + combined = combined.minreal() |
| 598 | + np.testing.assert_allclose(combined.dcgain(), np.eye(sys.ninputs), |
| 599 | + atol=1e-7) |
| 600 | + T = np.linspace(0., 0.3, 100) |
| 601 | + U = np.random.rand(sys.ninputs, len(T)) |
| 602 | + R = combined.forced_response(T=T, U=U, squeeze=False) |
| 603 | + # Check that the output is the same as the input |
| 604 | + np.testing.assert_allclose(R.outputs, U) |
601 | 605 |
|
602 | 606 | @slycotonly |
603 | 607 | def test_truediv(self, sys222, sys322): |
|
0 commit comments