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
array_api_strict/_linalg.py
CHANGED
|
@@ -1,33 +1,23 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from collections.abc import Sequence
|
|
3
2
|
from functools import partial
|
|
3
|
+
from typing import Literal, NamedTuple
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import numpy.linalg
|
|
4
7
|
|
|
5
|
-
from .
|
|
6
|
-
_floating_dtypes,
|
|
7
|
-
_numeric_dtypes,
|
|
8
|
-
float32,
|
|
9
|
-
complex64,
|
|
10
|
-
complex128,
|
|
11
|
-
)
|
|
8
|
+
from ._array_object import Array
|
|
12
9
|
from ._data_type_functions import finfo
|
|
13
|
-
from .
|
|
10
|
+
from ._dtypes import DType, _floating_dtypes, _numeric_dtypes, complex64, complex128
|
|
14
11
|
from ._elementwise_functions import conj
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
12
|
+
from ._flags import get_array_api_strict_flags, requires_extension
|
|
13
|
+
from ._manipulation_functions import reshape
|
|
14
|
+
from ._statistical_functions import _np_dtype_sumprod
|
|
17
15
|
|
|
18
16
|
try:
|
|
19
|
-
from numpy._core.numeric import normalize_axis_tuple
|
|
17
|
+
from numpy._core.numeric import normalize_axis_tuple # type: ignore[attr-defined]
|
|
20
18
|
except ImportError:
|
|
21
|
-
from numpy.core.numeric import normalize_axis_tuple
|
|
22
|
-
|
|
23
|
-
from typing import TYPE_CHECKING
|
|
24
|
-
if TYPE_CHECKING:
|
|
25
|
-
from ._typing import Literal, Optional, Sequence, Tuple, Union, Dtype
|
|
19
|
+
from numpy.core.numeric import normalize_axis_tuple # type: ignore[no-redef]
|
|
26
20
|
|
|
27
|
-
from typing import NamedTuple
|
|
28
|
-
|
|
29
|
-
import numpy.linalg
|
|
30
|
-
import numpy as np
|
|
31
21
|
|
|
32
22
|
class EighResult(NamedTuple):
|
|
33
23
|
eigenvalues: Array
|
|
@@ -175,7 +165,13 @@ def inv(x: Array, /) -> Array:
|
|
|
175
165
|
# -np.inf, 'fro', 'nuc']]], but Literal does not support floating-point
|
|
176
166
|
# literals.
|
|
177
167
|
@requires_extension('linalg')
|
|
178
|
-
def matrix_norm(
|
|
168
|
+
def matrix_norm(
|
|
169
|
+
x: Array,
|
|
170
|
+
/,
|
|
171
|
+
*,
|
|
172
|
+
keepdims: bool = False,
|
|
173
|
+
ord: float | Literal["fro", "nuc"] | None = "fro",
|
|
174
|
+
) -> Array: # noqa: F821
|
|
179
175
|
"""
|
|
180
176
|
Array API compatible wrapper for :py:func:`np.linalg.norm <numpy.linalg.norm>`.
|
|
181
177
|
|
|
@@ -186,7 +182,10 @@ def matrix_norm(x: Array, /, *, keepdims: bool = False, ord: Optional[Union[int,
|
|
|
186
182
|
if x.dtype not in _floating_dtypes:
|
|
187
183
|
raise TypeError('Only floating-point dtypes are allowed in matrix_norm')
|
|
188
184
|
|
|
189
|
-
return Array._new(
|
|
185
|
+
return Array._new(
|
|
186
|
+
np.linalg.norm(x._array, axis=(-2, -1), keepdims=keepdims, ord=ord),
|
|
187
|
+
device=x.device,
|
|
188
|
+
)
|
|
190
189
|
|
|
191
190
|
|
|
192
191
|
@requires_extension('linalg')
|
|
@@ -206,7 +205,7 @@ def matrix_power(x: Array, n: int, /) -> Array:
|
|
|
206
205
|
|
|
207
206
|
# Note: the keyword argument name rtol is different from np.linalg.matrix_rank
|
|
208
207
|
@requires_extension('linalg')
|
|
209
|
-
def matrix_rank(x: Array, /, *, rtol:
|
|
208
|
+
def matrix_rank(x: Array, /, *, rtol: float | Array | None = None) -> Array:
|
|
210
209
|
"""
|
|
211
210
|
Array API compatible wrapper for :py:func:`np.matrix_rank <numpy.matrix_rank>`.
|
|
212
211
|
|
|
@@ -218,13 +217,12 @@ def matrix_rank(x: Array, /, *, rtol: Optional[Union[float, Array]] = None) -> A
|
|
|
218
217
|
raise np.linalg.LinAlgError("1-dimensional array given. Array must be at least two-dimensional")
|
|
219
218
|
S = np.linalg.svd(x._array, compute_uv=False)
|
|
220
219
|
if rtol is None:
|
|
221
|
-
tol = S.max(axis=-1, keepdims=True) * max(x.shape[-2:]) * finfo(S.dtype).eps
|
|
220
|
+
tol = S.max(axis=-1, keepdims=True) * max(x.shape[-2:]) * np.finfo(S.dtype).eps
|
|
222
221
|
else:
|
|
223
|
-
if isinstance(rtol, Array)
|
|
224
|
-
rtol = rtol._array
|
|
222
|
+
rtol_np = rtol._array if isinstance(rtol, Array) else np.asarray(rtol)
|
|
225
223
|
# Note: this is different from np.linalg.matrix_rank, which does not multiply
|
|
226
224
|
# the tolerance by the largest singular value.
|
|
227
|
-
tol = S.max(axis=-1, keepdims=True)*
|
|
225
|
+
tol = S.max(axis=-1, keepdims=True) * rtol_np[..., np.newaxis]
|
|
228
226
|
return Array._new(np.count_nonzero(S > tol, axis=-1), device=x.device)
|
|
229
227
|
|
|
230
228
|
|
|
@@ -252,7 +250,7 @@ def outer(x1: Array, x2: Array, /) -> Array:
|
|
|
252
250
|
|
|
253
251
|
# Note: the keyword argument name rtol is different from np.linalg.pinv
|
|
254
252
|
@requires_extension('linalg')
|
|
255
|
-
def pinv(x: Array, /, *, rtol:
|
|
253
|
+
def pinv(x: Array, /, *, rtol: float | Array | None = None) -> Array:
|
|
256
254
|
"""
|
|
257
255
|
Array API compatible wrapper for :py:func:`np.linalg.pinv <numpy.linalg.pinv>`.
|
|
258
256
|
|
|
@@ -267,9 +265,8 @@ def pinv(x: Array, /, *, rtol: Optional[Union[float, Array]] = None) -> Array:
|
|
|
267
265
|
# default tolerance by max(M, N).
|
|
268
266
|
if rtol is None:
|
|
269
267
|
rtol = max(x.shape[-2:]) * finfo(x.dtype).eps
|
|
270
|
-
if isinstance(rtol, Array)
|
|
271
|
-
|
|
272
|
-
return Array._new(np.linalg.pinv(x._array, rcond=rtol), device=x.device)
|
|
268
|
+
rtol_np = rtol._array if isinstance(rtol, Array) else rtol
|
|
269
|
+
return Array._new(np.linalg.pinv(x._array, rcond=rtol_np), device=x.device)
|
|
273
270
|
|
|
274
271
|
@requires_extension('linalg')
|
|
275
272
|
def qr(x: Array, /, *, mode: Literal['reduced', 'complete'] = 'reduced') -> QRResult: # noqa: F821
|
|
@@ -312,14 +309,14 @@ def slogdet(x: Array, /) -> SlogdetResult:
|
|
|
312
309
|
|
|
313
310
|
# To workaround this, the below is the code from np.linalg.solve except
|
|
314
311
|
# only calling solve1 in the exactly 1D case.
|
|
315
|
-
def _solve(a, b):
|
|
312
|
+
def _solve(a: np.ndarray, b: np.ndarray) -> np.ndarray:
|
|
316
313
|
try:
|
|
317
|
-
from numpy.linalg._linalg import (
|
|
314
|
+
from numpy.linalg._linalg import ( # type: ignore[attr-defined]
|
|
318
315
|
_makearray, _assert_stacked_2d, _assert_stacked_square,
|
|
319
316
|
_commonType, isComplexType, _raise_linalgerror_singular
|
|
320
317
|
)
|
|
321
318
|
except ImportError:
|
|
322
|
-
from numpy.linalg.linalg import (
|
|
319
|
+
from numpy.linalg.linalg import ( # type: ignore[attr-defined]
|
|
323
320
|
_makearray, _assert_stacked_2d, _assert_stacked_square,
|
|
324
321
|
_commonType, isComplexType, _raise_linalgerror_singular
|
|
325
322
|
)
|
|
@@ -382,14 +379,14 @@ def svd(x: Array, /, *, full_matrices: bool = True) -> SVDResult:
|
|
|
382
379
|
# Note: svdvals is not in NumPy (but it is in SciPy). It is equivalent to
|
|
383
380
|
# np.linalg.svd(compute_uv=False).
|
|
384
381
|
@requires_extension('linalg')
|
|
385
|
-
def svdvals(x: Array, /) ->
|
|
382
|
+
def svdvals(x: Array, /) -> Array:
|
|
386
383
|
if x.dtype not in _floating_dtypes:
|
|
387
384
|
raise TypeError('Only floating-point dtypes are allowed in svdvals')
|
|
388
385
|
return Array._new(np.linalg.svd(x._array, compute_uv=False), device=x.device)
|
|
389
386
|
|
|
390
387
|
# Note: trace is the numpy top-level namespace, not np.linalg
|
|
391
388
|
@requires_extension('linalg')
|
|
392
|
-
def trace(x: Array, /, *, offset: int = 0, dtype:
|
|
389
|
+
def trace(x: Array, /, *, offset: int = 0, dtype: DType | None = None) -> Array:
|
|
393
390
|
"""
|
|
394
391
|
Array API compatible wrapper for :py:func:`np.trace <numpy.trace>`.
|
|
395
392
|
|
|
@@ -398,19 +395,13 @@ def trace(x: Array, /, *, offset: int = 0, dtype: Optional[Dtype] = None) -> Arr
|
|
|
398
395
|
if x.dtype not in _numeric_dtypes:
|
|
399
396
|
raise TypeError('Only numeric dtypes are allowed in trace')
|
|
400
397
|
|
|
401
|
-
# Note: trace() works the same as sum() and prod() (see
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if get_array_api_strict_flags()['api_version'] < '2023.12':
|
|
405
|
-
if x.dtype == float32:
|
|
406
|
-
dtype = np.float64
|
|
407
|
-
elif x.dtype == complex64:
|
|
408
|
-
dtype = np.complex128
|
|
409
|
-
else:
|
|
410
|
-
dtype = dtype._np_dtype
|
|
398
|
+
# Note: trace() works the same as sum() and prod() (see _statistical_functions.py)
|
|
399
|
+
np_dtype = _np_dtype_sumprod(x, dtype)
|
|
400
|
+
|
|
411
401
|
# Note: trace always operates on the last two axes, whereas np.trace
|
|
412
402
|
# operates on the first two axes by default
|
|
413
|
-
|
|
403
|
+
res = np.trace(x._array, offset=offset, axis1=-2, axis2=-1, dtype=np_dtype)
|
|
404
|
+
return Array._new(np.asarray(res), device=x.device)
|
|
414
405
|
|
|
415
406
|
# Note: the name here is different from norm(). The array API norm is split
|
|
416
407
|
# into matrix_norm and vector_norm().
|
|
@@ -418,7 +409,14 @@ def trace(x: Array, /, *, offset: int = 0, dtype: Optional[Dtype] = None) -> Arr
|
|
|
418
409
|
# The type for ord should be Optional[Union[int, float, Literal[np.inf,
|
|
419
410
|
# -np.inf]]] but Literal does not support floating-point literals.
|
|
420
411
|
@requires_extension('linalg')
|
|
421
|
-
def vector_norm(
|
|
412
|
+
def vector_norm(
|
|
413
|
+
x: Array,
|
|
414
|
+
/,
|
|
415
|
+
*,
|
|
416
|
+
axis: int | tuple[int, ...] | None = None,
|
|
417
|
+
keepdims: bool = False,
|
|
418
|
+
ord: float = 2,
|
|
419
|
+
) -> Array:
|
|
422
420
|
"""
|
|
423
421
|
Array API compatible wrapper for :py:func:`np.linalg.norm <numpy.linalg.norm>`.
|
|
424
422
|
|
|
@@ -456,8 +454,8 @@ def vector_norm(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = No
|
|
|
456
454
|
# We can't reuse np.linalg.norm(keepdims) because of the reshape hacks
|
|
457
455
|
# above to avoid matrix norm logic.
|
|
458
456
|
shape = list(x.shape)
|
|
459
|
-
|
|
460
|
-
for i in
|
|
457
|
+
axis_tup = normalize_axis_tuple(range(x.ndim) if axis is None else axis, x.ndim)
|
|
458
|
+
for i in axis_tup:
|
|
461
459
|
shape[i] = 1
|
|
462
460
|
res = reshape(res, tuple(shape))
|
|
463
461
|
|
|
@@ -480,7 +478,13 @@ def matmul(x1: Array, x2: Array, /) -> Array:
|
|
|
480
478
|
|
|
481
479
|
# Note: tensordot is the numpy top-level namespace but not in np.linalg
|
|
482
480
|
@requires_extension('linalg')
|
|
483
|
-
def tensordot(
|
|
481
|
+
def tensordot(
|
|
482
|
+
x1: Array,
|
|
483
|
+
x2: Array,
|
|
484
|
+
/,
|
|
485
|
+
*,
|
|
486
|
+
axes: int | tuple[Sequence[int], Sequence[int]] = 2,
|
|
487
|
+
) -> Array:
|
|
484
488
|
from ._linear_algebra_functions import tensordot
|
|
485
489
|
return tensordot(x1, x2, axes=axes)
|
|
486
490
|
|
|
@@ -4,19 +4,15 @@ them here with wrappers in linalg so that the wrappers can be disabled if the
|
|
|
4
4
|
linalg extension is disabled in the flags.
|
|
5
5
|
|
|
6
6
|
"""
|
|
7
|
+
from collections.abc import Sequence
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
import numpy as np
|
|
10
|
+
import numpy.linalg
|
|
9
11
|
|
|
10
|
-
from ._dtypes import _numeric_dtypes
|
|
11
12
|
from ._array_object import Array
|
|
13
|
+
from ._dtypes import _numeric_dtypes
|
|
12
14
|
from ._flags import get_array_api_strict_flags
|
|
13
15
|
|
|
14
|
-
from typing import TYPE_CHECKING
|
|
15
|
-
if TYPE_CHECKING:
|
|
16
|
-
from ._typing import Sequence, Tuple, Union
|
|
17
|
-
|
|
18
|
-
import numpy.linalg
|
|
19
|
-
import numpy as np
|
|
20
16
|
|
|
21
17
|
# Note: matmul is the numpy top-level namespace but not in np.linalg
|
|
22
18
|
def matmul(x1: Array, x2: Array, /) -> Array:
|
|
@@ -38,7 +34,13 @@ def matmul(x1: Array, x2: Array, /) -> Array:
|
|
|
38
34
|
# Note: tensordot is the numpy top-level namespace but not in np.linalg
|
|
39
35
|
|
|
40
36
|
# Note: axes must be a tuple, unlike np.tensordot where it can be an array or array-like.
|
|
41
|
-
def tensordot(
|
|
37
|
+
def tensordot(
|
|
38
|
+
x1: Array,
|
|
39
|
+
x2: Array,
|
|
40
|
+
/,
|
|
41
|
+
*,
|
|
42
|
+
axes: int | tuple[Sequence[int], Sequence[int]] = 2,
|
|
43
|
+
) -> Array:
|
|
42
44
|
# Note: the restriction to numeric dtypes only is different from
|
|
43
45
|
# np.tensordot.
|
|
44
46
|
if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes:
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import numpy as np
|
|
2
2
|
|
|
3
3
|
from ._array_object import Array
|
|
4
4
|
from ._creation_functions import asarray
|
|
5
5
|
from ._data_type_functions import astype, result_type
|
|
6
6
|
from ._dtypes import _integer_dtypes, int64, uint64
|
|
7
|
-
from ._flags import
|
|
8
|
-
|
|
9
|
-
from typing import TYPE_CHECKING
|
|
7
|
+
from ._flags import get_array_api_strict_flags, requires_api_version
|
|
10
8
|
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from typing import List, Optional, Tuple, Union
|
|
13
|
-
|
|
14
|
-
import numpy as np
|
|
15
9
|
|
|
16
10
|
# Note: the function name is different here
|
|
17
11
|
def concat(
|
|
18
|
-
arrays:
|
|
12
|
+
arrays: tuple[Array, ...] | list[Array], /, *, axis: int | None = 0
|
|
19
13
|
) -> Array:
|
|
20
14
|
"""
|
|
21
15
|
Array API compatible wrapper for :py:func:`np.concatenate <numpy.concatenate>`.
|
|
@@ -29,8 +23,11 @@ def concat(
|
|
|
29
23
|
raise ValueError("concat inputs must all be on the same device")
|
|
30
24
|
result_device = arrays[0].device
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
return Array._new(
|
|
26
|
+
np_arrays = tuple(a._array for a in arrays)
|
|
27
|
+
return Array._new(
|
|
28
|
+
np.concatenate(np_arrays, axis=axis, dtype=dtype._np_dtype),
|
|
29
|
+
device=result_device,
|
|
30
|
+
)
|
|
34
31
|
|
|
35
32
|
|
|
36
33
|
def expand_dims(x: Array, /, *, axis: int) -> Array:
|
|
@@ -42,7 +39,7 @@ def expand_dims(x: Array, /, *, axis: int) -> Array:
|
|
|
42
39
|
return Array._new(np.expand_dims(x._array, axis), device=x.device)
|
|
43
40
|
|
|
44
41
|
|
|
45
|
-
def flip(x: Array, /, *, axis:
|
|
42
|
+
def flip(x: Array, /, *, axis: int | tuple[int, ...] | None = None) -> Array:
|
|
46
43
|
"""
|
|
47
44
|
Array API compatible wrapper for :py:func:`np.flip <numpy.flip>`.
|
|
48
45
|
|
|
@@ -53,8 +50,8 @@ def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) ->
|
|
|
53
50
|
@requires_api_version('2023.12')
|
|
54
51
|
def moveaxis(
|
|
55
52
|
x: Array,
|
|
56
|
-
source:
|
|
57
|
-
destination:
|
|
53
|
+
source: int | tuple[int, ...],
|
|
54
|
+
destination: int | tuple[int, ...],
|
|
58
55
|
/,
|
|
59
56
|
) -> Array:
|
|
60
57
|
"""
|
|
@@ -66,7 +63,7 @@ def moveaxis(
|
|
|
66
63
|
|
|
67
64
|
# Note: The function name is different here (see also matrix_transpose).
|
|
68
65
|
# Unlike transpose(), the axes argument is required.
|
|
69
|
-
def permute_dims(x: Array, /, axes:
|
|
66
|
+
def permute_dims(x: Array, /, axes: tuple[int, ...]) -> Array:
|
|
70
67
|
"""
|
|
71
68
|
Array API compatible wrapper for :py:func:`np.transpose <numpy.transpose>`.
|
|
72
69
|
|
|
@@ -77,10 +74,10 @@ def permute_dims(x: Array, /, axes: Tuple[int, ...]) -> Array:
|
|
|
77
74
|
@requires_api_version('2023.12')
|
|
78
75
|
def repeat(
|
|
79
76
|
x: Array,
|
|
80
|
-
repeats:
|
|
77
|
+
repeats: int | Array,
|
|
81
78
|
/,
|
|
82
79
|
*,
|
|
83
|
-
axis:
|
|
80
|
+
axis: int | None = None,
|
|
84
81
|
) -> Array:
|
|
85
82
|
"""
|
|
86
83
|
Array API compatible wrapper for :py:func:`np.repeat <numpy.repeat>`.
|
|
@@ -108,17 +105,16 @@ def repeat(
|
|
|
108
105
|
repeats = astype(repeats, int64)
|
|
109
106
|
return Array._new(np.repeat(x._array, repeats._array, axis=axis), device=x.device)
|
|
110
107
|
|
|
108
|
+
|
|
111
109
|
# Note: the optional argument is called 'shape', not 'newshape'
|
|
112
|
-
def reshape(x: Array,
|
|
113
|
-
/,
|
|
114
|
-
shape: Tuple[int, ...],
|
|
115
|
-
*,
|
|
116
|
-
copy: Optional[bool] = None) -> Array:
|
|
110
|
+
def reshape(x: Array, /, shape: tuple[int, ...], *, copy: bool | None = None) -> Array:
|
|
117
111
|
"""
|
|
118
112
|
Array API compatible wrapper for :py:func:`np.reshape <numpy.reshape>`.
|
|
119
113
|
|
|
120
114
|
See its docstring for more information.
|
|
121
115
|
"""
|
|
116
|
+
if not isinstance(shape, tuple):
|
|
117
|
+
raise TypeError(f"`shape` must be a tuple of ints; got {shape=} instead.")
|
|
122
118
|
|
|
123
119
|
data = x._array
|
|
124
120
|
if copy:
|
|
@@ -135,19 +131,23 @@ def reshape(x: Array,
|
|
|
135
131
|
def roll(
|
|
136
132
|
x: Array,
|
|
137
133
|
/,
|
|
138
|
-
shift:
|
|
134
|
+
shift: int | tuple[int, ...],
|
|
139
135
|
*,
|
|
140
|
-
axis:
|
|
136
|
+
axis: int | tuple[int, ...] | None = None,
|
|
141
137
|
) -> Array:
|
|
142
138
|
"""
|
|
143
139
|
Array API compatible wrapper for :py:func:`np.roll <numpy.roll>`.
|
|
144
140
|
|
|
145
141
|
See its docstring for more information.
|
|
146
142
|
"""
|
|
143
|
+
if not isinstance(shift, int | tuple):
|
|
144
|
+
raise ValueError(
|
|
145
|
+
f"`shift` can only be an int or a tuple, got {type(shift)=} instead."
|
|
146
|
+
)
|
|
147
147
|
return Array._new(np.roll(x._array, shift, axis=axis), device=x.device)
|
|
148
148
|
|
|
149
149
|
|
|
150
|
-
def squeeze(x: Array, /, axis:
|
|
150
|
+
def squeeze(x: Array, /, axis: int | tuple[int, ...]) -> Array:
|
|
151
151
|
"""
|
|
152
152
|
Array API compatible wrapper for :py:func:`np.squeeze <numpy.squeeze>`.
|
|
153
153
|
|
|
@@ -161,7 +161,7 @@ def squeeze(x: Array, /, axis: Union[int, Tuple[int, ...]]) -> Array:
|
|
|
161
161
|
return Array._new(np.squeeze(x._array, axis=axis), device=x.device)
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
def stack(arrays:
|
|
164
|
+
def stack(arrays: tuple[Array, ...] | list[Array], /, *, axis: int = 0) -> Array:
|
|
165
165
|
"""
|
|
166
166
|
Array API compatible wrapper for :py:func:`np.stack <numpy.stack>`.
|
|
167
167
|
|
|
@@ -172,12 +172,12 @@ def stack(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: int = 0) ->
|
|
|
172
172
|
if len({a.device for a in arrays}) > 1:
|
|
173
173
|
raise ValueError("concat inputs must all be on the same device")
|
|
174
174
|
result_device = arrays[0].device
|
|
175
|
-
|
|
176
|
-
return Array._new(np.stack(
|
|
175
|
+
np_arrays = tuple(a._array for a in arrays)
|
|
176
|
+
return Array._new(np.stack(np_arrays, axis=axis), device=result_device)
|
|
177
177
|
|
|
178
178
|
|
|
179
179
|
@requires_api_version('2023.12')
|
|
180
|
-
def tile(x: Array, repetitions:
|
|
180
|
+
def tile(x: Array, repetitions: tuple[int, ...], /) -> Array:
|
|
181
181
|
"""
|
|
182
182
|
Array API compatible wrapper for :py:func:`np.tile <numpy.tile>`.
|
|
183
183
|
|
|
@@ -190,7 +190,7 @@ def tile(x: Array, repetitions: Tuple[int, ...], /) -> Array:
|
|
|
190
190
|
|
|
191
191
|
# Note: this function is new
|
|
192
192
|
@requires_api_version('2023.12')
|
|
193
|
-
def unstack(x: Array, /, *, axis: int = 0) ->
|
|
193
|
+
def unstack(x: Array, /, *, axis: int = 0) -> tuple[Array, ...]:
|
|
194
194
|
if not (-x.ndim <= axis < x.ndim):
|
|
195
195
|
raise ValueError("axis out of range")
|
|
196
196
|
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from ._array_object import Array
|
|
4
|
-
from ._dtypes import _result_type, _real_numeric_dtypes, bool as _bool
|
|
5
|
-
from ._flags import requires_data_dependent_shapes, requires_api_version, get_array_api_strict_flags
|
|
6
|
-
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from typing import Literal, Optional, Tuple, Union
|
|
1
|
+
from typing import Literal
|
|
10
2
|
|
|
11
3
|
import numpy as np
|
|
12
4
|
|
|
5
|
+
from ._array_object import Array
|
|
6
|
+
from ._dtypes import _real_numeric_dtypes, _result_type
|
|
7
|
+
from ._dtypes import bool as _bool
|
|
8
|
+
from ._flags import requires_api_version, requires_data_dependent_shapes
|
|
9
|
+
from ._helpers import _maybe_normalize_py_scalars
|
|
10
|
+
|
|
13
11
|
|
|
14
|
-
def argmax(x: Array, /, *, axis:
|
|
12
|
+
def argmax(x: Array, /, *, axis: int | None = None, keepdims: bool = False) -> Array:
|
|
15
13
|
"""
|
|
16
14
|
Array API compatible wrapper for :py:func:`np.argmax <numpy.argmax>`.
|
|
17
15
|
|
|
@@ -22,7 +20,7 @@ def argmax(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -
|
|
|
22
20
|
return Array._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims)), device=x.device)
|
|
23
21
|
|
|
24
22
|
|
|
25
|
-
def argmin(x: Array, /, *, axis:
|
|
23
|
+
def argmin(x: Array, /, *, axis: int | None = None, keepdims: bool = False) -> Array:
|
|
26
24
|
"""
|
|
27
25
|
Array API compatible wrapper for :py:func:`np.argmin <numpy.argmin>`.
|
|
28
26
|
|
|
@@ -34,7 +32,7 @@ def argmin(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -
|
|
|
34
32
|
|
|
35
33
|
|
|
36
34
|
@requires_data_dependent_shapes
|
|
37
|
-
def nonzero(x: Array, /) ->
|
|
35
|
+
def nonzero(x: Array, /) -> tuple[Array, ...]:
|
|
38
36
|
"""
|
|
39
37
|
Array API compatible wrapper for :py:func:`np.nonzero <numpy.nonzero>`.
|
|
40
38
|
|
|
@@ -51,7 +49,7 @@ def count_nonzero(
|
|
|
51
49
|
x: Array,
|
|
52
50
|
/,
|
|
53
51
|
*,
|
|
54
|
-
axis:
|
|
52
|
+
axis: int | tuple[int, ...] | None = None,
|
|
55
53
|
keepdims: bool = False,
|
|
56
54
|
) -> Array:
|
|
57
55
|
"""
|
|
@@ -70,7 +68,7 @@ def searchsorted(
|
|
|
70
68
|
/,
|
|
71
69
|
*,
|
|
72
70
|
side: Literal["left", "right"] = "left",
|
|
73
|
-
sorter:
|
|
71
|
+
sorter: Array | None = None,
|
|
74
72
|
) -> Array:
|
|
75
73
|
"""
|
|
76
74
|
Array API compatible wrapper for :py:func:`np.searchsorted <numpy.searchsorted>`.
|
|
@@ -83,36 +81,27 @@ def searchsorted(
|
|
|
83
81
|
if x1.device != x2.device:
|
|
84
82
|
raise ValueError(f"Arrays from two different devices ({x1.device} and {x2.device}) can not be combined.")
|
|
85
83
|
|
|
86
|
-
|
|
84
|
+
np_sorter = sorter._array if sorter is not None else None
|
|
87
85
|
# TODO: The sort order of nans and signed zeros is implementation
|
|
88
86
|
# dependent. Should we error/warn if they are present?
|
|
89
87
|
|
|
90
88
|
# x1 must be 1-D, but NumPy already requires this.
|
|
91
|
-
return Array._new(
|
|
89
|
+
return Array._new(
|
|
90
|
+
np.searchsorted(x1._array, x2._array, side=side, sorter=np_sorter),
|
|
91
|
+
device=x1.device,
|
|
92
|
+
)
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
x1: bool | int | float | complex | Array,
|
|
96
|
-
x2: bool | int | float | complex | Array, /
|
|
97
|
-
) -> Array:
|
|
94
|
+
|
|
95
|
+
def where(condition: Array, x1: Array | complex, x2: Array | complex, /) -> Array:
|
|
98
96
|
"""
|
|
99
97
|
Array API compatible wrapper for :py:func:`np.where <numpy.where>`.
|
|
100
98
|
|
|
101
99
|
See its docstring for more information.
|
|
102
100
|
"""
|
|
103
|
-
if
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if isinstance(x1, (bool, float, complex, int)):
|
|
107
|
-
x1 = Array._new(np.asarray(x1), device=condition.device)
|
|
108
|
-
num_scalars += 1
|
|
109
|
-
|
|
110
|
-
if isinstance(x2, (bool, float, complex, int)):
|
|
111
|
-
x2 = Array._new(np.asarray(x2), device=condition.device)
|
|
112
|
-
num_scalars += 1
|
|
101
|
+
if not isinstance(condition, Array):
|
|
102
|
+
raise TypeError(f"`condition` must be an Array; got {type(condition)}")
|
|
113
103
|
|
|
114
|
-
|
|
115
|
-
raise ValueError("One of x1, x2 arguments must be an array.")
|
|
104
|
+
x1, x2 = _maybe_normalize_py_scalars(x1, x2, "all", "where")
|
|
116
105
|
|
|
117
106
|
# Call result type here just to raise on disallowed type combinations
|
|
118
107
|
_result_type(x1.dtype, x2.dtype)
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from ._array_object import Array
|
|
4
|
-
|
|
5
|
-
from ._flags import requires_data_dependent_shapes
|
|
6
|
-
|
|
7
1
|
from typing import NamedTuple
|
|
8
2
|
|
|
9
3
|
import numpy as np
|
|
10
4
|
|
|
5
|
+
from ._array_object import Array
|
|
6
|
+
from ._flags import requires_data_dependent_shapes
|
|
7
|
+
|
|
11
8
|
# Note: np.unique() is split into four functions in the array API:
|
|
12
9
|
# unique_all, unique_counts, unique_inverse, and unique_values (this is done
|
|
13
10
|
# to remove polymorphic return types).
|
|
@@ -20,6 +17,7 @@ import numpy as np
|
|
|
20
17
|
# Note: The functions here return a namedtuple (np.unique() returns a normal
|
|
21
18
|
# tuple).
|
|
22
19
|
|
|
20
|
+
|
|
23
21
|
class UniqueAllResult(NamedTuple):
|
|
24
22
|
values: Array
|
|
25
23
|
indices: Array
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
2
4
|
|
|
3
5
|
from ._array_object import Array
|
|
4
6
|
from ._dtypes import _real_numeric_dtypes
|
|
5
7
|
|
|
6
|
-
import numpy as np
|
|
7
|
-
|
|
8
8
|
|
|
9
9
|
# Note: the descending keyword argument is new in this function
|
|
10
10
|
def argsort(
|
|
@@ -18,7 +18,7 @@ def argsort(
|
|
|
18
18
|
if x.dtype not in _real_numeric_dtypes:
|
|
19
19
|
raise TypeError("Only real numeric dtypes are allowed in argsort")
|
|
20
20
|
# Note: this keyword argument is different, and the default is different.
|
|
21
|
-
kind = "stable" if stable else "quicksort"
|
|
21
|
+
kind: Literal["stable", "quicksort"] = "stable" if stable else "quicksort"
|
|
22
22
|
if not descending:
|
|
23
23
|
res = np.argsort(x._array, axis=axis, kind=kind)
|
|
24
24
|
else:
|
|
@@ -35,6 +35,7 @@ def argsort(
|
|
|
35
35
|
res = max_i - res
|
|
36
36
|
return Array._new(res, device=x.device)
|
|
37
37
|
|
|
38
|
+
|
|
38
39
|
# Note: the descending keyword argument is new in this function
|
|
39
40
|
def sort(
|
|
40
41
|
x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True
|
|
@@ -47,8 +48,7 @@ def sort(
|
|
|
47
48
|
if x.dtype not in _real_numeric_dtypes:
|
|
48
49
|
raise TypeError("Only real numeric dtypes are allowed in sort")
|
|
49
50
|
# Note: this keyword argument is different, and the default is different.
|
|
50
|
-
|
|
51
|
-
res = np.sort(x._array, axis=axis, kind=kind)
|
|
51
|
+
res = np.sort(x._array, axis=axis, kind="stable" if stable else "quicksort")
|
|
52
52
|
if descending:
|
|
53
53
|
res = np.flip(res, axis=axis)
|
|
54
54
|
return Array._new(res, device=x.device)
|