diff --git a/_service b/_service new file mode 100644 index 0000000..413b85c --- /dev/null +++ b/_service @@ -0,0 +1,3 @@ + + + diff --git a/control-0.8.2.tar.gz b/control-0.8.2.tar.gz deleted file mode 100644 index 042838f..0000000 --- a/control-0.8.2.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:726e8c36a253a54c8886df31f860d740d70de4f8b041421d5df078c3bff3aadb -size 182584 diff --git a/control-0.8.3.tar.gz b/control-0.8.3.tar.gz new file mode 100644 index 0000000..3dfbeb8 --- /dev/null +++ b/control-0.8.3.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fcfdcf39f96523cb1f2cf7bf7b8ae68ec3ef8350e5c55e17e02afdb0872edbb +size 249610 diff --git a/python-control-fixtestaugw.patch b/pr365-copy-PR-320-for-robust_array_test.patch similarity index 63% rename from python-control-fixtestaugw.patch rename to pr365-copy-PR-320-for-robust_array_test.patch index ec16daa..6d0ccb6 100644 --- a/python-control-fixtestaugw.patch +++ b/pr365-copy-PR-320-for-robust_array_test.patch @@ -1,8 +1,17 @@ -diff --git a/control/tests/robust_test.py b/control/tests/robust_test.py -index 9a3419f..b23f06c 100644 ---- a/control/tests/robust_test.py -+++ b/control/tests/robust_test.py -@@ -245,7 +245,7 @@ class TestAugw(unittest.TestCase): +From b4b3d10c1ab5ef1bbf4d992f045408f6f6e88de7 Mon Sep 17 00:00:00 2001 +From: Benjamin Greiner +Date: Sat, 4 Jan 2020 22:30:42 -0800 +Subject: [PATCH] copy PR 320 for robust_array_test + +--- + control/tests/robust_array_test.py | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/control/tests/robust_array_test.py b/control/tests/robust_array_test.py +index 51114f87..62cf8c6c 100644 +--- a/control/tests/robust_array_test.py ++++ b/control/tests/robust_array_test.py +@@ -261,7 +261,7 @@ def testMimoW3(self): @unittest.skipIf(not slycot_check(), "slycot not installed") def testMimoW123(self): """MIMO plant with all weights""" @@ -11,7 +20,7 @@ index 9a3419f..b23f06c 100644 g = ss([[-1., -2], [-3, -4]], [[1., 0.], [0., 1.]], [[1., 0.], [0., 1.]], -@@ -295,10 +295,10 @@ class TestAugw(unittest.TestCase): +@@ -311,10 +311,10 @@ def testMimoW123(self): self.siso_almost_equal(w2[1, 1], p[3, 3]) # u->z3 should be w3*g w3g = w3 * g; diff --git a/pr366-ease-precision-tolerance.patch b/pr366-ease-precision-tolerance.patch new file mode 100644 index 0000000..b382bd2 --- /dev/null +++ b/pr366-ease-precision-tolerance.patch @@ -0,0 +1,181 @@ +From f4915eb44c45b17a9ac271e7acec58470243b5fa Mon Sep 17 00:00:00 2001 +From: Benjamin Greiner +Date: Mon, 6 Jan 2020 12:19:56 -0800 +Subject: [PATCH] ease precision tolerenace for iosys tests + +--- + control/tests/iosys_test.py | 42 ++++++++++++++++++------------------- + 1 file changed, 21 insertions(+), 21 deletions(-) + +diff --git a/control/tests/iosys_test.py b/control/tests/iosys_test.py +index aaf2243c..9fdac09c 100644 +--- a/control/tests/iosys_test.py ++++ b/control/tests/iosys_test.py +@@ -60,7 +60,7 @@ def test_linear_iosys(self): + lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0) + ios_t, ios_y = ios.input_output_response(iosys, T, U, X0) + np.testing.assert_array_almost_equal(lti_t, ios_t) +- np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3) ++ np.testing.assert_allclose(lti_y, ios_y,atol=0.002,rtol=0.) + + @unittest.skipIf(StrictVersion(sp.__version__) < "1.0", + "requires SciPy 1.0 or greater") +@@ -75,7 +75,7 @@ def test_tf2io(self): + lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0) + ios_t, ios_y = ios.input_output_response(iosys, T, U, X0) + np.testing.assert_array_almost_equal(lti_t, ios_t) +- np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3) ++ np.testing.assert_allclose(lti_y, ios_y,atol=0.002,rtol=0.) + + def test_ss2io(self): + # Create an input/output system from the linear system +@@ -161,7 +161,7 @@ def test_nonlinear_iosys(self): + lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0) + ios_t, ios_y = ios.input_output_response(nlsys, T, U, X0) + np.testing.assert_array_almost_equal(lti_t, ios_t) +- np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3) ++ np.testing.assert_allclose(lti_y, ios_y,atol=0.002,rtol=0.) + + def test_linearize(self): + # Create a single input/single output linear system +@@ -214,7 +214,7 @@ def test_connect(self): + iosys_series, T, U, X0, return_x=True) + lti_t, lti_y, lti_x = ct.forced_response(linsys_series, T, U, X0) + np.testing.assert_array_almost_equal(lti_t, ios_t) +- np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3) ++ np.testing.assert_allclose(lti_y, ios_y,atol=0.002,rtol=0.) + + # Connect systems with different timebases + linsys2c = self.siso_linsys +@@ -231,7 +231,7 @@ def test_connect(self): + iosys_series, T, U, X0, return_x=True) + lti_t, lti_y, lti_x = ct.forced_response(linsys_series, T, U, X0) + np.testing.assert_array_almost_equal(lti_t, ios_t) +- np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3) ++ np.testing.assert_allclose(lti_y, ios_y,atol=0.002,rtol=0.) + + # Feedback interconnection + linsys_feedback = ct.feedback(linsys1, linsys2) +@@ -246,7 +246,7 @@ def test_connect(self): + iosys_feedback, T, U, X0, return_x=True) + lti_t, lti_y, lti_x = ct.forced_response(linsys_feedback, T, U, X0) + np.testing.assert_array_almost_equal(lti_t, ios_t) +- np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3) ++ np.testing.assert_allclose(lti_y, ios_y,atol=0.002,rtol=0.) + + @unittest.skipIf(StrictVersion(sp.__version__) < "1.0", + "requires SciPy 1.0 or greater") +@@ -357,7 +357,7 @@ def test_summer(self): + + lin_t, lin_y, lin_x = ct.forced_response(linsys_parallel, T, U, X0) + ios_t, ios_y = ios.input_output_response(iosys_parallel, T, U, X0) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + @unittest.skipIf(StrictVersion(sp.__version__) < "1.0", + "requires SciPy 1.0 or greater") +@@ -420,7 +420,7 @@ def test_feedback(self): + + ios_t, ios_y = ios.input_output_response(iosys, T, U, X0) + lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0) +- np.testing.assert_array_almost_equal(ios_y, lti_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lti_y,atol=0.002,rtol=0.) + + @unittest.skipIf(StrictVersion(sp.__version__) < "1.0", + "requires SciPy 1.0 or greater") +@@ -442,7 +442,7 @@ def test_bdalg_functions(self): + iosys_series = ct.series(linio1, linio2) + lin_t, lin_y, lin_x = ct.forced_response(linsys_series, T, U, X0) + ios_t, ios_y = ios.input_output_response(iosys_series, T, U, X0) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + # Make sure that systems don't commute + linsys_series = ct.series(linsys2, linsys1) +@@ -454,21 +454,21 @@ def test_bdalg_functions(self): + iosys_parallel = ct.parallel(linio1, linio2) + lin_t, lin_y, lin_x = ct.forced_response(linsys_parallel, T, U, X0) + ios_t, ios_y = ios.input_output_response(iosys_parallel, T, U, X0) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + # Negation + linsys_negate = ct.negate(linsys1) + iosys_negate = ct.negate(linio1) + lin_t, lin_y, lin_x = ct.forced_response(linsys_negate, T, U, X0) + ios_t, ios_y = ios.input_output_response(iosys_negate, T, U, X0) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + # Feedback interconnection + linsys_feedback = ct.feedback(linsys1, linsys2) + iosys_feedback = ct.feedback(linio1, linio2) + lin_t, lin_y, lin_x = ct.forced_response(linsys_feedback, T, U, X0) + ios_t, ios_y = ios.input_output_response(iosys_feedback, T, U, X0) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + @unittest.skipIf(StrictVersion(sp.__version__) < "1.0", + "requires SciPy 1.0 or greater") +@@ -496,26 +496,26 @@ def test_nonsquare_bdalg(self): + iosys_multiply = iosys_3i2o * iosys_2i3o + lin_t, lin_y, lin_x = ct.forced_response(linsys_multiply, T, U2, X0) + ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U2, X0) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + linsys_multiply = linsys_2i3o * linsys_3i2o + iosys_multiply = iosys_2i3o * iosys_3i2o + lin_t, lin_y, lin_x = ct.forced_response(linsys_multiply, T, U3, X0) + ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U3, X0) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + # Right multiplication + # TODO: add real tests once conversion from other types is supported + iosys_multiply = ios.InputOutputSystem.__rmul__(iosys_3i2o, iosys_2i3o) + ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U3, X0) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + # Feedback + linsys_multiply = ct.feedback(linsys_3i2o, linsys_2i3o) + iosys_multiply = iosys_3i2o.feedback(iosys_2i3o) + lin_t, lin_y, lin_x = ct.forced_response(linsys_multiply, T, U3, X0) + ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U3, X0) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + # Mismatch should generate exception + args = (iosys_3i2o, iosys_3i2o) +@@ -536,8 +536,8 @@ def test_discrete(self): + # Simulate and compare to LTI output + ios_t, ios_y = ios.input_output_response(lnios, T, U, X0) + lin_t, lin_y, lin_x = ct.forced_response(linsys, T, U, X0) +- np.testing.assert_array_almost_equal(ios_t, lin_t, decimal=3) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_t, lin_t,atol=0.002,rtol=0.) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + # Test MIMO system, converted to discrete time + linsys = ct.StateSpace(self.mimo_linsys1) +@@ -552,8 +552,8 @@ def test_discrete(self): + # Simulate and compare to LTI output + ios_t, ios_y = ios.input_output_response(lnios, T, U, X0) + lin_t, lin_y, lin_x = ct.forced_response(linsys, T, U, X0) +- np.testing.assert_array_almost_equal(ios_t, lin_t, decimal=3) +- np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) ++ np.testing.assert_allclose(ios_t, lin_t,atol=0.002,rtol=0.) ++ np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) + + def test_find_eqpts(self): + """Test find_eqpt function""" +@@ -738,7 +738,7 @@ def test_params(self): + + # Check to make sure results are OK + np.testing.assert_array_almost_equal(lti_t, ios_t) +- np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3) ++ np.testing.assert_allclose(lti_y, ios_y,atol=0.002,rtol=0.) + + def test_named_signals(self): + sys1 = ios.NonlinearIOSystem( diff --git a/python-control-pr317.patch b/python-control-pr317.patch deleted file mode 100644 index 400c3d6..0000000 --- a/python-control-pr317.patch +++ /dev/null @@ -1,143 +0,0 @@ -From e1e319f844edb2e6a22aa815ca42806d47c6cf5f Mon Sep 17 00:00:00 2001 -From: Rory Yorke -Date: Sun, 23 Jun 2019 14:46:23 +0200 -Subject: [PATCH] Use numpy.longdouble instead of numpy.float128 in testing - -numpy.float128 doesn't exist on all platforms, e.g., Windows 64-bit. - -When numpy.float128 does exist (e.g., Linux 64-bit), it's typically an -alias for numpy.longdouble. ---- - control/tests/xferfcn_input_test.py | 28 ++++++++++++++-------------- - 1 file changed, 14 insertions(+), 14 deletions(-) - -diff --git a/control/tests/xferfcn_input_test.py b/control/tests/xferfcn_input_test.py -index 0471e885..0d6ca56f 100644 ---- a/control/tests/xferfcn_input_test.py -+++ b/control/tests/xferfcn_input_test.py -@@ -7,7 +7,7 @@ - import numpy as np - - from numpy import int, int8, int16, int32, int64 --from numpy import float, float16, float32, float64, float128 -+from numpy import float, float16, float32, float64, longdouble - from numpy import all, ndarray, array - - from control.xferfcn import _clean_part -@@ -73,7 +73,7 @@ def test_clean_part_tuple(self): - - def test_clean_part_all_scalar_types(self): - """Test single scalar value for all valid data types.""" -- for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: -+ for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, longdouble]: - num = dtype(1) - num_ = _clean_part(num) - -@@ -92,7 +92,7 @@ def test_clean_part_np_array(self): - - def test_clean_part_all_np_array_types(self): - """Test scalar value in numpy array of ndim=0 for all data types.""" -- for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: -+ for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, longdouble]: - num = np.array(1, dtype=dtype) - num_ = _clean_part(num) - -@@ -102,7 +102,7 @@ def test_clean_part_all_np_array_types(self): - - def test_clean_part_all_np_array_types2(self): - """Test numpy array for all types.""" -- for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: -+ for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, longdouble]: - num = np.array([1, 2], dtype=dtype) - num_ = _clean_part(num) - -@@ -112,7 +112,7 @@ def test_clean_part_all_np_array_types2(self): - - def test_clean_part_list_all_types(self): - """Test list of a single value for all data types.""" -- for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: -+ for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, longdouble]: - num = [dtype(1)] - num_ = _clean_part(num) - assert isinstance(num_, list) -@@ -121,7 +121,7 @@ def test_clean_part_list_all_types(self): - - def test_clean_part_list_all_types2(self): - """List of list of numbers of all data types.""" -- for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: -+ for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, longdouble]: - num = [dtype(1), dtype(2)] - num_ = _clean_part(num) - assert isinstance(num_, list) -@@ -130,7 +130,7 @@ def test_clean_part_list_all_types2(self): - - def test_clean_part_tuple_all_types(self): - """Test tuple of a single value for all data types.""" -- for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: -+ for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, longdouble]: - num = (dtype(1),) - num_ = _clean_part(num) - assert isinstance(num_, list) -@@ -139,7 +139,7 @@ def test_clean_part_tuple_all_types(self): - - def test_clean_part_tuple_all_types2(self): - """Test tuple of a single value for all data types.""" -- for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: -+ for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, longdouble]: - num = (dtype(1), dtype(2)) - num_ = _clean_part(num) - assert isinstance(num_, list) -@@ -184,7 +184,7 @@ def test_clean_part_list_list_list_floats(self): - - def test_clean_part_list_list_array(self): - """List of list of numpy arrays for all valid types.""" -- for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: -+ for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, longdouble: - num = [[array([1, 1], dtype=dtype), array([2, 2], dtype=dtype)]] - num_ = _clean_part(num) - -@@ -195,7 +195,7 @@ def test_clean_part_list_list_array(self): - - def test_clean_part_tuple_list_array(self): - """Tuple of list of numpy arrays for all valid types.""" -- for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: -+ for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, longdouble: - num = ([array([1, 1], dtype=dtype), array([2, 2], dtype=dtype)],) - num_ = _clean_part(num) - -@@ -206,7 +206,7 @@ def test_clean_part_tuple_list_array(self): - - def test_clean_part_list_tuple_array(self): - """List of tuple of numpy array for all valid types.""" -- for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: -+ for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, longdouble: - num = [(array([1, 1], dtype=dtype), array([2, 2], dtype=dtype))] - num_ = _clean_part(num) - -@@ -217,7 +217,7 @@ def test_clean_part_list_tuple_array(self): - - def test_clean_part_tuple_tuples_arrays(self): - """Tuple of tuples of numpy arrays for all valid types.""" -- for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: -+ for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, longdouble: - num = ((array([1, 1], dtype=dtype), array([2, 2], dtype=dtype)), - (array([3, 4], dtype=dtype), array([4, 4], dtype=dtype))) - num_ = _clean_part(num) -@@ -229,7 +229,7 @@ def test_clean_part_tuple_tuples_arrays(self): - - def test_clean_part_list_tuples_arrays(self): - """List of tuples of numpy arrays for all valid types.""" -- for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: -+ for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, longdouble: - num = [(array([1, 1], dtype=dtype), array([2, 2], dtype=dtype)), - (array([3, 4], dtype=dtype), array([4, 4], dtype=dtype))] - num_ = _clean_part(num) -@@ -241,7 +241,7 @@ def test_clean_part_list_tuples_arrays(self): - - def test_clean_part_list_list_arrays(self): - """List of list of numpy arrays for all valid types.""" -- for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: -+ for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, longdouble: - num = [[array([1, 1], dtype=dtype), array([2, 2], dtype=dtype)], - [array([3, 3], dtype=dtype), array([4, 4], dtype=dtype)]] - num_ = _clean_part(num) diff --git a/python-control-pr345.patch b/python-control-pr345.patch deleted file mode 100644 index b0ccbe9..0000000 --- a/python-control-pr345.patch +++ /dev/null @@ -1,238 +0,0 @@ -diff --git a/control/tests/xferfcn_test.py b/control/tests/xferfcn_test.py -index 35e411b..ee779f9 100644 ---- a/control/tests/xferfcn_test.py -+++ b/control/tests/xferfcn_test.py -@@ -403,8 +403,57 @@ class TestXferFcn(unittest.TestCase): - np.testing.assert_array_equal(omega, true_omega) - - # Tests for TransferFunction.pole and TransferFunction.zero. -- -- @unittest.skipIf(not slycot_check(), "slycot not installed") -+ -+ def test_common_den(self): -+ """ Test the helper function to compute common denomitators.""" -+ -+ # _common_den() computes the common denominator per input/column. -+ # The testing columns are: -+ # 0: no common poles -+ # 1: regular common poles -+ # 2: poles with multiplicity, -+ # 3: complex poles -+ # 4: complex poles below threshold -+ -+ eps = np.finfo(float).eps -+ tol_imag = np.sqrt(eps*5*2*2)*0.9 -+ -+ numin = [[[1.], [1.], [1.], [1.], [1.]], -+ [[1.], [1.], [1.], [1.], [1.]]] -+ denin = [[[1., 3., 2.], # 0: poles: [-1, -2] -+ [1., 6., 11., 6.], # 1: poles: [-1, -2, -3] -+ [1., 6., 11., 6.], # 2: poles: [-1, -2, -3] -+ [1., 6., 11., 6.], # 3: poles: [-1, -2, -3] -+ [1., 6., 11., 6.]], # 4: poles: [-1, -2, -3], -+ [[1., 12., 47., 60.], # 0: poles: [-3, -4, -5] -+ [1., 9., 26., 24.], # 1: poles: [-2, -3, -4] -+ [1., 7., 16., 12.], # 2: poles: [-2, -2, -3] -+ [1., 7., 17., 15.], # 3: poles: [-2+1J, -2-1J, -3], -+ np.poly([-2 + tol_imag * 1J, -2 - tol_imag * 1J, -3])]] -+ numref = np.array([ -+ [[0., 0., 1., 12., 47., 60.], -+ [0., 0., 0., 1., 4., 0.], -+ [0., 0., 0., 1., 2., 0.], -+ [0., 0., 0., 1., 4., 5.], -+ [0., 0., 0., 1., 2., 0.]], -+ [[0., 0., 0., 1., 3., 2.], -+ [0., 0., 0., 1., 1., 0.], -+ [0., 0., 0., 1., 1., 0.], -+ [0., 0., 0., 1., 3., 2.], -+ [0., 0., 0., 1., 1., 0.]]]) -+ denref = np.array( -+ [[1., 15., 85., 225., 274., 120.], -+ [1., 10., 35., 50., 24., 0.], -+ [1., 8., 23., 28., 12., 0.], -+ [1., 10., 40., 80., 79., 30.], -+ [1., 8., 23., 28., 12., 0.]]) -+ sys = TransferFunction(numin, denin) -+ num, den, denorder = sys._common_den() -+ np.testing.assert_array_almost_equal(num[:2, :, :], numref) -+ np.testing.assert_array_almost_equal(num[2:, :, :], -+ np.zeros((3, 5, 6))) -+ np.testing.assert_array_almost_equal(den, denref) -+ - def test_pole_mimo(self): - """Test for correct MIMO poles.""" - -@@ -414,13 +463,12 @@ class TestXferFcn(unittest.TestCase): - - np.testing.assert_array_almost_equal(p, [-2., -2., -7., -3., -2.]) - -- @unittest.skipIf(not slycot_check(), "slycot not installed") - def test_double_cancelling_poles_siso(self): -- -+ - H = TransferFunction([1, 1], [1, 2, 1]) - p = H.pole() - np.testing.assert_array_almost_equal(p, [-1, -1]) -- -+ - # Tests for TransferFunction.feedback - def test_feedback_siso(self): - """Test for correct SISO transfer function feedback.""" -diff --git a/control/xferfcn.py b/control/xferfcn.py -index 1ef0661..a913061 100644 ---- a/control/xferfcn.py -+++ b/control/xferfcn.py -@@ -57,7 +57,6 @@ from numpy import angle, array, empty, finfo, ndarray, ones, \ - polyadd, polymul, polyval, roots, sqrt, zeros, squeeze, exp, pi, \ - where, delete, real, poly, nonzero - import scipy as sp --from numpy.polynomial.polynomial import polyfromroots - from scipy.signal import lti, tf2zpk, zpk2tf, cont2discrete - from copy import deepcopy - from warnings import warn -@@ -774,8 +773,6 @@ class TransferFunction(LTI): - output numerator array num is modified to use the common - denominator for this input/column; the coefficient arrays are also - padded with zeros to be the same size for all num/den. -- num is an sys.outputs by sys.inputs -- by len(d) array. - - Parameters - ---------- -@@ -786,17 +783,20 @@ class TransferFunction(LTI): - Returns - ------- - num: array -- Multi-dimensional array of numerator coefficients. num[i][j] -- gives the numerator coefficient array for the ith input and jth -- output, also prepared for use in td04ad; matches the denorder -- order; highest coefficient starts on the left. -+ n by n by kd where n = max(sys.outputs,sys.inputs) -+ kd = max(denorder)+1 -+ Multi-dimensional array of numerator coefficients. num[i,j] -+ gives the numerator coefficient array for the ith output and jth -+ input; padded for use in td04ad ('C' option); matches the -+ denorder order; highest coefficient starts on the left. - - den: array -+ sys.inputs by kd - Multi-dimensional array of coefficients for common denominator - polynomial, one row per input. The array is prepared for use in - slycot td04ad, the first element is the highest-order polynomial -- coefficiend of s, matching the order in denorder, if denorder < -- number of columns in den, the den is padded with zeros -+ coefficient of s, matching the order in denorder. If denorder < -+ number of columns in den, the den is padded with zeros. - - denorder: array of int, orders of den, one per input - -@@ -810,16 +810,18 @@ class TransferFunction(LTI): - - # Machine precision for floats. - eps = finfo(float).eps -+ real_tol = sqrt(eps * self.inputs * self.outputs) - -- # Decide on the tolerance to use in deciding of a pole is complex -+ # The tolerance to use in deciding if a pole is complex - if (imag_tol is None): -- imag_tol = 1e-8 # TODO: figure out the right number to use -+ imag_tol = 2 * real_tol - - # A list to keep track of cumulative poles found as we scan - # self.den[..][..] - poles = [[] for j in range(self.inputs)] - - # RvP, new implementation 180526, issue #194 -+ # BG, modification, issue #343, PR #354 - - # pre-calculate the poles for all num, den - # has zeros, poles, gain, list for pole indices not in den, -@@ -838,30 +840,37 @@ class TransferFunction(LTI): - poleset[-1].append([z, p, k, [], 0]) - - # collect all individual poles -- epsnm = eps * self.inputs * self.outputs - for j in range(self.inputs): - for i in range(self.outputs): - currentpoles = poleset[i][j][1] - nothave = ones(currentpoles.shape, dtype=bool) - for ip, p in enumerate(poles[j]): -- idx, = nonzero( -- (abs(currentpoles - p) < epsnm) * nothave) -- if len(idx): -- nothave[idx[0]] = False -+ collect = (np.isclose(currentpoles.real, p.real, -+ atol=real_tol) & -+ np.isclose(currentpoles.imag, p.imag, -+ atol=imag_tol) & -+ nothave) -+ if np.any(collect): -+ # mark first found pole as already collected -+ nothave[nonzero(collect)[0][0]] = False - else: - # remember id of pole not in tf - poleset[i][j][3].append(ip) - for h, c in zip(nothave, currentpoles): - if h: -+ if abs(c.imag) < imag_tol: -+ c = c.real - poles[j].append(c) - # remember how many poles now known - poleset[i][j][4] = len(poles[j]) - - # figure out maximum number of poles, for sizing the den -- npmax = max([len(p) for p in poles]) -- den = zeros((self.inputs, npmax + 1), dtype=float) -+ maxindex = max([len(p) for p in poles]) -+ den = zeros((self.inputs, maxindex + 1), dtype=float) - num = zeros((max(1, self.outputs, self.inputs), -- max(1, self.outputs, self.inputs), npmax + 1), dtype=float) -+ max(1, self.outputs, self.inputs), -+ maxindex + 1), -+ dtype=float) - denorder = zeros((self.inputs,), dtype=int) - - for j in range(self.inputs): -@@ -872,11 +881,10 @@ class TransferFunction(LTI): - num[i, j, 0] = poleset[i][j][2] - else: - # create the denominator matching this input -- # polyfromroots gives coeffs in opposite order from what we use -- # coefficients should be padded on right, ending at np -- np = len(poles[j]) -- den[j, np::-1] = polyfromroots(poles[j]).real -- denorder[j] = np -+ # coefficients should be padded on right, ending at maxindex -+ maxindex = len(poles[j]) -+ den[j, :maxindex+1] = poly(poles[j]) -+ denorder[j] = maxindex - - # now create the numerator, also padded on the right - for i in range(self.outputs): -@@ -885,22 +893,15 @@ class TransferFunction(LTI): - # add all poles not found in the original denominator, - # and the ones later added from other denominators - for ip in chain(poleset[i][j][3], -- range(poleset[i][j][4], np)): -+ range(poleset[i][j][4], maxindex)): - nwzeros.append(poles[j][ip]) - -- numpoly = poleset[i][j][2] * polyfromroots(nwzeros).real -- # print(numpoly, den[j]) -- # polyfromroots gives coeffs in opposite order => invert -+ numpoly = poleset[i][j][2] * np.atleast_1d(poly(nwzeros)) - # numerator polynomial should be padded on left and right -- # ending at np to line up with what td04ad expects... -- num[i, j, np + 1 - len(numpoly):np + 1] = numpoly[::-1] -+ # ending at maxindex to line up with what td04ad expects. -+ num[i, j, maxindex+1-len(numpoly):maxindex+1] = numpoly - # print(num[i, j]) - -- if (abs(den.imag) > epsnm).any(): -- print("Warning: The denominator has a nontrivial imaginary part: %f" -- % abs(den.imag).max()) -- den = den.real -- - return num, den, denorder - - def sample(self, Ts, method='zoh', alpha=None): diff --git a/python-control.changes b/python-control.changes index 11f0758..239144b 100644 --- a/python-control.changes +++ b/python-control.changes @@ -1,3 +1,20 @@ +------------------------------------------------------------------- +Sat Jan 18 01:18:39 UTC 2020 - Benjamin Greiner + +- update to version 0.8.3 +- remove patches that were merged upstream: + python-control-fixtestaugw.patch + python-control-pr317.patch + python-control-pr345.patch +- pr365-copy-PR-320-for-robust_array_test.patch + upstream PR#365 the former fixtestaugw patch for the new + duplicated array test +- pr366-ease-precision-tolerenace.patch + upstream PR#366 to pass the checks on more architectures +- remove Python 2 package +- run all tests in xvfb env and prealloc differently for i586 + architecture + ------------------------------------------------------------------- Wed Nov 27 18:13:20 UTC 2019 - Benjamin Greiner diff --git a/python-control.spec b/python-control.spec index 49f9398..4b8412c 100644 --- a/python-control.spec +++ b/python-control.spec @@ -1,7 +1,7 @@ # # spec file for package python-control # -# Copyright (c) 2019 SUSE LLC +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,16 +17,16 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} +%define skip_python2 1 Name: python-control -Version: 0.8.2 +Version: 0.8.3 Release: 0 Summary: Python control systems library License: BSD-3-Clause URL: http://python-control.sourceforge.net Source: https://files.pythonhosted.org/packages/source/c/control/control-%{version}.tar.gz -Patch0: python-control-fixtestaugw.patch -Patch1: python-control-pr317.patch -Patch2: python-control-pr345.patch +Patch0: pr365-copy-PR-320-for-robust_array_test.patch +Patch1: pr366-ease-precision-tolerance.patch BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -37,12 +37,11 @@ BuildArch: noarch # SECTION test requirements BuildRequires: %{python_module matplotlib-qt5} BuildRequires: %{python_module matplotlib} -BuildRequires: %{python_module nose-exclude} BuildRequires: %{python_module nose} BuildRequires: %{python_module numpy} BuildRequires: %{python_module scipy} BuildRequires: %{python_module slycot} -BuildRequires: libtcmalloc4 +BuildRequires: libjemalloc2 BuildRequires: xvfb-run # /SECTION %python_subpackages @@ -55,7 +54,6 @@ operations for analysis and design of feedback control systems. %setup -q -n control-%{version} %patch0 -p1 %patch1 -p1 -%patch2 -p1 %build %python_build @@ -66,21 +64,15 @@ operations for analysis and design of feedback control systems. %check # The default Agg backend does not define the toolbar attribute in the Figure -# Manager used by some tests, so we run those tests with the Qt5 backend in a +# Manager used by some tests, so we run the tests with the Qt5 backend in a # virtual X server environment %if %{_arch} == i386 - export LD_PRELOAD="%{_libdir}/libtcmalloc_minimal.so.4" + # preload malloc library to avoid free() error on i586 architecture + export LD_PRELOAD="%{_libdir}/libjemalloc.so.2" %endif %{python_expand export PYTHONPATH=%{buildroot}%{$python_sitelib} -export MPLBACKEND="Agg" -nosetests-%$python_bin_suffix \ - --exclude-test control.tests.sisotool_test \ - --exclude-test control.tests.rlocus_test export MPLBACKEND="Qt5Agg" -export LD_PRELOAD="%{_libdir}/libtcmalloc_minimal.so.4" -xvfb-run -a nosetests-%$python_bin_suffix \ - control.tests.sisotool_test \ - control.tests.rlocus_test +xvfb-run -a $python setup.py test } %files %{python_files}