array-api-strict 2.1.2__tar.gz → 2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. {array_api_strict-2.1.2 → array_api_strict-2.2}/PKG-INFO +4 -5
  2. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/__init__.py +8 -10
  3. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_array_object.py +44 -17
  4. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_creation_functions.py +23 -0
  5. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_elementwise_functions.py +25 -0
  6. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_flags.py +8 -3
  7. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_indexing_functions.py +12 -0
  8. array_api_strict-2.2/array_api_strict/_info.py +139 -0
  9. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_typing.py +4 -3
  10. array_api_strict-2.2/array_api_strict/_utility_functions.py +69 -0
  11. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_version.py +3 -3
  12. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_array_object.py +60 -17
  13. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_elementwise_functions.py +8 -2
  14. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_flags.py +53 -7
  15. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict.egg-info/PKG-INFO +4 -5
  16. array_api_strict-2.2/array_api_strict.egg-info/requires.txt +1 -0
  17. {array_api_strict-2.1.2 → array_api_strict-2.2}/setup.py +3 -4
  18. array_api_strict-2.1.2/array_api_strict/_info.py +0 -132
  19. array_api_strict-2.1.2/array_api_strict/_utility_functions.py +0 -39
  20. array_api_strict-2.1.2/array_api_strict.egg-info/requires.txt +0 -1
  21. {array_api_strict-2.1.2 → array_api_strict-2.2}/LICENSE +0 -0
  22. {array_api_strict-2.1.2 → array_api_strict-2.2}/README.md +0 -0
  23. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_constants.py +0 -0
  24. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_data_type_functions.py +0 -0
  25. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_dtypes.py +0 -0
  26. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_fft.py +0 -0
  27. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_linalg.py +0 -0
  28. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_linear_algebra_functions.py +0 -0
  29. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_manipulation_functions.py +0 -0
  30. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_searching_functions.py +0 -0
  31. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_set_functions.py +0 -0
  32. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_sorting_functions.py +0 -0
  33. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/_statistical_functions.py +0 -0
  34. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/__init__.py +0 -0
  35. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/conftest.py +0 -0
  36. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_creation_functions.py +0 -0
  37. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_data_type_functions.py +0 -0
  38. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_device_support.py +0 -0
  39. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_indexing_functions.py +0 -0
  40. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_linalg.py +0 -0
  41. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_manipulation_functions.py +0 -0
  42. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_set_functions.py +0 -0
  43. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_sorting_functions.py +0 -0
  44. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_statistical_functions.py +0 -0
  45. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict/tests/test_validation.py +0 -0
  46. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict.egg-info/SOURCES.txt +0 -0
  47. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict.egg-info/dependency_links.txt +0 -0
  48. {array_api_strict-2.1.2 → array_api_strict-2.2}/array_api_strict.egg-info/top_level.txt +0 -0
  49. {array_api_strict-2.1.2 → array_api_strict-2.2}/setup.cfg +0 -0
  50. {array_api_strict-2.1.2 → array_api_strict-2.2}/versioneer.py +0 -0
@@ -1,22 +1,21 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: array_api_strict
3
- Version: 2.1.2
3
+ Version: 2.2
4
4
  Summary: A strict, minimal implementation of the Python array API standard.
5
5
  Home-page: https://data-apis.org/array-api-strict/
6
6
  Author: Consortium for Python Data API Standards
7
7
  License: MIT
8
8
  Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
9
10
  Classifier: Programming Language :: Python :: 3.10
10
11
  Classifier: Programming Language :: Python :: 3.11
11
12
  Classifier: Programming Language :: Python :: 3.12
12
- Classifier: Programming Language :: Python :: 3.13
13
- Classifier: Intended Audience :: Developers
14
13
  Classifier: License :: OSI Approved :: BSD License
15
14
  Classifier: Operating System :: OS Independent
16
- Requires-Python: >=3.10
15
+ Requires-Python: >=3.9
17
16
  Description-Content-Type: text/markdown
18
17
  License-File: LICENSE
19
- Requires-Dist: numpy>=2.1
18
+ Requires-Dist: numpy
20
19
 
21
20
  # array-api-strict
22
21
 
@@ -16,12 +16,6 @@ consuming libraries to test their array API usage.
16
16
 
17
17
  """
18
18
 
19
- import numpy as np
20
- from numpy.lib import NumpyVersion
21
-
22
- if NumpyVersion(np.__version__) < NumpyVersion('2.1.0'):
23
- raise ImportError("array-api-strict requires NumPy >= 2.1.0")
24
-
25
19
  __all__ = []
26
20
 
27
21
  # Warning: __array_api_version__ could change globally with
@@ -178,10 +172,12 @@ from ._elementwise_functions import (
178
172
  minimum,
179
173
  multiply,
180
174
  negative,
175
+ nextafter,
181
176
  not_equal,
182
177
  positive,
183
178
  pow,
184
179
  real,
180
+ reciprocal,
185
181
  remainder,
186
182
  round,
187
183
  sign,
@@ -246,10 +242,12 @@ __all__ += [
246
242
  "minimum",
247
243
  "multiply",
248
244
  "negative",
245
+ "nextafter",
249
246
  "not_equal",
250
247
  "positive",
251
248
  "pow",
252
249
  "real",
250
+ "reciprocal",
253
251
  "remainder",
254
252
  "round",
255
253
  "sign",
@@ -264,9 +262,9 @@ __all__ += [
264
262
  "trunc",
265
263
  ]
266
264
 
267
- from ._indexing_functions import take
265
+ from ._indexing_functions import take, take_along_axis
268
266
 
269
- __all__ += ["take"]
267
+ __all__ += ["take", "take_along_axis"]
270
268
 
271
269
  from ._info import __array_namespace_info__
272
270
 
@@ -311,9 +309,9 @@ from ._statistical_functions import cumulative_sum, max, mean, min, prod, std, s
311
309
 
312
310
  __all__ += ["cumulative_sum", "max", "mean", "min", "prod", "std", "sum", "var"]
313
311
 
314
- from ._utility_functions import all, any
312
+ from ._utility_functions import all, any, diff
315
313
 
316
- __all__ += ["all", "any"]
314
+ __all__ += ["all", "any", "diff"]
317
315
 
318
316
  from ._array_object import Device
319
317
  __all__ += ["Device"]
@@ -66,7 +66,9 @@ ALL_DEVICES = (CPU_DEVICE, Device("device1"), Device("device2"))
66
66
 
67
67
  _default = object()
68
68
 
69
- _allow_array = False
69
+ # See https://github.com/data-apis/array-api-strict/issues/67 and the comment
70
+ # on __array__ below.
71
+ _allow_array = True
70
72
 
71
73
  class Array:
72
74
  """
@@ -147,22 +149,37 @@ class Array:
147
149
  mid = np.array2string(self._array, separator=', ', prefix=prefix, suffix=suffix)
148
150
  return prefix + mid + suffix
149
151
 
150
- # Disallow __array__, meaning calling `np.func()` on an array_api_strict
151
- # array will give an error. If we don't explicitly disallow it, NumPy
152
- # defaults to creating an object dtype array, which would lead to
153
- # confusing error messages at best and surprising bugs at worst.
154
- #
155
- # The alternative of course is to just support __array__, which is what we
156
- # used to do. But this isn't actually supported by the standard, so it can
152
+ # In the future, _allow_array will be set to False, which will disallow
153
+ # __array__. This means calling `np.func()` on an array_api_strict array
154
+ # will give an error. If we don't explicitly disallow it, NumPy defaults
155
+ # to creating an object dtype array, which would lead to confusing error
156
+ # messages at best and surprising bugs at worst. The reason for doing this
157
+ # is that __array__ is not actually supported by the standard, so it can
157
158
  # lead to code assuming np.asarray(other_array) would always work in the
158
159
  # standard.
160
+ #
161
+ # This was implemented historically for compatibility, and removing it has
162
+ # caused issues for some libraries (see
163
+ # https://github.com/data-apis/array-api-strict/issues/67).
159
164
  def __array__(self, dtype: None | np.dtype[Any] = None, copy: None | bool = None) -> npt.NDArray[Any]:
160
165
  # We have to allow this to be internally enabled as there's no other
161
166
  # easy way to parse a list of Array objects in asarray().
162
167
  if _allow_array:
163
168
  if self._device != CPU_DEVICE:
164
169
  raise RuntimeError(f"Can not convert array on the '{self._device}' device to a Numpy array.")
165
- return np.asarray(self._array, dtype=dtype, copy=copy)
170
+ # copy keyword is new in 2.0.0; for older versions don't use it
171
+ # retry without that keyword.
172
+ if np.__version__[0] < '2':
173
+ return np.asarray(self._array, dtype=dtype)
174
+ elif np.__version__.startswith('2.0.0-dev0'):
175
+ # Handle dev version for which we can't know based on version
176
+ # number whether or not the copy keyword is supported.
177
+ try:
178
+ return np.asarray(self._array, dtype=dtype, copy=copy)
179
+ except TypeError:
180
+ return np.asarray(self._array, dtype=dtype)
181
+ else:
182
+ return np.asarray(self._array, dtype=dtype, copy=copy)
166
183
  raise ValueError("Conversion from an array_api_strict array to a NumPy ndarray is not supported")
167
184
 
168
185
  # These are various helper functions to make the array behavior match the
@@ -574,14 +591,24 @@ class Array:
574
591
  if copy is not _default:
575
592
  raise ValueError("The copy argument to __dlpack__ requires at least version 2023.12 of the array API")
576
593
 
577
- kwargs = {'stream': stream}
578
- if max_version is not _default:
579
- kwargs['max_version'] = max_version
580
- if dl_device is not _default:
581
- kwargs['dl_device'] = dl_device
582
- if copy is not _default:
583
- kwargs['copy'] = copy
584
- return self._array.__dlpack__(**kwargs)
594
+ if np.lib.NumpyVersion(np.__version__) < '2.1.0':
595
+ if max_version not in [_default, None]:
596
+ raise NotImplementedError("The max_version argument to __dlpack__ is not yet implemented")
597
+ if dl_device not in [_default, None]:
598
+ raise NotImplementedError("The device argument to __dlpack__ is not yet implemented")
599
+ if copy not in [_default, None]:
600
+ raise NotImplementedError("The copy argument to __dlpack__ is not yet implemented")
601
+
602
+ return self._array.__dlpack__(stream=stream)
603
+ else:
604
+ kwargs = {'stream': stream}
605
+ if max_version is not _default:
606
+ kwargs['max_version'] = max_version
607
+ if dl_device is not _default:
608
+ kwargs['dl_device'] = dl_device
609
+ if copy is not _default:
610
+ kwargs['copy'] = copy
611
+ return self._array.__dlpack__(**kwargs)
585
612
 
586
613
  def __dlpack_device__(self: Array, /) -> Tuple[IntEnum, int]:
587
614
  """
@@ -83,6 +83,29 @@ def asarray(
83
83
  if isinstance(obj, Array) and device is None:
84
84
  device = obj.device
85
85
 
86
+ if np.lib.NumpyVersion(np.__version__) < '2.0.0':
87
+ if copy is False:
88
+ # Note: copy=False is not yet implemented in np.asarray for
89
+ # NumPy 1
90
+
91
+ # Work around it by creating the new array and seeing if NumPy
92
+ # copies it.
93
+ if isinstance(obj, Array):
94
+ new_array = np.array(obj._array, copy=copy, dtype=_np_dtype)
95
+ if new_array is not obj._array:
96
+ raise ValueError("Unable to avoid copy while creating an array from given array.")
97
+ return Array._new(new_array, device=device)
98
+ elif _supports_buffer_protocol(obj):
99
+ # Buffer protocol will always support no-copy
100
+ return Array._new(np.array(obj, copy=copy, dtype=_np_dtype), device=device)
101
+ else:
102
+ # No-copy is unsupported for Python built-in types.
103
+ raise ValueError("Unable to avoid copy while creating an array from given object.")
104
+
105
+ if copy is None:
106
+ # NumPy 1 treats copy=False the same as the standard copy=None
107
+ copy = False
108
+
86
109
  if isinstance(obj, Array):
87
110
  return Array._new(np.array(obj._array, copy=copy, dtype=_np_dtype), device=device)
88
111
  if dtype is None and isinstance(obj, int) and (obj > 2 ** 64 or obj < -(2 ** 63)):
@@ -805,6 +805,20 @@ def negative(x: Array, /) -> Array:
805
805
  return Array._new(np.negative(x._array), device=x.device)
806
806
 
807
807
 
808
+ @requires_api_version('2024.12')
809
+ def nextafter(x1: Array, x2: Array, /) -> Array:
810
+ """
811
+ Array API compatible wrapper for :py:func:`np.nextafter <numpy.nextafter>`.
812
+
813
+ See its docstring for more information.
814
+ """
815
+ if x1.device != x2.device:
816
+ raise ValueError(f"Arrays from two different devices ({x1.device} and {x2.device}) can not be combined.")
817
+ if x1.dtype not in _real_floating_dtypes or x2.dtype not in _real_floating_dtypes:
818
+ raise TypeError("Only real floating-point dtypes are allowed in nextafter")
819
+ x1, x2 = Array._normalize_two_args(x1, x2)
820
+ return Array._new(np.nextafter(x1._array, x2._array), device=x1.device)
821
+
808
822
  def not_equal(x1: Array, x2: Array, /) -> Array:
809
823
  """
810
824
  Array API compatible wrapper for :py:func:`np.not_equal <numpy.not_equal>`.
@@ -858,6 +872,17 @@ def real(x: Array, /) -> Array:
858
872
  return Array._new(np.real(x._array), device=x.device)
859
873
 
860
874
 
875
+ @requires_api_version('2024.12')
876
+ def reciprocal(x: Array, /) -> Array:
877
+ """
878
+ Array API compatible wrapper for :py:func:`np.reciprocal <numpy.reciprocal>`.
879
+
880
+ See its docstring for more information.
881
+ """
882
+ if x.dtype not in _floating_dtypes:
883
+ raise TypeError("Only floating-point dtypes are allowed in reciprocal")
884
+ return Array._new(np.reciprocal(x._array), device=x.device)
885
+
861
886
  def remainder(x1: Array, x2: Array, /) -> Array:
862
887
  """
863
888
  Array API compatible wrapper for :py:func:`np.remainder <numpy.remainder>`.
@@ -24,6 +24,8 @@ supported_versions = (
24
24
  "2023.12",
25
25
  )
26
26
 
27
+ draft_version = "2024.12"
28
+
27
29
  API_VERSION = default_version = "2023.12"
28
30
 
29
31
  BOOLEAN_INDEXING = True
@@ -70,8 +72,8 @@ def set_array_api_strict_flags(
70
72
  ----------
71
73
  api_version : str, optional
72
74
  The version of the standard to use. Supported versions are:
73
- ``{supported_versions}``. The default version number is
74
- ``{default_version!r}``.
75
+ ``{supported_versions}``, plus the draft version ``{draft_version}``.
76
+ The default version number is ``{default_version!r}``.
75
77
 
76
78
  Note that 2021.12 is supported, but currently gives the same thing as
77
79
  2022.12 (except that the fft extension will be disabled).
@@ -134,10 +136,12 @@ def set_array_api_strict_flags(
134
136
  global API_VERSION, BOOLEAN_INDEXING, DATA_DEPENDENT_SHAPES, ENABLED_EXTENSIONS
135
137
 
136
138
  if api_version is not None:
137
- if api_version not in supported_versions:
139
+ if api_version not in [*supported_versions, draft_version]:
138
140
  raise ValueError(f"Unsupported standard version {api_version!r}")
139
141
  if api_version == "2021.12":
140
142
  warnings.warn("The 2021.12 version of the array API specification was requested but the returned namespace is actually version 2022.12", stacklevel=2)
143
+ if api_version == draft_version:
144
+ warnings.warn(f"The {draft_version} version of the array API specification is in draft status. Not all features are implemented in array_api_strict, some functions may not be fully tested, and behaviors are subject to change before the final standard release.")
141
145
  API_VERSION = api_version
142
146
  array_api_strict.__array_api_version__ = API_VERSION
143
147
 
@@ -169,6 +173,7 @@ set_array_api_strict_flags.__doc__ = set_array_api_strict_flags.__doc__.format(
169
173
  supported_versions=supported_versions,
170
174
  default_version=default_version,
171
175
  default_extensions=default_extensions,
176
+ draft_version=draft_version,
172
177
  )
173
178
 
174
179
  def get_array_api_strict_flags():
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from ._array_object import Array
4
4
  from ._dtypes import _integer_dtypes
5
+ from ._flags import requires_api_version
5
6
 
6
7
  from typing import TYPE_CHECKING
7
8
 
@@ -25,3 +26,14 @@ def take(x: Array, indices: Array, /, *, axis: Optional[int] = None) -> Array:
25
26
  if x.device != indices.device:
26
27
  raise ValueError(f"Arrays from two different devices ({x.device} and {indices.device}) can not be combined.")
27
28
  return Array._new(np.take(x._array, indices._array, axis=axis), device=x.device)
29
+
30
+ @requires_api_version('2024.12')
31
+ def take_along_axis(x: Array, indices: Array, /, *, axis: int = -1) -> Array:
32
+ """
33
+ Array API compatible wrapper for :py:func:`np.take_along_axis <numpy.take_along_axis>`.
34
+
35
+ See its docstring for more information.
36
+ """
37
+ if x.device != indices.device:
38
+ raise ValueError(f"Arrays from two different devices ({x.device} and {indices.device}) can not be combined.")
39
+ return Array._new(np.take_along_axis(x._array, indices._array, axis), device=x.device)
@@ -0,0 +1,139 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import numpy as np
6
+
7
+ if TYPE_CHECKING:
8
+ from typing import Optional, Union, Tuple, List
9
+ from ._typing import device, DefaultDataTypes, DataTypes, Capabilities
10
+
11
+ from ._array_object import ALL_DEVICES, CPU_DEVICE
12
+ from ._flags import get_array_api_strict_flags, requires_api_version
13
+ from ._dtypes import bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128
14
+
15
+ @requires_api_version('2023.12')
16
+ class __array_namespace_info__:
17
+ @requires_api_version('2023.12')
18
+ def capabilities(self) -> Capabilities:
19
+ flags = get_array_api_strict_flags()
20
+ res = {"boolean indexing": flags['boolean_indexing'],
21
+ "data-dependent shapes": flags['data_dependent_shapes'],
22
+ }
23
+ if flags['api_version'] >= '2024.12':
24
+ # maxdims is 32 for NumPy 1.x and 64 for NumPy 2.0. Eventually we will
25
+ # drop support for NumPy 1 but for now, just compute the number
26
+ # directly
27
+ for i in range(1, 100):
28
+ try:
29
+ np.zeros((1,)*i)
30
+ except ValueError:
31
+ maxdims = i - 1
32
+ break
33
+ else:
34
+ raise RuntimeError("Could not get max dimensions (this is a bug in array-api-strict)")
35
+ res['max dimensions'] = maxdims
36
+ return res
37
+
38
+ @requires_api_version('2023.12')
39
+ def default_device(self) -> device:
40
+ return CPU_DEVICE
41
+
42
+ @requires_api_version('2023.12')
43
+ def default_dtypes(
44
+ self,
45
+ *,
46
+ device: Optional[device] = None,
47
+ ) -> DefaultDataTypes:
48
+ return {
49
+ "real floating": float64,
50
+ "complex floating": complex128,
51
+ "integral": int64,
52
+ "indexing": int64,
53
+ }
54
+
55
+ @requires_api_version('2023.12')
56
+ def dtypes(
57
+ self,
58
+ *,
59
+ device: Optional[device] = None,
60
+ kind: Optional[Union[str, Tuple[str, ...]]] = None,
61
+ ) -> DataTypes:
62
+ if kind is None:
63
+ return {
64
+ "bool": bool,
65
+ "int8": int8,
66
+ "int16": int16,
67
+ "int32": int32,
68
+ "int64": int64,
69
+ "uint8": uint8,
70
+ "uint16": uint16,
71
+ "uint32": uint32,
72
+ "uint64": uint64,
73
+ "float32": float32,
74
+ "float64": float64,
75
+ "complex64": complex64,
76
+ "complex128": complex128,
77
+ }
78
+ if kind == "bool":
79
+ return {"bool": bool}
80
+ if kind == "signed integer":
81
+ return {
82
+ "int8": int8,
83
+ "int16": int16,
84
+ "int32": int32,
85
+ "int64": int64,
86
+ }
87
+ if kind == "unsigned integer":
88
+ return {
89
+ "uint8": uint8,
90
+ "uint16": uint16,
91
+ "uint32": uint32,
92
+ "uint64": uint64,
93
+ }
94
+ if kind == "integral":
95
+ return {
96
+ "int8": int8,
97
+ "int16": int16,
98
+ "int32": int32,
99
+ "int64": int64,
100
+ "uint8": uint8,
101
+ "uint16": uint16,
102
+ "uint32": uint32,
103
+ "uint64": uint64,
104
+ }
105
+ if kind == "real floating":
106
+ return {
107
+ "float32": float32,
108
+ "float64": float64,
109
+ }
110
+ if kind == "complex floating":
111
+ return {
112
+ "complex64": complex64,
113
+ "complex128": complex128,
114
+ }
115
+ if kind == "numeric":
116
+ return {
117
+ "int8": int8,
118
+ "int16": int16,
119
+ "int32": int32,
120
+ "int64": int64,
121
+ "uint8": uint8,
122
+ "uint16": uint16,
123
+ "uint32": uint32,
124
+ "uint64": uint64,
125
+ "float32": float32,
126
+ "float64": float64,
127
+ "complex64": complex64,
128
+ "complex128": complex128,
129
+ }
130
+ if isinstance(kind, tuple):
131
+ res = {}
132
+ for k in kind:
133
+ res.update(self.dtypes(kind=k))
134
+ return res
135
+ raise ValueError(f"unsupported kind: {kind!r}")
136
+
137
+ @requires_api_version('2023.12')
138
+ def devices(self) -> List[device]:
139
+ return list(ALL_DEVICES)
@@ -21,7 +21,6 @@ import sys
21
21
 
22
22
  from typing import (
23
23
  Any,
24
- ModuleType,
25
24
  TypedDict,
26
25
  TypeVar,
27
26
  Protocol,
@@ -29,6 +28,7 @@ from typing import (
29
28
 
30
29
  from ._array_object import Array, _device
31
30
  from ._dtypes import _DType
31
+ from ._info import __array_namespace_info__
32
32
 
33
33
  _T_co = TypeVar("_T_co", covariant=True)
34
34
 
@@ -41,7 +41,7 @@ Device = _device
41
41
 
42
42
  Dtype = _DType
43
43
 
44
- Info = ModuleType
44
+ Info = __array_namespace_info__
45
45
 
46
46
  if sys.version_info >= (3, 12):
47
47
  from collections.abc import Buffer as SupportsBufferProtocol
@@ -54,7 +54,8 @@ class SupportsDLPack(Protocol):
54
54
  def __dlpack__(self, /, *, stream: None = ...) -> PyCapsule: ...
55
55
 
56
56
  Capabilities = TypedDict(
57
- "Capabilities", {"boolean indexing": bool, "data-dependent shapes": bool}
57
+ "Capabilities", {"boolean indexing": bool, "data-dependent shapes": bool,
58
+ "max dimensions": int}
58
59
  )
59
60
 
60
61
  DefaultDataTypes = TypedDict(
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ from ._array_object import Array
4
+ from ._flags import requires_api_version
5
+ from ._dtypes import _numeric_dtypes
6
+
7
+ from typing import TYPE_CHECKING
8
+ if TYPE_CHECKING:
9
+ from typing import Optional, Tuple, Union
10
+
11
+ import numpy as np
12
+
13
+
14
+ def all(
15
+ x: Array,
16
+ /,
17
+ *,
18
+ axis: Optional[Union[int, Tuple[int, ...]]] = None,
19
+ keepdims: bool = False,
20
+ ) -> Array:
21
+ """
22
+ Array API compatible wrapper for :py:func:`np.all <numpy.all>`.
23
+
24
+ See its docstring for more information.
25
+ """
26
+ return Array._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims)), device=x.device)
27
+
28
+
29
+ def any(
30
+ x: Array,
31
+ /,
32
+ *,
33
+ axis: Optional[Union[int, Tuple[int, ...]]] = None,
34
+ keepdims: bool = False,
35
+ ) -> Array:
36
+ """
37
+ Array API compatible wrapper for :py:func:`np.any <numpy.any>`.
38
+
39
+ See its docstring for more information.
40
+ """
41
+ return Array._new(np.asarray(np.any(x._array, axis=axis, keepdims=keepdims)), device=x.device)
42
+
43
+ @requires_api_version('2024.12')
44
+ def diff(
45
+ x: Array,
46
+ /,
47
+ *,
48
+ axis: int = -1,
49
+ n: int = 1,
50
+ prepend: Optional[Array] = None,
51
+ append: Optional[Array] = None,
52
+ ) -> Array:
53
+ if x.dtype not in _numeric_dtypes:
54
+ raise TypeError("Only numeric dtypes are allowed in diff")
55
+
56
+ # TODO: The type promotion behavior for prepend and append is not
57
+ # currently specified.
58
+
59
+ # NumPy does not support prepend=None or append=None
60
+ kwargs = dict(axis=axis, n=n)
61
+ if prepend is not None:
62
+ if prepend.device != x.device:
63
+ raise ValueError(f"Arrays from two different devices ({prepend.device} and {x.device}) can not be combined.")
64
+ kwargs['prepend'] = prepend._array
65
+ if append is not None:
66
+ if append.device != x.device:
67
+ raise ValueError(f"Arrays from two different devices ({append.device} and {x.device}) can not be combined.")
68
+ kwargs['append'] = append._array
69
+ return Array._new(np.diff(x._array, **kwargs), device=x.device)
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-11-07T18:09:02-0700",
11
+ "date": "2024-11-11T15:30:59-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "c9fe697bec8de8e402d8beb71bbeb96ca70c772e",
15
- "version": "2.1.2"
14
+ "full-revisionid": "fe4f34ad30ba59c1c981037dd181f6b4daf23b22",
15
+ "version": "2.2"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -364,6 +364,23 @@ def test_array_conversion():
364
364
  with pytest.raises(RuntimeError, match="Can not convert array"):
365
365
  asarray([a])
366
366
 
367
+ def test__array__():
368
+ # __array__ should work for now
369
+ a = ones((2, 3))
370
+ np.array(a)
371
+
372
+ # Test the _allow_array private global flag for disabling it in the
373
+ # future.
374
+ from .. import _array_object
375
+ original_value = _array_object._allow_array
376
+ try:
377
+ _array_object._allow_array = False
378
+ a = ones((2, 3))
379
+ with pytest.raises(ValueError, match="Conversion from an array_api_strict array to a NumPy ndarray is not supported"):
380
+ np.array(a)
381
+ finally:
382
+ _array_object._allow_array = original_value
383
+
367
384
  def test_allow_newaxis():
368
385
  a = ones(5)
369
386
  indexed_a = a[None, :]
@@ -436,8 +453,12 @@ def test_array_namespace():
436
453
  assert a.__array_namespace__(api_version="2021.12") is array_api_strict
437
454
  assert array_api_strict.__array_api_version__ == "2021.12"
438
455
 
456
+ with pytest.warns(UserWarning):
457
+ assert a.__array_namespace__(api_version="2024.12") is array_api_strict
458
+ assert array_api_strict.__array_api_version__ == "2024.12"
459
+
439
460
  pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="2021.11"))
440
- pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="2024.12"))
461
+ pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="2025.12"))
441
462
 
442
463
  def test_iter():
443
464
  pytest.raises(TypeError, lambda: iter(asarray(3)))
@@ -448,7 +469,7 @@ def test_iter():
448
469
  pytest.raises(TypeError, lambda: iter(ones((3, 3))))
449
470
 
450
471
  @pytest.mark.parametrize("api_version", ['2021.12', '2022.12', '2023.12'])
451
- def dlpack_2023_12(api_version):
472
+ def test_dlpack_2023_12(api_version):
452
473
  if api_version == '2021.12':
453
474
  with pytest.warns(UserWarning):
454
475
  set_array_api_strict_flags(api_version=api_version)
@@ -456,19 +477,41 @@ def dlpack_2023_12(api_version):
456
477
  set_array_api_strict_flags(api_version=api_version)
457
478
 
458
479
  a = asarray([1, 2, 3], dtype=int8)
459
-
460
- # Do not error
480
+ # Never an error
461
481
  a.__dlpack__()
462
- a.__dlpack__(dl_device=CPU_DEVICE)
463
- a.__dlpack__(dl_device=None)
464
- a.__dlpack__(max_version=(1, 0))
465
- a.__dlpack__(max_version=None)
466
- a.__dlpack__(copy=False)
467
- a.__dlpack__(copy=True)
468
- a.__dlpack__(copy=None)
469
-
470
- x = np.from_dlpack(a)
471
- assert isinstance(x, np.ndarray)
472
- assert x.dtype == np.int8
473
- assert x.shape == (3,)
474
- assert np.all(x == np.asarray([1, 2, 3]))
482
+
483
+ if api_version < '2023.12':
484
+ pytest.raises(ValueError, lambda:
485
+ a.__dlpack__(dl_device=a.__dlpack_device__()))
486
+ pytest.raises(ValueError, lambda:
487
+ a.__dlpack__(dl_device=None))
488
+ pytest.raises(ValueError, lambda:
489
+ a.__dlpack__(max_version=(1, 0)))
490
+ pytest.raises(ValueError, lambda:
491
+ a.__dlpack__(max_version=None))
492
+ pytest.raises(ValueError, lambda:
493
+ a.__dlpack__(copy=False))
494
+ pytest.raises(ValueError, lambda:
495
+ a.__dlpack__(copy=True))
496
+ pytest.raises(ValueError, lambda:
497
+ a.__dlpack__(copy=None))
498
+ elif np.lib.NumpyVersion(np.__version__) < '2.1.0':
499
+ pytest.raises(NotImplementedError, lambda:
500
+ a.__dlpack__(dl_device=CPU_DEVICE))
501
+ a.__dlpack__(dl_device=None)
502
+ pytest.raises(NotImplementedError, lambda:
503
+ a.__dlpack__(max_version=(1, 0)))
504
+ a.__dlpack__(max_version=None)
505
+ pytest.raises(NotImplementedError, lambda:
506
+ a.__dlpack__(copy=False))
507
+ pytest.raises(NotImplementedError, lambda:
508
+ a.__dlpack__(copy=True))
509
+ a.__dlpack__(copy=None)
510
+ else:
511
+ a.__dlpack__(dl_device=a.__dlpack_device__())
512
+ a.__dlpack__(dl_device=None)
513
+ a.__dlpack__(max_version=(1, 0))
514
+ a.__dlpack__(max_version=None)
515
+ a.__dlpack__(copy=False)
516
+ a.__dlpack__(copy=True)
517
+ a.__dlpack__(copy=None)
@@ -2,6 +2,8 @@ from inspect import signature, getmodule
2
2
 
3
3
  from numpy.testing import assert_raises
4
4
 
5
+ import pytest
6
+
5
7
  from .. import asarray, _elementwise_functions
6
8
  from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift
7
9
  from .._dtypes import (
@@ -79,10 +81,12 @@ elementwise_function_input_types = {
79
81
  "minimum": "real numeric",
80
82
  "multiply": "numeric",
81
83
  "negative": "numeric",
84
+ "nextafter": "real floating-point",
82
85
  "not_equal": "all",
83
86
  "positive": "numeric",
84
87
  "pow": "numeric",
85
88
  "real": "complex floating-point",
89
+ "reciprocal": "floating-point",
86
90
  "remainder": "real numeric",
87
91
  "round": "numeric",
88
92
  "sign": "numeric",
@@ -126,7 +130,8 @@ def test_function_device_persists():
126
130
  yield asarray(1., dtype=d)
127
131
 
128
132
  # Use the latest version of the standard so all functions are included
129
- set_array_api_strict_flags(api_version="2023.12")
133
+ with pytest.warns(UserWarning):
134
+ set_array_api_strict_flags(api_version="2024.12")
130
135
 
131
136
  for func_name, types in elementwise_function_input_types.items():
132
137
  dtypes = _dtype_categories[types]
@@ -162,7 +167,8 @@ def test_function_types():
162
167
  yield asarray(1.0, dtype=d)
163
168
 
164
169
  # Use the latest version of the standard so all functions are included
165
- set_array_api_strict_flags(api_version="2023.12")
170
+ with pytest.warns(UserWarning):
171
+ set_array_api_strict_flags(api_version="2024.12")
166
172
 
167
173
  for x in _array_vals():
168
174
  for func_name, types in elementwise_function_input_types.items():
@@ -3,8 +3,6 @@ import subprocess
3
3
 
4
4
  from .._flags import (set_array_api_strict_flags, get_array_api_strict_flags,
5
5
  reset_array_api_strict_flags)
6
- from .._info import (capabilities, default_device, default_dtypes, devices,
7
- dtypes)
8
6
  from .._fft import (fft, ifft, fftn, ifftn, rfft, irfft, rfftn, irfftn, hfft,
9
7
  ihfft, fftfreq, rfftfreq, fftshift, ifftshift)
10
8
  from .._linalg import (cholesky, cross, det, diagonal, eigh, eigvalsh, inv,
@@ -99,6 +97,21 @@ def test_flags_api_version_2023_12():
99
97
  'enabled_extensions': ('linalg', 'fft'),
100
98
  }
101
99
 
100
+ def test_flags_api_version_2024_12():
101
+ # Make sure setting the version to 2024.12 issues a warning.
102
+ with pytest.warns(UserWarning) as record:
103
+ set_array_api_strict_flags(api_version='2024.12')
104
+ assert len(record) == 1
105
+ assert '2024.12' in str(record[0].message)
106
+ assert 'draft' in str(record[0].message)
107
+ flags = get_array_api_strict_flags()
108
+ assert flags == {
109
+ 'api_version': '2024.12',
110
+ 'boolean_indexing': True,
111
+ 'data_dependent_shapes': True,
112
+ 'enabled_extensions': ('linalg', 'fft'),
113
+ }
114
+
102
115
  def test_setting_flags_invalid():
103
116
  # Test setting flags with invalid values
104
117
  pytest.raises(ValueError, lambda:
@@ -245,14 +258,17 @@ def test_fft(func_name):
245
258
  set_array_api_strict_flags(enabled_extensions=('fft',))
246
259
  func()
247
260
 
261
+ # Test functionality even if the info object is already created
262
+ _info = xp.__array_namespace_info__()
263
+
248
264
  api_version_2023_12_examples = {
249
265
  '__array_namespace_info__': lambda: xp.__array_namespace_info__(),
250
266
  # Test these functions directly to ensure they are properly decorated
251
- 'capabilities': capabilities,
252
- 'default_device': default_device,
253
- 'default_dtypes': default_dtypes,
254
- 'devices': devices,
255
- 'dtypes': dtypes,
267
+ 'capabilities': _info.capabilities,
268
+ 'default_device': _info.default_device,
269
+ 'default_dtypes': _info.default_dtypes,
270
+ 'devices': _info.devices,
271
+ 'dtypes': _info.dtypes,
256
272
  'clip': lambda: xp.clip(xp.asarray([1, 2, 3]), 1, 2),
257
273
  'copysign': lambda: xp.copysign(xp.asarray([1., 2., 3.]), xp.asarray([-1., -1., -1.])),
258
274
  'cumulative_sum': lambda: xp.cumulative_sum(xp.asarray([1, 2, 3])),
@@ -285,6 +301,36 @@ def test_api_version_2023_12(func_name):
285
301
  set_array_api_strict_flags(api_version='2022.12')
286
302
  pytest.raises(RuntimeError, func)
287
303
 
304
+ api_version_2024_12_examples = {
305
+ 'diff': lambda: xp.diff(xp.asarray([0, 1, 2])),
306
+ 'nextafter': lambda: xp.nextafter(xp.asarray(0.), xp.asarray(1.)),
307
+ 'reciprocal': lambda: xp.reciprocal(xp.asarray([2.])),
308
+ 'take_along_axis': lambda: xp.take_along_axis(xp.zeros((2, 3)),
309
+ xp.zeros((1, 4), dtype=xp.int64)),
310
+ }
311
+
312
+ @pytest.mark.parametrize('func_name', api_version_2024_12_examples.keys())
313
+ def test_api_version_2024_12(func_name):
314
+ func = api_version_2024_12_examples[func_name]
315
+
316
+ # By default, these functions should error
317
+ pytest.raises(RuntimeError, func)
318
+
319
+ # In 2022.12 and 2023.12, these functions should error
320
+ set_array_api_strict_flags(api_version='2022.12')
321
+ pytest.raises(RuntimeError, func)
322
+ set_array_api_strict_flags(api_version='2023.12')
323
+ pytest.raises(RuntimeError, func)
324
+
325
+ # They should not error in 2024.12
326
+ with pytest.warns(UserWarning):
327
+ set_array_api_strict_flags(api_version='2024.12')
328
+ func()
329
+
330
+ # Test the behavior gets updated properly
331
+ set_array_api_strict_flags(api_version='2023.12')
332
+ pytest.raises(RuntimeError, func)
333
+
288
334
  def test_disabled_extensions():
289
335
  # Test that xp.extension errors when an extension is disabled, and that
290
336
  # xp.__all__ is updated properly.
@@ -1,22 +1,21 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: array_api_strict
3
- Version: 2.1.2
3
+ Version: 2.2
4
4
  Summary: A strict, minimal implementation of the Python array API standard.
5
5
  Home-page: https://data-apis.org/array-api-strict/
6
6
  Author: Consortium for Python Data API Standards
7
7
  License: MIT
8
8
  Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
9
10
  Classifier: Programming Language :: Python :: 3.10
10
11
  Classifier: Programming Language :: Python :: 3.11
11
12
  Classifier: Programming Language :: Python :: 3.12
12
- Classifier: Programming Language :: Python :: 3.13
13
- Classifier: Intended Audience :: Developers
14
13
  Classifier: License :: OSI Approved :: BSD License
15
14
  Classifier: Operating System :: OS Independent
16
- Requires-Python: >=3.10
15
+ Requires-Python: >=3.9
17
16
  Description-Content-Type: text/markdown
18
17
  License-File: LICENSE
19
- Requires-Dist: numpy>=2.1
18
+ Requires-Dist: numpy
20
19
 
21
20
  # array-api-strict
22
21
 
@@ -15,15 +15,14 @@ setup(
15
15
  long_description_content_type="text/markdown",
16
16
  url="https://data-apis.org/array-api-strict/",
17
17
  license="MIT",
18
- python_requires=">=3.10",
19
- install_requires=["numpy>=2.1"],
18
+ python_requires=">=3.9",
19
+ install_requires=["numpy"],
20
20
  classifiers=[
21
21
  "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.9",
22
23
  "Programming Language :: Python :: 3.10",
23
24
  "Programming Language :: Python :: 3.11",
24
25
  "Programming Language :: Python :: 3.12",
25
- "Programming Language :: Python :: 3.13",
26
- "Intended Audience :: Developers",
27
26
  "License :: OSI Approved :: BSD License",
28
27
  "Operating System :: OS Independent",
29
28
  ],
@@ -1,132 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING
4
-
5
- if TYPE_CHECKING:
6
- from typing import Optional, Union, Tuple, List
7
- from ._typing import device, DefaultDataTypes, DataTypes, Capabilities, Info
8
-
9
- from ._array_object import ALL_DEVICES, CPU_DEVICE
10
- from ._flags import get_array_api_strict_flags, requires_api_version
11
- from ._dtypes import bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128
12
-
13
- @requires_api_version('2023.12')
14
- def __array_namespace_info__() -> Info:
15
- import array_api_strict._info
16
- return array_api_strict._info
17
-
18
- @requires_api_version('2023.12')
19
- def capabilities() -> Capabilities:
20
- flags = get_array_api_strict_flags()
21
- return {"boolean indexing": flags['boolean_indexing'],
22
- "data-dependent shapes": flags['data_dependent_shapes'],
23
- }
24
-
25
- @requires_api_version('2023.12')
26
- def default_device() -> device:
27
- return CPU_DEVICE
28
-
29
- @requires_api_version('2023.12')
30
- def default_dtypes(
31
- *,
32
- device: Optional[device] = None,
33
- ) -> DefaultDataTypes:
34
- return {
35
- "real floating": float64,
36
- "complex floating": complex128,
37
- "integral": int64,
38
- "indexing": int64,
39
- }
40
-
41
- @requires_api_version('2023.12')
42
- def dtypes(
43
- *,
44
- device: Optional[device] = None,
45
- kind: Optional[Union[str, Tuple[str, ...]]] = None,
46
- ) -> DataTypes:
47
- if kind is None:
48
- return {
49
- "bool": bool,
50
- "int8": int8,
51
- "int16": int16,
52
- "int32": int32,
53
- "int64": int64,
54
- "uint8": uint8,
55
- "uint16": uint16,
56
- "uint32": uint32,
57
- "uint64": uint64,
58
- "float32": float32,
59
- "float64": float64,
60
- "complex64": complex64,
61
- "complex128": complex128,
62
- }
63
- if kind == "bool":
64
- return {"bool": bool}
65
- if kind == "signed integer":
66
- return {
67
- "int8": int8,
68
- "int16": int16,
69
- "int32": int32,
70
- "int64": int64,
71
- }
72
- if kind == "unsigned integer":
73
- return {
74
- "uint8": uint8,
75
- "uint16": uint16,
76
- "uint32": uint32,
77
- "uint64": uint64,
78
- }
79
- if kind == "integral":
80
- return {
81
- "int8": int8,
82
- "int16": int16,
83
- "int32": int32,
84
- "int64": int64,
85
- "uint8": uint8,
86
- "uint16": uint16,
87
- "uint32": uint32,
88
- "uint64": uint64,
89
- }
90
- if kind == "real floating":
91
- return {
92
- "float32": float32,
93
- "float64": float64,
94
- }
95
- if kind == "complex floating":
96
- return {
97
- "complex64": complex64,
98
- "complex128": complex128,
99
- }
100
- if kind == "numeric":
101
- return {
102
- "int8": int8,
103
- "int16": int16,
104
- "int32": int32,
105
- "int64": int64,
106
- "uint8": uint8,
107
- "uint16": uint16,
108
- "uint32": uint32,
109
- "uint64": uint64,
110
- "float32": float32,
111
- "float64": float64,
112
- "complex64": complex64,
113
- "complex128": complex128,
114
- }
115
- if isinstance(kind, tuple):
116
- res = {}
117
- for k in kind:
118
- res.update(dtypes(kind=k))
119
- return res
120
- raise ValueError(f"unsupported kind: {kind!r}")
121
-
122
- @requires_api_version('2023.12')
123
- def devices() -> List[device]:
124
- return list(ALL_DEVICES)
125
-
126
- __all__ = [
127
- "capabilities",
128
- "default_device",
129
- "default_dtypes",
130
- "devices",
131
- "dtypes",
132
- ]
@@ -1,39 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from ._array_object import Array
4
-
5
- from typing import TYPE_CHECKING
6
- if TYPE_CHECKING:
7
- from typing import Optional, Tuple, Union
8
-
9
- import numpy as np
10
-
11
-
12
- def all(
13
- x: Array,
14
- /,
15
- *,
16
- axis: Optional[Union[int, Tuple[int, ...]]] = None,
17
- keepdims: bool = False,
18
- ) -> Array:
19
- """
20
- Array API compatible wrapper for :py:func:`np.all <numpy.all>`.
21
-
22
- See its docstring for more information.
23
- """
24
- return Array._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims)), device=x.device)
25
-
26
-
27
- def any(
28
- x: Array,
29
- /,
30
- *,
31
- axis: Optional[Union[int, Tuple[int, ...]]] = None,
32
- keepdims: bool = False,
33
- ) -> Array:
34
- """
35
- Array API compatible wrapper for :py:func:`np.any <numpy.any>`.
36
-
37
- See its docstring for more information.
38
- """
39
- return Array._new(np.asarray(np.any(x._array, axis=axis, keepdims=keepdims)), device=x.device)
@@ -1 +0,0 @@
1
- numpy>=2.1
File without changes