array-api-strict 2.2__py3-none-any.whl → 2.3__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 +44 -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 +24 -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.dist-info/METADATA +68 -0
- array_api_strict-2.3.dist-info/RECORD +43 -0
- {array_api_strict-2.2.dist-info → array_api_strict-2.3.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.dist-info}/LICENSE +0 -0
- {array_api_strict-2.2.dist-info → array_api_strict-2.3.dist-info}/top_level.txt +0 -0
|
@@ -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)))
|
|
@@ -236,3 +236,10 @@ def from_dlpack_2023_12(api_version):
|
|
|
236
236
|
pytest.raises(exception, lambda: from_dlpack(capsule, copy=False))
|
|
237
237
|
pytest.raises(exception, lambda: from_dlpack(capsule, copy=True))
|
|
238
238
|
pytest.raises(exception, lambda: from_dlpack(capsule, copy=None))
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def test_from_dlpack_default_device():
|
|
242
|
+
x = asarray([1, 2, 3])
|
|
243
|
+
y = from_dlpack(x)
|
|
244
|
+
z = from_dlpack(np.asarray([1, 2, 3]))
|
|
245
|
+
assert x.device == y.device == z.device == CPU_DEVICE
|
|
@@ -6,9 +6,9 @@ from numpy.testing import assert_raises
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
|
|
8
8
|
from .._creation_functions import asarray
|
|
9
|
-
from .._data_type_functions import astype, can_cast, isdtype
|
|
9
|
+
from .._data_type_functions import astype, can_cast, isdtype, result_type
|
|
10
10
|
from .._dtypes import (
|
|
11
|
-
bool, int8, int16, uint8, float64,
|
|
11
|
+
bool, int8, int16, uint8, float64, int64
|
|
12
12
|
)
|
|
13
13
|
from .._flags import set_array_api_strict_flags
|
|
14
14
|
|
|
@@ -31,15 +31,17 @@ def test_can_cast(from_, to, expected):
|
|
|
31
31
|
def test_isdtype_strictness():
|
|
32
32
|
assert_raises(TypeError, lambda: isdtype(float64, 64))
|
|
33
33
|
assert_raises(ValueError, lambda: isdtype(float64, 'f8'))
|
|
34
|
-
|
|
35
34
|
assert_raises(TypeError, lambda: isdtype(float64, (('integral',),)))
|
|
35
|
+
assert_raises(TypeError, lambda: isdtype(float64, None))
|
|
36
|
+
assert_raises(TypeError, lambda: isdtype(np.float64, float64))
|
|
37
|
+
assert_raises(TypeError, lambda: isdtype(asarray(1.0), float64))
|
|
38
|
+
|
|
36
39
|
with assert_raises(TypeError), warnings.catch_warnings(record=True) as w:
|
|
37
40
|
warnings.simplefilter("always")
|
|
38
41
|
isdtype(float64, np.object_)
|
|
39
42
|
assert len(w) == 1
|
|
40
43
|
assert issubclass(w[-1].category, UserWarning)
|
|
41
44
|
|
|
42
|
-
assert_raises(TypeError, lambda: isdtype(float64, None))
|
|
43
45
|
with assert_raises(TypeError), warnings.catch_warnings(record=True) as w:
|
|
44
46
|
warnings.simplefilter("always")
|
|
45
47
|
isdtype(float64, np.float64)
|
|
@@ -68,3 +70,21 @@ def astype_device(api_version):
|
|
|
68
70
|
else:
|
|
69
71
|
pytest.raises(TypeError, lambda: astype(a, int8, device=None))
|
|
70
72
|
pytest.raises(TypeError, lambda: astype(a, int8, device=a.device))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@pytest.mark.parametrize("api_version", ['2023.12', '2024.12'])
|
|
76
|
+
def test_result_type_py_scalars(api_version):
|
|
77
|
+
if api_version <= '2023.12':
|
|
78
|
+
set_array_api_strict_flags(api_version=api_version)
|
|
79
|
+
|
|
80
|
+
with pytest.raises(TypeError):
|
|
81
|
+
result_type(int16, 3)
|
|
82
|
+
else:
|
|
83
|
+
set_array_api_strict_flags(api_version=api_version)
|
|
84
|
+
|
|
85
|
+
assert result_type(int8, 3) == int8
|
|
86
|
+
assert result_type(uint8, 3) == uint8
|
|
87
|
+
assert result_type(float64, 3) == float64
|
|
88
|
+
|
|
89
|
+
with pytest.raises(TypeError):
|
|
90
|
+
result_type(int64, True)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from inspect import signature, getmodule
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from pytest import raises as assert_raises
|
|
4
|
+
from numpy.testing import suppress_warnings
|
|
4
5
|
|
|
5
|
-
import pytest
|
|
6
6
|
|
|
7
7
|
from .. import asarray, _elementwise_functions
|
|
8
8
|
from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift
|
|
@@ -19,6 +19,8 @@ from .._dtypes import (
|
|
|
19
19
|
)
|
|
20
20
|
from .._flags import set_array_api_strict_flags
|
|
21
21
|
|
|
22
|
+
from .test_array_object import _check_op_array_scalar, BIG_INT
|
|
23
|
+
|
|
22
24
|
import array_api_strict
|
|
23
25
|
|
|
24
26
|
|
|
@@ -49,7 +51,7 @@ elementwise_function_input_types = {
|
|
|
49
51
|
"bitwise_xor": "integer or boolean",
|
|
50
52
|
"ceil": "real numeric",
|
|
51
53
|
"clip": "real numeric",
|
|
52
|
-
"conj": "
|
|
54
|
+
"conj": "numeric",
|
|
53
55
|
"copysign": "real floating-point",
|
|
54
56
|
"cos": "floating-point",
|
|
55
57
|
"cosh": "floating-point",
|
|
@@ -85,7 +87,7 @@ elementwise_function_input_types = {
|
|
|
85
87
|
"not_equal": "all",
|
|
86
88
|
"positive": "numeric",
|
|
87
89
|
"pow": "numeric",
|
|
88
|
-
"real": "
|
|
90
|
+
"real": "numeric",
|
|
89
91
|
"reciprocal": "floating-point",
|
|
90
92
|
"remainder": "real numeric",
|
|
91
93
|
"round": "numeric",
|
|
@@ -120,6 +122,7 @@ def test_missing_functions():
|
|
|
120
122
|
# Ensure the above dictionary is complete.
|
|
121
123
|
import array_api_strict._elementwise_functions as mod
|
|
122
124
|
mod_funcs = [n for n in dir(mod) if getmodule(getattr(mod, n)) is mod]
|
|
125
|
+
mod_funcs = [n for n in mod_funcs if not n.startswith("_")]
|
|
123
126
|
assert set(mod_funcs) == set(elementwise_function_input_types)
|
|
124
127
|
|
|
125
128
|
|
|
@@ -130,8 +133,7 @@ def test_function_device_persists():
|
|
|
130
133
|
yield asarray(1., dtype=d)
|
|
131
134
|
|
|
132
135
|
# Use the latest version of the standard so all functions are included
|
|
133
|
-
|
|
134
|
-
set_array_api_strict_flags(api_version="2024.12")
|
|
136
|
+
set_array_api_strict_flags(api_version="2024.12")
|
|
135
137
|
|
|
136
138
|
for func_name, types in elementwise_function_input_types.items():
|
|
137
139
|
dtypes = _dtype_categories[types]
|
|
@@ -167,8 +169,7 @@ def test_function_types():
|
|
|
167
169
|
yield asarray(1.0, dtype=d)
|
|
168
170
|
|
|
169
171
|
# Use the latest version of the standard so all functions are included
|
|
170
|
-
|
|
171
|
-
set_array_api_strict_flags(api_version="2024.12")
|
|
172
|
+
set_array_api_strict_flags(api_version="2024.12")
|
|
172
173
|
|
|
173
174
|
for x in _array_vals():
|
|
174
175
|
for func_name, types in elementwise_function_input_types.items():
|
|
@@ -202,3 +203,60 @@ def test_bitwise_shift_error():
|
|
|
202
203
|
assert_raises(
|
|
203
204
|
ValueError, lambda: bitwise_right_shift(asarray([1, 1]), asarray([1, -1]))
|
|
204
205
|
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def test_scalars():
|
|
210
|
+
# mirror test_array_object.py::test_operators()
|
|
211
|
+
#
|
|
212
|
+
# Also check that binary functions accept (array, scalar) and (scalar, array)
|
|
213
|
+
# arguments, and reject (scalar, scalar) arguments.
|
|
214
|
+
|
|
215
|
+
# Use the latest version of the standard so that scalars are actually allowed
|
|
216
|
+
set_array_api_strict_flags(api_version="2024.12")
|
|
217
|
+
|
|
218
|
+
def _array_vals():
|
|
219
|
+
for d in _integer_dtypes:
|
|
220
|
+
yield asarray(1, dtype=d)
|
|
221
|
+
for d in _boolean_dtypes:
|
|
222
|
+
yield asarray(False, dtype=d)
|
|
223
|
+
for d in _floating_dtypes:
|
|
224
|
+
yield asarray(1.0, dtype=d)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
for func_name, dtypes in elementwise_function_input_types.items():
|
|
228
|
+
func = getattr(_elementwise_functions, func_name)
|
|
229
|
+
if nargs(func) != 2:
|
|
230
|
+
continue
|
|
231
|
+
|
|
232
|
+
nocomplex = [
|
|
233
|
+
'atan2', 'copysign', 'floor_divide', 'hypot', 'logaddexp', 'nextafter',
|
|
234
|
+
'remainder',
|
|
235
|
+
'greater', 'less', 'greater_equal', 'less_equal', 'maximum', 'minimum',
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
for s in [1, 1.0, 1j, BIG_INT, False]:
|
|
239
|
+
for a in _array_vals():
|
|
240
|
+
for func1 in [lambda s: func(a, s), lambda s: func(s, a)]:
|
|
241
|
+
|
|
242
|
+
if func_name in nocomplex and type(s) == complex:
|
|
243
|
+
allowed = False
|
|
244
|
+
else:
|
|
245
|
+
allowed = _check_op_array_scalar(dtypes, a, s, func1, func_name)
|
|
246
|
+
|
|
247
|
+
# only check `func(array, scalar) == `func(array, array)` if
|
|
248
|
+
# the former is legal under the promotion rules
|
|
249
|
+
if allowed:
|
|
250
|
+
conv_scalar = a._promote_scalar(s)
|
|
251
|
+
|
|
252
|
+
with suppress_warnings() as sup:
|
|
253
|
+
# ignore warnings from pow(BIG_INT)
|
|
254
|
+
sup.filter(RuntimeWarning,
|
|
255
|
+
"invalid value encountered in power")
|
|
256
|
+
assert func(s, a) == func(conv_scalar, a)
|
|
257
|
+
assert func(a, s) == func(a, conv_scalar)
|
|
258
|
+
|
|
259
|
+
with assert_raises(TypeError):
|
|
260
|
+
func(s, s)
|
|
261
|
+
|
|
262
|
+
|
|
@@ -19,7 +19,7 @@ import pytest
|
|
|
19
19
|
def test_flag_defaults():
|
|
20
20
|
flags = get_array_api_strict_flags()
|
|
21
21
|
assert flags == {
|
|
22
|
-
'api_version': '
|
|
22
|
+
'api_version': '2024.12',
|
|
23
23
|
'boolean_indexing': True,
|
|
24
24
|
'data_dependent_shapes': True,
|
|
25
25
|
'enabled_extensions': ('linalg', 'fft'),
|
|
@@ -36,7 +36,7 @@ def test_reset_flags():
|
|
|
36
36
|
reset_array_api_strict_flags()
|
|
37
37
|
flags = get_array_api_strict_flags()
|
|
38
38
|
assert flags == {
|
|
39
|
-
'api_version': '
|
|
39
|
+
'api_version': '2024.12',
|
|
40
40
|
'boolean_indexing': True,
|
|
41
41
|
'data_dependent_shapes': True,
|
|
42
42
|
'enabled_extensions': ('linalg', 'fft'),
|
|
@@ -47,7 +47,7 @@ def test_setting_flags():
|
|
|
47
47
|
set_array_api_strict_flags(data_dependent_shapes=False)
|
|
48
48
|
flags = get_array_api_strict_flags()
|
|
49
49
|
assert flags == {
|
|
50
|
-
'api_version': '
|
|
50
|
+
'api_version': '2024.12',
|
|
51
51
|
'boolean_indexing': True,
|
|
52
52
|
'data_dependent_shapes': False,
|
|
53
53
|
'enabled_extensions': ('linalg', 'fft'),
|
|
@@ -55,7 +55,7 @@ def test_setting_flags():
|
|
|
55
55
|
set_array_api_strict_flags(enabled_extensions=('fft',))
|
|
56
56
|
flags = get_array_api_strict_flags()
|
|
57
57
|
assert flags == {
|
|
58
|
-
'api_version': '
|
|
58
|
+
'api_version': '2024.12',
|
|
59
59
|
'boolean_indexing': True,
|
|
60
60
|
'data_dependent_shapes': False,
|
|
61
61
|
'enabled_extensions': ('fft',),
|
|
@@ -98,15 +98,26 @@ def test_flags_api_version_2023_12():
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
def test_flags_api_version_2024_12():
|
|
101
|
-
|
|
101
|
+
set_array_api_strict_flags(api_version='2024.12')
|
|
102
|
+
flags = get_array_api_strict_flags()
|
|
103
|
+
assert flags == {
|
|
104
|
+
'api_version': '2024.12',
|
|
105
|
+
'boolean_indexing': True,
|
|
106
|
+
'data_dependent_shapes': True,
|
|
107
|
+
'enabled_extensions': ('linalg', 'fft'),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_flags_api_version_2025_12():
|
|
112
|
+
# Make sure setting the version to 2025.12 issues a warning.
|
|
102
113
|
with pytest.warns(UserWarning) as record:
|
|
103
|
-
set_array_api_strict_flags(api_version='
|
|
114
|
+
set_array_api_strict_flags(api_version='2025.12')
|
|
104
115
|
assert len(record) == 1
|
|
105
|
-
assert '
|
|
116
|
+
assert '2025.12' in str(record[0].message)
|
|
106
117
|
assert 'draft' in str(record[0].message)
|
|
107
118
|
flags = get_array_api_strict_flags()
|
|
108
119
|
assert flags == {
|
|
109
|
-
'api_version': '
|
|
120
|
+
'api_version': '2025.12',
|
|
110
121
|
'boolean_indexing': True,
|
|
111
122
|
'data_dependent_shapes': True,
|
|
112
123
|
'enabled_extensions': ('linalg', 'fft'),
|
|
@@ -125,9 +136,12 @@ def test_setting_flags_invalid():
|
|
|
125
136
|
|
|
126
137
|
def test_api_version():
|
|
127
138
|
# Test defaults
|
|
128
|
-
assert xp.__array_api_version__ == '
|
|
139
|
+
assert xp.__array_api_version__ == '2024.12'
|
|
129
140
|
|
|
130
141
|
# Test setting the version
|
|
142
|
+
set_array_api_strict_flags(api_version='2023.12')
|
|
143
|
+
assert xp.__array_api_version__ == '2023.12'
|
|
144
|
+
|
|
131
145
|
set_array_api_strict_flags(api_version='2022.12')
|
|
132
146
|
assert xp.__array_api_version__ == '2022.12'
|
|
133
147
|
|
|
@@ -307,14 +321,16 @@ api_version_2024_12_examples = {
|
|
|
307
321
|
'reciprocal': lambda: xp.reciprocal(xp.asarray([2.])),
|
|
308
322
|
'take_along_axis': lambda: xp.take_along_axis(xp.zeros((2, 3)),
|
|
309
323
|
xp.zeros((1, 4), dtype=xp.int64)),
|
|
324
|
+
'count_nonzero': lambda: xp.count_nonzero(xp.arange(3)),
|
|
325
|
+
'cumulative_prod': lambda: xp.cumulative_prod(xp.arange(1, 5)),
|
|
310
326
|
}
|
|
311
327
|
|
|
312
328
|
@pytest.mark.parametrize('func_name', api_version_2024_12_examples.keys())
|
|
313
329
|
def test_api_version_2024_12(func_name):
|
|
314
330
|
func = api_version_2024_12_examples[func_name]
|
|
315
331
|
|
|
316
|
-
# By default, these functions should error
|
|
317
|
-
|
|
332
|
+
# By default, these functions should not error
|
|
333
|
+
func()
|
|
318
334
|
|
|
319
335
|
# In 2022.12 and 2023.12, these functions should error
|
|
320
336
|
set_array_api_strict_flags(api_version='2022.12')
|
|
@@ -322,11 +338,6 @@ def test_api_version_2024_12(func_name):
|
|
|
322
338
|
set_array_api_strict_flags(api_version='2023.12')
|
|
323
339
|
pytest.raises(RuntimeError, func)
|
|
324
340
|
|
|
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
341
|
# Test the behavior gets updated properly
|
|
331
342
|
set_array_api_strict_flags(api_version='2023.12')
|
|
332
343
|
pytest.raises(RuntimeError, func)
|
|
@@ -433,9 +444,9 @@ def test_environment_variables():
|
|
|
433
444
|
# ARRAY_API_STRICT_API_VERSION
|
|
434
445
|
('''\
|
|
435
446
|
import array_api_strict as xp
|
|
436
|
-
assert xp.__array_api_version__ == '
|
|
447
|
+
assert xp.__array_api_version__ == '2024.12'
|
|
437
448
|
|
|
438
|
-
assert xp.get_array_api_strict_flags()['api_version'] == '
|
|
449
|
+
assert xp.get_array_api_strict_flags()['api_version'] == '2024.12'
|
|
439
450
|
|
|
440
451
|
''', {}),
|
|
441
452
|
*[
|
|
@@ -11,7 +11,7 @@ from .._manipulation_functions import (
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def test_concat_errors():
|
|
14
|
-
assert_raises(TypeError, lambda: concat((1, 1), axis=None))
|
|
14
|
+
assert_raises((TypeError, ValueError), lambda: concat((1, 1), axis=None))
|
|
15
15
|
assert_raises(TypeError, lambda: concat([asarray([1], dtype=int8),
|
|
16
16
|
asarray([1], dtype=float64)]))
|
|
17
17
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
import array_api_strict as xp
|
|
4
|
+
|
|
5
|
+
from array_api_strict import ArrayAPIStrictFlags
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_where_with_scalars():
|
|
9
|
+
x = xp.asarray([1, 2, 3, 1])
|
|
10
|
+
|
|
11
|
+
# Versions up to and including 2023.12 don't support scalar arguments
|
|
12
|
+
with ArrayAPIStrictFlags(api_version='2023.12'):
|
|
13
|
+
with pytest.raises(AttributeError, match="object has no attribute 'dtype'"):
|
|
14
|
+
xp.where(x == 1, 42, 44)
|
|
15
|
+
|
|
16
|
+
# Versions after 2023.12 support scalar arguments
|
|
17
|
+
x_where = xp.where(x == 1, xp.asarray(42), 44)
|
|
18
|
+
|
|
19
|
+
expected = xp.asarray([42, 44, 44, 42])
|
|
20
|
+
assert xp.all(x_where == expected)
|
|
21
|
+
|
|
22
|
+
# The spec does not allow both x1 and x2 to be scalars
|
|
23
|
+
with pytest.raises(ValueError, match="One of"):
|
|
24
|
+
xp.where(x == 1, 42, 44)
|