array-api-strict 2.2__py3-none-any.whl → 2.3.1__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.
Files changed (30) hide show
  1. array_api_strict/__init__.py +10 -7
  2. array_api_strict/_array_object.py +31 -19
  3. array_api_strict/_creation_functions.py +2 -0
  4. array_api_strict/_data_type_functions.py +34 -3
  5. array_api_strict/_dtypes.py +1 -0
  6. array_api_strict/_elementwise_functions.py +140 -471
  7. array_api_strict/_fft.py +31 -5
  8. array_api_strict/_flags.py +10 -8
  9. array_api_strict/_helpers.py +37 -0
  10. array_api_strict/_linear_algebra_functions.py +1 -1
  11. array_api_strict/_manipulation_functions.py +5 -0
  12. array_api_strict/_searching_functions.py +34 -5
  13. array_api_strict/_statistical_functions.py +42 -5
  14. array_api_strict/_version.py +16 -16
  15. array_api_strict/tests/test_array_object.py +146 -74
  16. array_api_strict/tests/test_creation_functions.py +7 -0
  17. array_api_strict/tests/test_data_type_functions.py +24 -4
  18. array_api_strict/tests/test_elementwise_functions.py +66 -8
  19. array_api_strict/tests/test_flags.py +29 -18
  20. array_api_strict/tests/test_manipulation_functions.py +1 -1
  21. array_api_strict/tests/test_searching_functions.py +44 -0
  22. array_api_strict/tests/test_statistical_functions.py +19 -1
  23. array_api_strict/tests/test_validation.py +1 -1
  24. array_api_strict-2.3.1.dist-info/METADATA +68 -0
  25. array_api_strict-2.3.1.dist-info/RECORD +43 -0
  26. {array_api_strict-2.2.dist-info → array_api_strict-2.3.1.dist-info}/WHEEL +1 -1
  27. array_api_strict-2.2.dist-info/METADATA +0 -37
  28. array_api_strict-2.2.dist-info/RECORD +0 -41
  29. {array_api_strict-2.2.dist-info → array_api_strict-2.3.1.dist-info}/LICENSE +0 -0
  30. {array_api_strict-2.2.dist-info → array_api_strict-2.3.1.dist-info}/top_level.txt +0 -0
@@ -293,9 +293,9 @@ from ._manipulation_functions import (
293
293
 
294
294
  __all__ += ["concat", "expand_dims", "flip", "moveaxis", "permute_dims", "repeat", "reshape", "roll", "squeeze", "stack", "tile", "unstack"]
295
295
 
296
- from ._searching_functions import argmax, argmin, nonzero, searchsorted, where
296
+ from ._searching_functions import argmax, argmin, nonzero, count_nonzero, searchsorted, where
297
297
 
298
- __all__ += ["argmax", "argmin", "nonzero", "searchsorted", "where"]
298
+ __all__ += ["argmax", "argmin", "nonzero", "count_nonzero", "searchsorted", "where"]
299
299
 
300
300
  from ._set_functions import unique_all, unique_counts, unique_inverse, unique_values
301
301
 
@@ -305,9 +305,9 @@ from ._sorting_functions import argsort, sort
305
305
 
306
306
  __all__ += ["argsort", "sort"]
307
307
 
308
- 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
309
309
 
310
- __all__ += ["cumulative_sum", "max", "mean", "min", "prod", "std", "sum", "var"]
310
+ __all__ += ["cumulative_sum", "cumulative_prod", "max", "mean", "min", "prod", "std", "sum", "var"]
311
311
 
312
312
  from ._utility_functions import all, any, diff
313
313
 
@@ -327,9 +327,12 @@ from ._flags import (
327
327
 
328
328
  __all__ += ['set_array_api_strict_flags', 'get_array_api_strict_flags', 'reset_array_api_strict_flags', 'ArrayAPIStrictFlags']
329
329
 
330
- from . import _version
331
- __version__ = _version.get_versions()['version']
332
- del _version
330
+ try:
331
+ from . import _version
332
+ __version__ = _version.__version__
333
+ del _version
334
+ except ImportError:
335
+ __version__ = "unknown"
333
336
 
334
337
 
335
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 _complex_floating_dtypes:
274
+ if self.dtype not in _floating_dtypes:
276
275
  raise TypeError(
277
- "Python complex scalars can only be promoted with complex floating-point arrays."
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=self.dtype._np_dtype), device=self.device)
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("The boolean_indexing flag has been disabled for array-api-strict")
485
-
486
- elif i.dtype in _integer_dtypes and i.ndim != 0:
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
- return A[0]
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
- return t
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
@@ -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 +