array-api-strict 2.1.3__py3-none-any.whl → 2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- array_api_strict/__init__.py +18 -11
- array_api_strict/_array_object.py +31 -19
- array_api_strict/_creation_functions.py +2 -0
- array_api_strict/_data_type_functions.py +34 -3
- array_api_strict/_dtypes.py +1 -0
- array_api_strict/_elementwise_functions.py +146 -452
- array_api_strict/_fft.py +31 -5
- array_api_strict/_flags.py +16 -9
- array_api_strict/_helpers.py +37 -0
- array_api_strict/_indexing_functions.py +12 -0
- array_api_strict/_info.py +121 -114
- array_api_strict/_linear_algebra_functions.py +1 -1
- array_api_strict/_manipulation_functions.py +5 -0
- array_api_strict/_searching_functions.py +44 -5
- array_api_strict/_statistical_functions.py +42 -5
- array_api_strict/_typing.py +4 -3
- array_api_strict/_utility_functions.py +30 -0
- array_api_strict/_version.py +16 -16
- array_api_strict/tests/test_array_object.py +148 -72
- array_api_strict/tests/test_creation_functions.py +7 -0
- array_api_strict/tests/test_data_type_functions.py +24 -4
- array_api_strict/tests/test_elementwise_functions.py +69 -5
- array_api_strict/tests/test_flags.py +71 -14
- array_api_strict/tests/test_manipulation_functions.py +1 -1
- array_api_strict/tests/test_searching_functions.py +24 -0
- array_api_strict/tests/test_statistical_functions.py +19 -1
- array_api_strict/tests/test_validation.py +1 -1
- array_api_strict-2.3.dist-info/METADATA +68 -0
- array_api_strict-2.3.dist-info/RECORD +43 -0
- {array_api_strict-2.1.3.dist-info → array_api_strict-2.3.dist-info}/WHEEL +1 -1
- array_api_strict-2.1.3.dist-info/METADATA +0 -37
- array_api_strict-2.1.3.dist-info/RECORD +0 -41
- {array_api_strict-2.1.3.dist-info → array_api_strict-2.3.dist-info}/LICENSE +0 -0
- {array_api_strict-2.1.3.dist-info → array_api_strict-2.3.dist-info}/top_level.txt +0 -0
array_api_strict/__init__.py
CHANGED
|
@@ -172,10 +172,12 @@ from ._elementwise_functions import (
|
|
|
172
172
|
minimum,
|
|
173
173
|
multiply,
|
|
174
174
|
negative,
|
|
175
|
+
nextafter,
|
|
175
176
|
not_equal,
|
|
176
177
|
positive,
|
|
177
178
|
pow,
|
|
178
179
|
real,
|
|
180
|
+
reciprocal,
|
|
179
181
|
remainder,
|
|
180
182
|
round,
|
|
181
183
|
sign,
|
|
@@ -240,10 +242,12 @@ __all__ += [
|
|
|
240
242
|
"minimum",
|
|
241
243
|
"multiply",
|
|
242
244
|
"negative",
|
|
245
|
+
"nextafter",
|
|
243
246
|
"not_equal",
|
|
244
247
|
"positive",
|
|
245
248
|
"pow",
|
|
246
249
|
"real",
|
|
250
|
+
"reciprocal",
|
|
247
251
|
"remainder",
|
|
248
252
|
"round",
|
|
249
253
|
"sign",
|
|
@@ -258,9 +262,9 @@ __all__ += [
|
|
|
258
262
|
"trunc",
|
|
259
263
|
]
|
|
260
264
|
|
|
261
|
-
from ._indexing_functions import take
|
|
265
|
+
from ._indexing_functions import take, take_along_axis
|
|
262
266
|
|
|
263
|
-
__all__ += ["take"]
|
|
267
|
+
__all__ += ["take", "take_along_axis"]
|
|
264
268
|
|
|
265
269
|
from ._info import __array_namespace_info__
|
|
266
270
|
|
|
@@ -289,9 +293,9 @@ from ._manipulation_functions import (
|
|
|
289
293
|
|
|
290
294
|
__all__ += ["concat", "expand_dims", "flip", "moveaxis", "permute_dims", "repeat", "reshape", "roll", "squeeze", "stack", "tile", "unstack"]
|
|
291
295
|
|
|
292
|
-
from ._searching_functions import argmax, argmin, nonzero, searchsorted, where
|
|
296
|
+
from ._searching_functions import argmax, argmin, nonzero, count_nonzero, searchsorted, where
|
|
293
297
|
|
|
294
|
-
__all__ += ["argmax", "argmin", "nonzero", "searchsorted", "where"]
|
|
298
|
+
__all__ += ["argmax", "argmin", "nonzero", "count_nonzero", "searchsorted", "where"]
|
|
295
299
|
|
|
296
300
|
from ._set_functions import unique_all, unique_counts, unique_inverse, unique_values
|
|
297
301
|
|
|
@@ -301,13 +305,13 @@ from ._sorting_functions import argsort, sort
|
|
|
301
305
|
|
|
302
306
|
__all__ += ["argsort", "sort"]
|
|
303
307
|
|
|
304
|
-
from ._statistical_functions import cumulative_sum, max, mean, min, prod, std, sum, var
|
|
308
|
+
from ._statistical_functions import cumulative_sum, cumulative_prod, max, mean, min, prod, std, sum, var
|
|
305
309
|
|
|
306
|
-
__all__ += ["cumulative_sum", "max", "mean", "min", "prod", "std", "sum", "var"]
|
|
310
|
+
__all__ += ["cumulative_sum", "cumulative_prod", "max", "mean", "min", "prod", "std", "sum", "var"]
|
|
307
311
|
|
|
308
|
-
from ._utility_functions import all, any
|
|
312
|
+
from ._utility_functions import all, any, diff
|
|
309
313
|
|
|
310
|
-
__all__ += ["all", "any"]
|
|
314
|
+
__all__ += ["all", "any", "diff"]
|
|
311
315
|
|
|
312
316
|
from ._array_object import Device
|
|
313
317
|
__all__ += ["Device"]
|
|
@@ -323,9 +327,12 @@ from ._flags import (
|
|
|
323
327
|
|
|
324
328
|
__all__ += ['set_array_api_strict_flags', 'get_array_api_strict_flags', 'reset_array_api_strict_flags', 'ArrayAPIStrictFlags']
|
|
325
329
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
330
|
+
try:
|
|
331
|
+
from . import _version
|
|
332
|
+
__version__ = _version.__version__
|
|
333
|
+
del _version
|
|
334
|
+
except ImportError:
|
|
335
|
+
__version__ = "unknown"
|
|
329
336
|
|
|
330
337
|
|
|
331
338
|
# Extensions can be enabled or disabled dynamically. In order to make
|
|
@@ -26,10 +26,12 @@ from ._dtypes import (
|
|
|
26
26
|
_integer_dtypes,
|
|
27
27
|
_integer_or_boolean_dtypes,
|
|
28
28
|
_floating_dtypes,
|
|
29
|
+
_real_floating_dtypes,
|
|
29
30
|
_complex_floating_dtypes,
|
|
30
31
|
_numeric_dtypes,
|
|
31
32
|
_result_type,
|
|
32
33
|
_dtype_categories,
|
|
34
|
+
_real_to_complex_map,
|
|
33
35
|
)
|
|
34
36
|
from ._flags import get_array_api_strict_flags, set_array_api_strict_flags
|
|
35
37
|
|
|
@@ -126,12 +128,6 @@ class Array:
|
|
|
126
128
|
# These functions are not required by the spec, but are implemented for
|
|
127
129
|
# the sake of usability.
|
|
128
130
|
|
|
129
|
-
def __str__(self: Array, /) -> str:
|
|
130
|
-
"""
|
|
131
|
-
Performs the operation __str__.
|
|
132
|
-
"""
|
|
133
|
-
return self._array.__str__().replace("array", "Array")
|
|
134
|
-
|
|
135
131
|
def __repr__(self: Array, /) -> str:
|
|
136
132
|
"""
|
|
137
133
|
Performs the operation __repr__.
|
|
@@ -149,6 +145,8 @@ class Array:
|
|
|
149
145
|
mid = np.array2string(self._array, separator=', ', prefix=prefix, suffix=suffix)
|
|
150
146
|
return prefix + mid + suffix
|
|
151
147
|
|
|
148
|
+
__str__ = __repr__
|
|
149
|
+
|
|
152
150
|
# In the future, _allow_array will be set to False, which will disallow
|
|
153
151
|
# __array__. This means calling `np.func()` on an array_api_strict array
|
|
154
152
|
# will give an error. If we don't explicitly disallow it, NumPy defaults
|
|
@@ -247,6 +245,7 @@ class Array:
|
|
|
247
245
|
"""
|
|
248
246
|
from ._data_type_functions import iinfo
|
|
249
247
|
|
|
248
|
+
target_dtype = self.dtype
|
|
250
249
|
# Note: Only Python scalar types that match the array dtype are
|
|
251
250
|
# allowed.
|
|
252
251
|
if isinstance(scalar, bool):
|
|
@@ -272,10 +271,13 @@ class Array:
|
|
|
272
271
|
"Python float scalars can only be promoted with floating-point arrays."
|
|
273
272
|
)
|
|
274
273
|
elif isinstance(scalar, complex):
|
|
275
|
-
if self.dtype not in
|
|
274
|
+
if self.dtype not in _floating_dtypes:
|
|
276
275
|
raise TypeError(
|
|
277
|
-
"Python complex scalars can only be promoted with
|
|
276
|
+
"Python complex scalars can only be promoted with floating-point arrays."
|
|
278
277
|
)
|
|
278
|
+
# 1j * array(floating) is allowed
|
|
279
|
+
if self.dtype in _real_floating_dtypes:
|
|
280
|
+
target_dtype = _real_to_complex_map[self.dtype]
|
|
279
281
|
else:
|
|
280
282
|
raise TypeError("'scalar' must be a Python scalar")
|
|
281
283
|
|
|
@@ -286,7 +288,7 @@ class Array:
|
|
|
286
288
|
# behavior for integers within the bounds of the integer dtype.
|
|
287
289
|
# Outside of those bounds we use the default NumPy behavior (either
|
|
288
290
|
# cast or raise OverflowError).
|
|
289
|
-
return Array._new(np.array(scalar, dtype=
|
|
291
|
+
return Array._new(np.array(scalar, dtype=target_dtype._np_dtype), device=self.device)
|
|
290
292
|
|
|
291
293
|
@staticmethod
|
|
292
294
|
def _normalize_two_args(x1, x2) -> Tuple[Array, Array]:
|
|
@@ -325,7 +327,7 @@ class Array:
|
|
|
325
327
|
|
|
326
328
|
# Note: A large fraction of allowed indices are disallowed here (see the
|
|
327
329
|
# docstring below)
|
|
328
|
-
def _validate_index(self, key):
|
|
330
|
+
def _validate_index(self, key, op="getitem"):
|
|
329
331
|
"""
|
|
330
332
|
Validate an index according to the array API.
|
|
331
333
|
|
|
@@ -388,11 +390,16 @@ class Array:
|
|
|
388
390
|
"zero-dimensional integer arrays and boolean arrays "
|
|
389
391
|
"are specified in the Array API."
|
|
390
392
|
)
|
|
393
|
+
if op == "setitem":
|
|
394
|
+
if isinstance(i, Array) and i.dtype in _integer_dtypes:
|
|
395
|
+
raise IndexError("Fancy indexing __setitem__ is not supported.")
|
|
391
396
|
|
|
392
397
|
nonexpanding_key = []
|
|
393
398
|
single_axes = []
|
|
394
399
|
n_ellipsis = 0
|
|
395
400
|
key_has_mask = False
|
|
401
|
+
key_has_index_array = False
|
|
402
|
+
key_has_slices = False
|
|
396
403
|
for i in _key:
|
|
397
404
|
if i is not None:
|
|
398
405
|
nonexpanding_key.append(i)
|
|
@@ -401,6 +408,8 @@ class Array:
|
|
|
401
408
|
if isinstance(i, Array):
|
|
402
409
|
if i.dtype in _boolean_dtypes:
|
|
403
410
|
key_has_mask = True
|
|
411
|
+
elif i.dtype in _integer_dtypes:
|
|
412
|
+
key_has_index_array = True
|
|
404
413
|
single_axes.append(i)
|
|
405
414
|
else:
|
|
406
415
|
# i must not be an array here, to avoid elementwise equals
|
|
@@ -408,6 +417,8 @@ class Array:
|
|
|
408
417
|
n_ellipsis += 1
|
|
409
418
|
else:
|
|
410
419
|
single_axes.append(i)
|
|
420
|
+
if isinstance(i, slice):
|
|
421
|
+
key_has_slices = True
|
|
411
422
|
|
|
412
423
|
n_single_axes = len(single_axes)
|
|
413
424
|
if n_ellipsis > 1:
|
|
@@ -425,6 +436,12 @@ class Array:
|
|
|
425
436
|
"specified in the Array API."
|
|
426
437
|
)
|
|
427
438
|
|
|
439
|
+
if (key_has_index_array and (n_ellipsis > 0 or key_has_slices or key_has_mask)):
|
|
440
|
+
raise IndexError(
|
|
441
|
+
"Integer index arrays are only allowed with integer indices; "
|
|
442
|
+
f"got {key}."
|
|
443
|
+
)
|
|
444
|
+
|
|
428
445
|
if n_ellipsis == 0:
|
|
429
446
|
indexed_shape = self.shape
|
|
430
447
|
else:
|
|
@@ -481,14 +498,9 @@ class Array:
|
|
|
481
498
|
"Array API when the array is the sole index."
|
|
482
499
|
)
|
|
483
500
|
if not get_array_api_strict_flags()['boolean_indexing']:
|
|
484
|
-
raise RuntimeError(
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
raise IndexError(
|
|
488
|
-
f"Single-axes index {i} is a non-zero-dimensional "
|
|
489
|
-
"integer array, but advanced integer indexing is not "
|
|
490
|
-
"specified in the Array API."
|
|
491
|
-
)
|
|
501
|
+
raise RuntimeError(
|
|
502
|
+
"The boolean_indexing flag has been disabled for array-api-strict"
|
|
503
|
+
)
|
|
492
504
|
elif isinstance(i, tuple):
|
|
493
505
|
raise IndexError(
|
|
494
506
|
f"Single-axes index {i} is a tuple, but nested tuple "
|
|
@@ -900,7 +912,7 @@ class Array:
|
|
|
900
912
|
"""
|
|
901
913
|
# Note: Only indices required by the spec are allowed. See the
|
|
902
914
|
# docstring of _validate_index
|
|
903
|
-
self._validate_index(key)
|
|
915
|
+
self._validate_index(key, op="setitem")
|
|
904
916
|
if isinstance(key, Array):
|
|
905
917
|
# Indexing self._array with array_api_strict arrays can be erroneous
|
|
906
918
|
key = key._array
|
|
@@ -226,6 +226,8 @@ def from_dlpack(
|
|
|
226
226
|
# Going to wait for upstream numpy support
|
|
227
227
|
if device is not _default:
|
|
228
228
|
_check_device(device)
|
|
229
|
+
else:
|
|
230
|
+
device = None
|
|
229
231
|
if copy not in [_default, None]:
|
|
230
232
|
raise NotImplementedError("The copy argument to from_dlpack is not yet implemented")
|
|
231
233
|
|
|
@@ -42,6 +42,13 @@ def astype(
|
|
|
42
42
|
|
|
43
43
|
if not copy and dtype == x.dtype:
|
|
44
44
|
return x
|
|
45
|
+
|
|
46
|
+
if isdtype(x.dtype, 'complex floating') and not isdtype(dtype, 'complex floating'):
|
|
47
|
+
raise TypeError(
|
|
48
|
+
f'The Array API standard stipulates that casting {x.dtype} to {dtype} should not be permitted. '
|
|
49
|
+
'array-api-strict thus prohibits this conversion.'
|
|
50
|
+
)
|
|
51
|
+
|
|
45
52
|
return Array._new(x._array.astype(dtype=dtype._np_dtype, copy=copy), device=device)
|
|
46
53
|
|
|
47
54
|
|
|
@@ -160,6 +167,9 @@ def isdtype(
|
|
|
160
167
|
https://data-apis.org/array-api/latest/API_specification/generated/array_api.isdtype.html
|
|
161
168
|
for more details
|
|
162
169
|
"""
|
|
170
|
+
if not isinstance(dtype, _DType):
|
|
171
|
+
raise TypeError(f"'dtype' must be a dtype, not a {type(dtype)!r}")
|
|
172
|
+
|
|
163
173
|
if isinstance(kind, tuple):
|
|
164
174
|
# Disallow nested tuples
|
|
165
175
|
if any(isinstance(k, tuple) for k in kind):
|
|
@@ -187,7 +197,7 @@ def isdtype(
|
|
|
187
197
|
else:
|
|
188
198
|
raise TypeError(f"'kind' must be a dtype, str, or tuple of dtypes and strs, not {type(kind).__name__}")
|
|
189
199
|
|
|
190
|
-
def result_type(*arrays_and_dtypes: Union[Array, Dtype]) -> Dtype:
|
|
200
|
+
def result_type(*arrays_and_dtypes: Union[Array, Dtype, int, float, complex, bool]) -> Dtype:
|
|
191
201
|
"""
|
|
192
202
|
Array API compatible wrapper for :py:func:`np.result_type <numpy.result_type>`.
|
|
193
203
|
|
|
@@ -198,19 +208,40 @@ def result_type(*arrays_and_dtypes: Union[Array, Dtype]) -> Dtype:
|
|
|
198
208
|
# too many extra type promotions like int64 + uint64 -> float64, and does
|
|
199
209
|
# value-based casting on scalar arrays.
|
|
200
210
|
A = []
|
|
211
|
+
scalars = []
|
|
201
212
|
for a in arrays_and_dtypes:
|
|
202
213
|
if isinstance(a, Array):
|
|
203
214
|
a = a.dtype
|
|
215
|
+
elif isinstance(a, (bool, int, float, complex)):
|
|
216
|
+
scalars.append(a)
|
|
204
217
|
elif isinstance(a, np.ndarray) or a not in _all_dtypes:
|
|
205
218
|
raise TypeError("result_type() inputs must be array_api arrays or dtypes")
|
|
206
219
|
A.append(a)
|
|
207
220
|
|
|
221
|
+
# remove python scalars
|
|
222
|
+
A = [a for a in A if not isinstance(a, (bool, int, float, complex))]
|
|
223
|
+
|
|
208
224
|
if len(A) == 0:
|
|
209
225
|
raise ValueError("at least one array or dtype is required")
|
|
210
226
|
elif len(A) == 1:
|
|
211
|
-
|
|
227
|
+
result = A[0]
|
|
212
228
|
else:
|
|
213
229
|
t = A[0]
|
|
214
230
|
for t2 in A[1:]:
|
|
215
231
|
t = _result_type(t, t2)
|
|
216
|
-
|
|
232
|
+
result = t
|
|
233
|
+
|
|
234
|
+
if len(scalars) == 0:
|
|
235
|
+
return result
|
|
236
|
+
|
|
237
|
+
if get_array_api_strict_flags()['api_version'] <= '2023.12':
|
|
238
|
+
raise TypeError("result_type() inputs must be array_api arrays or dtypes")
|
|
239
|
+
|
|
240
|
+
# promote python scalars given the result_type for all arrays/dtypes
|
|
241
|
+
from ._creation_functions import empty
|
|
242
|
+
arr = empty(1, dtype=result)
|
|
243
|
+
for s in scalars:
|
|
244
|
+
x = arr._promote_scalar(s)
|
|
245
|
+
result = _result_type(x.dtype, result)
|
|
246
|
+
|
|
247
|
+
return result
|
array_api_strict/_dtypes.py
CHANGED
|
@@ -126,6 +126,7 @@ _dtype_categories = {
|
|
|
126
126
|
"floating-point": _floating_dtypes,
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
_real_to_complex_map = {float32: complex64, float64: complex128}
|
|
129
130
|
|
|
130
131
|
# Note: the spec defines a restricted type promotion table compared to NumPy.
|
|
131
132
|
# In particular, cross-kind promotions like integer + float or boolean +
|