array-api-strict 2.3__py3-none-any.whl → 2.4__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 +11 -5
- array_api_strict/_array_object.py +226 -200
- array_api_strict/_constants.py +1 -1
- array_api_strict/_creation_functions.py +97 -119
- array_api_strict/_data_type_functions.py +73 -64
- array_api_strict/_dtypes.py +35 -24
- array_api_strict/_elementwise_functions.py +138 -482
- array_api_strict/_fft.py +28 -32
- array_api_strict/_flags.py +65 -26
- array_api_strict/_helpers.py +23 -14
- array_api_strict/_indexing_functions.py +3 -8
- array_api_strict/_info.py +64 -69
- array_api_strict/_linalg.py +57 -53
- array_api_strict/_linear_algebra_functions.py +11 -9
- array_api_strict/_manipulation_functions.py +30 -30
- array_api_strict/_searching_functions.py +22 -33
- array_api_strict/_set_functions.py +4 -6
- array_api_strict/_sorting_functions.py +6 -6
- array_api_strict/_statistical_functions.py +57 -64
- array_api_strict/_typing.py +30 -50
- array_api_strict/_utility_functions.py +11 -13
- array_api_strict/_version.py +2 -2
- array_api_strict/py.typed +0 -0
- array_api_strict/tests/test_array_object.py +163 -56
- array_api_strict/tests/test_creation_functions.py +14 -12
- array_api_strict/tests/test_data_type_functions.py +40 -6
- array_api_strict/tests/test_elementwise_functions.py +94 -45
- array_api_strict/tests/test_searching_functions.py +79 -1
- array_api_strict/tests/test_validation.py +1 -3
- {array_api_strict-2.3.dist-info → array_api_strict-2.4.dist-info}/METADATA +5 -4
- array_api_strict-2.4.dist-info/RECORD +44 -0
- {array_api_strict-2.3.dist-info → array_api_strict-2.4.dist-info}/WHEEL +1 -1
- array_api_strict-2.3.dist-info/RECORD +0 -43
- {array_api_strict-2.3.dist-info → array_api_strict-2.4.dist-info}/LICENSE +0 -0
- {array_api_strict-2.3.dist-info → array_api_strict-2.4.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import sys
|
|
1
2
|
import operator
|
|
2
3
|
from builtins import all as all_
|
|
3
4
|
|
|
@@ -5,7 +6,7 @@ from numpy.testing import assert_raises, suppress_warnings
|
|
|
5
6
|
import numpy as np
|
|
6
7
|
import pytest
|
|
7
8
|
|
|
8
|
-
from .. import ones, arange, reshape, asarray, result_type, all, equal
|
|
9
|
+
from .. import ones, arange, reshape, asarray, result_type, all, equal, stack
|
|
9
10
|
from .._array_object import Array, CPU_DEVICE, Device
|
|
10
11
|
from .._dtypes import (
|
|
11
12
|
_all_dtypes,
|
|
@@ -100,34 +101,71 @@ def test_validate_index():
|
|
|
100
101
|
assert_raises(IndexError, lambda: a[:])
|
|
101
102
|
assert_raises(IndexError, lambda: a[idx])
|
|
102
103
|
|
|
104
|
+
class DummyIndex:
|
|
105
|
+
def __init__(self, x):
|
|
106
|
+
self.x = x
|
|
107
|
+
def __index__(self):
|
|
108
|
+
return self.x
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
|
|
111
|
+
@pytest.mark.parametrize("device", [None, "CPU_DEVICE", "device1", "device2"])
|
|
112
|
+
@pytest.mark.parametrize(
|
|
113
|
+
"integer_index",
|
|
114
|
+
[
|
|
115
|
+
0,
|
|
116
|
+
np.int8(0),
|
|
117
|
+
np.uint8(0),
|
|
118
|
+
np.int16(0),
|
|
119
|
+
np.uint16(0),
|
|
120
|
+
np.int32(0),
|
|
121
|
+
np.uint32(0),
|
|
122
|
+
np.int64(0),
|
|
123
|
+
np.uint64(0),
|
|
124
|
+
DummyIndex(0),
|
|
125
|
+
],
|
|
126
|
+
)
|
|
127
|
+
def test_indexing_ints(integer_index, device):
|
|
128
|
+
# Ensure indexing with different integer types works on all Devices.
|
|
129
|
+
device = None if device is None else Device(device)
|
|
130
|
+
|
|
131
|
+
a = arange(5, device=device)
|
|
132
|
+
assert a[(integer_index,)] == a[integer_index] == a[0]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@pytest.mark.parametrize("device", [None, "CPU_DEVICE", "device1", "device2"])
|
|
136
|
+
def test_indexing_arrays(device):
|
|
105
137
|
# indexing with 1D integer arrays and mixes of integers and 1D integer are allowed
|
|
138
|
+
device = None if device is None else Device(device)
|
|
106
139
|
|
|
107
140
|
# 1D array
|
|
108
|
-
a = arange(5)
|
|
109
|
-
idx = asarray([1, 0, 1, 2, -1])
|
|
141
|
+
a = arange(5, device=device)
|
|
142
|
+
idx = asarray([1, 0, 1, 2, -1], device=device)
|
|
110
143
|
a_idx = a[idx]
|
|
111
144
|
|
|
112
|
-
a_idx_loop =
|
|
145
|
+
a_idx_loop = stack([a[idx[i]] for i in range(idx.shape[0])])
|
|
113
146
|
assert all(a_idx == a_idx_loop)
|
|
147
|
+
assert a_idx.shape == idx.shape
|
|
148
|
+
assert a.device == idx.device == a_idx.device
|
|
114
149
|
|
|
115
150
|
# setitem with arrays is not allowed
|
|
116
151
|
with assert_raises(IndexError):
|
|
117
152
|
a[idx] = 42
|
|
118
153
|
|
|
119
154
|
# mixed array and integer indexing
|
|
120
|
-
a = reshape(arange(3*4), (3, 4))
|
|
121
|
-
idx = asarray([1, 0, 1, 2, -1])
|
|
155
|
+
a = reshape(arange(3*4, device=device), (3, 4))
|
|
156
|
+
idx = asarray([1, 0, 1, 2, -1], device=device)
|
|
122
157
|
a_idx = a[idx, 1]
|
|
123
|
-
|
|
124
|
-
a_idx_loop = asarray([a[idx[i], 1] for i in range(idx.shape[0])])
|
|
158
|
+
a_idx_loop = stack([a[idx[i], 1] for i in range(idx.shape[0])])
|
|
125
159
|
assert all(a_idx == a_idx_loop)
|
|
160
|
+
assert a_idx.shape == idx.shape
|
|
161
|
+
assert a.device == idx.device == a_idx.device
|
|
126
162
|
|
|
127
163
|
# index with two arrays
|
|
128
164
|
a_idx = a[idx, idx]
|
|
129
|
-
a_idx_loop =
|
|
165
|
+
a_idx_loop = stack([a[idx[i], idx[i]] for i in range(idx.shape[0])])
|
|
130
166
|
assert all(a_idx == a_idx_loop)
|
|
167
|
+
assert a_idx.shape == a_idx.shape
|
|
168
|
+
assert a.device == idx.device == a_idx.device
|
|
131
169
|
|
|
132
170
|
# setitem with arrays is not allowed
|
|
133
171
|
with assert_raises(IndexError):
|
|
@@ -135,7 +173,24 @@ def test_indexing_arrays():
|
|
|
135
173
|
|
|
136
174
|
# smoke test indexing with ndim > 1 arrays
|
|
137
175
|
idx = idx[..., None]
|
|
138
|
-
a[idx, idx]
|
|
176
|
+
a_idx = a[idx, idx]
|
|
177
|
+
assert a.device == idx.device == a_idx.device
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_indexing_arrays_different_devices():
|
|
181
|
+
# Ensure indexing via array on different device errors
|
|
182
|
+
device1 = Device("CPU_DEVICE")
|
|
183
|
+
device2 = Device("device1")
|
|
184
|
+
|
|
185
|
+
a = arange(5, device=device1)
|
|
186
|
+
idx1 = asarray([1, 0, 1, 2, -1], device=device2)
|
|
187
|
+
idx2 = asarray([1, 0, 1, 2, -1], device=device1)
|
|
188
|
+
|
|
189
|
+
with pytest.raises(ValueError, match="Array indexing is only allowed when"):
|
|
190
|
+
a[idx1]
|
|
191
|
+
|
|
192
|
+
with pytest.raises(ValueError, match="Array indexing is only allowed when"):
|
|
193
|
+
a[idx1, idx2]
|
|
139
194
|
|
|
140
195
|
|
|
141
196
|
def test_promoted_scalar_inherits_device():
|
|
@@ -201,30 +256,37 @@ def _check_op_array_scalar(dtypes, a, s, func, func_name, BIG_INT=BIG_INT):
|
|
|
201
256
|
func(s)
|
|
202
257
|
return False
|
|
203
258
|
|
|
259
|
+
binary_op_dtypes = {
|
|
260
|
+
"__add__": "numeric",
|
|
261
|
+
"__and__": "integer or boolean",
|
|
262
|
+
"__eq__": "all",
|
|
263
|
+
"__floordiv__": "real numeric",
|
|
264
|
+
"__ge__": "real numeric",
|
|
265
|
+
"__gt__": "real numeric",
|
|
266
|
+
"__le__": "real numeric",
|
|
267
|
+
"__lshift__": "integer",
|
|
268
|
+
"__lt__": "real numeric",
|
|
269
|
+
"__mod__": "real numeric",
|
|
270
|
+
"__mul__": "numeric",
|
|
271
|
+
"__ne__": "all",
|
|
272
|
+
"__or__": "integer or boolean",
|
|
273
|
+
"__pow__": "numeric",
|
|
274
|
+
"__rshift__": "integer",
|
|
275
|
+
"__sub__": "numeric",
|
|
276
|
+
"__truediv__": "floating-point",
|
|
277
|
+
"__xor__": "integer or boolean",
|
|
278
|
+
}
|
|
279
|
+
unary_op_dtypes = {
|
|
280
|
+
"__abs__": "numeric",
|
|
281
|
+
"__invert__": "integer or boolean",
|
|
282
|
+
"__neg__": "numeric",
|
|
283
|
+
"__pos__": "numeric",
|
|
284
|
+
}
|
|
204
285
|
|
|
205
286
|
def test_operators():
|
|
206
287
|
# For every operator, we test that it works for the required type
|
|
207
288
|
# combinations and raises TypeError otherwise
|
|
208
|
-
|
|
209
|
-
"__add__": "numeric",
|
|
210
|
-
"__and__": "integer or boolean",
|
|
211
|
-
"__eq__": "all",
|
|
212
|
-
"__floordiv__": "real numeric",
|
|
213
|
-
"__ge__": "real numeric",
|
|
214
|
-
"__gt__": "real numeric",
|
|
215
|
-
"__le__": "real numeric",
|
|
216
|
-
"__lshift__": "integer",
|
|
217
|
-
"__lt__": "real numeric",
|
|
218
|
-
"__mod__": "real numeric",
|
|
219
|
-
"__mul__": "numeric",
|
|
220
|
-
"__ne__": "all",
|
|
221
|
-
"__or__": "integer or boolean",
|
|
222
|
-
"__pow__": "numeric",
|
|
223
|
-
"__rshift__": "integer",
|
|
224
|
-
"__sub__": "numeric",
|
|
225
|
-
"__truediv__": "floating-point",
|
|
226
|
-
"__xor__": "integer or boolean",
|
|
227
|
-
}
|
|
289
|
+
|
|
228
290
|
# Recompute each time because of in-place ops
|
|
229
291
|
def _array_vals():
|
|
230
292
|
for d in _integer_dtypes:
|
|
@@ -283,12 +345,6 @@ def test_operators():
|
|
|
283
345
|
else:
|
|
284
346
|
assert_raises(TypeError, lambda: getattr(x, _op)(y))
|
|
285
347
|
|
|
286
|
-
unary_op_dtypes = {
|
|
287
|
-
"__abs__": "numeric",
|
|
288
|
-
"__invert__": "integer or boolean",
|
|
289
|
-
"__neg__": "numeric",
|
|
290
|
-
"__pos__": "numeric",
|
|
291
|
-
}
|
|
292
348
|
for op, dtypes in unary_op_dtypes.items():
|
|
293
349
|
for a in _array_vals():
|
|
294
350
|
if (
|
|
@@ -356,6 +412,55 @@ def test_operators():
|
|
|
356
412
|
x.__imatmul__(y)
|
|
357
413
|
|
|
358
414
|
|
|
415
|
+
@pytest.mark.parametrize("op,dtypes", binary_op_dtypes.items())
|
|
416
|
+
def test_binary_operators_numpy_scalars(op, dtypes):
|
|
417
|
+
"""
|
|
418
|
+
Test that NumPy scalars (np.generic) are explicitly disallowed.
|
|
419
|
+
|
|
420
|
+
This must notably include np.float64 and np.complex128, which are
|
|
421
|
+
subclasses of float and complex respectively, so they need
|
|
422
|
+
special treatment in order to be rejected.
|
|
423
|
+
"""
|
|
424
|
+
match = "Expected Array or Python scalar"
|
|
425
|
+
|
|
426
|
+
if dtypes not in ("numeric", "integer", "real numeric", "floating-point"):
|
|
427
|
+
a = asarray(True)
|
|
428
|
+
func = getattr(a, op)
|
|
429
|
+
with pytest.raises(TypeError, match=match):
|
|
430
|
+
func(np.bool_(True))
|
|
431
|
+
|
|
432
|
+
if dtypes != "floating-point":
|
|
433
|
+
a = asarray(1)
|
|
434
|
+
func = getattr(a, op)
|
|
435
|
+
with pytest.raises(TypeError, match=match):
|
|
436
|
+
func(np.int64(1))
|
|
437
|
+
|
|
438
|
+
if dtypes not in ("integer", "integer or boolean"):
|
|
439
|
+
a = asarray(1.,)
|
|
440
|
+
func = getattr(a, op)
|
|
441
|
+
with pytest.raises(TypeError, match=match):
|
|
442
|
+
func(np.float32(1.))
|
|
443
|
+
with pytest.raises(TypeError, match=match):
|
|
444
|
+
func(np.float64(1.))
|
|
445
|
+
|
|
446
|
+
if dtypes not in ("integer", "integer or boolean", "real numeric"):
|
|
447
|
+
a = asarray(1.,)
|
|
448
|
+
func = getattr(a, op)
|
|
449
|
+
with pytest.raises(TypeError, match=match):
|
|
450
|
+
func(np.complex64(1.))
|
|
451
|
+
with pytest.raises(TypeError, match=match):
|
|
452
|
+
func(np.complex128(1.))
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
@pytest.mark.parametrize("op,dtypes", binary_op_dtypes.items())
|
|
456
|
+
def test_binary_operators_device_mismatch(op, dtypes):
|
|
457
|
+
dtype = float64 if dtypes == "floating-point" else int64
|
|
458
|
+
a = asarray(1, dtype=dtype, device=CPU_DEVICE)
|
|
459
|
+
b = asarray(1, dtype=dtype, device=Device("device1"))
|
|
460
|
+
with pytest.raises(ValueError, match="different devices"):
|
|
461
|
+
getattr(a, op)(b)
|
|
462
|
+
|
|
463
|
+
|
|
359
464
|
def test_python_scalar_construtors():
|
|
360
465
|
b = asarray(False)
|
|
361
466
|
i = asarray(0)
|
|
@@ -422,35 +527,37 @@ def test_array_properties():
|
|
|
422
527
|
assert b.mT.shape == (3, 2)
|
|
423
528
|
|
|
424
529
|
|
|
530
|
+
@pytest.mark.xfail(sys.version_info.major*100 + sys.version_info.minor < 312,
|
|
531
|
+
reason="array conversion relies on buffer protocol, and "
|
|
532
|
+
"requires python >= 3.12"
|
|
533
|
+
)
|
|
425
534
|
def test_array_conversion():
|
|
426
535
|
# Check that arrays on the CPU device can be converted to NumPy
|
|
427
536
|
# but arrays on other devices can't. Note this is testing the logic in
|
|
428
537
|
# __array__, which is only used in asarray when converting lists of
|
|
429
538
|
# arrays.
|
|
430
539
|
a = ones((2, 3))
|
|
431
|
-
asarray(
|
|
540
|
+
np.asarray(a)
|
|
432
541
|
|
|
433
542
|
for device in ("device1", "device2"):
|
|
434
543
|
a = ones((2, 3), device=array_api_strict.Device(device))
|
|
435
|
-
with pytest.raises(RuntimeError,
|
|
436
|
-
asarray(
|
|
544
|
+
with pytest.raises((RuntimeError, ValueError)):
|
|
545
|
+
np.asarray(a)
|
|
546
|
+
|
|
547
|
+
# __buffer__ should work for now for conversion to numpy
|
|
548
|
+
a = ones((2, 3))
|
|
549
|
+
na = np.array(a)
|
|
550
|
+
assert na.shape == (2, 3)
|
|
551
|
+
assert na.dtype == np.float64
|
|
437
552
|
|
|
438
|
-
|
|
439
|
-
|
|
553
|
+
@pytest.mark.skipif(not sys.version_info.major*100 + sys.version_info.minor < 312,
|
|
554
|
+
reason="conversion to numpy errors out unless python >= 3.12"
|
|
555
|
+
)
|
|
556
|
+
def test_array_conversion_2():
|
|
440
557
|
a = ones((2, 3))
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
# future.
|
|
445
|
-
from .. import _array_object
|
|
446
|
-
original_value = _array_object._allow_array
|
|
447
|
-
try:
|
|
448
|
-
_array_object._allow_array = False
|
|
449
|
-
a = ones((2, 3))
|
|
450
|
-
with pytest.raises(ValueError, match="Conversion from an array_api_strict array to a NumPy ndarray is not supported"):
|
|
451
|
-
np.array(a)
|
|
452
|
-
finally:
|
|
453
|
-
_array_object._allow_array = original_value
|
|
558
|
+
with pytest.raises(TypeError):
|
|
559
|
+
np.array(a)
|
|
560
|
+
|
|
454
561
|
|
|
455
562
|
def test_allow_newaxis():
|
|
456
563
|
a = ones(5)
|
|
@@ -533,7 +640,7 @@ def test_array_namespace():
|
|
|
533
640
|
pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="2026.12"))
|
|
534
641
|
|
|
535
642
|
def test_iter():
|
|
536
|
-
pytest.raises(TypeError, lambda: iter(asarray(3)))
|
|
643
|
+
pytest.raises(TypeError, lambda: next(iter(asarray(3))))
|
|
537
644
|
assert list(ones(3)) == [asarray(1.), asarray(1.), asarray(1.)]
|
|
538
645
|
assert all_(isinstance(a, Array) for a in iter(ones(3)))
|
|
539
646
|
assert all_(a.shape == () for a in iter(ones(3)))
|
|
@@ -22,7 +22,7 @@ from .._creation_functions import (
|
|
|
22
22
|
zeros,
|
|
23
23
|
zeros_like,
|
|
24
24
|
)
|
|
25
|
-
from .._dtypes import
|
|
25
|
+
from .._dtypes import float32, float64
|
|
26
26
|
from .._array_object import Array, CPU_DEVICE, Device
|
|
27
27
|
from .._flags import set_array_api_strict_flags
|
|
28
28
|
|
|
@@ -97,18 +97,20 @@ def test_asarray_copy():
|
|
|
97
97
|
a[0] = 0
|
|
98
98
|
assert all(b[0] == 0)
|
|
99
99
|
|
|
100
|
+
|
|
100
101
|
def test_asarray_list_of_lists():
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
res
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
lst = [[1, 2, 3], [4, 5, 6]]
|
|
103
|
+
res = asarray(lst)
|
|
104
|
+
assert res.shape == (2, 3)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_asarray_nested_arrays():
|
|
108
|
+
# do not allow arrays in nested sequences
|
|
109
|
+
with pytest.raises(TypeError):
|
|
110
|
+
asarray([[1, 2, 3], asarray([4, 5, 6])])
|
|
111
|
+
|
|
112
|
+
with pytest.raises(TypeError):
|
|
113
|
+
asarray([1, asarray(1)])
|
|
112
114
|
|
|
113
115
|
|
|
114
116
|
def test_asarray_device_inference():
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
|
|
3
|
+
import numpy as np
|
|
3
4
|
import pytest
|
|
4
|
-
|
|
5
5
|
from numpy.testing import assert_raises
|
|
6
|
-
import numpy as np
|
|
7
6
|
|
|
8
7
|
from .._creation_functions import asarray
|
|
9
|
-
from .._data_type_functions import astype, can_cast, isdtype, result_type
|
|
10
|
-
from .._dtypes import
|
|
11
|
-
bool, int8, int16, uint8, float64, int64
|
|
12
|
-
)
|
|
8
|
+
from .._data_type_functions import astype, can_cast, finfo, iinfo, isdtype, result_type
|
|
9
|
+
from .._dtypes import bool, float64, int8, int16, int64, uint8
|
|
13
10
|
from .._flags import set_array_api_strict_flags
|
|
14
11
|
|
|
15
12
|
|
|
@@ -88,3 +85,40 @@ def test_result_type_py_scalars(api_version):
|
|
|
88
85
|
|
|
89
86
|
with pytest.raises(TypeError):
|
|
90
87
|
result_type(int64, True)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_finfo_iinfo_dtypelike():
|
|
91
|
+
"""np.finfo() and np.iinfo() accept any DTypeLike.
|
|
92
|
+
Array API only accepts Array | DType.
|
|
93
|
+
"""
|
|
94
|
+
match = "must be a dtype or array"
|
|
95
|
+
with pytest.raises(TypeError, match=match):
|
|
96
|
+
finfo("float64")
|
|
97
|
+
with pytest.raises(TypeError, match=match):
|
|
98
|
+
finfo(float)
|
|
99
|
+
with pytest.raises(TypeError, match=match):
|
|
100
|
+
iinfo("int8")
|
|
101
|
+
with pytest.raises(TypeError, match=match):
|
|
102
|
+
iinfo(int)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_finfo_iinfo_wrap_output():
|
|
106
|
+
"""Test that the finfo(...).dtype and iinfo(...).dtype
|
|
107
|
+
are array-api-strict.DType objects; not numpy.dtype.
|
|
108
|
+
"""
|
|
109
|
+
# Note: array_api_strict.DType objects are not singletons
|
|
110
|
+
assert finfo(float64).dtype == float64
|
|
111
|
+
assert iinfo(int8).dtype == int8
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@pytest.mark.parametrize("func,arg", [(finfo, float64), (iinfo, int8)])
|
|
115
|
+
def test_finfo_iinfo_output_assumptions(func, arg):
|
|
116
|
+
"""There should be no expectation for the output of finfo()/iinfo()
|
|
117
|
+
to be comparable, hashable, or writeable.
|
|
118
|
+
"""
|
|
119
|
+
obj = func(arg)
|
|
120
|
+
assert obj != func(arg) # Defaut behaviour for custom classes
|
|
121
|
+
with pytest.raises(TypeError):
|
|
122
|
+
hash(obj)
|
|
123
|
+
with pytest.raises(Exception, match="cannot assign"):
|
|
124
|
+
obj.min = 0
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
from inspect import signature, getmodule
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
4
5
|
from numpy.testing import suppress_warnings
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
from .. import asarray, _elementwise_functions
|
|
9
|
+
from .._array_object import ALL_DEVICES, CPU_DEVICE, Device
|
|
8
10
|
from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift
|
|
9
11
|
from .._dtypes import (
|
|
10
12
|
_dtype_categories,
|
|
11
13
|
_boolean_dtypes,
|
|
12
14
|
_floating_dtypes,
|
|
13
15
|
_integer_dtypes,
|
|
16
|
+
bool as xp_bool,
|
|
17
|
+
float64,
|
|
14
18
|
int8,
|
|
15
19
|
int16,
|
|
16
20
|
int32,
|
|
17
21
|
int64,
|
|
18
22
|
uint64,
|
|
19
23
|
)
|
|
20
|
-
from .._flags import set_array_api_strict_flags
|
|
21
|
-
|
|
22
24
|
from .test_array_object import _check_op_array_scalar, BIG_INT
|
|
23
25
|
|
|
24
26
|
import array_api_strict
|
|
@@ -104,6 +106,13 @@ elementwise_function_input_types = {
|
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
|
|
109
|
+
elementwise_binary_function_names = [
|
|
110
|
+
func_name
|
|
111
|
+
for func_name in elementwise_function_input_types
|
|
112
|
+
if nargs(getattr(_elementwise_functions, func_name)) == 2
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
|
|
107
116
|
def test_nargs():
|
|
108
117
|
# Explicitly check number of arguments for a few functions
|
|
109
118
|
assert nargs(array_api_strict.logaddexp) == 2
|
|
@@ -126,33 +135,81 @@ def test_missing_functions():
|
|
|
126
135
|
assert set(mod_funcs) == set(elementwise_function_input_types)
|
|
127
136
|
|
|
128
137
|
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
@pytest.mark.parametrize("device", ALL_DEVICES)
|
|
139
|
+
@pytest.mark.parametrize("func_name,types", elementwise_function_input_types.items())
|
|
140
|
+
def test_elementwise_function_device_persists(func_name, types, device):
|
|
141
|
+
"""Test that the device of the input and output array are the same"""
|
|
131
142
|
def _array_vals(dtypes):
|
|
132
|
-
for
|
|
133
|
-
yield asarray(1., dtype=
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
for
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
for dtype in dtypes:
|
|
144
|
+
yield asarray(1., dtype=dtype, device=device)
|
|
145
|
+
|
|
146
|
+
dtypes = _dtype_categories[types]
|
|
147
|
+
func = getattr(_elementwise_functions, func_name)
|
|
148
|
+
|
|
149
|
+
for x in _array_vals(dtypes):
|
|
150
|
+
if nargs(func) == 2:
|
|
151
|
+
# This way we don't have to deal with incompatible
|
|
152
|
+
# types of the two arguments.
|
|
153
|
+
r = func(x, x)
|
|
154
|
+
assert r.device == x.device
|
|
155
|
+
|
|
156
|
+
else:
|
|
157
|
+
# `atanh` needs a slightly different input value from
|
|
158
|
+
# everyone else
|
|
159
|
+
if func_name == "atanh":
|
|
160
|
+
x -= 0.1
|
|
161
|
+
r = func(x)
|
|
162
|
+
assert r.device == x.device
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@pytest.mark.parametrize("func_name", elementwise_binary_function_names)
|
|
166
|
+
def test_elementwise_function_device_mismatch(func_name):
|
|
167
|
+
func = getattr(_elementwise_functions, func_name)
|
|
168
|
+
dtypes = elementwise_function_input_types[func_name]
|
|
169
|
+
if dtypes in ("floating-point", "real floating-point"):
|
|
170
|
+
dtype = float64
|
|
171
|
+
elif dtypes == "boolean":
|
|
172
|
+
dtype = xp_bool
|
|
173
|
+
else:
|
|
174
|
+
dtype = int64
|
|
175
|
+
|
|
176
|
+
a = asarray(1, dtype=dtype, device=CPU_DEVICE)
|
|
177
|
+
b = asarray(1, dtype=dtype, device=Device("device1"))
|
|
178
|
+
_ = func(a, a)
|
|
179
|
+
with pytest.raises(ValueError, match="different devices"):
|
|
180
|
+
func(a, b)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@pytest.mark.parametrize("func_name", elementwise_function_input_types)
|
|
184
|
+
def test_elementwise_function_numpy_scalars(func_name):
|
|
185
|
+
"""
|
|
186
|
+
Test that NumPy scalars (np.generic) are explicitly disallowed.
|
|
187
|
+
|
|
188
|
+
This must notably include np.float64 and np.complex128, which are
|
|
189
|
+
subclasses of float and complex respectively, so they need
|
|
190
|
+
special treatment in order to be rejected.
|
|
191
|
+
"""
|
|
192
|
+
func = getattr(_elementwise_functions, func_name)
|
|
193
|
+
dtypes = elementwise_function_input_types[func_name]
|
|
194
|
+
xp_dtypes = _dtype_categories[dtypes]
|
|
195
|
+
np_dtypes = [dtype._np_dtype for dtype in xp_dtypes]
|
|
196
|
+
|
|
197
|
+
value = 0.5 if func_name == "atanh" else 1
|
|
198
|
+
for xp_dtype in xp_dtypes:
|
|
199
|
+
for np_dtype in np_dtypes:
|
|
200
|
+
a = asarray(value, dtype=xp_dtype, device=CPU_DEVICE)
|
|
201
|
+
b = np.asarray(value, dtype=np_dtype)[()]
|
|
141
202
|
|
|
142
|
-
for x in _array_vals(dtypes):
|
|
143
203
|
if nargs(func) == 2:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
204
|
+
_ = func(a, a)
|
|
205
|
+
with pytest.raises(TypeError, match="neither Array nor Python scalars"):
|
|
206
|
+
func(a, b)
|
|
207
|
+
with pytest.raises(TypeError, match="neither Array nor Python scalars"):
|
|
208
|
+
func(b, a)
|
|
149
209
|
else:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
x -= 0.1
|
|
154
|
-
r = func(x)
|
|
155
|
-
assert r.device == x.device
|
|
210
|
+
_ = func(a)
|
|
211
|
+
with pytest.raises(TypeError, match="allowed"):
|
|
212
|
+
func(b)
|
|
156
213
|
|
|
157
214
|
|
|
158
215
|
def test_function_types():
|
|
@@ -168,9 +225,6 @@ def test_function_types():
|
|
|
168
225
|
for d in _floating_dtypes:
|
|
169
226
|
yield asarray(1.0, dtype=d)
|
|
170
227
|
|
|
171
|
-
# Use the latest version of the standard so all functions are included
|
|
172
|
-
set_array_api_strict_flags(api_version="2024.12")
|
|
173
|
-
|
|
174
228
|
for x in _array_vals():
|
|
175
229
|
for func_name, types in elementwise_function_input_types.items():
|
|
176
230
|
dtypes = _dtype_categories[types]
|
|
@@ -187,23 +241,23 @@ def test_function_types():
|
|
|
187
241
|
or x.dtype in _floating_dtypes and y.dtype not in _floating_dtypes
|
|
188
242
|
or y.dtype in _floating_dtypes and x.dtype not in _floating_dtypes
|
|
189
243
|
):
|
|
190
|
-
|
|
244
|
+
with pytest.raises(TypeError):
|
|
245
|
+
func(x, y)
|
|
191
246
|
if x.dtype not in dtypes or y.dtype not in dtypes:
|
|
192
|
-
|
|
247
|
+
with pytest.raises(TypeError):
|
|
248
|
+
func(x, y)
|
|
193
249
|
else:
|
|
194
250
|
if x.dtype not in dtypes:
|
|
195
|
-
|
|
251
|
+
with pytest.raises(TypeError):
|
|
252
|
+
func(x)
|
|
196
253
|
|
|
197
254
|
|
|
198
255
|
def test_bitwise_shift_error():
|
|
199
256
|
# bitwise shift functions should raise when the second argument is negative
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
ValueError, lambda: bitwise_right_shift(asarray([1, 1]), asarray([1, -1]))
|
|
205
|
-
)
|
|
206
|
-
|
|
257
|
+
with pytest.raises(ValueError):
|
|
258
|
+
bitwise_left_shift(asarray([1, 1]), asarray([1, -1]))
|
|
259
|
+
with pytest.raises(ValueError):
|
|
260
|
+
bitwise_right_shift(asarray([1, 1]), asarray([1, -1]))
|
|
207
261
|
|
|
208
262
|
|
|
209
263
|
def test_scalars():
|
|
@@ -212,9 +266,6 @@ def test_scalars():
|
|
|
212
266
|
# Also check that binary functions accept (array, scalar) and (scalar, array)
|
|
213
267
|
# arguments, and reject (scalar, scalar) arguments.
|
|
214
268
|
|
|
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
269
|
def _array_vals():
|
|
219
270
|
for d in _integer_dtypes:
|
|
220
271
|
yield asarray(1, dtype=d)
|
|
@@ -256,7 +307,5 @@ def test_scalars():
|
|
|
256
307
|
assert func(s, a) == func(conv_scalar, a)
|
|
257
308
|
assert func(a, s) == func(a, conv_scalar)
|
|
258
309
|
|
|
259
|
-
with
|
|
310
|
+
with pytest.raises(TypeError):
|
|
260
311
|
func(s, s)
|
|
261
|
-
|
|
262
|
-
|