From 066d47e45e521ad2a1a703caa75793622bd69efa Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Wed, 24 Mar 2021 22:34:54 +0100 Subject: [PATCH 1/4] test that rss and drss return strictly_proper, if desired --- control/tests/statesp_test.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index 67cf950e7..dd250bc80 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -855,6 +855,17 @@ def test_pole(self, states, outputs, inputs): for z in p: assert z.real < 0 + @pytest.mark.parametrize('strictly_proper', [True, False]) + def test_strictly_proper(self, strictly_proper): + """Test that the strictly_proper argument returns a correct D.""" + for i in range(100): + # The probability that drss(..., strictly_proper=False) returns an + # all zero D 100 times in a row is 0.5**100 = 7.89e-31 + sys = rss(1, 1, 1, strictly_proper=strictly_proper) + if np.all(sys.D == 0.) == strictly_proper: + break + assert np.all(sys.D == 0.) == strictly_proper + class TestDrss: """These are tests for the proper functionality of statesp.drss.""" @@ -884,6 +895,17 @@ def test_pole(self, states, outputs, inputs): for z in p: assert abs(z) < 1 + @pytest.mark.parametrize('strictly_proper', [True, False]) + def test_strictly_proper(self, strictly_proper): + """Test that the strictly_proper argument returns a correct D.""" + for i in range(100): + # The probability that drss(..., strictly_proper=False) returns an + # all zero D 100 times in a row is 0.5**100 = 7.89e-31 + sys = drss(1, 1, 1, strictly_proper=strictly_proper) + if np.all(sys.D == 0.) == strictly_proper: + break + assert np.all(sys.D == 0.) == strictly_proper + class TestLTIConverter: """Test returnScipySignalLTI method""" From 98a08b60cba11b6cab5054a53d502b450017b57a Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Wed, 24 Mar 2021 22:35:29 +0100 Subject: [PATCH 2/4] test that drss returns a discrete time system with undefined time step --- control/tests/statesp_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index dd250bc80..ce52c262e 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -884,6 +884,7 @@ def test_shape(self, states, outputs, inputs): assert sys.nstates == states assert sys.ninputs == inputs assert sys.noutputs == outputs + assert sys.dt is True @pytest.mark.parametrize('states', range(1, maxStates)) @pytest.mark.parametrize('outputs', range(1, maxIO)) From 8e3a14196b33b49dc9482ae156a280fd181d9117 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Wed, 24 Mar 2021 22:37:33 +0100 Subject: [PATCH 3/4] return a discrete time system with drss() --- control/statesp.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/control/statesp.py b/control/statesp.py index 03349b0ac..59c45550c 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -1434,11 +1434,11 @@ def _convert_to_statespace(sys, **kw): # TODO: add discrete time option -def _rss_generate(states, inputs, outputs, type, strictly_proper=False): +def _rss_generate(states, inputs, outputs, cdtype, strictly_proper=False): """Generate a random state space. This does the actual random state space generation expected from rss and - drss. type is 'c' for continuous systems and 'd' for discrete systems. + drss. cdtype is 'c' for continuous systems and 'd' for discrete systems. """ @@ -1465,6 +1465,8 @@ def _rss_generate(states, inputs, outputs, type, strictly_proper=False): if outputs < 1 or outputs % 1: raise ValueError("outputs must be a positive integer. outputs = %g." % outputs) + if cdtype not in ['c', 'd']: # pragma: no cover + raise ValueError("cdtype must be `c` or `d`") # Make some poles for A. Preallocate a complex array. poles = zeros(states) + zeros(states) * 0.j @@ -1484,16 +1486,16 @@ def _rss_generate(states, inputs, outputs, type, strictly_proper=False): i += 2 elif rand() < pReal or i == states - 1: # No-oscillation pole. - if type == 'c': + if cdtype == 'c': poles[i] = -exp(randn()) + 0.j - elif type == 'd': + else: poles[i] = 2. * rand() - 1. i += 1 else: # Complex conjugate pair of oscillating poles. - if type == 'c': + if cdtype == 'c': poles[i] = complex(-exp(randn()), 3. * exp(randn())) - elif type == 'd': + else: mag = rand() phase = 2. * math.pi * rand() poles[i] = complex(mag * cos(phase), mag * sin(phase)) @@ -1546,7 +1548,11 @@ def _rss_generate(states, inputs, outputs, type, strictly_proper=False): C = C * Cmask D = D * Dmask if not strictly_proper else zeros(D.shape) - return StateSpace(A, B, C, D) + if cdtype == 'c': + ss_args = (A, B, C, D) + else: + ss_args = (A, B, C, D, True) + return StateSpace(*ss_args) # Convert a MIMO system to a SISO system @@ -1825,15 +1831,14 @@ def rss(states=1, outputs=1, inputs=1, strictly_proper=False): Parameters ---------- - states : integer + states : int Number of state variables - inputs : integer + inputs : int Number of system inputs - outputs : integer + outputs : inte Number of system outputs strictly_proper : bool, optional - If set to 'True', returns a proper system (no direct term). Default - value is 'False'. + If set to 'True', returns a proper system (no direct term). Returns ------- @@ -1867,12 +1872,15 @@ def drss(states=1, outputs=1, inputs=1, strictly_proper=False): Parameters ---------- - states : integer + states : int Number of state variables inputs : integer Number of system inputs - outputs : integer + outputs : int Number of system outputs + strictly_proper: bool, optional + If set to 'True', returns a proper system (no direct term). + Returns ------- From e50ce23d2dd7a977b6768b03f25c54a7b0515fef Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Wed, 24 Mar 2021 23:00:05 +0100 Subject: [PATCH 4/4] add test to cover _rss_generate (rss and drss) errors on invalid input --- control/statesp.py | 4 ++-- control/tests/statesp_test.py | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/control/statesp.py b/control/statesp.py index 59c45550c..c12583111 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -1465,7 +1465,7 @@ def _rss_generate(states, inputs, outputs, cdtype, strictly_proper=False): if outputs < 1 or outputs % 1: raise ValueError("outputs must be a positive integer. outputs = %g." % outputs) - if cdtype not in ['c', 'd']: # pragma: no cover + if cdtype not in ['c', 'd']: raise ValueError("cdtype must be `c` or `d`") # Make some poles for A. Preallocate a complex array. @@ -1835,7 +1835,7 @@ def rss(states=1, outputs=1, inputs=1, strictly_proper=False): Number of state variables inputs : int Number of system inputs - outputs : inte + outputs : int Number of system outputs strictly_proper : bool, optional If set to 'True', returns a proper system (no direct term). diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index ce52c262e..71e7cc4bc 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -19,7 +19,7 @@ from control.dtime import sample_system from control.lti import evalfr from control.statesp import (StateSpace, _convert_to_statespace, drss, - rss, ss, tf2ss, _statesp_defaults) + rss, ss, tf2ss, _statesp_defaults, _rss_generate) from control.tests.conftest import ismatarrayout, slycotonly from control.xferfcn import TransferFunction, ss2tf @@ -866,6 +866,17 @@ def test_strictly_proper(self, strictly_proper): break assert np.all(sys.D == 0.) == strictly_proper + @pytest.mark.parametrize('par, errmatch', + [((-1, 1, 1, 'c'), 'states must be'), + ((1, -1, 1, 'c'), 'inputs must be'), + ((1, 1, -1, 'c'), 'outputs must be'), + ((1, 1, 1, 'x'), 'cdtype must be'), + ]) + def test_rss_invalid(self, par, errmatch): + """Test invalid inputs for rss() and drss().""" + with pytest.raises(ValueError, match=errmatch): + _rss_generate(*par) + class TestDrss: """These are tests for the proper functionality of statesp.drss.""" pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy