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}