Skip to content

Support NumPy 2 #994

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 4 commits into from
Apr 21, 2024
Merged

Support NumPy 2 #994

merged 4 commits into from
Apr 21, 2024

Conversation

bnavigator
Copy link
Contributor

Numpy 2.0.0rc1 is out and a release is coming soon.

@bnavigator
Copy link
Contributor Author

Fixes error

[   58s] ==================================== ERRORS ====================================
[   58s] ___ ERROR at setup of TestTimeresp.test_step_response_siso[siso_ss1-kwargs0] ___
[   58s] [gw0] linux -- Python 3.10.14 /usr/bin/python3.10
[   58s] 
[   58s] self = <control.tests.timeresp_test.TestTimeresp object at 0x7fed247aa830>
[   58s] request = <SubRequest 'tsystem' for <Function test_step_response_siso[siso_ss1-kwargs0]>>
[   58s] 
[   58s]     @pytest.fixture
[   58s]     def tsystem(self, request):
[   58s]         """Define some test systems"""
[   58s]     
[   58s]         """continuous"""
[   58s]         A = np.array([[1., -2.], [3., -4.]])
[   58s]         B = np.array([[5.], [7.]])
[   58s]         C = np.array([[6., 8.]])
[   58s]         D = np.array([[9.]])
[   58s]         siso_ss1 = TSys(StateSpace(A, B, C, D, 0))
[   58s]         siso_ss1.t = np.linspace(0, 1, 10)
[   58s]         siso_ss1.ystep = np.array([9., 17.6457, 24.7072, 30.4855, 35.2234,
[   58s]                                    39.1165, 42.3227, 44.9694, 47.1599,
[   58s]                                    48.9776])
[   58s]         siso_ss1.X0 = np.array([[.5], [1.]])
[   58s]         siso_ss1.yinitial = np.array([11., 8.1494, 5.9361, 4.2258, 2.9118,
[   58s]                                       1.9092, 1.1508, 0.5833, 0.1645, -0.1391])
[   58s]         ss1 = siso_ss1.sys
[   58s]     
[   58s]         """D=0, continuous"""
[   58s]         siso_ss2 = TSys(StateSpace(ss1.A, ss1.B, ss1.C, 0, 0))
[   58s]         siso_ss2.t = siso_ss1.t
[   58s]         siso_ss2.ystep = siso_ss1.ystep - 9
[   58s]         siso_ss2.initial = siso_ss1.yinitial - 9
[   58s]         siso_ss2.yimpulse = np.array([86., 70.1808, 57.3753, 46.9975, 38.5766,
[   58s]                                       31.7344, 26.1668, 21.6292, 17.9245,
[   58s]                                       14.8945])
[   58s]     
[   58s]         """System with unspecified timebase"""
[   58s]         siso_ss2_dtnone = TSys(StateSpace(ss1.A, ss1.B, ss1.C, 0, None))
[   58s]         siso_ss2_dtnone.t = np.arange(0, 10, 1.)
[   58s]         siso_ss2_dtnone.ystep = np.array([0., 86., -72., 230., -360.,  806.,
[   58s]                                           -1512.,  3110., -6120., 12326.])
[   58s]     
[   58s]         siso_tf1 = TSys(TransferFunction([1], [1, 2, 1], 0))
[   58s]     
[   58s]         siso_tf2 = copy(siso_ss1)
[   58s]         siso_tf2.sys = ss2tf(siso_ss1.sys)
[   58s]     
[   58s]         """MIMO system, contains ``siso_ss1`` twice"""
[   58s]         mimo_ss1 = copy(siso_ss1)
[   58s]         A = np.zeros((4, 4))
[   58s]         A[:2, :2] = siso_ss1.sys.A
[   58s]         A[2:, 2:] = siso_ss1.sys.A
[   58s]         B = np.zeros((4, 2))
[   58s]         B[:2, :1] = siso_ss1.sys.B
[   58s]         B[2:, 1:] = siso_ss1.sys.B
[   58s]         C = np.zeros((2, 4))
[   58s]         C[:1, :2] = siso_ss1.sys.C
[   58s]         C[1:, 2:] = siso_ss1.sys.C
[   58s]         D = np.zeros((2, 2))
[   58s]         D[:1, :1] = siso_ss1.sys.D
[   58s]         D[1:, 1:] = siso_ss1.sys.D
[   58s]         mimo_ss1.sys = StateSpace(A, B, C, D)
[   58s]     
[   58s]         """MIMO system, contains ``siso_ss2`` twice"""
[   58s]         mimo_ss2 = copy(siso_ss2)
[   58s]         A = np.zeros((4, 4))
[   58s]         A[:2, :2] = siso_ss2.sys.A
[   58s]         A[2:, 2:] = siso_ss2.sys.A
[   58s]         B = np.zeros((4, 2))
[   58s]         B[:2, :1] = siso_ss2.sys.B
[   58s]         B[2:, 1:] = siso_ss2.sys.B
[   58s]         C = np.zeros((2, 4))
[   58s]         C[:1, :2] = siso_ss2.sys.C
[   58s]         C[1:, 2:] = siso_ss2.sys.C
[   58s]         D = np.zeros((2, 2))
[   58s]         mimo_ss2.sys = StateSpace(A, B, C, D, 0)
[   58s]     
[   58s]         """discrete"""
[   58s]         siso_dtf0 = TSys(TransferFunction([1.], [1., 0.], 1.))
[   58s]         siso_dtf0.t = np.arange(4)
[   58s]         siso_dtf0.yimpulse = [0., 1., 0., 0.]
[   58s]     
[   58s]         siso_dtf1 =  TSys(TransferFunction([1], [1, 1, 0.25], True))
[   58s]         siso_dtf1.t = np.arange(0, 5, 1)
[   58s]         siso_dtf1.ystep = np.array([0.  , 0.  , 1.  , 0.  , 0.75])
[   58s]     
[   58s]         siso_dtf2 = TSys(TransferFunction([1], [1, 1, 0.25], 0.2))
[   58s]         siso_dtf2.t = np.arange(0, 5, 0.2)
[   58s]         siso_dtf2.ystep = np.array(
[   58s]             [0.    , 0.    , 1.    , 0.    , 0.75  , 0.25  ,
[   58s]              0.5625, 0.375 , 0.4844, 0.4219, 0.457 , 0.4375,
[   58s]              0.4482, 0.4424, 0.4456, 0.4438, 0.4448, 0.4443,
[   58s]              0.4445, 0.4444, 0.4445, 0.4444, 0.4445, 0.4444,
[   58s]              0.4444])
[   58s]     
[   58s]         """Time step which leads to rounding errors for time vector length"""
[   58s]         num = [-0.10966442, 0.12431949]
[   58s]         den = [1., -1.86789511, 0.88255018]
[   58s]         dt = 0.12493963338370018
[   58s]         siso_dtf3 = TSys(TransferFunction(num, den, dt))
[   58s]         siso_dtf3.t = np.linspace(0, 9*dt, 10)
[   58s]         siso_dtf3.ystep = np.array(
[   58s]             [ 0.    , -0.1097, -0.1902, -0.2438, -0.2729,
[   58s]              -0.2799, -0.2674, -0.2377, -0.1934, -0.1368])
[   58s]     
[   58s]         """dtf1 converted statically, because Slycot and Scipy produce
[   58s]         different realizations, wich means different initial condtions,"""
[   58s]         siso_dss1 = copy(siso_dtf1)
[   58s]         siso_dss1.sys = StateSpace([[-1., -0.25],
[   58s]                                     [ 1.,  0.]],
[   58s]                                    [[1.],
[   58s]                                     [0.]],
[   58s]                                    [[0., 1.]],
[   58s]                                    [[0.]],
[   58s]                                    True)
[   58s]         siso_dss1.X0 = [0.5, 1.]
[   58s]         siso_dss1.yinitial = np.array([1., 0.5, -0.75, 0.625, -0.4375])
[   58s]     
[   58s]         siso_dss2 = copy(siso_dtf2)
[   58s]         siso_dss2.sys = tf2ss(siso_dtf2.sys)
[   58s]     
[   58s]         mimo_dss1 = TSys(StateSpace(ss1.A, ss1.B, ss1.C, ss1.D, True))
[   58s]         mimo_dss1.t = np.arange(0, 5, 0.2)
[   58s]     
[   58s]         mimo_dss2 = copy(mimo_ss1)
[   58s]         mimo_dss2.sys = c2d(mimo_ss1.sys, mimo_ss1.t[1]-mimo_ss1.t[0])
[   58s]     
[   58s]         mimo_tf2 = copy(mimo_ss2)
[   58s]         tf_ = ss2tf(siso_ss2.sys)
[   58s]         mimo_tf2.sys = TransferFunction(
[   58s]             [[tf_.num[0][0], [0]], [[0], tf_.num[0][0]]],
[   58s]             [[tf_.den[0][0], [1]], [[1], tf_.den[0][0]]],
[   58s]             0)
[   58s]     
[   58s]         mimo_dtf1 = copy(siso_dtf1)
[   58s]         tf_ = siso_dtf1.sys
[   58s]         mimo_dtf1.sys = TransferFunction(
[   58s]             [[tf_.num[0][0], [0]], [[0], tf_.num[0][0]]],
[   58s]             [[tf_.den[0][0], [1]], [[1], tf_.den[0][0]]],
[   58s]             True)
[   58s]     
[   58s]         # for pole cancellation tests
[   58s]         pole_cancellation = TSys(TransferFunction(
[   58s]             [1.067e+05, 5.791e+04],
[   58s]             [10.67, 1.067e+05, 5.791e+04]))
[   58s]     
[   58s]         no_pole_cancellation = TSys(TransferFunction(
[   58s]             [1.881e+06],
[   58s]             [188.1, 1.881e+06]))
[   58s]     
[   58s]         # System Type 1 - Step response not stationary:  G(s)=1/s(s+1)
[   58s]         siso_tf_type1 = TSys(TransferFunction(1, [1, 1, 0]))
[   58s]         siso_tf_type1.step_info = {
[   58s] >            'RiseTime': np.NaN,
[   58s]              'SettlingTime': np.NaN,
[   58s]              'SettlingMin': np.NaN,
[   58s]              'SettlingMax': np.NaN,
[   58s]              'Overshoot': np.NaN,
[   58s]              'Undershoot': np.NaN,
[   58s]              'Peak': np.Inf,
[   58s]              'PeakTime': np.Inf,
[   58s]              'SteadyStateValue': np.NaN}
[   58s] 
[   58s] control/tests/timeresp_test.py:176: 
[   58s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
[   58s] 
[   58s] attr = 'NaN'
[   58s] 
[   58s]     def __getattr__(attr):
[   58s]         # Warn for expired attributes
[   58s]         import warnings
[   58s]     
[   58s]         if attr == "linalg":
[   58s]             import numpy.linalg as linalg
[   58s]             return linalg
[   58s]         elif attr == "fft":
[   58s]             import numpy.fft as fft
[   58s]             return fft
[   58s]         elif attr == "dtypes":
[   58s]             import numpy.dtypes as dtypes
[   58s]             return dtypes
[   58s]         elif attr == "random":
[   58s]             import numpy.random as random
[   58s]             return random
[   58s]         elif attr == "polynomial":
[   58s]             import numpy.polynomial as polynomial
[   58s]             return polynomial
[   58s]         elif attr == "ma":
[   58s]             import numpy.ma as ma
[   58s]             return ma
[   58s]         elif attr == "ctypeslib":
[   58s]             import numpy.ctypeslib as ctypeslib
[   58s]             return ctypeslib
[   58s]         elif attr == "exceptions":
[   58s]             import numpy.exceptions as exceptions
[   58s]             return exceptions
[   58s]         elif attr == "testing":
[   58s]             import numpy.testing as testing
[   58s]             return testing
[   58s]         elif attr == "matlib":
[   58s]             import numpy.matlib as matlib
[   58s]             return matlib
[   58s]         elif attr == "f2py":
[   58s]             import numpy.f2py as f2py
[   58s]             return f2py
[   58s]         elif attr == "typing":
[   58s]             import numpy.typing as typing
[   58s]             return typing
[   58s]         elif attr == "rec":
[   58s]             import numpy.rec as rec
[   58s]             return rec
[   58s]         elif attr == "char":
[   58s]             import numpy.char as char
[   58s]             return char
[   58s]         elif attr == "array_api":
[   58s]             raise AttributeError("`numpy.array_api` is not available from "
[   58s]                                  "numpy 2.0 onwards")
[   58s]         elif attr == "core":
[   58s]             import numpy.core as core
[   58s]             return core
[   58s]         elif attr == "strings":
[   58s]             import numpy.strings as strings
[   58s]             return strings
[   58s]         elif attr == "distutils":
[   58s]             if 'distutils' in __numpy_submodules__:
[   58s]                 import numpy.distutils as distutils
[   58s]                 return distutils
[   58s]             else:
[   58s]                 raise AttributeError("`numpy.distutils` is not available from "
[   58s]                                      "Python 3.12 onwards")
[   58s]     
[   58s]         if attr in __future_scalars__:
[   58s]             # And future warnings for those that will change, but also give
[   58s]             # the AttributeError
[   58s]             warnings.warn(
[   58s]                 f"In the future `np.{attr}` will be defined as the "
[   58s]                 "corresponding NumPy scalar.", FutureWarning, stacklevel=2)
[   58s]     
[   58s]         if attr in __former_attrs__:
[   58s]             raise AttributeError(__former_attrs__[attr])
[   58s]     
[   58s]         if attr in __expired_attributes__:
[   58s] >           raise AttributeError(
[   58s]                 f"`np.{attr}` was removed in the NumPy 2.0 release. "
[   58s]                 f"{__expired_attributes__[attr]}"
[   58s]             )
[   58s] E           AttributeError: `np.NaN` was removed in the NumPy 2.0 release. Use `np.nan` instead.. Did you mean: 'nan'?
[   58s] 
[   58s] /usr/lib64/python3.10/site-packages/numpy/__init__.py:397: AttributeError

@coveralls
Copy link

coveralls commented Apr 20, 2024

Coverage Status

coverage: 94.503% (-0.001%) from 94.504%
when pulling ebb8a52 on bnavigator:numpy2
into 1bb0a46 on python-control:main.

@bnavigator bnavigator marked this pull request as ready for review April 20, 2024 14:36
@bnavigator
Copy link
Contributor Author

We don't have a CI job with numpy 2.0.0rc1 set up and I don't think we need to, but tests are executed here:

https://build.opensuse.org/package/show/home:bnavigator:numpy/python-control

@murrayrm
Copy link
Member

These changes look good to me.

@bnavigator Any reason to wait before merging? If not, I'll merge in a day or so.

@bnavigator
Copy link
Contributor Author

No reason to wait. Thanks for the review.

I suggest releasing a 0.10.1 soon, so that our users don't get caught by surprise after the numpy 2 release.

Copy link
Member

@slivingston slivingston left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bnavigator bnavigator merged commit e4a03e8 into python-control:main Apr 21, 2024
23 checks passed
bmwiedemann pushed a commit to bmwiedemann/openSUSE that referenced this pull request Apr 21, 2024
https://build.opensuse.org/request/show/1169382
by user bnavigator + anag+factory
- Add control-pr994-numpy2.patch
  gh#python-control/python-control#994 (forwarded request 1169381 from bnavigator)
@murrayrm murrayrm added this to the 0.10.1 milestone Jun 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants