array-api-strict 2.5__py3-none-any.whl → 2.6__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/_array_object.py +46 -40
- array_api_strict/_creation_functions.py +94 -34
- array_api_strict/_devices.py +136 -0
- array_api_strict/_dtypes.py +3 -1
- array_api_strict/_fft.py +21 -1
- array_api_strict/_info.py +16 -78
- array_api_strict/_statistical_functions.py +8 -2
- array_api_strict/_version.py +2 -2
- array_api_strict/tests/test_array_object.py +44 -1
- array_api_strict/tests/test_creation_functions.py +159 -2
- array_api_strict/tests/test_device_support.py +49 -7
- array_api_strict/tests/test_elementwise_functions.py +6 -1
- array_api_strict/tests/test_searching_functions.py +1 -1
- array_api_strict/tests/test_statistical_functions.py +16 -0
- {array_api_strict-2.5.dist-info → array_api_strict-2.6.dist-info}/METADATA +1 -1
- {array_api_strict-2.5.dist-info → array_api_strict-2.6.dist-info}/RECORD +19 -18
- {array_api_strict-2.5.dist-info → array_api_strict-2.6.dist-info}/LICENSE +0 -0
- {array_api_strict-2.5.dist-info → array_api_strict-2.6.dist-info}/WHEEL +0 -0
- {array_api_strict-2.5.dist-info → array_api_strict-2.6.dist-info}/top_level.txt +0 -0
|
@@ -20,7 +20,7 @@ import sys
|
|
|
20
20
|
from collections.abc import Iterator
|
|
21
21
|
from enum import IntEnum
|
|
22
22
|
from types import EllipsisType, ModuleType
|
|
23
|
-
from typing import Any,
|
|
23
|
+
from typing import Any, Literal, SupportsIndex, Callable
|
|
24
24
|
|
|
25
25
|
import numpy as np
|
|
26
26
|
import numpy.typing as npt
|
|
@@ -40,35 +40,14 @@ from ._dtypes import (
|
|
|
40
40
|
_real_to_complex_map,
|
|
41
41
|
_result_type,
|
|
42
42
|
)
|
|
43
|
+
from ._devices import (
|
|
44
|
+
CPU_DEVICE, Device, device_supports_dtype, _normalize_dl_device, _DLPACK_DEVICE_FOR,
|
|
45
|
+
DLDeviceType
|
|
46
|
+
)
|
|
43
47
|
from ._flags import get_array_api_strict_flags, set_array_api_strict_flags
|
|
44
48
|
from ._typing import PyCapsule
|
|
45
49
|
|
|
46
50
|
|
|
47
|
-
class Device:
|
|
48
|
-
_device: Final[str]
|
|
49
|
-
__slots__ = ("_device", "__weakref__")
|
|
50
|
-
|
|
51
|
-
def __init__(self, device: str = "CPU_DEVICE"):
|
|
52
|
-
if device not in ("CPU_DEVICE", "device1", "device2"):
|
|
53
|
-
raise ValueError(f"The device '{device}' is not a valid choice.")
|
|
54
|
-
self._device = device
|
|
55
|
-
|
|
56
|
-
def __repr__(self) -> str:
|
|
57
|
-
return f"array_api_strict.Device('{self._device}')"
|
|
58
|
-
|
|
59
|
-
def __eq__(self, other: object) -> bool:
|
|
60
|
-
if not isinstance(other, Device):
|
|
61
|
-
return False
|
|
62
|
-
return self._device == other._device
|
|
63
|
-
|
|
64
|
-
def __hash__(self) -> int:
|
|
65
|
-
return hash(("Device", self._device))
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
CPU_DEVICE = Device()
|
|
69
|
-
ALL_DEVICES = (CPU_DEVICE, Device("device1"), Device("device2"))
|
|
70
|
-
|
|
71
|
-
|
|
72
51
|
class Array:
|
|
73
52
|
"""
|
|
74
53
|
n-d array object for the array API namespace.
|
|
@@ -113,10 +92,15 @@ class Array:
|
|
|
113
92
|
raise TypeError(
|
|
114
93
|
f"The array_api_strict namespace does not support the dtype '{x.dtype}'"
|
|
115
94
|
)
|
|
116
|
-
|
|
117
|
-
obj._dtype = _dtype
|
|
95
|
+
|
|
118
96
|
if device is None:
|
|
119
97
|
device = CPU_DEVICE
|
|
98
|
+
if not device_supports_dtype(device, _dtype):
|
|
99
|
+
raise ValueError(f"Device {device!r} does not support dtype={_dtype!r}.")
|
|
100
|
+
|
|
101
|
+
obj._array = x
|
|
102
|
+
obj._dtype = _dtype
|
|
103
|
+
|
|
120
104
|
obj._device = device
|
|
121
105
|
return obj
|
|
122
106
|
|
|
@@ -400,7 +384,7 @@ class Array:
|
|
|
400
384
|
isinstance(i, SupportsIndex) # i.e. ints
|
|
401
385
|
or isinstance(i, slice)
|
|
402
386
|
or i == Ellipsis
|
|
403
|
-
or i is None
|
|
387
|
+
or (op == "getitem" and i is None) # `None` disallowed in setitem
|
|
404
388
|
or isinstance(i, Array)
|
|
405
389
|
or isinstance(i, np.ndarray)
|
|
406
390
|
):
|
|
@@ -630,22 +614,40 @@ class Array:
|
|
|
630
614
|
raise NotImplementedError("The copy argument to __dlpack__ is not yet implemented")
|
|
631
615
|
|
|
632
616
|
return self._array.__dlpack__(stream=stream)
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
617
|
+
|
|
618
|
+
kwargs: dict[str, Any] = {'stream': stream}
|
|
619
|
+
self_dl_device = _normalize_dl_device(*_DLPACK_DEVICE_FOR[self._device])
|
|
620
|
+
cpu_dl_device = _normalize_dl_device(DLDeviceType.kDLCPU, 0)
|
|
621
|
+
numpy_dl_device: tuple[IntEnum, int] | None = None
|
|
622
|
+
|
|
623
|
+
if dl_device not in [_undef, None]:
|
|
624
|
+
requested = _normalize_dl_device(dl_device[0], dl_device[1])
|
|
625
|
+
if requested == self_dl_device:
|
|
626
|
+
pass
|
|
627
|
+
elif requested == cpu_dl_device and self_dl_device != cpu_dl_device:
|
|
628
|
+
if copy is False:
|
|
629
|
+
raise BufferError(
|
|
630
|
+
"Cannot export array to CPU without copying when copy=False"
|
|
631
|
+
)
|
|
632
|
+
if copy is _undef:
|
|
633
|
+
copy = True
|
|
634
|
+
numpy_dl_device = (DLDeviceType.kDLCPU, 0)
|
|
635
|
+
else:
|
|
636
|
+
raise BufferError("unsupported device requested")
|
|
637
|
+
|
|
638
|
+
if max_version is not _undef:
|
|
639
|
+
kwargs['max_version'] = max_version
|
|
640
|
+
if numpy_dl_device is not None:
|
|
641
|
+
kwargs['dl_device'] = numpy_dl_device
|
|
642
|
+
if copy is not _undef:
|
|
643
|
+
kwargs['copy'] = copy
|
|
644
|
+
return self._array.__dlpack__(**kwargs)
|
|
642
645
|
|
|
643
646
|
def __dlpack_device__(self) -> tuple[IntEnum, int]:
|
|
644
647
|
"""
|
|
645
648
|
Performs the operation __dlpack_device__.
|
|
646
649
|
"""
|
|
647
|
-
|
|
648
|
-
return self._array.__dlpack_device__()
|
|
650
|
+
return _DLPACK_DEVICE_FOR[self._device]
|
|
649
651
|
|
|
650
652
|
def __eq__(self, other: Array | complex, /) -> Array: # type: ignore[override]
|
|
651
653
|
"""
|
|
@@ -960,6 +962,10 @@ class Array:
|
|
|
960
962
|
other = value
|
|
961
963
|
if isinstance(value, (bool, int, float, complex)):
|
|
962
964
|
other = self._promote_scalar(value)
|
|
965
|
+
else:
|
|
966
|
+
if value.device != self.device:
|
|
967
|
+
raise ValueError(f"mismatched devices: {self.device = } != {value.device =}.")
|
|
968
|
+
|
|
963
969
|
dt = _result_type(self.dtype, other.dtype)
|
|
964
970
|
if dt != self.dtype:
|
|
965
971
|
raise TypeError(f"mismatched dtypes: {self.dtype = } and {other.dtype = }")
|
|
@@ -5,7 +5,11 @@ from typing import TYPE_CHECKING, Literal
|
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
|
|
8
|
-
from ._dtypes import DType, _all_dtypes, _np_dtype
|
|
8
|
+
from ._dtypes import DType, _all_dtypes, _np_dtype, bool as xp_bool
|
|
9
|
+
from ._devices import (
|
|
10
|
+
Device, device_supports_dtype, get_default_dtypes,
|
|
11
|
+
check_device as _check_device
|
|
12
|
+
)
|
|
9
13
|
from ._flags import get_array_api_strict_flags
|
|
10
14
|
from ._typing import NestedSequence, SupportsBufferProtocol, SupportsDLPack
|
|
11
15
|
|
|
@@ -14,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
14
18
|
from typing_extensions import TypeIs
|
|
15
19
|
|
|
16
20
|
# Circular import
|
|
17
|
-
from ._array_object import Array
|
|
21
|
+
from ._array_object import Array
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
class Undef(Enum):
|
|
@@ -24,10 +28,15 @@ class Undef(Enum):
|
|
|
24
28
|
_undef = Undef.UNDEF
|
|
25
29
|
|
|
26
30
|
|
|
27
|
-
def _check_valid_dtype(dtype: DType | None) -> None:
|
|
31
|
+
def _check_valid_dtype(dtype: DType | None, device: Device | None = None) -> None:
|
|
28
32
|
# Note: Only spelling dtypes as the dtype objects is supported.
|
|
29
|
-
if dtype not
|
|
30
|
-
|
|
33
|
+
if dtype is not None:
|
|
34
|
+
if dtype not in _all_dtypes:
|
|
35
|
+
raise ValueError(f"dtype must be one of the supported dtypes, got {dtype!r}")
|
|
36
|
+
|
|
37
|
+
if device is not None:
|
|
38
|
+
if not device_supports_dtype(device, dtype):
|
|
39
|
+
raise ValueError(f"Device {device!r} does not support dtype={dtype!r}.")
|
|
31
40
|
|
|
32
41
|
|
|
33
42
|
def _supports_buffer_protocol(obj: object) -> TypeIs[SupportsBufferProtocol]:
|
|
@@ -38,18 +47,6 @@ def _supports_buffer_protocol(obj: object) -> TypeIs[SupportsBufferProtocol]:
|
|
|
38
47
|
return True
|
|
39
48
|
|
|
40
49
|
|
|
41
|
-
def _check_device(device: Device | None) -> None:
|
|
42
|
-
# _array_object imports in this file are inside the functions to avoid
|
|
43
|
-
# circular imports
|
|
44
|
-
from ._array_object import ALL_DEVICES, Device
|
|
45
|
-
|
|
46
|
-
if device is not None and not isinstance(device, Device):
|
|
47
|
-
raise ValueError(f"Unsupported device {device!r}")
|
|
48
|
-
|
|
49
|
-
if device is not None and device not in ALL_DEVICES:
|
|
50
|
-
raise ValueError(f"Unsupported device {device!r}")
|
|
51
|
-
|
|
52
|
-
|
|
53
50
|
def asarray(
|
|
54
51
|
obj: Array | complex | NestedSequence[complex] | SupportsBufferProtocol,
|
|
55
52
|
/,
|
|
@@ -65,11 +62,12 @@ def asarray(
|
|
|
65
62
|
"""
|
|
66
63
|
from ._array_object import Array
|
|
67
64
|
|
|
68
|
-
|
|
65
|
+
_check_device(device)
|
|
66
|
+
_check_valid_dtype(dtype, device)
|
|
69
67
|
_np_dtype = None
|
|
70
68
|
if dtype is not None:
|
|
71
69
|
_np_dtype = dtype._np_dtype
|
|
72
|
-
|
|
70
|
+
|
|
73
71
|
if isinstance(obj, Array) and device is None:
|
|
74
72
|
device = obj.device
|
|
75
73
|
|
|
@@ -108,6 +106,27 @@ def asarray(
|
|
|
108
106
|
raise OverflowError("Integer out of bounds for array dtypes")
|
|
109
107
|
|
|
110
108
|
res = np.array(obj, dtype=_np_dtype, copy=copy)
|
|
109
|
+
|
|
110
|
+
# numpy default dtype may differ; if so, adjust the dtype
|
|
111
|
+
if dtype is None and device is not None:
|
|
112
|
+
res_dtype = DType(res.dtype)
|
|
113
|
+
# The dtype selected by Numpy might not be the default dtype
|
|
114
|
+
# on this device. We thus find the default dtype for the dtype "kind", and
|
|
115
|
+
# cast to the device-appropriate default.
|
|
116
|
+
from ._data_type_functions import isdtype
|
|
117
|
+
if isdtype(res_dtype, "bool"):
|
|
118
|
+
target_dtype = DType("bool")
|
|
119
|
+
elif isdtype(res_dtype, "integral"):
|
|
120
|
+
target_dtype = get_default_dtypes(device)["integral"]
|
|
121
|
+
elif isdtype(res_dtype, "real floating"):
|
|
122
|
+
target_dtype = get_default_dtypes(device)["real floating"]
|
|
123
|
+
elif isdtype(res_dtype, "complex floating"):
|
|
124
|
+
target_dtype = get_default_dtypes(device)["complex floating"]
|
|
125
|
+
else:
|
|
126
|
+
raise ValueError(f"{res_dtype = } not understood.")
|
|
127
|
+
|
|
128
|
+
res = res.astype(target_dtype._np_dtype)
|
|
129
|
+
|
|
111
130
|
return Array._new(res, device=device)
|
|
112
131
|
|
|
113
132
|
|
|
@@ -127,8 +146,13 @@ def arange(
|
|
|
127
146
|
"""
|
|
128
147
|
from ._array_object import Array
|
|
129
148
|
|
|
130
|
-
_check_valid_dtype(dtype)
|
|
131
149
|
_check_device(device)
|
|
150
|
+
_check_valid_dtype(dtype, device)
|
|
151
|
+
if dtype is None:
|
|
152
|
+
if any(isinstance(x, float) for x in (start, stop, step)):
|
|
153
|
+
dtype = get_default_dtypes(device)["real floating"]
|
|
154
|
+
else:
|
|
155
|
+
dtype = get_default_dtypes(device)["integral"]
|
|
132
156
|
|
|
133
157
|
return Array._new(
|
|
134
158
|
np.arange(start, stop, step, dtype=_np_dtype(dtype)),
|
|
@@ -149,8 +173,10 @@ def empty(
|
|
|
149
173
|
"""
|
|
150
174
|
from ._array_object import Array
|
|
151
175
|
|
|
152
|
-
_check_valid_dtype(dtype)
|
|
153
176
|
_check_device(device)
|
|
177
|
+
_check_valid_dtype(dtype, device)
|
|
178
|
+
if dtype is None:
|
|
179
|
+
dtype = get_default_dtypes(device)["real floating"]
|
|
154
180
|
|
|
155
181
|
return Array._new(np.empty(shape, dtype=_np_dtype(dtype)), device=device)
|
|
156
182
|
|
|
@@ -165,10 +191,12 @@ def empty_like(
|
|
|
165
191
|
"""
|
|
166
192
|
from ._array_object import Array
|
|
167
193
|
|
|
168
|
-
_check_valid_dtype(dtype)
|
|
169
194
|
_check_device(device)
|
|
170
195
|
if device is None:
|
|
171
196
|
device = x.device
|
|
197
|
+
if dtype is None:
|
|
198
|
+
dtype = x.dtype
|
|
199
|
+
_check_valid_dtype(dtype, device)
|
|
172
200
|
|
|
173
201
|
return Array._new(np.empty_like(x._array, dtype=_np_dtype(dtype)), device=device)
|
|
174
202
|
|
|
@@ -189,8 +217,10 @@ def eye(
|
|
|
189
217
|
"""
|
|
190
218
|
from ._array_object import Array
|
|
191
219
|
|
|
192
|
-
_check_valid_dtype(dtype)
|
|
193
220
|
_check_device(device)
|
|
221
|
+
_check_valid_dtype(dtype, device)
|
|
222
|
+
if dtype is None:
|
|
223
|
+
dtype = get_default_dtypes(device)["real floating"]
|
|
194
224
|
|
|
195
225
|
return Array._new(
|
|
196
226
|
np.eye(n_rows, M=n_cols, k=k, dtype=_np_dtype(dtype)), device=device
|
|
@@ -212,15 +242,20 @@ def from_dlpack(
|
|
|
212
242
|
if copy is not _undef:
|
|
213
243
|
raise ValueError("The copy argument to from_dlpack requires at least version 2023.12 of the array API")
|
|
214
244
|
|
|
215
|
-
# Going to wait for upstream numpy support
|
|
216
245
|
if device is not _undef:
|
|
217
246
|
_check_device(device)
|
|
218
247
|
else:
|
|
219
248
|
device = None
|
|
220
|
-
|
|
221
|
-
|
|
249
|
+
if hasattr(x, "__dlpack_device__"):
|
|
250
|
+
from ._devices import _device_from_dlpack_device
|
|
222
251
|
|
|
223
|
-
|
|
252
|
+
dl_type, dl_id = x.__dlpack_device__()
|
|
253
|
+
device = _device_from_dlpack_device(dl_type, dl_id)
|
|
254
|
+
if copy in [_undef, None]:
|
|
255
|
+
# numpy 1.26 does not have the copy= arg
|
|
256
|
+
return Array._new(np.from_dlpack(x), device=device)
|
|
257
|
+
|
|
258
|
+
return Array._new(np.from_dlpack(x, copy=copy), device=device)
|
|
224
259
|
|
|
225
260
|
|
|
226
261
|
def full(
|
|
@@ -237,12 +272,22 @@ def full(
|
|
|
237
272
|
"""
|
|
238
273
|
from ._array_object import Array
|
|
239
274
|
|
|
240
|
-
_check_valid_dtype(dtype)
|
|
241
275
|
_check_device(device)
|
|
276
|
+
_check_valid_dtype(dtype, device)
|
|
242
277
|
|
|
243
278
|
if not isinstance(fill_value, bool | int | float | complex):
|
|
244
279
|
msg = f"Expected Python scalar fill_value, got type {type(fill_value)}"
|
|
245
280
|
raise TypeError(msg)
|
|
281
|
+
|
|
282
|
+
if dtype is None:
|
|
283
|
+
if type(fill_value) == bool:
|
|
284
|
+
dtype = xp_bool
|
|
285
|
+
else:
|
|
286
|
+
kind = {
|
|
287
|
+
int: "integral", float: "real floating", complex: "complex floating"
|
|
288
|
+
}[type(fill_value)]
|
|
289
|
+
dtype = get_default_dtypes(device)[kind]
|
|
290
|
+
|
|
246
291
|
res = np.full(shape, fill_value, dtype=_np_dtype(dtype))
|
|
247
292
|
if DType(res.dtype) not in _all_dtypes:
|
|
248
293
|
# This will happen if the fill value is not something that NumPy
|
|
@@ -266,10 +311,12 @@ def full_like(
|
|
|
266
311
|
"""
|
|
267
312
|
from ._array_object import Array
|
|
268
313
|
|
|
269
|
-
_check_valid_dtype(dtype)
|
|
270
314
|
_check_device(device)
|
|
271
315
|
if device is None:
|
|
272
316
|
device = x.device
|
|
317
|
+
if dtype is None:
|
|
318
|
+
dtype = x.dtype
|
|
319
|
+
_check_valid_dtype(dtype, device)
|
|
273
320
|
|
|
274
321
|
if not isinstance(fill_value, bool | int | float | complex):
|
|
275
322
|
msg = f"Expected Python scalar fill_value, got type {type(fill_value)}"
|
|
@@ -300,8 +347,13 @@ def linspace(
|
|
|
300
347
|
"""
|
|
301
348
|
from ._array_object import Array
|
|
302
349
|
|
|
303
|
-
_check_valid_dtype(dtype)
|
|
304
350
|
_check_device(device)
|
|
351
|
+
_check_valid_dtype(dtype, device)
|
|
352
|
+
if dtype is None:
|
|
353
|
+
if isinstance(start, complex) or isinstance(stop, complex):
|
|
354
|
+
dtype = get_default_dtypes(device)["complex floating"]
|
|
355
|
+
else:
|
|
356
|
+
dtype = get_default_dtypes(device)["real floating"]
|
|
305
357
|
|
|
306
358
|
return Array._new(
|
|
307
359
|
np.linspace(start, stop, num, dtype=_np_dtype(dtype), endpoint=endpoint),
|
|
@@ -353,8 +405,10 @@ def ones(
|
|
|
353
405
|
"""
|
|
354
406
|
from ._array_object import Array
|
|
355
407
|
|
|
356
|
-
_check_valid_dtype(dtype)
|
|
357
408
|
_check_device(device)
|
|
409
|
+
_check_valid_dtype(dtype, device)
|
|
410
|
+
if dtype is None:
|
|
411
|
+
dtype = get_default_dtypes(device)["real floating"]
|
|
358
412
|
|
|
359
413
|
return Array._new(np.ones(shape, dtype=_np_dtype(dtype)), device=device)
|
|
360
414
|
|
|
@@ -369,10 +423,12 @@ def ones_like(
|
|
|
369
423
|
"""
|
|
370
424
|
from ._array_object import Array
|
|
371
425
|
|
|
372
|
-
_check_valid_dtype(dtype)
|
|
373
426
|
_check_device(device)
|
|
374
427
|
if device is None:
|
|
375
428
|
device = x.device
|
|
429
|
+
if dtype is None:
|
|
430
|
+
dtype = x.dtype
|
|
431
|
+
_check_valid_dtype(dtype, device)
|
|
376
432
|
|
|
377
433
|
return Array._new(np.ones_like(x._array, dtype=_np_dtype(dtype)), device=device)
|
|
378
434
|
|
|
@@ -418,8 +474,10 @@ def zeros(
|
|
|
418
474
|
"""
|
|
419
475
|
from ._array_object import Array
|
|
420
476
|
|
|
421
|
-
_check_valid_dtype(dtype)
|
|
422
477
|
_check_device(device)
|
|
478
|
+
_check_valid_dtype(dtype, device)
|
|
479
|
+
if dtype is None:
|
|
480
|
+
dtype = get_default_dtypes(device)["real floating"]
|
|
423
481
|
|
|
424
482
|
return Array._new(np.zeros(shape, dtype=_np_dtype(dtype)), device=device)
|
|
425
483
|
|
|
@@ -434,9 +492,11 @@ def zeros_like(
|
|
|
434
492
|
"""
|
|
435
493
|
from ._array_object import Array
|
|
436
494
|
|
|
437
|
-
_check_valid_dtype(dtype)
|
|
438
495
|
_check_device(device)
|
|
439
496
|
if device is None:
|
|
440
497
|
device = x.device
|
|
498
|
+
if dtype is None:
|
|
499
|
+
dtype = x.dtype
|
|
500
|
+
_check_valid_dtype(dtype, device)
|
|
441
501
|
|
|
442
502
|
return Array._new(np.zeros_like(x._array, dtype=_np_dtype(dtype)), device=device)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from typing import Final
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
|
|
4
|
+
from ._dtypes import (
|
|
5
|
+
DType, float32, float64, complex64, complex128, int64,
|
|
6
|
+
_all_dtypes, _boolean_dtypes, _signed_integer_dtypes,
|
|
7
|
+
_unsigned_integer_dtypes, _integer_dtypes, _real_floating_dtypes,
|
|
8
|
+
_complex_floating_dtypes, _numeric_dtypes
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
_ALL_DEVICE_NAMES = ("CPU_DEVICE", "device1", "device2", "no_float64")
|
|
12
|
+
|
|
13
|
+
class Device:
|
|
14
|
+
_device: Final[str]
|
|
15
|
+
__slots__ = ("_device", "__weakref__")
|
|
16
|
+
|
|
17
|
+
def __init__(self, device: str = "CPU_DEVICE"):
|
|
18
|
+
if device not in _ALL_DEVICE_NAMES:
|
|
19
|
+
raise ValueError(f"The device '{device}' is not a valid choice.")
|
|
20
|
+
self._device = device
|
|
21
|
+
|
|
22
|
+
def __repr__(self) -> str:
|
|
23
|
+
return f"array_api_strict.Device('{self._device}')"
|
|
24
|
+
|
|
25
|
+
def __eq__(self, other: object) -> bool:
|
|
26
|
+
if not isinstance(other, Device):
|
|
27
|
+
return False
|
|
28
|
+
return self._device == other._device
|
|
29
|
+
|
|
30
|
+
def __hash__(self) -> int:
|
|
31
|
+
return hash(("Device", self._device))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
CPU_DEVICE = Device()
|
|
35
|
+
NO_FLOAT64_DEVICE = Device("no_float64")
|
|
36
|
+
|
|
37
|
+
ALL_DEVICES = (CPU_DEVICE, Device("device1"), Device("device2"), NO_FLOAT64_DEVICE)
|
|
38
|
+
|
|
39
|
+
class DLDeviceType(IntEnum):
|
|
40
|
+
kDLCPU = 1
|
|
41
|
+
kDLCUDA = 2
|
|
42
|
+
kDLMETAL = 8
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_DLPACK_DEVICE_FOR: Final[dict[Device, tuple[DLDeviceType, int]]] = {
|
|
46
|
+
CPU_DEVICE: (DLDeviceType.kDLCPU, 0),
|
|
47
|
+
Device("device1"): (DLDeviceType.kDLCUDA, 0),
|
|
48
|
+
Device("device2"): (DLDeviceType.kDLCUDA, 1),
|
|
49
|
+
NO_FLOAT64_DEVICE: (DLDeviceType.kDLMETAL, 0),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_DLPACK_DEVICE_TO_LOGICAL: Final[dict[tuple[int, int], Device]] = {
|
|
53
|
+
(int(device_type), device_id): logical_device
|
|
54
|
+
for logical_device, (device_type, device_id) in _DLPACK_DEVICE_FOR.items()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _normalize_dl_device(device_type: IntEnum | int, device_id: int) -> tuple[int, int]:
|
|
59
|
+
return (int(device_type), device_id)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _device_from_dlpack_device(
|
|
63
|
+
device_type: IntEnum | int, device_id: int
|
|
64
|
+
) -> Device:
|
|
65
|
+
# NB: if the (device_type, device_id) pair not known, raise
|
|
66
|
+
return _DLPACK_DEVICE_TO_LOGICAL[_normalize_dl_device(device_type, device_id)]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def check_device(device: Device | None) -> None:
|
|
70
|
+
if device is not None and not isinstance(device, Device):
|
|
71
|
+
raise ValueError(f"Unsupported device {device!r}")
|
|
72
|
+
|
|
73
|
+
if device is not None and device not in ALL_DEVICES:
|
|
74
|
+
raise ValueError(f"Unsupported device {device!r}")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# Helpers for device-specific dtype support
|
|
78
|
+
|
|
79
|
+
def get_default_dtypes(device: Device | None = None) -> dict[str, DType]:
|
|
80
|
+
if device == NO_FLOAT64_DEVICE:
|
|
81
|
+
# mimic an MPS device which does not have float64 at all
|
|
82
|
+
return {
|
|
83
|
+
"real floating": float32,
|
|
84
|
+
"complex floating": complex64,
|
|
85
|
+
"integral": int64,
|
|
86
|
+
"indexing": int64,
|
|
87
|
+
}
|
|
88
|
+
elif device == Device('device2'):
|
|
89
|
+
# mimic a torch CPU device: support float64 but default to float32
|
|
90
|
+
return {
|
|
91
|
+
"real floating": float32,
|
|
92
|
+
"complex floating": complex64,
|
|
93
|
+
"integral": int64,
|
|
94
|
+
"indexing": int64,
|
|
95
|
+
}
|
|
96
|
+
else:
|
|
97
|
+
return {
|
|
98
|
+
"real floating": float64,
|
|
99
|
+
"complex floating": complex128,
|
|
100
|
+
"integral": int64,
|
|
101
|
+
"indexing": int64,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def device_supports_dtype(device: Device | None, dtype: DType |None) -> bool:
|
|
106
|
+
"""True if `device` supports `dtype`, False otherwise."""
|
|
107
|
+
# Device("no_float64") supports all dtypes except float64 and complex128
|
|
108
|
+
if device == NO_FLOAT64_DEVICE:
|
|
109
|
+
return dtype not in (float64, complex128)
|
|
110
|
+
|
|
111
|
+
# All other devices support all dtypes
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _map_supported(dtypes: list[DType], device: Device) -> dict[str, DType]:
|
|
116
|
+
return {
|
|
117
|
+
dt._canonical_name: dt
|
|
118
|
+
for dt in dtypes
|
|
119
|
+
if device_supports_dtype(device, dt)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# _info.dtypes() maps "kind" -> dict of {name: dtype}
|
|
124
|
+
# Note that "kinds" differ from "categories" above, per the spec.
|
|
125
|
+
|
|
126
|
+
_kind_to_dtypes = {
|
|
127
|
+
None: _all_dtypes,
|
|
128
|
+
"bool": _boolean_dtypes,
|
|
129
|
+
"signed integer": _signed_integer_dtypes,
|
|
130
|
+
"unsigned integer": _unsigned_integer_dtypes,
|
|
131
|
+
"integral": _integer_dtypes,
|
|
132
|
+
"real floating": _real_floating_dtypes,
|
|
133
|
+
"complex floating": _complex_floating_dtypes,
|
|
134
|
+
"numeric": _numeric_dtypes
|
|
135
|
+
}
|
|
136
|
+
|
array_api_strict/_dtypes.py
CHANGED
|
@@ -11,9 +11,11 @@ import numpy.typing as npt
|
|
|
11
11
|
|
|
12
12
|
class DType:
|
|
13
13
|
_np_dtype: Final[np.dtype[Any]]
|
|
14
|
-
|
|
14
|
+
_canonical_name: Final[Any]
|
|
15
|
+
__slots__ = ("_np_dtype", "_canonical_name", "__weakref__")
|
|
15
16
|
|
|
16
17
|
def __init__(self, np_dtype: npt.DTypeLike):
|
|
18
|
+
self._canonical_name = np_dtype
|
|
17
19
|
self._np_dtype = np.dtype(np_dtype)
|
|
18
20
|
|
|
19
21
|
def __repr__(self) -> str:
|
array_api_strict/_fft.py
CHANGED
|
@@ -3,7 +3,8 @@ from typing import Literal
|
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
|
-
from ._array_object import
|
|
6
|
+
from ._array_object import Array
|
|
7
|
+
from ._devices import ALL_DEVICES, Device, device_supports_dtype
|
|
7
8
|
from ._data_type_functions import astype
|
|
8
9
|
from ._dtypes import (
|
|
9
10
|
DType,
|
|
@@ -13,6 +14,7 @@ from ._dtypes import (
|
|
|
13
14
|
complex64,
|
|
14
15
|
float32,
|
|
15
16
|
)
|
|
17
|
+
from ._info import __array_namespace_info__
|
|
16
18
|
from ._flags import requires_extension
|
|
17
19
|
|
|
18
20
|
|
|
@@ -268,6 +270,15 @@ def fftfreq(
|
|
|
268
270
|
np_result = np.fft.fftfreq(n, d=d)
|
|
269
271
|
if dtype:
|
|
270
272
|
np_result = np_result.astype(dtype._np_dtype)
|
|
273
|
+
|
|
274
|
+
if not device_supports_dtype(device, DType(np_result.dtype)):
|
|
275
|
+
if dtype:
|
|
276
|
+
# user input unsupported
|
|
277
|
+
raise ValueError(f"Device {device!r} does not support dtype={dtype!r}.")
|
|
278
|
+
|
|
279
|
+
dt = __array_namespace_info__().default_dtypes(device=device)["real floating"]
|
|
280
|
+
np_result = np_result.astype(dt._np_dtype)
|
|
281
|
+
|
|
271
282
|
return Array._new(np_result, device=device)
|
|
272
283
|
|
|
273
284
|
@requires_extension('fft')
|
|
@@ -292,6 +303,15 @@ def rfftfreq(
|
|
|
292
303
|
np_result = np.fft.rfftfreq(n, d=d)
|
|
293
304
|
if dtype:
|
|
294
305
|
np_result = np_result.astype(dtype._np_dtype)
|
|
306
|
+
|
|
307
|
+
if not device_supports_dtype(device, DType(np_result.dtype)):
|
|
308
|
+
if dtype:
|
|
309
|
+
# user input unsupported
|
|
310
|
+
raise ValueError(f"Device {device!r} does not support dtype={dtype!r}.")
|
|
311
|
+
|
|
312
|
+
dt = __array_namespace_info__().default_dtypes(device=device)["real floating"]
|
|
313
|
+
np_result = np_result.astype(dt._np_dtype)
|
|
314
|
+
|
|
295
315
|
return Array._new(np_result, device=device)
|
|
296
316
|
|
|
297
317
|
@requires_extension('fft')
|
array_api_strict/_info.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
|
-
from . import
|
|
4
|
-
from .
|
|
3
|
+
from . import _devices
|
|
4
|
+
from ._devices import ALL_DEVICES, CPU_DEVICE, Device
|
|
5
5
|
from ._flags import get_array_api_strict_flags, requires_api_version
|
|
6
6
|
from ._typing import Capabilities, DataTypes, DefaultDataTypes
|
|
7
7
|
|
|
@@ -40,12 +40,7 @@ class __array_namespace_info__:
|
|
|
40
40
|
*,
|
|
41
41
|
device: Device | None = None,
|
|
42
42
|
) -> DefaultDataTypes:
|
|
43
|
-
return
|
|
44
|
-
"real floating": dt.float64,
|
|
45
|
-
"complex floating": dt.complex128,
|
|
46
|
-
"integral": dt.int64,
|
|
47
|
-
"indexing": dt.int64,
|
|
48
|
-
}
|
|
43
|
+
return _devices.get_default_dtypes(device)
|
|
49
44
|
|
|
50
45
|
@requires_api_version('2023.12')
|
|
51
46
|
def dtypes(
|
|
@@ -54,78 +49,21 @@ class __array_namespace_info__:
|
|
|
54
49
|
device: Device | None = None,
|
|
55
50
|
kind: str | tuple[str, ...] | None = None,
|
|
56
51
|
) -> DataTypes:
|
|
57
|
-
if
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"float64": dt.float64,
|
|
70
|
-
"complex64": dt.complex64,
|
|
71
|
-
"complex128": dt.complex128,
|
|
72
|
-
}
|
|
73
|
-
if kind == "bool":
|
|
74
|
-
return {"bool": dt.bool}
|
|
75
|
-
if kind == "signed integer":
|
|
76
|
-
return {
|
|
77
|
-
"int8": dt.int8,
|
|
78
|
-
"int16": dt.int16,
|
|
79
|
-
"int32": dt.int32,
|
|
80
|
-
"int64": dt.int64,
|
|
81
|
-
}
|
|
82
|
-
if kind == "unsigned integer":
|
|
83
|
-
return {
|
|
84
|
-
"uint8": dt.uint8,
|
|
85
|
-
"uint16": dt.uint16,
|
|
86
|
-
"uint32": dt.uint32,
|
|
87
|
-
"uint64": dt.uint64,
|
|
88
|
-
}
|
|
89
|
-
if kind == "integral":
|
|
90
|
-
return {
|
|
91
|
-
"int8": dt.int8,
|
|
92
|
-
"int16": dt.int16,
|
|
93
|
-
"int32": dt.int32,
|
|
94
|
-
"int64": dt.int64,
|
|
95
|
-
"uint8": dt.uint8,
|
|
96
|
-
"uint16": dt.uint16,
|
|
97
|
-
"uint32": dt.uint32,
|
|
98
|
-
"uint64": dt.uint64,
|
|
99
|
-
}
|
|
100
|
-
if kind == "real floating":
|
|
101
|
-
return {
|
|
102
|
-
"float32": dt.float32,
|
|
103
|
-
"float64": dt.float64,
|
|
104
|
-
}
|
|
105
|
-
if kind == "complex floating":
|
|
106
|
-
return {
|
|
107
|
-
"complex64": dt.complex64,
|
|
108
|
-
"complex128": dt.complex128,
|
|
109
|
-
}
|
|
110
|
-
if kind == "numeric":
|
|
111
|
-
return {
|
|
112
|
-
"int8": dt.int8,
|
|
113
|
-
"int16": dt.int16,
|
|
114
|
-
"int32": dt.int32,
|
|
115
|
-
"int64": dt.int64,
|
|
116
|
-
"uint8": dt.uint8,
|
|
117
|
-
"uint16": dt.uint16,
|
|
118
|
-
"uint32": dt.uint32,
|
|
119
|
-
"uint64": dt.uint64,
|
|
120
|
-
"float32": dt.float32,
|
|
121
|
-
"float64": dt.float64,
|
|
122
|
-
"complex64": dt.complex64,
|
|
123
|
-
"complex128": dt.complex128,
|
|
124
|
-
}
|
|
125
|
-
if isinstance(kind, tuple):
|
|
52
|
+
if device is None:
|
|
53
|
+
device = CPU_DEVICE
|
|
54
|
+
if isinstance(kind, type(None) | str):
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
dtypes = _devices._kind_to_dtypes[kind]
|
|
58
|
+
except KeyError:
|
|
59
|
+
raise ValueError(f"unsupported kind: {kind!r}")
|
|
60
|
+
res = _devices._map_supported(dtypes, device)
|
|
61
|
+
return res
|
|
62
|
+
|
|
63
|
+
elif isinstance(kind, tuple):
|
|
126
64
|
res: DataTypes = {}
|
|
127
65
|
for k in kind:
|
|
128
|
-
res.update(self.dtypes(kind=k))
|
|
66
|
+
res.update(self.dtypes(kind=k, device=device))
|
|
129
67
|
return res
|
|
130
68
|
raise ValueError(f"unsupported kind: {kind!r}")
|
|
131
69
|
|
|
@@ -39,7 +39,10 @@ def cumulative_sum(
|
|
|
39
39
|
if include_initial:
|
|
40
40
|
if axis < 0:
|
|
41
41
|
axis += x.ndim
|
|
42
|
-
x = concat(
|
|
42
|
+
x = concat(
|
|
43
|
+
[zeros(x.shape[:axis] + (1,) + x.shape[axis + 1:], dtype=x.dtype, device=x.device), x],
|
|
44
|
+
axis=axis
|
|
45
|
+
)
|
|
43
46
|
return Array._new(np.cumsum(x._array, axis=axis, dtype=_np_dtype(dtype)), device=x.device)
|
|
44
47
|
|
|
45
48
|
|
|
@@ -66,7 +69,10 @@ def cumulative_prod(
|
|
|
66
69
|
if include_initial:
|
|
67
70
|
if axis < 0:
|
|
68
71
|
axis += x.ndim
|
|
69
|
-
x = concat(
|
|
72
|
+
x = concat(
|
|
73
|
+
[ones(x.shape[:axis] + (1,) + x.shape[axis + 1:], dtype=x.dtype, device=x.device), x],
|
|
74
|
+
axis=axis
|
|
75
|
+
)
|
|
70
76
|
return Array._new(np.cumprod(x._array, axis=axis, dtype=_np_dtype(dtype)), device=x.device)
|
|
71
77
|
|
|
72
78
|
|
array_api_strict/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.
|
|
32
|
-
__version_tuple__ = version_tuple = (2,
|
|
31
|
+
__version__ = version = '2.6'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 6)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -9,7 +9,8 @@ import numpy as np
|
|
|
9
9
|
import pytest
|
|
10
10
|
|
|
11
11
|
from .. import ones, arange, reshape, asarray, result_type, all, equal, stack
|
|
12
|
-
from .._array_object import Array
|
|
12
|
+
from .._array_object import Array
|
|
13
|
+
from .._devices import CPU_DEVICE, Device, DLDeviceType
|
|
13
14
|
from .._dtypes import (
|
|
14
15
|
_all_dtypes,
|
|
15
16
|
_boolean_dtypes,
|
|
@@ -220,6 +221,14 @@ def test_setitem_invalid_promotions():
|
|
|
220
221
|
a[0] = asarray(3.5j, dtype=complex128)
|
|
221
222
|
|
|
222
223
|
|
|
224
|
+
def test_setitem_device_transfer():
|
|
225
|
+
a = arange(3)
|
|
226
|
+
b = arange(4, 1, -1, device=Device('device1'))
|
|
227
|
+
|
|
228
|
+
with pytest.raises(ValueError):
|
|
229
|
+
a[:] = b[:]
|
|
230
|
+
|
|
231
|
+
|
|
223
232
|
def test_promoted_scalar_inherits_device():
|
|
224
233
|
device1 = Device("device1")
|
|
225
234
|
x = asarray([1., 2, 3], device=device1)
|
|
@@ -749,6 +758,40 @@ def test_dlpack_2023_12(api_version):
|
|
|
749
758
|
a.__dlpack__(copy=True)
|
|
750
759
|
a.__dlpack__(copy=None)
|
|
751
760
|
|
|
761
|
+
@pytest.mark.parametrize(
|
|
762
|
+
("device", "expected"),
|
|
763
|
+
[
|
|
764
|
+
(CPU_DEVICE, (DLDeviceType.kDLCPU, 0)),
|
|
765
|
+
(Device("device1"), (DLDeviceType.kDLCUDA, 0)),
|
|
766
|
+
(Device("device2"), (DLDeviceType.kDLCUDA, 1)),
|
|
767
|
+
],
|
|
768
|
+
)
|
|
769
|
+
def test_dlpack_device_numbers(device, expected):
|
|
770
|
+
a = asarray([1, 2, 3], device=device)
|
|
771
|
+
assert a.__dlpack_device__() == expected
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
@pytest.mark.parametrize("device", [CPU_DEVICE, Device("device1"), Device("device2")])
|
|
775
|
+
def test_dlpack_export_with_matching_dl_device(device):
|
|
776
|
+
if np.lib.NumpyVersion(np.__version__) < "2.1.0":
|
|
777
|
+
pytest.skip("dl_device argument requires NumPy >= 2.1.0")
|
|
778
|
+
set_array_api_strict_flags(api_version="2023.12")
|
|
779
|
+
|
|
780
|
+
a = asarray([1, 2, 3], device=device)
|
|
781
|
+
a.__dlpack__(dl_device=a.__dlpack_device__())
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
@pytest.mark.parametrize("device", [Device("device1"), Device("device2")])
|
|
785
|
+
def test_dlpack_cross_device_export_buffer_error(device):
|
|
786
|
+
if np.lib.NumpyVersion(np.__version__) < "2.1.0":
|
|
787
|
+
pytest.skip("dl_device argument requires NumPy >= 2.1.0")
|
|
788
|
+
set_array_api_strict_flags(api_version="2023.12")
|
|
789
|
+
|
|
790
|
+
a = asarray([1, 2, 3], device=device)
|
|
791
|
+
with pytest.raises(BufferError):
|
|
792
|
+
a.__dlpack__(dl_device=(DLDeviceType.kDLCPU, 0), copy=False)
|
|
793
|
+
|
|
794
|
+
|
|
752
795
|
def test_pickle():
|
|
753
796
|
"""Check that arrays are pickleable (despite raising on `__new__`)"""
|
|
754
797
|
a = ones(2)
|
|
@@ -22,8 +22,10 @@ from .._creation_functions import (
|
|
|
22
22
|
zeros,
|
|
23
23
|
zeros_like,
|
|
24
24
|
)
|
|
25
|
-
from .._dtypes import float32, float64
|
|
26
|
-
from .._array_object import Array
|
|
25
|
+
from .._dtypes import float32, float64, complex64, bool as xp_bool
|
|
26
|
+
from .._array_object import Array
|
|
27
|
+
from .._devices import CPU_DEVICE, ALL_DEVICES, Device
|
|
28
|
+
from .._info import __array_namespace_info__
|
|
27
29
|
from .._flags import set_array_api_strict_flags
|
|
28
30
|
|
|
29
31
|
def test_asarray_errors():
|
|
@@ -212,6 +214,7 @@ def test_zeros_like_errors():
|
|
|
212
214
|
assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype=int))
|
|
213
215
|
assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype="i"))
|
|
214
216
|
|
|
217
|
+
|
|
215
218
|
def test_meshgrid_dtype_errors():
|
|
216
219
|
# Doesn't raise
|
|
217
220
|
meshgrid()
|
|
@@ -221,6 +224,150 @@ def test_meshgrid_dtype_errors():
|
|
|
221
224
|
assert_raises(ValueError, lambda: meshgrid(asarray([1.], dtype=float32), asarray([1.], dtype=float64)))
|
|
222
225
|
|
|
223
226
|
|
|
227
|
+
|
|
228
|
+
def _full(a, *args, **kwds):
|
|
229
|
+
return full(a, fill_value=42.0, *args, **kwds)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _full_like(a, *args, **kwds):
|
|
233
|
+
return full_like(a, fill_value=42.0, *args, **kwds)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class TestDefaultDType:
|
|
237
|
+
|
|
238
|
+
info = __array_namespace_info__()
|
|
239
|
+
|
|
240
|
+
@pytest.mark.parametrize("device", ALL_DEVICES)
|
|
241
|
+
@pytest.mark.parametrize("func", [empty, zeros, ones, _full])
|
|
242
|
+
def test_ones_etc(self, func, device):
|
|
243
|
+
a = func(1, device=device)
|
|
244
|
+
assert a.dtype == self.info.default_dtypes(device=device)["real floating"]
|
|
245
|
+
|
|
246
|
+
@pytest.mark.parametrize("func", [empty_like, zeros_like, ones_like, _full_like])
|
|
247
|
+
def test_ones_like_etc_correct(self, func):
|
|
248
|
+
# float32 is preserved
|
|
249
|
+
a = ones(2, dtype=float32)
|
|
250
|
+
device = Device('no_float64')
|
|
251
|
+
b = func(a, device=device)
|
|
252
|
+
assert b.dtype == self.info.default_dtypes(device=device)["real floating"]
|
|
253
|
+
assert b.device == device
|
|
254
|
+
|
|
255
|
+
@pytest.mark.parametrize("func", [empty_like, zeros_like, ones_like, _full_like])
|
|
256
|
+
def test_ones_like_etc_incorrect(self, func):
|
|
257
|
+
a = ones(2)
|
|
258
|
+
assert a.dtype == float64
|
|
259
|
+
assert a.device == Device()
|
|
260
|
+
|
|
261
|
+
# XXX: a.dtype not supported by the device: ValueError or TypeError?
|
|
262
|
+
|
|
263
|
+
# >>> a = torch.ones(3, dtype=torch.float64, device='cpu')
|
|
264
|
+
# >>> torch.ones_like(a, device='mps')
|
|
265
|
+
# TypeError: Cannot convert a MPS Tensor to float64 dtype as the MPS framework
|
|
266
|
+
# doesn't support float64.
|
|
267
|
+
|
|
268
|
+
# incompatible dtype inferred from `a.dtype`
|
|
269
|
+
with pytest.raises((TypeError, ValueError)):
|
|
270
|
+
func(a, device=Device('no_float64'))
|
|
271
|
+
|
|
272
|
+
# `a.dtype` is compatible but the explicit dtype= argument is incompatible
|
|
273
|
+
a = ones(2, dtype=float32)
|
|
274
|
+
with pytest.raises((TypeError, ValueError)):
|
|
275
|
+
func(a, device=Device('no_float64'), dtype=float64)
|
|
276
|
+
|
|
277
|
+
def test_eye(self):
|
|
278
|
+
device = Device('no_float64')
|
|
279
|
+
a = eye(3, device=device)
|
|
280
|
+
assert a.dtype == self.info.default_dtypes(device=device)["real floating"]
|
|
281
|
+
assert a.device == device
|
|
282
|
+
|
|
283
|
+
with pytest.raises((TypeError, ValueError)):
|
|
284
|
+
eye(3, device=device, dtype=float64)
|
|
285
|
+
|
|
286
|
+
def test_linspace(self):
|
|
287
|
+
device = Device('no_float64')
|
|
288
|
+
|
|
289
|
+
a = linspace(1, 10, 11, device=device)
|
|
290
|
+
assert a.dtype == self.info.default_dtypes(device=device)["real floating"]
|
|
291
|
+
assert a.device == device
|
|
292
|
+
|
|
293
|
+
a = linspace(1+0j, 10, 11, device=device)
|
|
294
|
+
assert a.dtype == self.info.default_dtypes(device=device)["complex floating"]
|
|
295
|
+
|
|
296
|
+
with pytest.raises((TypeError, ValueError)):
|
|
297
|
+
linspace(1, 10, 11, device=device, dtype=float64)
|
|
298
|
+
|
|
299
|
+
def test_arange(self):
|
|
300
|
+
device = Device('no_float64')
|
|
301
|
+
|
|
302
|
+
a = arange(0, 10, 1, device=device)
|
|
303
|
+
assert a.dtype == self.info.default_dtypes(device=device)["integral"]
|
|
304
|
+
assert a.device == device
|
|
305
|
+
|
|
306
|
+
a = arange(0.0, 10, 1, device=device)
|
|
307
|
+
assert a.dtype == self.info.default_dtypes(device=device)["real floating"]
|
|
308
|
+
assert a.device == device
|
|
309
|
+
|
|
310
|
+
with pytest.raises((TypeError, ValueError)):
|
|
311
|
+
arange(0, 10, 1, device=device, dtype=float64)
|
|
312
|
+
|
|
313
|
+
with pytest.raises((TypeError, ValueError)):
|
|
314
|
+
arange(0.0, 10, 1, device=device, dtype=float64)
|
|
315
|
+
|
|
316
|
+
def test_asarray(self):
|
|
317
|
+
device = Device('no_float64')
|
|
318
|
+
|
|
319
|
+
### asarray(python_object)
|
|
320
|
+
for x in (True, [False,]):
|
|
321
|
+
arr = asarray(x, device=device)
|
|
322
|
+
assert arr.dtype == xp_bool
|
|
323
|
+
assert arr.device == device
|
|
324
|
+
|
|
325
|
+
for x in [1, [1,]]:
|
|
326
|
+
arr = asarray(x, device=device)
|
|
327
|
+
assert arr.dtype == self.info.default_dtypes(device=device)['integral']
|
|
328
|
+
assert arr.device == device
|
|
329
|
+
|
|
330
|
+
for x in [1.0, [1.0,]]:
|
|
331
|
+
arr = asarray(x, device=device)
|
|
332
|
+
assert arr.dtype == self.info.default_dtypes(device=device)['real floating']
|
|
333
|
+
assert arr.device == device
|
|
334
|
+
|
|
335
|
+
for x in [1j, [1j,]]:
|
|
336
|
+
arr = asarray(x, device=device)
|
|
337
|
+
assert arr.dtype == self.info.default_dtypes(device=device)['complex floating']
|
|
338
|
+
assert arr.device == device
|
|
339
|
+
|
|
340
|
+
# asarray(python_object, dtype=unsupported_by_device)
|
|
341
|
+
with pytest.raises(ValueError, match="Device"):
|
|
342
|
+
asarray(1, dtype=float64, device=device)
|
|
343
|
+
|
|
344
|
+
### asarray(array)
|
|
345
|
+
|
|
346
|
+
# compatible dtypes, device transfer
|
|
347
|
+
src = asarray(1, dtype=float32, device=Device('device1'))
|
|
348
|
+
dst = asarray(src, device=device)
|
|
349
|
+
assert dst.device == device
|
|
350
|
+
assert dst.dtype == float32
|
|
351
|
+
|
|
352
|
+
# incompatible dtypes, device transfer
|
|
353
|
+
src = asarray(1, dtype=float64, device=Device('device1'))
|
|
354
|
+
|
|
355
|
+
with pytest.raises(ValueError, match="Device"):
|
|
356
|
+
asarray(src, device=device)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def test_asarray_device_2():
|
|
360
|
+
# device2 allows float64 but defaults to float32
|
|
361
|
+
x = asarray([1.0], device=Device('device2'))
|
|
362
|
+
assert x.dtype == float32
|
|
363
|
+
|
|
364
|
+
x = asarray([1j], device=Device('device2'))
|
|
365
|
+
assert x.dtype == complex64
|
|
366
|
+
|
|
367
|
+
y = asarray([1.0], device=Device('device2'), dtype=float64)
|
|
368
|
+
assert y.dtype == float64
|
|
369
|
+
|
|
370
|
+
|
|
224
371
|
@pytest.mark.parametrize("api_version", ['2021.12', '2022.12', '2023.12'])
|
|
225
372
|
def from_dlpack_2023_12(api_version):
|
|
226
373
|
if api_version != '2022.12':
|
|
@@ -247,3 +394,13 @@ def test_from_dlpack_default_device():
|
|
|
247
394
|
y = from_dlpack(x)
|
|
248
395
|
z = from_dlpack(np.asarray([1, 2, 3]))
|
|
249
396
|
assert x.device == y.device == z.device == CPU_DEVICE
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
@pytest.mark.parametrize(
|
|
400
|
+
"device",
|
|
401
|
+
[Device("device1"), Device("device2")],
|
|
402
|
+
)
|
|
403
|
+
def test_from_dlpack_preserves_device(device):
|
|
404
|
+
x = asarray([1, 2, 3], device=device)
|
|
405
|
+
y = from_dlpack(x)
|
|
406
|
+
assert y.device == device
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
-
import array_api_strict
|
|
3
|
+
import array_api_strict as xp
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
@pytest.mark.parametrize(
|
|
@@ -18,11 +18,11 @@ import array_api_strict
|
|
|
18
18
|
),
|
|
19
19
|
)
|
|
20
20
|
def test_fft_device_support_complex(func_name):
|
|
21
|
-
func = getattr(
|
|
22
|
-
x =
|
|
21
|
+
func = getattr(xp.fft, func_name)
|
|
22
|
+
x = xp.asarray(
|
|
23
23
|
[1, 2.0],
|
|
24
|
-
dtype=
|
|
25
|
-
device=
|
|
24
|
+
dtype=xp.complex64,
|
|
25
|
+
device=xp.Device("device1"),
|
|
26
26
|
)
|
|
27
27
|
y = func(x)
|
|
28
28
|
|
|
@@ -31,8 +31,50 @@ def test_fft_device_support_complex(func_name):
|
|
|
31
31
|
|
|
32
32
|
@pytest.mark.parametrize("func_name", ("rfft", "rfftn", "ihfft"))
|
|
33
33
|
def test_fft_device_support_real(func_name):
|
|
34
|
-
func = getattr(
|
|
35
|
-
x =
|
|
34
|
+
func = getattr(xp.fft, func_name)
|
|
35
|
+
x = xp.asarray([1, 2.0], device=xp.Device("device1"))
|
|
36
36
|
y = func(x)
|
|
37
37
|
|
|
38
38
|
assert x.device == y.device
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.mark.parametrize("func_name", ("fftfreq", "rfftfreq"))
|
|
42
|
+
def test_fft_default_dtype(func_name):
|
|
43
|
+
func = getattr(xp.fft, func_name)
|
|
44
|
+
device = xp.Device("no_float64")
|
|
45
|
+
res = func(3, device=device)
|
|
46
|
+
assert res.device == device
|
|
47
|
+
assert res.dtype == xp.__array_namespace_info__().default_dtypes(device=device)["real floating"]
|
|
48
|
+
|
|
49
|
+
with pytest.raises(ValueError):
|
|
50
|
+
func(3, device=device, dtype=xp.float64)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TestF32Device:
|
|
54
|
+
@pytest.mark.parametrize("dtype_str", ["float64", "complex128"])
|
|
55
|
+
def test_f64_raises(self, dtype_str):
|
|
56
|
+
f32_only_device = xp.Device("no_float64")
|
|
57
|
+
dtype = getattr(xp, dtype_str)
|
|
58
|
+
with pytest.raises(ValueError):
|
|
59
|
+
xp.arange(3, device=f32_only_device, dtype=dtype)
|
|
60
|
+
|
|
61
|
+
def test_info_no_f64(self):
|
|
62
|
+
f32_only_device = xp.Device("no_float64")
|
|
63
|
+
|
|
64
|
+
info = xp.__array_namespace_info__()
|
|
65
|
+
all_dtypes = info.dtypes(device=f32_only_device)
|
|
66
|
+
assert "float64" not in all_dtypes
|
|
67
|
+
assert "complex128" not in all_dtypes
|
|
68
|
+
|
|
69
|
+
def test_info_default_dtypes(self):
|
|
70
|
+
f32_only_device = xp.Device("no_float64")
|
|
71
|
+
info = xp.__array_namespace_info__()
|
|
72
|
+
defaults = info.default_dtypes(device=f32_only_device)
|
|
73
|
+
assert defaults["real floating"] == xp.float32
|
|
74
|
+
assert defaults["complex floating"] == xp.complex64
|
|
75
|
+
|
|
76
|
+
cpu_device = xp.Device()
|
|
77
|
+
info = xp.__array_namespace_info__()
|
|
78
|
+
defaults = info.default_dtypes(device=cpu_device)
|
|
79
|
+
assert defaults["real floating"] == xp.float64
|
|
80
|
+
assert defaults["complex floating"] == xp.complex128
|
|
@@ -6,7 +6,7 @@ import pytest
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
from .. import asarray, _elementwise_functions
|
|
9
|
-
from ..
|
|
9
|
+
from .._devices import ALL_DEVICES, CPU_DEVICE, Device
|
|
10
10
|
from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift
|
|
11
11
|
from .._dtypes import (
|
|
12
12
|
_dtype_categories,
|
|
@@ -21,6 +21,7 @@ from .._dtypes import (
|
|
|
21
21
|
int64,
|
|
22
22
|
uint64,
|
|
23
23
|
)
|
|
24
|
+
from .._info import __array_namespace_info__
|
|
24
25
|
from .test_array_object import _check_op_array_scalar, BIG_INT
|
|
25
26
|
|
|
26
27
|
import array_api_strict
|
|
@@ -144,6 +145,10 @@ def test_elementwise_function_device_persists(func_name, types, device):
|
|
|
144
145
|
yield asarray(1., dtype=dtype, device=device)
|
|
145
146
|
|
|
146
147
|
dtypes = _dtype_categories[types]
|
|
148
|
+
|
|
149
|
+
supported_dtypes = __array_namespace_info__().dtypes(device=device)
|
|
150
|
+
dtypes = [dt for dt in dtypes if dt in supported_dtypes]
|
|
151
|
+
|
|
147
152
|
func = getattr(_elementwise_functions, func_name)
|
|
148
153
|
|
|
149
154
|
for x in _array_vals(dtypes):
|
|
@@ -55,3 +55,19 @@ def test_mean_complex():
|
|
|
55
55
|
with pytest.raises(TypeError):
|
|
56
56
|
xp.mean(xp.arange(3))
|
|
57
57
|
|
|
58
|
+
|
|
59
|
+
def test_cumsum_device():
|
|
60
|
+
x = xp.arange(3, device=xp.Device('device1'))
|
|
61
|
+
y = xp.cumulative_sum(x, include_initial=True)
|
|
62
|
+
expected = xp.asarray([0, 0, 1, 3], device=x.device)
|
|
63
|
+
assert y.device == expected.device
|
|
64
|
+
assert xp.all(y == expected)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_cumprod_device():
|
|
68
|
+
x = xp.arange(1, 4, device=xp.Device('device1'))
|
|
69
|
+
y = xp.cumulative_prod(x, include_initial=True)
|
|
70
|
+
expected = xp.asarray([1, 1, 2, 6], device=x.device)
|
|
71
|
+
assert y.device == expected.device
|
|
72
|
+
assert xp.all(y == expected)
|
|
73
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: array_api_strict
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6
|
|
4
4
|
Summary: A strict, minimal implementation of the Python array API standard.
|
|
5
5
|
Author: Consortium for Python Data API Standards
|
|
6
6
|
License: Copyright (c) 2021-2024, NumPy Developers, Consortium for Python Data API Standards
|
|
@@ -1,44 +1,45 @@
|
|
|
1
1
|
array_api_strict/__init__.py,sha256=kexXRJ7WHgMTeMi80H6XiObC3ePtUEr5gf92dnwyPGE,7173
|
|
2
|
-
array_api_strict/_array_object.py,sha256=
|
|
2
|
+
array_api_strict/_array_object.py,sha256=wOCTJl6JaeOqbS1aoH0wbFlfx1vqR6Jzu0U-5w9-Ncw,54938
|
|
3
3
|
array_api_strict/_constants.py,sha256=ilkAGjIceZxT4zPywIoWbl3Xv8k19UOI4SSF6EPegpg,93
|
|
4
|
-
array_api_strict/_creation_functions.py,sha256=
|
|
4
|
+
array_api_strict/_creation_functions.py,sha256=1M6WYyTbVHfiFaTYkCZPG-j-dmt6LdifnM9Y4GD_TSo,15100
|
|
5
5
|
array_api_strict/_data_type_functions.py,sha256=5OPnsIj4QafrSlnizUZcO4DHO5u0ap9OyDNovV4qrns,8765
|
|
6
|
-
array_api_strict/
|
|
6
|
+
array_api_strict/_devices.py,sha256=k3QdhG2NKGw5DfHAZOvkJ-d_ft1Jn7083D4dXoeETwU,4225
|
|
7
|
+
array_api_strict/_dtypes.py,sha256=ejImxjVLg7-jSKrdKqGDCXZ8oHYKGhdl68WiP9uaIho,6688
|
|
7
8
|
array_api_strict/_elementwise_functions.py,sha256=D3jOalyR5-kDSzeGTZLKnGPVwjKBuPihkwn23YSxcUc,13198
|
|
8
|
-
array_api_strict/_fft.py,sha256=
|
|
9
|
+
array_api_strict/_fft.py,sha256=Ut1IgT6lIbpC2g_j2eFejLvW9NpctKuDwdOOJ5luNFE,10971
|
|
9
10
|
array_api_strict/_flags.py,sha256=JaaGahMa-yQjz9fq2E_cFQvhh5B5rVJKms9pMcwdKAg,14758
|
|
10
11
|
array_api_strict/_helpers.py,sha256=St8M41s5pdhdPdn1-BcdZCrNtRID4X9z5F28fBD3GNg,1915
|
|
11
12
|
array_api_strict/_indexing_functions.py,sha256=BHPtoyRUIoEMe28MkwUrHRSz5X71i8w_WkTuWQljrpI,1398
|
|
12
|
-
array_api_strict/_info.py,sha256=
|
|
13
|
+
array_api_strict/_info.py,sha256=U0EgZiPbksc0mcZTALlDx5AnKKUTChc9aR1CQOqFXAg,2551
|
|
13
14
|
array_api_strict/_linalg.py,sha256=HQjpkVPB0R59mOQBR4cqlZTUKOQTEjGEXbkwk3KNa2Y,21495
|
|
14
15
|
array_api_strict/_linear_algebra_functions.py,sha256=j8fSBharI31ugZHsUvm74Nv3SHxRrxqtmouK3AOH038,3800
|
|
15
16
|
array_api_strict/_manipulation_functions.py,sha256=KR0O7bMulglaX0DoqEWpiNK3V25D12mFdsqeq-vm8mg,6935
|
|
16
17
|
array_api_strict/_searching_functions.py,sha256=X7pT_kuGBpJltpo4MvVYiZdnKr7jgnH1DNtHiHTKdgw,4263
|
|
17
18
|
array_api_strict/_set_functions.py,sha256=yTKA2OqueeKwk8tp_XcpcsWliL02kgzT3NBzot5zuQ8,4196
|
|
18
19
|
array_api_strict/_sorting_functions.py,sha256=kw_ns3NajcA8ekpHikNhZxvVePXa3gjdZ7oTwFnBOmk,2074
|
|
19
|
-
array_api_strict/_statistical_functions.py,sha256=
|
|
20
|
+
array_api_strict/_statistical_functions.py,sha256=MVjoxIvy3eWfWtNXFq6VFMg-Ygmnn9_eixEa8z_EUrc,5744
|
|
20
21
|
array_api_strict/_typing.py,sha256=FVM2m6ZTZYDnznCo0SlMRpcXxGR49y6k4ejNEiuGUtc,1486
|
|
21
22
|
array_api_strict/_utility_functions.py,sha256=L7FJM-RT90gqkAqvMEfQ6FkzurTziHByROk_5R8a5fY,1938
|
|
22
|
-
array_api_strict/_version.py,sha256=
|
|
23
|
+
array_api_strict/_version.py,sha256=3bHZCrpqHyLVEDhNiGUyfzQKJwposHttYzrlwruXOpM,699
|
|
23
24
|
array_api_strict/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
25
|
array_api_strict/tests/__init__.py,sha256=t_2GZ3lKcsu4ec4GMKPUDYaeMUJyDquBlQAcPgj7kFE,282
|
|
25
26
|
array_api_strict/tests/conftest.py,sha256=-wIDryl2MdRLdSonU1G1xWssNq2TFKiWuHquNasXYA8,468
|
|
26
|
-
array_api_strict/tests/test_array_object.py,sha256=
|
|
27
|
-
array_api_strict/tests/test_creation_functions.py,sha256=
|
|
27
|
+
array_api_strict/tests/test_array_object.py,sha256=65VpWqlYnEgfiNJRM2v2grYwK-iEK6rAAju3pPmQGUY,30624
|
|
28
|
+
array_api_strict/tests/test_creation_functions.py,sha256=g4WXz-L5zYKGTG658oqpWtINW9UoSyaeD5IZLuq3vWk,14088
|
|
28
29
|
array_api_strict/tests/test_data_type_functions.py,sha256=6V7YeJavgsBmKtR1gvY1GTTlKzGyzPAHki1WtKFeVdU,4098
|
|
29
|
-
array_api_strict/tests/test_device_support.py,sha256=
|
|
30
|
-
array_api_strict/tests/test_elementwise_functions.py,sha256=
|
|
30
|
+
array_api_strict/tests/test_device_support.py,sha256=f3_OyYRMX9PPVQOOPsti9q5qgBGNyG86QxvgWdF_KPg,2361
|
|
31
|
+
array_api_strict/tests/test_elementwise_functions.py,sha256=4zzXA7b5NmZnWV4aKxsVjJWdLEZV8X9Zw1UFaKMeEII,11571
|
|
31
32
|
array_api_strict/tests/test_flags.py,sha256=g7Tajftvmc2ih0a6U57FxcpmleEiDoMgCvzUt2S7MF4,18982
|
|
32
33
|
array_api_strict/tests/test_indexing_functions.py,sha256=C23FnuowVDC1OBuDH06pvsd61qM2dTpTwzw6UgC4BoY,1281
|
|
33
34
|
array_api_strict/tests/test_linalg.py,sha256=CJ4JnCkWyysMUAih3PI-ZZeOH-X39H0p-RKBoEwk6Jw,5675
|
|
34
35
|
array_api_strict/tests/test_manipulation_functions.py,sha256=Yc92IvA3FaTZueqAUOFMkx3MH4PXCHDof7ckrJxudQs,1078
|
|
35
|
-
array_api_strict/tests/test_searching_functions.py,sha256=
|
|
36
|
+
array_api_strict/tests/test_searching_functions.py,sha256=sTOvPUrmzOKbJZDTm_fMecvPpkVJObwZOg-454-j7Cw,3436
|
|
36
37
|
array_api_strict/tests/test_set_functions.py,sha256=ikzab1j3SXxGf0OqwHrOAM13HwQetdhtB60Zc_gc1DE,542
|
|
37
38
|
array_api_strict/tests/test_sorting_functions.py,sha256=1xvQBKts5KTKYc-puMITX9iR_KTbzzVB8fY-SObad_Y,899
|
|
38
|
-
array_api_strict/tests/test_statistical_functions.py,sha256=
|
|
39
|
+
array_api_strict/tests/test_statistical_functions.py,sha256=c542tcGuxUQvjd8ML1U8I07UFuOhba19804-_-IC2Jc,2335
|
|
39
40
|
array_api_strict/tests/test_validation.py,sha256=VDci4SgcAhxzSFVnFYBvJWXEsWRxHARnq9TwTfH02xg,635
|
|
40
|
-
array_api_strict-2.
|
|
41
|
-
array_api_strict-2.
|
|
42
|
-
array_api_strict-2.
|
|
43
|
-
array_api_strict-2.
|
|
44
|
-
array_api_strict-2.
|
|
41
|
+
array_api_strict-2.6.dist-info/LICENSE,sha256=djDm4UfHAJmycwFUNyUmrTdRkrFF9Ac8uqIlbxH7j5Y,1585
|
|
42
|
+
array_api_strict-2.6.dist-info/METADATA,sha256=pwiqnybdKy12HJOL3jhV4oH_lbO66EvsekvSaeCCW8E,3613
|
|
43
|
+
array_api_strict-2.6.dist-info/WHEEL,sha256=5Mi1sN9lKoFv_gxcPtisEVrJZihrm_beibeg5R6xb4I,91
|
|
44
|
+
array_api_strict-2.6.dist-info/top_level.txt,sha256=M4cE8VLgRHp_d9TPkbk7_I4JriELFdEJz-NKJJqAkAE,17
|
|
45
|
+
array_api_strict-2.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|