array-api-strict 2.2__py3-none-any.whl → 2.3.1__py3-none-any.whl
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.
- array_api_strict/__init__.py +10 -7
- array_api_strict/_array_object.py +31 -19
- array_api_strict/_creation_functions.py +2 -0
- array_api_strict/_data_type_functions.py +34 -3
- array_api_strict/_dtypes.py +1 -0
- array_api_strict/_elementwise_functions.py +140 -471
- array_api_strict/_fft.py +31 -5
- array_api_strict/_flags.py +10 -8
- array_api_strict/_helpers.py +37 -0
- array_api_strict/_linear_algebra_functions.py +1 -1
- array_api_strict/_manipulation_functions.py +5 -0
- array_api_strict/_searching_functions.py +34 -5
- array_api_strict/_statistical_functions.py +42 -5
- array_api_strict/_version.py +16 -16
- array_api_strict/tests/test_array_object.py +146 -74
- array_api_strict/tests/test_creation_functions.py +7 -0
- array_api_strict/tests/test_data_type_functions.py +24 -4
- array_api_strict/tests/test_elementwise_functions.py +66 -8
- array_api_strict/tests/test_flags.py +29 -18
- array_api_strict/tests/test_manipulation_functions.py +1 -1
- array_api_strict/tests/test_searching_functions.py +44 -0
- array_api_strict/tests/test_statistical_functions.py +19 -1
- array_api_strict/tests/test_validation.py +1 -1
- array_api_strict-2.3.1.dist-info/METADATA +68 -0
- array_api_strict-2.3.1.dist-info/RECORD +43 -0
- {array_api_strict-2.2.dist-info → array_api_strict-2.3.1.dist-info}/WHEEL +1 -1
- array_api_strict-2.2.dist-info/METADATA +0 -37
- array_api_strict-2.2.dist-info/RECORD +0 -41
- {array_api_strict-2.2.dist-info → array_api_strict-2.3.1.dist-info}/LICENSE +0 -0
- {array_api_strict-2.2.dist-info → array_api_strict-2.3.1.dist-info}/top_level.txt +0 -0
array_api_strict/_fft.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
|
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
6
|
from typing import Union, Optional, Literal
|
|
7
|
-
from ._typing import Device
|
|
7
|
+
from ._typing import Device, Dtype as DType
|
|
8
8
|
from collections.abc import Sequence
|
|
9
9
|
|
|
10
10
|
from ._dtypes import (
|
|
@@ -251,7 +251,14 @@ def ihfft(
|
|
|
251
251
|
return res
|
|
252
252
|
|
|
253
253
|
@requires_extension('fft')
|
|
254
|
-
def fftfreq(
|
|
254
|
+
def fftfreq(
|
|
255
|
+
n: int,
|
|
256
|
+
/,
|
|
257
|
+
*,
|
|
258
|
+
d: float = 1.0,
|
|
259
|
+
dtype: Optional[DType] = None,
|
|
260
|
+
device: Optional[Device] = None
|
|
261
|
+
) -> Array:
|
|
255
262
|
"""
|
|
256
263
|
Array API compatible wrapper for :py:func:`np.fft.fftfreq <numpy.fft.fftfreq>`.
|
|
257
264
|
|
|
@@ -259,10 +266,23 @@ def fftfreq(n: int, /, *, d: float = 1.0, device: Optional[Device] = None) -> Ar
|
|
|
259
266
|
"""
|
|
260
267
|
if device is not None and device not in ALL_DEVICES:
|
|
261
268
|
raise ValueError(f"Unsupported device {device!r}")
|
|
262
|
-
|
|
269
|
+
if dtype and not dtype in _real_floating_dtypes:
|
|
270
|
+
raise ValueError(f"`dtype` must be a real floating-point type. Got {dtype=}.")
|
|
271
|
+
|
|
272
|
+
np_result = np.fft.fftfreq(n, d=d)
|
|
273
|
+
if dtype:
|
|
274
|
+
np_result = np_result.astype(dtype._np_dtype)
|
|
275
|
+
return Array._new(np_result, device=device)
|
|
263
276
|
|
|
264
277
|
@requires_extension('fft')
|
|
265
|
-
def rfftfreq(
|
|
278
|
+
def rfftfreq(
|
|
279
|
+
n: int,
|
|
280
|
+
/,
|
|
281
|
+
*,
|
|
282
|
+
d: float = 1.0,
|
|
283
|
+
dtype: Optional[DType] = None,
|
|
284
|
+
device: Optional[Device] = None
|
|
285
|
+
) -> Array:
|
|
266
286
|
"""
|
|
267
287
|
Array API compatible wrapper for :py:func:`np.fft.rfftfreq <numpy.fft.rfftfreq>`.
|
|
268
288
|
|
|
@@ -270,7 +290,13 @@ def rfftfreq(n: int, /, *, d: float = 1.0, device: Optional[Device] = None) -> A
|
|
|
270
290
|
"""
|
|
271
291
|
if device is not None and device not in ALL_DEVICES:
|
|
272
292
|
raise ValueError(f"Unsupported device {device!r}")
|
|
273
|
-
|
|
293
|
+
if dtype and not dtype in _real_floating_dtypes:
|
|
294
|
+
raise ValueError(f"`dtype` must be a real floating-point type. Got {dtype=}.")
|
|
295
|
+
|
|
296
|
+
np_result = np.fft.rfftfreq(n, d=d)
|
|
297
|
+
if dtype:
|
|
298
|
+
np_result = np_result.astype(dtype._np_dtype)
|
|
299
|
+
return Array._new(np_result, device=device)
|
|
274
300
|
|
|
275
301
|
@requires_extension('fft')
|
|
276
302
|
def fftshift(x: Array, /, *, axes: Union[int, Sequence[int]] = None) -> Array:
|
array_api_strict/_flags.py
CHANGED
|
@@ -22,11 +22,12 @@ supported_versions = (
|
|
|
22
22
|
"2021.12",
|
|
23
23
|
"2022.12",
|
|
24
24
|
"2023.12",
|
|
25
|
+
"2024.12"
|
|
25
26
|
)
|
|
26
27
|
|
|
27
|
-
draft_version = "
|
|
28
|
+
draft_version = "2025.12"
|
|
28
29
|
|
|
29
|
-
API_VERSION = default_version = "
|
|
30
|
+
API_VERSION = default_version = "2024.12"
|
|
30
31
|
|
|
31
32
|
BOOLEAN_INDEXING = True
|
|
32
33
|
|
|
@@ -169,12 +170,13 @@ def set_array_api_strict_flags(
|
|
|
169
170
|
set(default_extensions))
|
|
170
171
|
|
|
171
172
|
# We have to do this separately or it won't get added as the docstring
|
|
172
|
-
set_array_api_strict_flags.__doc__
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
if set_array_api_strict_flags.__doc__ is not None:
|
|
174
|
+
set_array_api_strict_flags.__doc__ = set_array_api_strict_flags.__doc__.format(
|
|
175
|
+
supported_versions=supported_versions,
|
|
176
|
+
default_version=default_version,
|
|
177
|
+
default_extensions=default_extensions,
|
|
178
|
+
draft_version=draft_version,
|
|
179
|
+
)
|
|
178
180
|
|
|
179
181
|
def get_array_api_strict_flags():
|
|
180
182
|
"""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Private helper routines.
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
from ._flags import get_array_api_strict_flags
|
|
5
|
+
from ._dtypes import _dtype_categories
|
|
6
|
+
|
|
7
|
+
_py_scalars = (bool, int, float, complex)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _maybe_normalize_py_scalars(x1, x2, dtype_category, func_name):
|
|
11
|
+
|
|
12
|
+
flags = get_array_api_strict_flags()
|
|
13
|
+
if flags["api_version"] < "2024.12":
|
|
14
|
+
# scalars will fail at the call site
|
|
15
|
+
return x1, x2
|
|
16
|
+
|
|
17
|
+
_allowed_dtypes = _dtype_categories[dtype_category]
|
|
18
|
+
|
|
19
|
+
if isinstance(x1, _py_scalars):
|
|
20
|
+
if isinstance(x2, _py_scalars):
|
|
21
|
+
raise TypeError(f"Two scalars not allowed, got {type(x1) = } and {type(x2) =}")
|
|
22
|
+
# x2 must be an array
|
|
23
|
+
if x2.dtype not in _allowed_dtypes:
|
|
24
|
+
raise TypeError(f"Only {dtype_category} dtypes are allowed {func_name}. Got {x2.dtype}.")
|
|
25
|
+
x1 = x2._promote_scalar(x1)
|
|
26
|
+
|
|
27
|
+
elif isinstance(x2, _py_scalars):
|
|
28
|
+
# x1 must be an array
|
|
29
|
+
if x1.dtype not in _allowed_dtypes:
|
|
30
|
+
raise TypeError(f"Only {dtype_category} dtypes are allowed {func_name}. Got {x1.dtype}.")
|
|
31
|
+
x2 = x1._promote_scalar(x2)
|
|
32
|
+
else:
|
|
33
|
+
if x1.dtype not in _allowed_dtypes or x2.dtype not in _allowed_dtypes:
|
|
34
|
+
raise TypeError(f"Only {dtype_category} dtypes are allowed in {func_name}(...). "
|
|
35
|
+
f"Got {x1.dtype} and {x2.dtype}.")
|
|
36
|
+
return x1, x2
|
|
37
|
+
|
|
@@ -86,5 +86,5 @@ def vecdot(x1: Array, x2: Array, /, *, axis: int = -1) -> Array:
|
|
|
86
86
|
x1_ = np.moveaxis(x1_, axis, -1)
|
|
87
87
|
x2_ = np.moveaxis(x2_, axis, -1)
|
|
88
88
|
|
|
89
|
-
res = x1_[..., None, :] @ x2_[..., None]
|
|
89
|
+
res = np.conj(x1_[..., None, :]) @ x2_[..., None]
|
|
90
90
|
return Array._new(res[..., 0, 0], device=x1.device)
|
|
@@ -153,6 +153,11 @@ def squeeze(x: Array, /, axis: Union[int, Tuple[int, ...]]) -> Array:
|
|
|
153
153
|
|
|
154
154
|
See its docstring for more information.
|
|
155
155
|
"""
|
|
156
|
+
if axis is None:
|
|
157
|
+
raise ValueError(
|
|
158
|
+
"squeeze(..., axis=None is not supported. See "
|
|
159
|
+
"https://github.com/data-apis/array-api/pull/100 for a discussion."
|
|
160
|
+
)
|
|
156
161
|
return Array._new(np.squeeze(x._array, axis=axis), device=x.device)
|
|
157
162
|
|
|
158
163
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from ._array_object import Array
|
|
4
|
-
from ._dtypes import _result_type, _real_numeric_dtypes
|
|
5
|
-
from ._flags import requires_data_dependent_shapes, requires_api_version
|
|
4
|
+
from ._dtypes import _result_type, _real_numeric_dtypes, bool as _bool
|
|
5
|
+
from ._flags import requires_data_dependent_shapes, requires_api_version, get_array_api_strict_flags
|
|
6
|
+
from ._helpers import _maybe_normalize_py_scalars
|
|
6
7
|
|
|
7
8
|
from typing import TYPE_CHECKING
|
|
8
9
|
if TYPE_CHECKING:
|
|
9
|
-
from typing import Literal, Optional, Tuple
|
|
10
|
+
from typing import Literal, Optional, Tuple, Union
|
|
10
11
|
|
|
11
12
|
import numpy as np
|
|
12
13
|
|
|
@@ -45,6 +46,24 @@ def nonzero(x: Array, /) -> Tuple[Array, ...]:
|
|
|
45
46
|
raise ValueError("nonzero is not allowed on 0-dimensional arrays")
|
|
46
47
|
return tuple(Array._new(i, device=x.device) for i in np.nonzero(x._array))
|
|
47
48
|
|
|
49
|
+
|
|
50
|
+
@requires_api_version('2024.12')
|
|
51
|
+
def count_nonzero(
|
|
52
|
+
x: Array,
|
|
53
|
+
/,
|
|
54
|
+
*,
|
|
55
|
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
|
56
|
+
keepdims: bool = False,
|
|
57
|
+
) -> Array:
|
|
58
|
+
"""
|
|
59
|
+
Array API compatible wrapper for :py:func:`np.count_nonzero <numpy.count_nonzero>`
|
|
60
|
+
|
|
61
|
+
See its docstring for more information.
|
|
62
|
+
"""
|
|
63
|
+
arr = np.count_nonzero(x._array, axis=axis, keepdims=keepdims)
|
|
64
|
+
return Array._new(np.asarray(arr), device=x.device)
|
|
65
|
+
|
|
66
|
+
|
|
48
67
|
@requires_api_version('2023.12')
|
|
49
68
|
def searchsorted(
|
|
50
69
|
x1: Array,
|
|
@@ -72,17 +91,27 @@ def searchsorted(
|
|
|
72
91
|
# x1 must be 1-D, but NumPy already requires this.
|
|
73
92
|
return Array._new(np.searchsorted(x1._array, x2._array, side=side, sorter=sorter), device=x1.device)
|
|
74
93
|
|
|
75
|
-
def where(
|
|
94
|
+
def where(
|
|
95
|
+
condition: Array,
|
|
96
|
+
x1: bool | int | float | complex | Array,
|
|
97
|
+
x2: bool | int | float | complex | Array, /
|
|
98
|
+
) -> Array:
|
|
76
99
|
"""
|
|
77
100
|
Array API compatible wrapper for :py:func:`np.where <numpy.where>`.
|
|
78
101
|
|
|
79
102
|
See its docstring for more information.
|
|
80
103
|
"""
|
|
104
|
+
if get_array_api_strict_flags()['api_version'] > '2023.12':
|
|
105
|
+
x1, x2 = _maybe_normalize_py_scalars(x1, x2, "all", "where")
|
|
106
|
+
|
|
81
107
|
# Call result type here just to raise on disallowed type combinations
|
|
82
108
|
_result_type(x1.dtype, x2.dtype)
|
|
109
|
+
|
|
110
|
+
if condition.dtype != _bool:
|
|
111
|
+
raise TypeError("`condition` must be have a boolean data type")
|
|
83
112
|
|
|
84
113
|
if len({a.device for a in (condition, x1, x2)}) > 1:
|
|
85
|
-
raise ValueError("where
|
|
114
|
+
raise ValueError("Inputs to `where` must all use the same device")
|
|
86
115
|
|
|
87
116
|
x1, x2 = Array._normalize_two_args(x1, x2)
|
|
88
117
|
return Array._new(np.where(condition._array, x1._array, x2._array), device=x1.device)
|
|
@@ -3,12 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
from ._dtypes import (
|
|
4
4
|
_real_floating_dtypes,
|
|
5
5
|
_real_numeric_dtypes,
|
|
6
|
+
_floating_dtypes,
|
|
6
7
|
_numeric_dtypes,
|
|
7
8
|
)
|
|
8
9
|
from ._array_object import Array
|
|
9
10
|
from ._dtypes import float32, complex64
|
|
10
11
|
from ._flags import requires_api_version, get_array_api_strict_flags
|
|
11
|
-
from ._creation_functions import zeros
|
|
12
|
+
from ._creation_functions import zeros, ones
|
|
12
13
|
from ._manipulation_functions import concat
|
|
13
14
|
|
|
14
15
|
from typing import TYPE_CHECKING
|
|
@@ -30,7 +31,6 @@ def cumulative_sum(
|
|
|
30
31
|
) -> Array:
|
|
31
32
|
if x.dtype not in _numeric_dtypes:
|
|
32
33
|
raise TypeError("Only numeric dtypes are allowed in cumulative_sum")
|
|
33
|
-
dt = x.dtype if dtype is None else dtype
|
|
34
34
|
if dtype is not None:
|
|
35
35
|
dtype = dtype._np_dtype
|
|
36
36
|
|
|
@@ -43,9 +43,40 @@ def cumulative_sum(
|
|
|
43
43
|
if include_initial:
|
|
44
44
|
if axis < 0:
|
|
45
45
|
axis += x.ndim
|
|
46
|
-
x = concat([zeros(x.shape[:axis] + (1,) + x.shape[axis + 1:], dtype=
|
|
46
|
+
x = concat([zeros(x.shape[:axis] + (1,) + x.shape[axis + 1:], dtype=x.dtype), x], axis=axis)
|
|
47
47
|
return Array._new(np.cumsum(x._array, axis=axis, dtype=dtype), device=x.device)
|
|
48
48
|
|
|
49
|
+
|
|
50
|
+
@requires_api_version('2024.12')
|
|
51
|
+
def cumulative_prod(
|
|
52
|
+
x: Array,
|
|
53
|
+
/,
|
|
54
|
+
*,
|
|
55
|
+
axis: Optional[int] = None,
|
|
56
|
+
dtype: Optional[Dtype] = None,
|
|
57
|
+
include_initial: bool = False,
|
|
58
|
+
) -> Array:
|
|
59
|
+
if x.dtype not in _numeric_dtypes:
|
|
60
|
+
raise TypeError("Only numeric dtypes are allowed in cumulative_prod")
|
|
61
|
+
if x.ndim == 0:
|
|
62
|
+
raise ValueError("Only ndim >= 1 arrays are allowed in cumulative_prod")
|
|
63
|
+
|
|
64
|
+
if dtype is not None:
|
|
65
|
+
dtype = dtype._np_dtype
|
|
66
|
+
|
|
67
|
+
if axis is None:
|
|
68
|
+
if x.ndim > 1:
|
|
69
|
+
raise ValueError("axis must be specified in cumulative_prod for more than one dimension")
|
|
70
|
+
axis = 0
|
|
71
|
+
|
|
72
|
+
# np.cumprod does not support include_initial
|
|
73
|
+
if include_initial:
|
|
74
|
+
if axis < 0:
|
|
75
|
+
axis += x.ndim
|
|
76
|
+
x = concat([ones(x.shape[:axis] + (1,) + x.shape[axis + 1:], dtype=x.dtype), x], axis=axis)
|
|
77
|
+
return Array._new(np.cumprod(x._array, axis=axis, dtype=dtype), device=x.device)
|
|
78
|
+
|
|
79
|
+
|
|
49
80
|
def max(
|
|
50
81
|
x: Array,
|
|
51
82
|
/,
|
|
@@ -65,8 +96,14 @@ def mean(
|
|
|
65
96
|
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
|
66
97
|
keepdims: bool = False,
|
|
67
98
|
) -> Array:
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
|
|
100
|
+
if get_array_api_strict_flags()['api_version'] > '2023.12':
|
|
101
|
+
allowed_dtypes = _floating_dtypes
|
|
102
|
+
else:
|
|
103
|
+
allowed_dtypes = _real_floating_dtypes
|
|
104
|
+
|
|
105
|
+
if x.dtype not in allowed_dtypes:
|
|
106
|
+
raise TypeError("Only floating-point dtypes are allowed in mean")
|
|
70
107
|
return Array._new(np.mean(x._array, axis=axis, keepdims=keepdims), device=x.device)
|
|
71
108
|
|
|
72
109
|
|
array_api_strict/_version.py
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
1
3
|
|
|
2
|
-
|
|
3
|
-
# revision-control system data, or from the parent directory name of an
|
|
4
|
-
# unpacked source archive. Distribution tarballs contain a pre-generated copy
|
|
5
|
-
# of this file.
|
|
4
|
+
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
TYPE_CHECKING = False
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from typing import Union
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"dirty": false,
|
|
13
|
-
"error": null,
|
|
14
|
-
"full-revisionid": "fe4f34ad30ba59c1c981037dd181f6b4daf23b22",
|
|
15
|
-
"version": "2.2"
|
|
16
|
-
}
|
|
17
|
-
''' # END VERSION_JSON
|
|
11
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
12
|
+
else:
|
|
13
|
+
VERSION_TUPLE = object
|
|
18
14
|
|
|
15
|
+
version: str
|
|
16
|
+
__version__: str
|
|
17
|
+
__version_tuple__: VERSION_TUPLE
|
|
18
|
+
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
__version__ = version = '2.3.1'
|
|
21
|
+
__version_tuple__ = version_tuple = (2, 3, 1)
|
|
@@ -5,7 +5,7 @@ from numpy.testing import assert_raises, suppress_warnings
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
|
-
from .. import ones, asarray, result_type, all, equal
|
|
8
|
+
from .. import ones, arange, reshape, asarray, result_type, all, equal
|
|
9
9
|
from .._array_object import Array, CPU_DEVICE, Device
|
|
10
10
|
from .._dtypes import (
|
|
11
11
|
_all_dtypes,
|
|
@@ -45,35 +45,46 @@ def test_validate_index():
|
|
|
45
45
|
a = ones((3, 4))
|
|
46
46
|
|
|
47
47
|
# Out of bounds slices are not allowed
|
|
48
|
-
assert_raises(IndexError, lambda: a[:4])
|
|
49
|
-
assert_raises(IndexError, lambda: a[:-4])
|
|
50
|
-
assert_raises(IndexError, lambda: a[:3:-1])
|
|
51
|
-
assert_raises(IndexError, lambda: a[:-5:-1])
|
|
52
|
-
assert_raises(IndexError, lambda: a[4
|
|
53
|
-
assert_raises(IndexError, lambda: a[-4
|
|
54
|
-
assert_raises(IndexError, lambda: a[4::-1])
|
|
55
|
-
assert_raises(IndexError, lambda: a[-4::-1])
|
|
56
|
-
|
|
57
|
-
assert_raises(IndexError, lambda: a[
|
|
58
|
-
assert_raises(IndexError, lambda: a[
|
|
59
|
-
assert_raises(IndexError, lambda: a[
|
|
60
|
-
assert_raises(IndexError, lambda: a[
|
|
61
|
-
assert_raises(IndexError, lambda: a[...,5:])
|
|
62
|
-
assert_raises(IndexError, lambda: a[
|
|
63
|
-
assert_raises(IndexError, lambda: a[...,5::-1])
|
|
64
|
-
assert_raises(IndexError, lambda: a[
|
|
48
|
+
assert_raises(IndexError, lambda: a[:4, 0])
|
|
49
|
+
assert_raises(IndexError, lambda: a[:-4, 0])
|
|
50
|
+
assert_raises(IndexError, lambda: a[:3:-1]) # XXX raises for a wrong reason
|
|
51
|
+
assert_raises(IndexError, lambda: a[:-5:-1, 0])
|
|
52
|
+
assert_raises(IndexError, lambda: a[4:, 0])
|
|
53
|
+
assert_raises(IndexError, lambda: a[-4:, 0])
|
|
54
|
+
assert_raises(IndexError, lambda: a[4::-1, 0])
|
|
55
|
+
assert_raises(IndexError, lambda: a[-4::-1, 0])
|
|
56
|
+
|
|
57
|
+
assert_raises(IndexError, lambda: a[..., :5])
|
|
58
|
+
assert_raises(IndexError, lambda: a[..., :-5])
|
|
59
|
+
assert_raises(IndexError, lambda: a[..., :5:-1])
|
|
60
|
+
assert_raises(IndexError, lambda: a[..., :-6:-1])
|
|
61
|
+
assert_raises(IndexError, lambda: a[..., 5:])
|
|
62
|
+
assert_raises(IndexError, lambda: a[..., -5:])
|
|
63
|
+
assert_raises(IndexError, lambda: a[..., 5::-1])
|
|
64
|
+
assert_raises(IndexError, lambda: a[..., -5::-1])
|
|
65
65
|
|
|
66
66
|
# Boolean indices cannot be part of a larger tuple index
|
|
67
|
-
assert_raises(IndexError, lambda: a[a[:,0]==1,0])
|
|
68
|
-
assert_raises(IndexError, lambda: a[a[:,0]==1
|
|
69
|
-
assert_raises(IndexError, lambda: a[..., a[0]==1])
|
|
67
|
+
assert_raises(IndexError, lambda: a[a[:, 0] == 1, 0])
|
|
68
|
+
assert_raises(IndexError, lambda: a[a[:, 0] == 1, ...])
|
|
69
|
+
assert_raises(IndexError, lambda: a[..., a[0] == 1])
|
|
70
70
|
assert_raises(IndexError, lambda: a[[True, True, True]])
|
|
71
71
|
assert_raises(IndexError, lambda: a[(True, True, True),])
|
|
72
72
|
|
|
73
|
-
#
|
|
74
|
-
idx = asarray([
|
|
75
|
-
assert_raises(IndexError, lambda: a[idx])
|
|
76
|
-
assert_raises(IndexError, lambda: a[idx
|
|
73
|
+
# Mixing 1D integer array indices with slices, ellipsis or booleans is not allowed
|
|
74
|
+
idx = asarray([0, 1])
|
|
75
|
+
assert_raises(IndexError, lambda: a[..., idx])
|
|
76
|
+
assert_raises(IndexError, lambda: a[:, idx])
|
|
77
|
+
assert_raises(IndexError, lambda: a[asarray([True, True]), idx])
|
|
78
|
+
|
|
79
|
+
# 1D integer array indices must have the same length
|
|
80
|
+
idx1 = asarray([0, 1])
|
|
81
|
+
idx2 = asarray([0, 1, 1])
|
|
82
|
+
assert_raises(IndexError, lambda: a[idx1, idx2])
|
|
83
|
+
|
|
84
|
+
# Non-integer array indices are not allowed
|
|
85
|
+
assert_raises(IndexError, lambda: a[ones(2), 0])
|
|
86
|
+
|
|
87
|
+
# Array-likes (lists, tuples) are not allowed as indices
|
|
77
88
|
assert_raises(IndexError, lambda: a[[0, 1]])
|
|
78
89
|
assert_raises(IndexError, lambda: a[(0, 1), (0, 1)])
|
|
79
90
|
assert_raises(IndexError, lambda: a[[0, 1]])
|
|
@@ -87,6 +98,45 @@ def test_validate_index():
|
|
|
87
98
|
assert_raises(IndexError, lambda: a[0,])
|
|
88
99
|
assert_raises(IndexError, lambda: a[0])
|
|
89
100
|
assert_raises(IndexError, lambda: a[:])
|
|
101
|
+
assert_raises(IndexError, lambda: a[idx])
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_indexing_arrays():
|
|
105
|
+
# indexing with 1D integer arrays and mixes of integers and 1D integer are allowed
|
|
106
|
+
|
|
107
|
+
# 1D array
|
|
108
|
+
a = arange(5)
|
|
109
|
+
idx = asarray([1, 0, 1, 2, -1])
|
|
110
|
+
a_idx = a[idx]
|
|
111
|
+
|
|
112
|
+
a_idx_loop = asarray([a[idx[i]] for i in range(idx.shape[0])])
|
|
113
|
+
assert all(a_idx == a_idx_loop)
|
|
114
|
+
|
|
115
|
+
# setitem with arrays is not allowed
|
|
116
|
+
with assert_raises(IndexError):
|
|
117
|
+
a[idx] = 42
|
|
118
|
+
|
|
119
|
+
# mixed array and integer indexing
|
|
120
|
+
a = reshape(arange(3*4), (3, 4))
|
|
121
|
+
idx = asarray([1, 0, 1, 2, -1])
|
|
122
|
+
a_idx = a[idx, 1]
|
|
123
|
+
|
|
124
|
+
a_idx_loop = asarray([a[idx[i], 1] for i in range(idx.shape[0])])
|
|
125
|
+
assert all(a_idx == a_idx_loop)
|
|
126
|
+
|
|
127
|
+
# index with two arrays
|
|
128
|
+
a_idx = a[idx, idx]
|
|
129
|
+
a_idx_loop = asarray([a[idx[i], idx[i]] for i in range(idx.shape[0])])
|
|
130
|
+
assert all(a_idx == a_idx_loop)
|
|
131
|
+
|
|
132
|
+
# setitem with arrays is not allowed
|
|
133
|
+
with assert_raises(IndexError):
|
|
134
|
+
a[idx, idx] = 42
|
|
135
|
+
|
|
136
|
+
# smoke test indexing with ndim > 1 arrays
|
|
137
|
+
idx = idx[..., None]
|
|
138
|
+
a[idx, idx]
|
|
139
|
+
|
|
90
140
|
|
|
91
141
|
def test_promoted_scalar_inherits_device():
|
|
92
142
|
device1 = Device("device1")
|
|
@@ -96,12 +146,68 @@ def test_promoted_scalar_inherits_device():
|
|
|
96
146
|
|
|
97
147
|
assert y.device == device1
|
|
98
148
|
|
|
149
|
+
|
|
150
|
+
BIG_INT = int(1e30)
|
|
151
|
+
|
|
152
|
+
def _check_op_array_scalar(dtypes, a, s, func, func_name, BIG_INT=BIG_INT):
|
|
153
|
+
# Test array op scalar. From the spec, the following combinations
|
|
154
|
+
# are supported:
|
|
155
|
+
|
|
156
|
+
# - Python bool for a bool array dtype,
|
|
157
|
+
# - a Python int within the bounds of the given dtype for integer array dtypes,
|
|
158
|
+
# - a Python int or float for real floating-point array dtypes
|
|
159
|
+
# - a Python int, float, or complex for complex floating-point array dtypes
|
|
160
|
+
|
|
161
|
+
# an exception: complex scalar <op> floating array
|
|
162
|
+
scalar_types_for_float = [float, int]
|
|
163
|
+
if not (func_name.startswith("__i")
|
|
164
|
+
or (func_name in ["__floordiv__", "__rfloordiv__", "__mod__", "__rmod__"]
|
|
165
|
+
and type(s) == complex)
|
|
166
|
+
):
|
|
167
|
+
scalar_types_for_float += [complex]
|
|
168
|
+
|
|
169
|
+
if ((dtypes == "all"
|
|
170
|
+
or dtypes == "numeric" and a.dtype in _numeric_dtypes
|
|
171
|
+
or dtypes == "real numeric" and a.dtype in _real_numeric_dtypes
|
|
172
|
+
or dtypes == "integer" and a.dtype in _integer_dtypes
|
|
173
|
+
or dtypes == "integer or boolean" and a.dtype in _integer_or_boolean_dtypes
|
|
174
|
+
or dtypes == "boolean" and a.dtype in _boolean_dtypes
|
|
175
|
+
or dtypes == "floating-point" and a.dtype in _floating_dtypes
|
|
176
|
+
or dtypes == "real floating-point" and a.dtype in _real_floating_dtypes
|
|
177
|
+
)
|
|
178
|
+
# bool is a subtype of int, which is why we avoid
|
|
179
|
+
# isinstance here.
|
|
180
|
+
and (a.dtype in _boolean_dtypes and type(s) == bool
|
|
181
|
+
or a.dtype in _integer_dtypes and type(s) == int
|
|
182
|
+
or a.dtype in _real_floating_dtypes and type(s) in scalar_types_for_float
|
|
183
|
+
or a.dtype in _complex_floating_dtypes and type(s) in [complex, float, int]
|
|
184
|
+
)):
|
|
185
|
+
if a.dtype in _integer_dtypes and s == BIG_INT:
|
|
186
|
+
with assert_raises(OverflowError):
|
|
187
|
+
func(s)
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
else:
|
|
191
|
+
# Only test for no error
|
|
192
|
+
with suppress_warnings() as sup:
|
|
193
|
+
# ignore warnings from pow(BIG_INT)
|
|
194
|
+
sup.filter(RuntimeWarning,
|
|
195
|
+
"invalid value encountered in power")
|
|
196
|
+
func(s)
|
|
197
|
+
return True
|
|
198
|
+
|
|
199
|
+
else:
|
|
200
|
+
with assert_raises(TypeError):
|
|
201
|
+
func(s)
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
|
|
99
205
|
def test_operators():
|
|
100
206
|
# For every operator, we test that it works for the required type
|
|
101
207
|
# combinations and raises TypeError otherwise
|
|
102
208
|
binary_op_dtypes = {
|
|
103
209
|
"__add__": "numeric",
|
|
104
|
-
"__and__": "
|
|
210
|
+
"__and__": "integer or boolean",
|
|
105
211
|
"__eq__": "all",
|
|
106
212
|
"__floordiv__": "real numeric",
|
|
107
213
|
"__ge__": "real numeric",
|
|
@@ -112,12 +218,12 @@ def test_operators():
|
|
|
112
218
|
"__mod__": "real numeric",
|
|
113
219
|
"__mul__": "numeric",
|
|
114
220
|
"__ne__": "all",
|
|
115
|
-
"__or__": "
|
|
221
|
+
"__or__": "integer or boolean",
|
|
116
222
|
"__pow__": "numeric",
|
|
117
223
|
"__rshift__": "integer",
|
|
118
224
|
"__sub__": "numeric",
|
|
119
|
-
"__truediv__": "floating",
|
|
120
|
-
"__xor__": "
|
|
225
|
+
"__truediv__": "floating-point",
|
|
226
|
+
"__xor__": "integer or boolean",
|
|
121
227
|
}
|
|
122
228
|
# Recompute each time because of in-place ops
|
|
123
229
|
def _array_vals():
|
|
@@ -128,8 +234,6 @@ def test_operators():
|
|
|
128
234
|
for d in _floating_dtypes:
|
|
129
235
|
yield asarray(1.0, dtype=d)
|
|
130
236
|
|
|
131
|
-
|
|
132
|
-
BIG_INT = int(1e30)
|
|
133
237
|
for op, dtypes in binary_op_dtypes.items():
|
|
134
238
|
ops = [op]
|
|
135
239
|
if op not in ["__eq__", "__ne__", "__le__", "__ge__", "__lt__", "__gt__"]:
|
|
@@ -139,40 +243,7 @@ def test_operators():
|
|
|
139
243
|
for s in [1, 1.0, 1j, BIG_INT, False]:
|
|
140
244
|
for _op in ops:
|
|
141
245
|
for a in _array_vals():
|
|
142
|
-
|
|
143
|
-
# are supported:
|
|
144
|
-
|
|
145
|
-
# - Python bool for a bool array dtype,
|
|
146
|
-
# - a Python int within the bounds of the given dtype for integer array dtypes,
|
|
147
|
-
# - a Python int or float for real floating-point array dtypes
|
|
148
|
-
# - a Python int, float, or complex for complex floating-point array dtypes
|
|
149
|
-
|
|
150
|
-
if ((dtypes == "all"
|
|
151
|
-
or dtypes == "numeric" and a.dtype in _numeric_dtypes
|
|
152
|
-
or dtypes == "real numeric" and a.dtype in _real_numeric_dtypes
|
|
153
|
-
or dtypes == "integer" and a.dtype in _integer_dtypes
|
|
154
|
-
or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes
|
|
155
|
-
or dtypes == "boolean" and a.dtype in _boolean_dtypes
|
|
156
|
-
or dtypes == "floating" and a.dtype in _floating_dtypes
|
|
157
|
-
)
|
|
158
|
-
# bool is a subtype of int, which is why we avoid
|
|
159
|
-
# isinstance here.
|
|
160
|
-
and (a.dtype in _boolean_dtypes and type(s) == bool
|
|
161
|
-
or a.dtype in _integer_dtypes and type(s) == int
|
|
162
|
-
or a.dtype in _real_floating_dtypes and type(s) in [float, int]
|
|
163
|
-
or a.dtype in _complex_floating_dtypes and type(s) in [complex, float, int]
|
|
164
|
-
)):
|
|
165
|
-
if a.dtype in _integer_dtypes and s == BIG_INT:
|
|
166
|
-
assert_raises(OverflowError, lambda: getattr(a, _op)(s))
|
|
167
|
-
else:
|
|
168
|
-
# Only test for no error
|
|
169
|
-
with suppress_warnings() as sup:
|
|
170
|
-
# ignore warnings from pow(BIG_INT)
|
|
171
|
-
sup.filter(RuntimeWarning,
|
|
172
|
-
"invalid value encountered in power")
|
|
173
|
-
getattr(a, _op)(s)
|
|
174
|
-
else:
|
|
175
|
-
assert_raises(TypeError, lambda: getattr(a, _op)(s))
|
|
246
|
+
_check_op_array_scalar(dtypes, a, s, getattr(a, _op), _op)
|
|
176
247
|
|
|
177
248
|
# Test array op array.
|
|
178
249
|
for _op in ops:
|
|
@@ -203,10 +274,10 @@ def test_operators():
|
|
|
203
274
|
or (dtypes == "real numeric" and x.dtype in _real_numeric_dtypes and y.dtype in _real_numeric_dtypes)
|
|
204
275
|
or (dtypes == "numeric" and x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes)
|
|
205
276
|
or dtypes == "integer" and x.dtype in _integer_dtypes and y.dtype in _integer_dtypes
|
|
206
|
-
or dtypes == "
|
|
277
|
+
or dtypes == "integer or boolean" and (x.dtype in _integer_dtypes and y.dtype in _integer_dtypes
|
|
207
278
|
or x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes)
|
|
208
279
|
or dtypes == "boolean" and x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes
|
|
209
|
-
or dtypes == "floating" and x.dtype in _floating_dtypes and y.dtype in _floating_dtypes
|
|
280
|
+
or dtypes == "floating-point" and x.dtype in _floating_dtypes and y.dtype in _floating_dtypes
|
|
210
281
|
):
|
|
211
282
|
getattr(x, _op)(y)
|
|
212
283
|
else:
|
|
@@ -214,7 +285,7 @@ def test_operators():
|
|
|
214
285
|
|
|
215
286
|
unary_op_dtypes = {
|
|
216
287
|
"__abs__": "numeric",
|
|
217
|
-
"__invert__": "
|
|
288
|
+
"__invert__": "integer or boolean",
|
|
218
289
|
"__neg__": "numeric",
|
|
219
290
|
"__pos__": "numeric",
|
|
220
291
|
}
|
|
@@ -223,7 +294,7 @@ def test_operators():
|
|
|
223
294
|
if (
|
|
224
295
|
dtypes == "numeric"
|
|
225
296
|
and a.dtype in _numeric_dtypes
|
|
226
|
-
or dtypes == "
|
|
297
|
+
or dtypes == "integer or boolean"
|
|
227
298
|
and a.dtype in _integer_or_boolean_dtypes
|
|
228
299
|
):
|
|
229
300
|
# Only test for no error
|
|
@@ -438,10 +509,10 @@ def test_array_keys_use_private_array():
|
|
|
438
509
|
def test_array_namespace():
|
|
439
510
|
a = ones((3, 3))
|
|
440
511
|
assert a.__array_namespace__() == array_api_strict
|
|
441
|
-
assert array_api_strict.__array_api_version__ == "
|
|
512
|
+
assert array_api_strict.__array_api_version__ == "2024.12"
|
|
442
513
|
|
|
443
514
|
assert a.__array_namespace__(api_version=None) is array_api_strict
|
|
444
|
-
assert array_api_strict.__array_api_version__ == "
|
|
515
|
+
assert array_api_strict.__array_api_version__ == "2024.12"
|
|
445
516
|
|
|
446
517
|
assert a.__array_namespace__(api_version="2022.12") is array_api_strict
|
|
447
518
|
assert array_api_strict.__array_api_version__ == "2022.12"
|
|
@@ -454,11 +525,12 @@ def test_array_namespace():
|
|
|
454
525
|
assert array_api_strict.__array_api_version__ == "2021.12"
|
|
455
526
|
|
|
456
527
|
with pytest.warns(UserWarning):
|
|
457
|
-
assert a.__array_namespace__(api_version="
|
|
458
|
-
assert array_api_strict.__array_api_version__ == "
|
|
528
|
+
assert a.__array_namespace__(api_version="2025.12") is array_api_strict
|
|
529
|
+
assert array_api_strict.__array_api_version__ == "2025.12"
|
|
530
|
+
|
|
459
531
|
|
|
460
532
|
pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="2021.11"))
|
|
461
|
-
pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="
|
|
533
|
+
pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="2026.12"))
|
|
462
534
|
|
|
463
535
|
def test_iter():
|
|
464
536
|
pytest.raises(TypeError, lambda: iter(asarray(3)))
|