array-api-extra 0.10.0__py3-none-any.whl → 0.10.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- array_api_extra/__init__.py +7 -3
- array_api_extra/_delegation.py +157 -0
- array_api_extra/_lib/_at.py +21 -6
- array_api_extra/_lib/_backends.py +2 -1
- array_api_extra/_lib/_funcs.py +59 -120
- array_api_extra/_lib/_lazy.py +4 -4
- array_api_extra/_lib/_testing.py +2 -269
- array_api_extra/_lib/_utils/_helpers.py +2 -34
- array_api_extra/testing.py +629 -43
- array_api_extra/tests/test_at.py +11 -12
- array_api_extra/tests/test_funcs.py +246 -158
- array_api_extra/tests/test_helpers.py +13 -14
- array_api_extra/tests/test_lazy.py +26 -26
- array_api_extra/tests/test_testing.py +256 -64
- array_api_extra/vendor_tests/_array_api_compat_vendor.py +5 -2
- array_api_extra/vendor_tests/test_vendor.py +15 -5
- {array_api_extra-0.10.0.dist-info → array_api_extra-0.10.3.dist-info}/METADATA +11 -4
- array_api_extra-0.10.3.dist-info/RECORD +30 -0
- array_api_extra-0.10.0.dist-info/RECORD +0 -30
- {array_api_extra-0.10.0.dist-info → array_api_extra-0.10.3.dist-info}/WHEEL +0 -0
- {array_api_extra-0.10.0.dist-info → array_api_extra-0.10.3.dist-info}/licenses/LICENSE +0 -0
array_api_extra/__init__.py
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"""Extra array functions built on top of the array API standard."""
|
|
2
2
|
|
|
3
|
+
from . import testing
|
|
3
4
|
from ._delegation import (
|
|
4
5
|
argpartition,
|
|
5
6
|
atleast_nd,
|
|
7
|
+
broadcast_shapes,
|
|
6
8
|
cov,
|
|
7
9
|
create_diagonal,
|
|
8
10
|
expand_dims,
|
|
9
11
|
isclose,
|
|
10
12
|
isin,
|
|
13
|
+
kron,
|
|
11
14
|
nan_to_num,
|
|
12
15
|
one_hot,
|
|
13
16
|
pad,
|
|
@@ -19,19 +22,19 @@ from ._delegation import (
|
|
|
19
22
|
)
|
|
20
23
|
from ._lib._at import at
|
|
21
24
|
from ._lib._funcs import (
|
|
25
|
+
angle,
|
|
22
26
|
apply_where,
|
|
23
|
-
broadcast_shapes,
|
|
24
27
|
default_dtype,
|
|
25
|
-
kron,
|
|
26
28
|
nunique,
|
|
27
29
|
)
|
|
28
30
|
from ._lib._lazy import lazy_apply
|
|
29
31
|
|
|
30
|
-
__version__ = "0.10.
|
|
32
|
+
__version__ = "0.10.3"
|
|
31
33
|
|
|
32
34
|
# pylint: disable=duplicate-code
|
|
33
35
|
__all__ = [
|
|
34
36
|
"__version__",
|
|
37
|
+
"angle",
|
|
35
38
|
"apply_where",
|
|
36
39
|
"argpartition",
|
|
37
40
|
"at",
|
|
@@ -53,5 +56,6 @@ __all__ = [
|
|
|
53
56
|
"searchsorted",
|
|
54
57
|
"setdiff1d",
|
|
55
58
|
"sinc",
|
|
59
|
+
"testing",
|
|
56
60
|
"union1d",
|
|
57
61
|
]
|
array_api_extra/_delegation.py
CHANGED
|
@@ -20,10 +20,12 @@ from ._lib._utils._typing import Array, DType
|
|
|
20
20
|
|
|
21
21
|
__all__ = [
|
|
22
22
|
"atleast_nd",
|
|
23
|
+
"broadcast_shapes",
|
|
23
24
|
"cov",
|
|
24
25
|
"create_diagonal",
|
|
25
26
|
"expand_dims",
|
|
26
27
|
"isclose",
|
|
28
|
+
"kron",
|
|
27
29
|
"nan_to_num",
|
|
28
30
|
"one_hot",
|
|
29
31
|
"pad",
|
|
@@ -81,6 +83,66 @@ def atleast_nd(x: Array, /, *, ndim: int, xp: ModuleType | None = None) -> Array
|
|
|
81
83
|
return _funcs.atleast_nd(x, ndim=ndim, xp=xp)
|
|
82
84
|
|
|
83
85
|
|
|
86
|
+
def broadcast_shapes(
|
|
87
|
+
*shapes: tuple[float | None, ...], xp: ModuleType | None = None
|
|
88
|
+
) -> tuple[int | None, ...]:
|
|
89
|
+
"""
|
|
90
|
+
Compute the shape of the broadcasted arrays.
|
|
91
|
+
|
|
92
|
+
Duplicates :func:`numpy.broadcast_shapes`, with additional support for
|
|
93
|
+
None and NaN sizes.
|
|
94
|
+
|
|
95
|
+
This is equivalent to ``xp.broadcast_arrays(arr1, arr2, ...)[0].shape``
|
|
96
|
+
without needing to worry about the backend potentially deep copying
|
|
97
|
+
the arrays.
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
*shapes : tuple[int | None, ...]
|
|
102
|
+
Shapes of the arrays to broadcast.
|
|
103
|
+
xp : array_namespace, optional
|
|
104
|
+
The standard-compatible namespace to use for native delegation.
|
|
105
|
+
Default: use the array-agnostic implementation.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
tuple[int | None, ...]
|
|
110
|
+
The shape of the broadcasted arrays.
|
|
111
|
+
|
|
112
|
+
See Also
|
|
113
|
+
--------
|
|
114
|
+
numpy.broadcast_shapes : Equivalent NumPy function.
|
|
115
|
+
array_api.broadcast_arrays : Function to broadcast actual arrays.
|
|
116
|
+
|
|
117
|
+
Notes
|
|
118
|
+
-----
|
|
119
|
+
This function accepts the Array API's ``None`` for unknown sizes,
|
|
120
|
+
as well as Dask's non-standard ``math.nan``.
|
|
121
|
+
Regardless of input, the output always contains ``None`` for unknown sizes.
|
|
122
|
+
|
|
123
|
+
Examples
|
|
124
|
+
--------
|
|
125
|
+
>>> import array_api_extra as xpx
|
|
126
|
+
>>> xpx.broadcast_shapes((2, 3), (2, 1))
|
|
127
|
+
(2, 3)
|
|
128
|
+
>>> xpx.broadcast_shapes((4, 2, 3), (2, 1), (1, 3))
|
|
129
|
+
(4, 2, 3)
|
|
130
|
+
"""
|
|
131
|
+
if (
|
|
132
|
+
xp is not None
|
|
133
|
+
and all(isinstance(size, int) for shape in shapes for size in shape)
|
|
134
|
+
and (
|
|
135
|
+
is_numpy_namespace(xp)
|
|
136
|
+
or is_cupy_namespace(xp)
|
|
137
|
+
or is_jax_namespace(xp)
|
|
138
|
+
or is_torch_namespace(xp)
|
|
139
|
+
)
|
|
140
|
+
):
|
|
141
|
+
return xp.broadcast_shapes(*shapes)
|
|
142
|
+
|
|
143
|
+
return _funcs.broadcast_shapes(*shapes)
|
|
144
|
+
|
|
145
|
+
|
|
84
146
|
def cov(m: Array, /, *, xp: ModuleType | None = None) -> Array:
|
|
85
147
|
"""
|
|
86
148
|
Estimate a covariance matrix (or a stack of covariance matrices).
|
|
@@ -418,6 +480,101 @@ def isclose(
|
|
|
418
480
|
return _funcs.isclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan, xp=xp)
|
|
419
481
|
|
|
420
482
|
|
|
483
|
+
def kron(
|
|
484
|
+
a: Array | complex,
|
|
485
|
+
b: Array | complex,
|
|
486
|
+
/,
|
|
487
|
+
*,
|
|
488
|
+
xp: ModuleType | None = None,
|
|
489
|
+
) -> Array:
|
|
490
|
+
"""
|
|
491
|
+
Kronecker product of two arrays.
|
|
492
|
+
|
|
493
|
+
Computes the Kronecker product, a composite array made of blocks of the
|
|
494
|
+
second array scaled by the first.
|
|
495
|
+
|
|
496
|
+
Equivalent to ``numpy.kron`` for NumPy arrays.
|
|
497
|
+
|
|
498
|
+
Parameters
|
|
499
|
+
----------
|
|
500
|
+
a, b : Array | int | float | complex
|
|
501
|
+
Input arrays or scalars. At least one must be an array.
|
|
502
|
+
xp : array_namespace, optional
|
|
503
|
+
The standard-compatible namespace for `a` and `b`. Default: infer.
|
|
504
|
+
|
|
505
|
+
Returns
|
|
506
|
+
-------
|
|
507
|
+
array
|
|
508
|
+
The Kronecker product of `a` and `b`.
|
|
509
|
+
|
|
510
|
+
Notes
|
|
511
|
+
-----
|
|
512
|
+
The function assumes that the number of dimensions of `a` and `b`
|
|
513
|
+
are the same, if necessary prepending the smallest with ones.
|
|
514
|
+
If ``a.shape = (r0,r1,..,rN)`` and ``b.shape = (s0,s1,...,sN)``,
|
|
515
|
+
the Kronecker product has shape ``(r0*s0, r1*s1, ..., rN*SN)``.
|
|
516
|
+
The elements are products of elements from `a` and `b`, organized
|
|
517
|
+
explicitly by::
|
|
518
|
+
|
|
519
|
+
kron(a,b)[k0,k1,...,kN] = a[i0,i1,...,iN] * b[j0,j1,...,jN]
|
|
520
|
+
|
|
521
|
+
where::
|
|
522
|
+
|
|
523
|
+
kt = it * st + jt, t = 0,...,N
|
|
524
|
+
|
|
525
|
+
In the common 2-D case (N=1), the block structure can be visualized::
|
|
526
|
+
|
|
527
|
+
[[ a[0,0]*b, a[0,1]*b, ... , a[0,-1]*b ],
|
|
528
|
+
[ ... ... ],
|
|
529
|
+
[ a[-1,0]*b, a[-1,1]*b, ... , a[-1,-1]*b ]]
|
|
530
|
+
|
|
531
|
+
Examples
|
|
532
|
+
--------
|
|
533
|
+
>>> import array_api_strict as xp
|
|
534
|
+
>>> import array_api_extra as xpx
|
|
535
|
+
>>> xpx.kron(xp.asarray([1, 10, 100]), xp.asarray([5, 6, 7]), xp=xp)
|
|
536
|
+
Array([ 5, 6, 7, 50, 60, 70, 500,
|
|
537
|
+
600, 700], dtype=array_api_strict.int64)
|
|
538
|
+
|
|
539
|
+
>>> xpx.kron(xp.asarray([5, 6, 7]), xp.asarray([1, 10, 100]), xp=xp)
|
|
540
|
+
Array([ 5, 50, 500, 6, 60, 600, 7,
|
|
541
|
+
70, 700], dtype=array_api_strict.int64)
|
|
542
|
+
|
|
543
|
+
>>> xpx.kron(xp.eye(2), xp.ones((2, 2)), xp=xp)
|
|
544
|
+
Array([[1., 1., 0., 0.],
|
|
545
|
+
[1., 1., 0., 0.],
|
|
546
|
+
[0., 0., 1., 1.],
|
|
547
|
+
[0., 0., 1., 1.]], dtype=array_api_strict.float64)
|
|
548
|
+
|
|
549
|
+
>>> a = xp.reshape(xp.arange(100), (2, 5, 2, 5))
|
|
550
|
+
>>> b = xp.reshape(xp.arange(24), (2, 3, 4))
|
|
551
|
+
>>> c = xpx.kron(a, b, xp=xp)
|
|
552
|
+
>>> c.shape
|
|
553
|
+
(2, 10, 6, 20)
|
|
554
|
+
>>> I = (1, 3, 0, 2)
|
|
555
|
+
>>> J = (0, 2, 1)
|
|
556
|
+
>>> J1 = (0,) + J # extend to ndim=4
|
|
557
|
+
>>> S1 = (1,) + b.shape
|
|
558
|
+
>>> K = tuple(xp.asarray(I) * xp.asarray(S1) + xp.asarray(J1))
|
|
559
|
+
>>> c[K] == a[I]*b[J]
|
|
560
|
+
Array(True, dtype=array_api_strict.bool)
|
|
561
|
+
"""
|
|
562
|
+
if xp is None:
|
|
563
|
+
xp = array_namespace(a, b)
|
|
564
|
+
|
|
565
|
+
a, b = asarrays(a, b, xp=xp)
|
|
566
|
+
|
|
567
|
+
if (
|
|
568
|
+
is_cupy_namespace(xp)
|
|
569
|
+
or is_jax_namespace(xp)
|
|
570
|
+
or is_numpy_namespace(xp)
|
|
571
|
+
or is_torch_namespace(xp)
|
|
572
|
+
):
|
|
573
|
+
return xp.kron(a, b)
|
|
574
|
+
|
|
575
|
+
return _funcs.kron(a, b, xp=xp)
|
|
576
|
+
|
|
577
|
+
|
|
421
578
|
def nan_to_num(
|
|
422
579
|
x: Array | float | complex,
|
|
423
580
|
/,
|
array_api_extra/_lib/_at.py
CHANGED
|
@@ -37,7 +37,7 @@ class _AtOp(Enum):
|
|
|
37
37
|
MAX = "max"
|
|
38
38
|
|
|
39
39
|
# @override from Python 3.12
|
|
40
|
-
def __str__(self) -> str: # pyright: ignore[reportImplicitOverride]
|
|
40
|
+
def __str__(self) -> str: # pyright: ignore[reportImplicitOverride] # pyrefly: ignore[missing-override-decorator]
|
|
41
41
|
"""
|
|
42
42
|
Return string representation (useful for pytest logs).
|
|
43
43
|
|
|
@@ -381,7 +381,7 @@ class at: # pylint: disable=invalid-name # numpydoc ignore=PR02
|
|
|
381
381
|
# Note for this and all other methods based on _iop:
|
|
382
382
|
# operator.iadd and operator.add subtly differ in behaviour, as
|
|
383
383
|
# only iadd will trigger exceptions when y has an incompatible dtype.
|
|
384
|
-
return self._op(_AtOp.ADD, operator.iadd, operator.add, y, copy=copy, xp=xp)
|
|
384
|
+
return self._op(_AtOp.ADD, operator.iadd, operator.add, y, copy=copy, xp=xp) # pyright: ignore[reportUnknownArgumentType]
|
|
385
385
|
|
|
386
386
|
def subtract(
|
|
387
387
|
self,
|
|
@@ -392,7 +392,12 @@ class at: # pylint: disable=invalid-name # numpydoc ignore=PR02
|
|
|
392
392
|
) -> Array: # numpydoc ignore=PR01,RT01
|
|
393
393
|
"""Apply ``x[idx] -= y`` and return the updated array."""
|
|
394
394
|
return self._op(
|
|
395
|
-
_AtOp.SUBTRACT,
|
|
395
|
+
_AtOp.SUBTRACT,
|
|
396
|
+
operator.isub, # pyright: ignore[reportUnknownArgumentType]
|
|
397
|
+
operator.sub,
|
|
398
|
+
y,
|
|
399
|
+
copy=copy,
|
|
400
|
+
xp=xp,
|
|
396
401
|
)
|
|
397
402
|
|
|
398
403
|
def multiply(
|
|
@@ -404,7 +409,12 @@ class at: # pylint: disable=invalid-name # numpydoc ignore=PR02
|
|
|
404
409
|
) -> Array: # numpydoc ignore=PR01,RT01
|
|
405
410
|
"""Apply ``x[idx] *= y`` and return the updated array."""
|
|
406
411
|
return self._op(
|
|
407
|
-
_AtOp.MULTIPLY,
|
|
412
|
+
_AtOp.MULTIPLY,
|
|
413
|
+
operator.imul, # pyright: ignore[reportUnknownArgumentType]
|
|
414
|
+
operator.mul,
|
|
415
|
+
y,
|
|
416
|
+
copy=copy,
|
|
417
|
+
xp=xp,
|
|
408
418
|
)
|
|
409
419
|
|
|
410
420
|
def divide(
|
|
@@ -416,7 +426,12 @@ class at: # pylint: disable=invalid-name # numpydoc ignore=PR02
|
|
|
416
426
|
) -> Array: # numpydoc ignore=PR01,RT01
|
|
417
427
|
"""Apply ``x[idx] /= y`` and return the updated array."""
|
|
418
428
|
return self._op(
|
|
419
|
-
_AtOp.DIVIDE,
|
|
429
|
+
_AtOp.DIVIDE,
|
|
430
|
+
operator.itruediv, # pyright: ignore[reportUnknownArgumentType]
|
|
431
|
+
operator.truediv, # pyright: ignore[reportUnknownArgumentType]
|
|
432
|
+
y,
|
|
433
|
+
copy=copy,
|
|
434
|
+
xp=xp,
|
|
420
435
|
)
|
|
421
436
|
|
|
422
437
|
def power(
|
|
@@ -427,7 +442,7 @@ class at: # pylint: disable=invalid-name # numpydoc ignore=PR02
|
|
|
427
442
|
xp: ModuleType | None = None,
|
|
428
443
|
) -> Array: # numpydoc ignore=PR01,RT01
|
|
429
444
|
"""Apply ``x[idx] **= y`` and return the updated array."""
|
|
430
|
-
return self._op(_AtOp.POWER, operator.ipow, operator.pow, y, copy=copy, xp=xp)
|
|
445
|
+
return self._op(_AtOp.POWER, operator.ipow, operator.pow, y, copy=copy, xp=xp) # pyright: ignore[reportUnknownArgumentType]
|
|
431
446
|
|
|
432
447
|
def min(
|
|
433
448
|
self,
|
|
@@ -47,11 +47,12 @@ class Backend(Enum): # numpydoc ignore=PR02
|
|
|
47
47
|
|
|
48
48
|
def pytest_param(self) -> Any:
|
|
49
49
|
"""
|
|
50
|
-
Backend as a pytest parameter
|
|
50
|
+
Backend as a pytest parameter.
|
|
51
51
|
|
|
52
52
|
Returns
|
|
53
53
|
-------
|
|
54
54
|
pytest.mark.ParameterSet
|
|
55
|
+
The backend as a pytest parameter.
|
|
55
56
|
"""
|
|
56
57
|
id_ = (
|
|
57
58
|
self.name.lower().replace("_gpu", ":gpu").replace("_readonly", ":readonly")
|
array_api_extra/_lib/_funcs.py
CHANGED
|
@@ -23,6 +23,7 @@ from ._utils._helpers import (
|
|
|
23
23
|
from ._utils._typing import Array, Device, DType
|
|
24
24
|
|
|
25
25
|
__all__ = [
|
|
26
|
+
"angle",
|
|
26
27
|
"apply_where",
|
|
27
28
|
"atleast_nd",
|
|
28
29
|
"broadcast_shapes",
|
|
@@ -220,46 +221,10 @@ def atleast_nd(x: Array, /, *, ndim: int, xp: ModuleType) -> Array:
|
|
|
220
221
|
|
|
221
222
|
# `float` in signature to accept `math.nan` for Dask.
|
|
222
223
|
# `int`s are still accepted as `float` is a superclass of `int` in typing
|
|
223
|
-
def broadcast_shapes(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
Duplicates :func:`numpy.broadcast_shapes`, with additional support for
|
|
228
|
-
None and NaN sizes.
|
|
229
|
-
|
|
230
|
-
This is equivalent to ``xp.broadcast_arrays(arr1, arr2, ...)[0].shape``
|
|
231
|
-
without needing to worry about the backend potentially deep copying
|
|
232
|
-
the arrays.
|
|
233
|
-
|
|
234
|
-
Parameters
|
|
235
|
-
----------
|
|
236
|
-
*shapes : tuple[int | None, ...]
|
|
237
|
-
Shapes of the arrays to broadcast.
|
|
238
|
-
|
|
239
|
-
Returns
|
|
240
|
-
-------
|
|
241
|
-
tuple[int | None, ...]
|
|
242
|
-
The shape of the broadcasted arrays.
|
|
243
|
-
|
|
244
|
-
See Also
|
|
245
|
-
--------
|
|
246
|
-
numpy.broadcast_shapes : Equivalent NumPy function.
|
|
247
|
-
array_api.broadcast_arrays : Function to broadcast actual arrays.
|
|
248
|
-
|
|
249
|
-
Notes
|
|
250
|
-
-----
|
|
251
|
-
This function accepts the Array API's ``None`` for unknown sizes,
|
|
252
|
-
as well as Dask's non-standard ``math.nan``.
|
|
253
|
-
Regardless of input, the output always contains ``None`` for unknown sizes.
|
|
254
|
-
|
|
255
|
-
Examples
|
|
256
|
-
--------
|
|
257
|
-
>>> import array_api_extra as xpx
|
|
258
|
-
>>> xpx.broadcast_shapes((2, 3), (2, 1))
|
|
259
|
-
(2, 3)
|
|
260
|
-
>>> xpx.broadcast_shapes((4, 2, 3), (2, 1), (1, 3))
|
|
261
|
-
(4, 2, 3)
|
|
262
|
-
"""
|
|
224
|
+
def broadcast_shapes( # numpydoc ignore=PR01,RT01
|
|
225
|
+
*shapes: tuple[float | None, ...],
|
|
226
|
+
) -> tuple[int | None, ...]:
|
|
227
|
+
"""See docstring in array_api_extra._delegation."""
|
|
263
228
|
if not shapes:
|
|
264
229
|
return () # Match NumPy output
|
|
265
230
|
|
|
@@ -291,7 +256,7 @@ def cov(m: Array, /, *, xp: ModuleType) -> Array: # numpydoc ignore=PR01,RT01
|
|
|
291
256
|
m = atleast_nd(m, ndim=2, xp=xp)
|
|
292
257
|
m = xp.astype(m, dtype)
|
|
293
258
|
|
|
294
|
-
avg =
|
|
259
|
+
avg = xp.mean(m, axis=-1, keepdims=True)
|
|
295
260
|
|
|
296
261
|
m_shape = eager_shape(m)
|
|
297
262
|
fact = m_shape[-1] - 1
|
|
@@ -440,87 +405,13 @@ def isclose(
|
|
|
440
405
|
|
|
441
406
|
|
|
442
407
|
def kron(
|
|
443
|
-
a: Array
|
|
444
|
-
b: Array
|
|
408
|
+
a: Array,
|
|
409
|
+
b: Array,
|
|
445
410
|
/,
|
|
446
411
|
*,
|
|
447
|
-
xp: ModuleType
|
|
448
|
-
) -> Array:
|
|
449
|
-
"""
|
|
450
|
-
Kronecker product of two arrays.
|
|
451
|
-
|
|
452
|
-
Computes the Kronecker product, a composite array made of blocks of the
|
|
453
|
-
second array scaled by the first.
|
|
454
|
-
|
|
455
|
-
Equivalent to ``numpy.kron`` for NumPy arrays.
|
|
456
|
-
|
|
457
|
-
Parameters
|
|
458
|
-
----------
|
|
459
|
-
a, b : Array | int | float | complex
|
|
460
|
-
Input arrays or scalars. At least one must be an array.
|
|
461
|
-
xp : array_namespace, optional
|
|
462
|
-
The standard-compatible namespace for `a` and `b`. Default: infer.
|
|
463
|
-
|
|
464
|
-
Returns
|
|
465
|
-
-------
|
|
466
|
-
array
|
|
467
|
-
The Kronecker product of `a` and `b`.
|
|
468
|
-
|
|
469
|
-
Notes
|
|
470
|
-
-----
|
|
471
|
-
The function assumes that the number of dimensions of `a` and `b`
|
|
472
|
-
are the same, if necessary prepending the smallest with ones.
|
|
473
|
-
If ``a.shape = (r0,r1,..,rN)`` and ``b.shape = (s0,s1,...,sN)``,
|
|
474
|
-
the Kronecker product has shape ``(r0*s0, r1*s1, ..., rN*SN)``.
|
|
475
|
-
The elements are products of elements from `a` and `b`, organized
|
|
476
|
-
explicitly by::
|
|
477
|
-
|
|
478
|
-
kron(a,b)[k0,k1,...,kN] = a[i0,i1,...,iN] * b[j0,j1,...,jN]
|
|
479
|
-
|
|
480
|
-
where::
|
|
481
|
-
|
|
482
|
-
kt = it * st + jt, t = 0,...,N
|
|
483
|
-
|
|
484
|
-
In the common 2-D case (N=1), the block structure can be visualized::
|
|
485
|
-
|
|
486
|
-
[[ a[0,0]*b, a[0,1]*b, ... , a[0,-1]*b ],
|
|
487
|
-
[ ... ... ],
|
|
488
|
-
[ a[-1,0]*b, a[-1,1]*b, ... , a[-1,-1]*b ]]
|
|
489
|
-
|
|
490
|
-
Examples
|
|
491
|
-
--------
|
|
492
|
-
>>> import array_api_strict as xp
|
|
493
|
-
>>> import array_api_extra as xpx
|
|
494
|
-
>>> xpx.kron(xp.asarray([1, 10, 100]), xp.asarray([5, 6, 7]), xp=xp)
|
|
495
|
-
Array([ 5, 6, 7, 50, 60, 70, 500,
|
|
496
|
-
600, 700], dtype=array_api_strict.int64)
|
|
497
|
-
|
|
498
|
-
>>> xpx.kron(xp.asarray([5, 6, 7]), xp.asarray([1, 10, 100]), xp=xp)
|
|
499
|
-
Array([ 5, 50, 500, 6, 60, 600, 7,
|
|
500
|
-
70, 700], dtype=array_api_strict.int64)
|
|
501
|
-
|
|
502
|
-
>>> xpx.kron(xp.eye(2), xp.ones((2, 2)), xp=xp)
|
|
503
|
-
Array([[1., 1., 0., 0.],
|
|
504
|
-
[1., 1., 0., 0.],
|
|
505
|
-
[0., 0., 1., 1.],
|
|
506
|
-
[0., 0., 1., 1.]], dtype=array_api_strict.float64)
|
|
507
|
-
|
|
508
|
-
>>> a = xp.reshape(xp.arange(100), (2, 5, 2, 5))
|
|
509
|
-
>>> b = xp.reshape(xp.arange(24), (2, 3, 4))
|
|
510
|
-
>>> c = xpx.kron(a, b, xp=xp)
|
|
511
|
-
>>> c.shape
|
|
512
|
-
(2, 10, 6, 20)
|
|
513
|
-
>>> I = (1, 3, 0, 2)
|
|
514
|
-
>>> J = (0, 2, 1)
|
|
515
|
-
>>> J1 = (0,) + J # extend to ndim=4
|
|
516
|
-
>>> S1 = (1,) + b.shape
|
|
517
|
-
>>> K = tuple(xp.asarray(I) * xp.asarray(S1) + xp.asarray(J1))
|
|
518
|
-
>>> c[K] == a[I]*b[J]
|
|
519
|
-
Array(True, dtype=array_api_strict.bool)
|
|
520
|
-
"""
|
|
521
|
-
if xp is None:
|
|
522
|
-
xp = array_namespace(a, b)
|
|
523
|
-
a, b = asarrays(a, b, xp=xp)
|
|
412
|
+
xp: ModuleType,
|
|
413
|
+
) -> Array: # numpydoc ignore=PR01,RT01
|
|
414
|
+
"""See docstring in array_api_extra._delegation."""
|
|
524
415
|
|
|
525
416
|
singletons = (1,) * (b.ndim - a.ndim)
|
|
526
417
|
a = cast(Array, xp.broadcast_to(a, singletons + a.shape))
|
|
@@ -818,3 +709,51 @@ def union1d(a: Array, b: Array, /, *, xp: ModuleType) -> Array:
|
|
|
818
709
|
b = xp.reshape(b, (-1,))
|
|
819
710
|
# XXX: `sparse` returns NumPy arrays from `unique_values`
|
|
820
711
|
return xp.asarray(xp.unique_values(xp.concat([a, b])))
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def angle(z: Array, /, *, deg: bool = False, xp: ModuleType | None = None) -> Array:
|
|
715
|
+
"""
|
|
716
|
+
Return the angle of the complex argument.
|
|
717
|
+
|
|
718
|
+
Parameters
|
|
719
|
+
----------
|
|
720
|
+
z : Array
|
|
721
|
+
Input array.
|
|
722
|
+
deg : bool, optional
|
|
723
|
+
Return angle in degrees if True, radians if False (default).
|
|
724
|
+
xp : array_namespace, optional
|
|
725
|
+
The standard-compatible namespace for `z`. Default: infer.
|
|
726
|
+
|
|
727
|
+
Returns
|
|
728
|
+
-------
|
|
729
|
+
array
|
|
730
|
+
The counterclockwise angle from the positive real axis on the complex
|
|
731
|
+
plane in the range ``(-pi, pi]``.
|
|
732
|
+
|
|
733
|
+
Notes
|
|
734
|
+
-----
|
|
735
|
+
Real input ``x`` is interpreted as ``x + 0j``.
|
|
736
|
+
|
|
737
|
+
Examples
|
|
738
|
+
--------
|
|
739
|
+
>>> import array_api_strict as xp
|
|
740
|
+
>>> import array_api_extra as xpx
|
|
741
|
+
>>> xpx.angle(xp.asarray([1.0, 1.0j, 1 + 1j]), xp=xp)
|
|
742
|
+
Array([0. , 1.57079633, 0.78539816], dtype=array_api_strict.float64)
|
|
743
|
+
>>> xpx.angle(xp.asarray([1.0, 1.0j, 1 + 1j]), deg=True, xp=xp)
|
|
744
|
+
Array([ 0., 90., 45.], dtype=array_api_strict.float64)
|
|
745
|
+
"""
|
|
746
|
+
if xp is None:
|
|
747
|
+
xp = array_namespace(z)
|
|
748
|
+
if xp.isdtype(z.dtype, "complex floating"):
|
|
749
|
+
zimag = xp.imag(z)
|
|
750
|
+
zreal = xp.real(z)
|
|
751
|
+
else:
|
|
752
|
+
if not xp.isdtype(z.dtype, "real floating"):
|
|
753
|
+
z = xp.astype(z, default_dtype(xp, device=_compat.device(z)))
|
|
754
|
+
zimag = xp.zeros_like(z)
|
|
755
|
+
zreal = z
|
|
756
|
+
a = xp.atan2(zimag, zreal)
|
|
757
|
+
if deg:
|
|
758
|
+
a = a * 180 / xp.pi
|
|
759
|
+
return a
|
array_api_extra/_lib/_lazy.py
CHANGED
|
@@ -30,7 +30,7 @@ else:
|
|
|
30
30
|
P = ParamSpec("P")
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
@overload
|
|
33
|
+
@overload # pyrefly: ignore[invalid-param-spec]
|
|
34
34
|
def lazy_apply( # type: ignore[valid-type]
|
|
35
35
|
func: Callable[P, Array | ArrayLike],
|
|
36
36
|
*args: Array | complex | None,
|
|
@@ -42,7 +42,7 @@ def lazy_apply( # type: ignore[valid-type]
|
|
|
42
42
|
) -> Array: ... # numpydoc ignore=GL08
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
@overload
|
|
45
|
+
@overload # pyrefly: ignore[invalid-param-spec]
|
|
46
46
|
def lazy_apply( # type: ignore[valid-type]
|
|
47
47
|
func: Callable[P, Sequence[Array | ArrayLike]],
|
|
48
48
|
*args: Array | complex | None,
|
|
@@ -54,7 +54,7 @@ def lazy_apply( # type: ignore[valid-type]
|
|
|
54
54
|
) -> tuple[Array, ...]: ... # numpydoc ignore=GL08
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def lazy_apply( # type: ignore[valid-type] # numpydoc ignore=GL07,SA04
|
|
57
|
+
def lazy_apply( # type: ignore[valid-type] # pyrefly: ignore[invalid-param-spec] # numpydoc ignore=GL07,SA04
|
|
58
58
|
func: Callable[P, Array | ArrayLike | Sequence[Array | ArrayLike]],
|
|
59
59
|
*args: Array | complex | None,
|
|
60
60
|
shape: tuple[int | None, ...] | Sequence[tuple[int | None, ...]] | None = None,
|
|
@@ -240,7 +240,7 @@ def lazy_apply( # type: ignore[valid-type] # numpydoc ignore=GL07,SA04
|
|
|
240
240
|
if is_dask_namespace(xp):
|
|
241
241
|
import dask
|
|
242
242
|
|
|
243
|
-
metas: list[Array] = [arg._meta for arg in array_args] # pylint: disable=protected-access
|
|
243
|
+
metas: list[Array] = [arg._meta for arg in array_args] # pylint: disable=protected-access # pyright: ignore[reportAttributeAccessIssue] # pyrefly: ignore[missing-attribute]
|
|
244
244
|
meta_xp = array_namespace(*metas)
|
|
245
245
|
|
|
246
246
|
wrapped = dask.delayed( # type: ignore[attr-defined] # pyright: ignore[reportPrivateImportUsage]
|