Download lib/python3.12/site-packages/numpy/array_api/_array_object.py from parallel-reasoner/env-full: direct link, hf CLI and curl.
- Browser
- Download file 43.8 kB
-
https://huggingface.co/parallel-reasoner/env-full/resolve/b9918be14658eb32f7f27abe8daee683afea2901/lib/python3.12/site-packages/numpy/array_api/_array_object.py
- Command line
-
hf download hf://parallel-reasoner/env-full@b9918be14658eb32f7f27abe8daee683afea2901/lib/python3.12/site-packages/numpy/array_api/_array_object.py
-
curl -L -o _array_object.py https://huggingface.co/parallel-reasoner/env-full/resolve/b9918be14658eb32f7f27abe8daee683afea2901/lib/python3.12/site-packages/numpy/array_api/_array_object.py
43.8 kB
| """ | |
| Wrapper class around the ndarray object for the array API standard. | |
| The array API standard defines some behaviors differently than ndarray, in | |
| particular, type promotion rules are different (the standard has no | |
| value-based casting). The standard also specifies a more limited subset of | |
| array methods and functionalities than are implemented on ndarray. Since the | |
| goal of the array_api namespace is to be a minimal implementation of the array | |
| API standard, we need to define a separate wrapper class for the array_api | |
| namespace. | |
| The standard compliant class is only a wrapper class. It is *not* a subclass | |
| of ndarray. | |
| """ | |
| from __future__ import annotations | |
| import operator | |
| from enum import IntEnum | |
| from ._creation_functions import asarray | |
| from ._dtypes import ( | |
| _all_dtypes, | |
| _boolean_dtypes, | |
| _integer_dtypes, | |
| _integer_or_boolean_dtypes, | |
| _floating_dtypes, | |
| _complex_floating_dtypes, | |
| _numeric_dtypes, | |
| _result_type, | |
| _dtype_categories, | |
| ) | |
| from typing import TYPE_CHECKING, Optional, Tuple, Union, Any, SupportsIndex | |
| import types | |
| if TYPE_CHECKING: | |
| from ._typing import Any, PyCapsule, Device, Dtype | |
| import numpy.typing as npt | |
| import numpy as np | |
| from numpy import array_api | |
| class Array: | |
| """ | |
| n-d array object for the array API namespace. | |
| See the docstring of :py:obj:`np.ndarray <numpy.ndarray>` for more | |
| information. | |
| This is a wrapper around numpy.ndarray that restricts the usage to only | |
| those things that are required by the array API namespace. Note, | |
| attributes on this object that start with a single underscore are not part | |
| of the API specification and should only be used internally. This object | |
| should not be constructed directly. Rather, use one of the creation | |
| functions, such as asarray(). | |
| """ | |
| _array: np.ndarray[Any, Any] | |
| # Use a custom constructor instead of __init__, as manually initializing | |
| # this class is not supported API. | |
| def _new(cls, x, /): | |
| """ | |
| This is a private method for initializing the array API Array | |
| object. | |
| Functions outside of the array_api submodule should not use this | |
| method. Use one of the creation functions instead, such as | |
| ``asarray``. | |
| """ | |
| obj = super().__new__(cls) | |
| # Note: The spec does not have array scalars, only 0-D arrays. | |
| if isinstance(x, np.generic): | |
| # Convert the array scalar to a 0-D array | |
| x = np.asarray(x) | |
| if x.dtype not in _all_dtypes: | |
| raise TypeError( | |
| f"The array_api namespace does not support the dtype '{x.dtype}'" | |
| ) | |
| obj._array = x | |
| return obj | |
| # Prevent Array() from working | |
| def __new__(cls, *args, **kwargs): | |
| raise TypeError( | |
| "The array_api Array object should not be instantiated directly. Use an array creation function, such as asarray(), instead." | |
| ) | |
| # These functions are not required by the spec, but are implemented for | |
| # the sake of usability. | |
| def __str__(self: Array, /) -> str: | |
| """ | |
| Performs the operation __str__. | |
| """ | |
| return self._array.__str__().replace("array", "Array") | |
| def __repr__(self: Array, /) -> str: | |
| """ | |
| Performs the operation __repr__. | |
| """ | |
| suffix = f", dtype={self.dtype.name})" | |
| if 0 in self.shape: | |
| prefix = "empty(" | |
| mid = str(self.shape) | |
| else: | |
| prefix = "Array(" | |
| mid = np.array2string(self._array, separator=', ', prefix=prefix, suffix=suffix) | |
| return prefix + mid + suffix | |
| # This function is not required by the spec, but we implement it here for | |
| # convenience so that np.asarray(np.array_api.Array) will work. | |
| def __array__(self, dtype: None | np.dtype[Any] = None) -> npt.NDArray[Any]: | |
| """ | |
| Warning: this method is NOT part of the array API spec. Implementers | |
| of other libraries need not include it, and users should not assume it | |
| will be present in other implementations. | |
| """ | |
| return np.asarray(self._array, dtype=dtype) | |
| # These are various helper functions to make the array behavior match the | |
| # spec in places where it either deviates from or is more strict than | |
| # NumPy behavior | |
| def _check_allowed_dtypes(self, other: bool | int | float | Array, dtype_category: str, op: str) -> Array: | |
| """ | |
| Helper function for operators to only allow specific input dtypes | |
| Use like | |
| other = self._check_allowed_dtypes(other, 'numeric', '__add__') | |
| if other is NotImplemented: | |
| return other | |
| """ | |
| if self.dtype not in _dtype_categories[dtype_category]: | |
| raise TypeError(f"Only {dtype_category} dtypes are allowed in {op}") | |
| if isinstance(other, (int, complex, float, bool)): | |
| other = self._promote_scalar(other) | |
| elif isinstance(other, Array): | |
| if other.dtype not in _dtype_categories[dtype_category]: | |
| raise TypeError(f"Only {dtype_category} dtypes are allowed in {op}") | |
| else: | |
| return NotImplemented | |
| # This will raise TypeError for type combinations that are not allowed | |
| # to promote in the spec (even if the NumPy array operator would | |
| # promote them). | |
| res_dtype = _result_type(self.dtype, other.dtype) | |
| if op.startswith("__i"): | |
| # Note: NumPy will allow in-place operators in some cases where | |
| # the type promoted operator does not match the left-hand side | |
| # operand. For example, | |
| # >>> a = np.array(1, dtype=np.int8) | |
| # >>> a += np.array(1, dtype=np.int16) | |
| # The spec explicitly disallows this. | |
| if res_dtype != self.dtype: | |
| raise TypeError( | |
| f"Cannot perform {op} with dtypes {self.dtype} and {other.dtype}" | |
| ) | |
| return other | |
| # Helper function to match the type promotion rules in the spec | |
| def _promote_scalar(self, scalar): | |
| """ | |
| Returns a promoted version of a Python scalar appropriate for use with | |
| operations on self. | |
| This may raise an OverflowError in cases where the scalar is an | |
| integer that is too large to fit in a NumPy integer dtype, or | |
| TypeError when the scalar type is incompatible with the dtype of self. | |
| """ | |
| # Note: Only Python scalar types that match the array dtype are | |
| # allowed. | |
| if isinstance(scalar, bool): | |
| if self.dtype not in _boolean_dtypes: | |
| raise TypeError( | |
| "Python bool scalars can only be promoted with bool arrays" | |
| ) | |
| elif isinstance(scalar, int): | |
| if self.dtype in _boolean_dtypes: | |
| raise TypeError( | |
| "Python int scalars cannot be promoted with bool arrays" | |
| ) | |
| if self.dtype in _integer_dtypes: | |
| info = np.iinfo(self.dtype) | |
| if not (info.min <= scalar <= info.max): | |
| raise OverflowError( | |
| "Python int scalars must be within the bounds of the dtype for integer arrays" | |
| ) | |
| # int + array(floating) is allowed | |
| elif isinstance(scalar, float): | |
| if self.dtype not in _floating_dtypes: | |
| raise TypeError( | |
| "Python float scalars can only be promoted with floating-point arrays." | |
| ) | |
| elif isinstance(scalar, complex): | |
| if self.dtype not in _complex_floating_dtypes: | |
| raise TypeError( | |
| "Python complex scalars can only be promoted with complex floating-point arrays." | |
| ) | |
| else: | |
| raise TypeError("'scalar' must be a Python scalar") | |
| # Note: scalars are unconditionally cast to the same dtype as the | |
| # array. | |
| # Note: the spec only specifies integer-dtype/int promotion | |
| # behavior for integers within the bounds of the integer dtype. | |
| # Outside of those bounds we use the default NumPy behavior (either | |
| # cast or raise OverflowError). | |
| return Array._new(np.array(scalar, self.dtype)) | |
| def _normalize_two_args(x1, x2) -> Tuple[Array, Array]: | |
| """ | |
| Normalize inputs to two arg functions to fix type promotion rules | |
| NumPy deviates from the spec type promotion rules in cases where one | |
| argument is 0-dimensional and the other is not. For example: | |
| >>> import numpy as np | |
| >>> a = np.array([1.0], dtype=np.float32) | |
| >>> b = np.array(1.0, dtype=np.float64) | |
| >>> np.add(a, b) # The spec says this should be float64 | |
| array([2.], dtype=float32) | |
| To fix this, we add a dimension to the 0-dimension array before passing it | |
| through. This works because a dimension would be added anyway from | |
| broadcasting, so the resulting shape is the same, but this prevents NumPy | |
| from not promoting the dtype. | |
| """ | |
| # Another option would be to use signature=(x1.dtype, x2.dtype, None), | |
| # but that only works for ufuncs, so we would have to call the ufuncs | |
| # directly in the operator methods. One should also note that this | |
| # sort of trick wouldn't work for functions like searchsorted, which | |
| # don't do normal broadcasting, but there aren't any functions like | |
| # that in the array API namespace. | |
| if x1.ndim == 0 and x2.ndim != 0: | |
| # The _array[None] workaround was chosen because it is relatively | |
| # performant. broadcast_to(x1._array, x2.shape) is much slower. We | |
| # could also manually type promote x2, but that is more complicated | |
| # and about the same performance as this. | |
| x1 = Array._new(x1._array[None]) | |
| elif x2.ndim == 0 and x1.ndim != 0: | |
| x2 = Array._new(x2._array[None]) | |
| return (x1, x2) | |
| # Note: A large fraction of allowed indices are disallowed here (see the | |
| # docstring below) | |
| def _validate_index(self, key): | |
| """ | |
| Validate an index according to the array API. | |
| The array API specification only requires a subset of indices that are | |
| supported by NumPy. This function will reject any index that is | |
| allowed by NumPy but not required by the array API specification. We | |
| always raise ``IndexError`` on such indices (the spec does not require | |
| any specific behavior on them, but this makes the NumPy array API | |
| namespace a minimal implementation of the spec). See | |
| https://data-apis.org/array-api/latest/API_specification/indexing.html | |
| for the full list of required indexing behavior | |
| This function raises IndexError if the index ``key`` is invalid. It | |
| only raises ``IndexError`` on indices that are not already rejected by | |
| NumPy, as NumPy will already raise the appropriate error on such | |
| indices. ``shape`` may be None, in which case, only cases that are | |
| independent of the array shape are checked. | |
| The following cases are allowed by NumPy, but not specified by the array | |
| API specification: | |
| - Indices to not include an implicit ellipsis at the end. That is, | |
| every axis of an array must be explicitly indexed or an ellipsis | |
| included. This behaviour is sometimes referred to as flat indexing. | |
| - The start and stop of a slice may not be out of bounds. In | |
| particular, for a slice ``i:j:k`` on an axis of size ``n``, only the | |
| following are allowed: | |
| - ``i`` or ``j`` omitted (``None``). | |
| - ``-n <= i <= max(0, n - 1)``. | |
| - For ``k > 0`` or ``k`` omitted (``None``), ``-n <= j <= n``. | |
| - For ``k < 0``, ``-n - 1 <= j <= max(0, n - 1)``. | |
| - Boolean array indices are not allowed as part of a larger tuple | |
| index. | |
| - Integer array indices are not allowed (with the exception of 0-D | |
| arrays, which are treated the same as scalars). | |
| Additionally, it should be noted that indices that would return a | |
| scalar in NumPy will return a 0-D array. Array scalars are not allowed | |
| in the specification, only 0-D arrays. This is done in the | |
| ``Array._new`` constructor, not this function. | |
| """ | |
| _key = key if isinstance(key, tuple) else (key,) | |
| for i in _key: | |
| if isinstance(i, bool) or not ( | |
| isinstance(i, SupportsIndex) # i.e. ints | |
| or isinstance(i, slice) | |
| or i == Ellipsis | |
| or i is None | |
| or isinstance(i, Array) | |
| or isinstance(i, np.ndarray) | |
| ): | |
| raise IndexError( | |
| f"Single-axes index {i} has {type(i)=}, but only " | |
| "integers, slices (:), ellipsis (...), newaxis (None), " | |
| "zero-dimensional integer arrays and boolean arrays " | |
| "are specified in the Array API." | |
| ) | |
| nonexpanding_key = [] | |
| single_axes = [] | |
| n_ellipsis = 0 | |
| key_has_mask = False | |
| for i in _key: | |
| if i is not None: | |
| nonexpanding_key.append(i) | |
| if isinstance(i, Array) or isinstance(i, np.ndarray): | |
| if i.dtype in _boolean_dtypes: | |
| key_has_mask = True | |
| single_axes.append(i) | |
| else: | |
| # i must not be an array here, to avoid elementwise equals | |
| if i == Ellipsis: | |
| n_ellipsis += 1 | |
| else: | |
| single_axes.append(i) | |
| n_single_axes = len(single_axes) | |
| if n_ellipsis > 1: | |
| return # handled by ndarray | |
| elif n_ellipsis == 0: | |
| # Note boolean masks must be the sole index, which we check for | |
| # later on. | |
| if not key_has_mask and n_single_axes < self.ndim: | |
| raise IndexError( | |
| f"{self.ndim=}, but the multi-axes index only specifies " | |
| f"{n_single_axes} dimensions. If this was intentional, " | |
| "add a trailing ellipsis (...) which expands into as many " | |
| "slices (:) as necessary - this is what np.ndarray arrays " | |
| "implicitly do, but such flat indexing behaviour is not " | |
| "specified in the Array API." | |
| ) | |
| if n_ellipsis == 0: | |
| indexed_shape = self.shape | |
| else: | |
| ellipsis_start = None | |
| for pos, i in enumerate(nonexpanding_key): | |
| if not (isinstance(i, Array) or isinstance(i, np.ndarray)): | |
| if i == Ellipsis: | |
| ellipsis_start = pos | |
| break | |
| assert ellipsis_start is not None # sanity check | |
| ellipsis_end = self.ndim - (n_single_axes - ellipsis_start) | |
| indexed_shape = ( | |
| self.shape[:ellipsis_start] + self.shape[ellipsis_end:] | |
| ) | |
| for i, side in zip(single_axes, indexed_shape): | |
| if isinstance(i, slice): | |
| if side == 0: | |
| f_range = "0 (or None)" | |
| else: | |
| f_range = f"between -{side} and {side - 1} (or None)" | |
| if i.start is not None: | |
| try: | |
| start = operator.index(i.start) | |
| except TypeError: | |
| pass # handled by ndarray | |
| else: | |
| if not (-side <= start <= side): | |
| raise IndexError( | |
| f"Slice {i} contains {start=}, but should be " | |
| f"{f_range} for an axis of size {side} " | |
| "(out-of-bounds starts are not specified in " | |
| "the Array API)" | |
| ) | |
| if i.stop is not None: | |
| try: | |
| stop = operator.index(i.stop) | |
| except TypeError: | |
| pass # handled by ndarray | |
| else: | |
| if not (-side <= stop <= side): | |
| raise IndexError( | |
| f"Slice {i} contains {stop=}, but should be " | |
| f"{f_range} for an axis of size {side} " | |
| "(out-of-bounds stops are not specified in " | |
| "the Array API)" | |
| ) | |
| elif isinstance(i, Array): | |
| if i.dtype in _boolean_dtypes and len(_key) != 1: | |
| assert isinstance(key, tuple) # sanity check | |
| raise IndexError( | |
| f"Single-axes index {i} is a boolean array and " | |
| f"{len(key)=}, but masking is only specified in the " | |
| "Array API when the array is the sole index." | |
| ) | |
| elif i.dtype in _integer_dtypes and i.ndim != 0: | |
| raise IndexError( | |
| f"Single-axes index {i} is a non-zero-dimensional " | |
| "integer array, but advanced integer indexing is not " | |
| "specified in the Array API." | |
| ) | |
| elif isinstance(i, tuple): | |
| raise IndexError( | |
| f"Single-axes index {i} is a tuple, but nested tuple " | |
| "indices are not specified in the Array API." | |
| ) | |
| # Everything below this line is required by the spec. | |
| def __abs__(self: Array, /) -> Array: | |
| """ | |
| Performs the operation __abs__. | |
| """ | |
| if self.dtype not in _numeric_dtypes: | |
| raise TypeError("Only numeric dtypes are allowed in __abs__") | |
| res = self._array.__abs__() | |
| return self.__class__._new(res) | |
| def __add__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __add__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__add__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__add__(other._array) | |
| return self.__class__._new(res) | |
| def __and__(self: Array, other: Union[int, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __and__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer or boolean", "__and__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__and__(other._array) | |
| return self.__class__._new(res) | |
| def __array_namespace__( | |
| self: Array, /, *, api_version: Optional[str] = None | |
| ) -> types.ModuleType: | |
| if api_version is not None and not api_version.startswith("2021."): | |
| raise ValueError(f"Unrecognized array API version: {api_version!r}") | |
| return array_api | |
| def __bool__(self: Array, /) -> bool: | |
| """ | |
| Performs the operation __bool__. | |
| """ | |
| # Note: This is an error here. | |
| if self._array.ndim != 0: | |
| raise TypeError("bool is only allowed on arrays with 0 dimensions") | |
| res = self._array.__bool__() | |
| return res | |
| def __complex__(self: Array, /) -> complex: | |
| """ | |
| Performs the operation __complex__. | |
| """ | |
| # Note: This is an error here. | |
| if self._array.ndim != 0: | |
| raise TypeError("complex is only allowed on arrays with 0 dimensions") | |
| res = self._array.__complex__() | |
| return res | |
| def __dlpack__(self: Array, /, *, stream: None = None) -> PyCapsule: | |
| """ | |
| Performs the operation __dlpack__. | |
| """ | |
| return self._array.__dlpack__(stream=stream) | |
| def __dlpack_device__(self: Array, /) -> Tuple[IntEnum, int]: | |
| """ | |
| Performs the operation __dlpack_device__. | |
| """ | |
| # Note: device support is required for this | |
| return self._array.__dlpack_device__() | |
| def __eq__(self: Array, other: Union[int, float, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __eq__. | |
| """ | |
| # Even though "all" dtypes are allowed, we still require them to be | |
| # promotable with each other. | |
| other = self._check_allowed_dtypes(other, "all", "__eq__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__eq__(other._array) | |
| return self.__class__._new(res) | |
| def __float__(self: Array, /) -> float: | |
| """ | |
| Performs the operation __float__. | |
| """ | |
| # Note: This is an error here. | |
| if self._array.ndim != 0: | |
| raise TypeError("float is only allowed on arrays with 0 dimensions") | |
| if self.dtype in _complex_floating_dtypes: | |
| raise TypeError("float is not allowed on complex floating-point arrays") | |
| res = self._array.__float__() | |
| return res | |
| def __floordiv__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __floordiv__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__floordiv__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__floordiv__(other._array) | |
| return self.__class__._new(res) | |
| def __ge__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __ge__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__ge__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__ge__(other._array) | |
| return self.__class__._new(res) | |
| def __getitem__( | |
| self: Array, | |
| key: Union[ | |
| int, | |
| slice, | |
| ellipsis, | |
| Tuple[Union[int, slice, ellipsis, None], ...], | |
| Array, | |
| ], | |
| /, | |
| ) -> Array: | |
| """ | |
| Performs the operation __getitem__. | |
| """ | |
| # Note: Only indices required by the spec are allowed. See the | |
| # docstring of _validate_index | |
| self._validate_index(key) | |
| if isinstance(key, Array): | |
| # Indexing self._array with array_api arrays can be erroneous | |
| key = key._array | |
| res = self._array.__getitem__(key) | |
| return self._new(res) | |
| def __gt__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __gt__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__gt__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__gt__(other._array) | |
| return self.__class__._new(res) | |
| def __int__(self: Array, /) -> int: | |
| """ | |
| Performs the operation __int__. | |
| """ | |
| # Note: This is an error here. | |
| if self._array.ndim != 0: | |
| raise TypeError("int is only allowed on arrays with 0 dimensions") | |
| if self.dtype in _complex_floating_dtypes: | |
| raise TypeError("int is not allowed on complex floating-point arrays") | |
| res = self._array.__int__() | |
| return res | |
| def __index__(self: Array, /) -> int: | |
| """ | |
| Performs the operation __index__. | |
| """ | |
| res = self._array.__index__() | |
| return res | |
| def __invert__(self: Array, /) -> Array: | |
| """ | |
| Performs the operation __invert__. | |
| """ | |
| if self.dtype not in _integer_or_boolean_dtypes: | |
| raise TypeError("Only integer or boolean dtypes are allowed in __invert__") | |
| res = self._array.__invert__() | |
| return self.__class__._new(res) | |
| def __le__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __le__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__le__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__le__(other._array) | |
| return self.__class__._new(res) | |
| def __lshift__(self: Array, other: Union[int, Array], /) -> Array: | |
| """ | |
| Performs the operation __lshift__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer", "__lshift__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__lshift__(other._array) | |
| return self.__class__._new(res) | |
| def __lt__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __lt__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__lt__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__lt__(other._array) | |
| return self.__class__._new(res) | |
| def __matmul__(self: Array, other: Array, /) -> Array: | |
| """ | |
| Performs the operation __matmul__. | |
| """ | |
| # matmul is not defined for scalars, but without this, we may get | |
| # the wrong error message from asarray. | |
| other = self._check_allowed_dtypes(other, "numeric", "__matmul__") | |
| if other is NotImplemented: | |
| return other | |
| res = self._array.__matmul__(other._array) | |
| return self.__class__._new(res) | |
| def __mod__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __mod__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__mod__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__mod__(other._array) | |
| return self.__class__._new(res) | |
| def __mul__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __mul__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__mul__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__mul__(other._array) | |
| return self.__class__._new(res) | |
| def __ne__(self: Array, other: Union[int, float, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __ne__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "all", "__ne__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__ne__(other._array) | |
| return self.__class__._new(res) | |
| def __neg__(self: Array, /) -> Array: | |
| """ | |
| Performs the operation __neg__. | |
| """ | |
| if self.dtype not in _numeric_dtypes: | |
| raise TypeError("Only numeric dtypes are allowed in __neg__") | |
| res = self._array.__neg__() | |
| return self.__class__._new(res) | |
| def __or__(self: Array, other: Union[int, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __or__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer or boolean", "__or__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__or__(other._array) | |
| return self.__class__._new(res) | |
| def __pos__(self: Array, /) -> Array: | |
| """ | |
| Performs the operation __pos__. | |
| """ | |
| if self.dtype not in _numeric_dtypes: | |
| raise TypeError("Only numeric dtypes are allowed in __pos__") | |
| res = self._array.__pos__() | |
| return self.__class__._new(res) | |
| def __pow__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __pow__. | |
| """ | |
| from ._elementwise_functions import pow | |
| other = self._check_allowed_dtypes(other, "numeric", "__pow__") | |
| if other is NotImplemented: | |
| return other | |
| # Note: NumPy's __pow__ does not follow type promotion rules for 0-d | |
| # arrays, so we use pow() here instead. | |
| return pow(self, other) | |
| def __rshift__(self: Array, other: Union[int, Array], /) -> Array: | |
| """ | |
| Performs the operation __rshift__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer", "__rshift__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rshift__(other._array) | |
| return self.__class__._new(res) | |
| def __setitem__( | |
| self, | |
| key: Union[ | |
| int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array | |
| ], | |
| value: Union[int, float, bool, Array], | |
| /, | |
| ) -> None: | |
| """ | |
| Performs the operation __setitem__. | |
| """ | |
| # Note: Only indices required by the spec are allowed. See the | |
| # docstring of _validate_index | |
| self._validate_index(key) | |
| if isinstance(key, Array): | |
| # Indexing self._array with array_api arrays can be erroneous | |
| key = key._array | |
| self._array.__setitem__(key, asarray(value)._array) | |
| def __sub__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __sub__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__sub__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__sub__(other._array) | |
| return self.__class__._new(res) | |
| # PEP 484 requires int to be a subtype of float, but __truediv__ should | |
| # not accept int. | |
| def __truediv__(self: Array, other: Union[float, Array], /) -> Array: | |
| """ | |
| Performs the operation __truediv__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "floating-point", "__truediv__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__truediv__(other._array) | |
| return self.__class__._new(res) | |
| def __xor__(self: Array, other: Union[int, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __xor__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer or boolean", "__xor__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__xor__(other._array) | |
| return self.__class__._new(res) | |
| def __iadd__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __iadd__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__iadd__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__iadd__(other._array) | |
| return self | |
| def __radd__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __radd__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__radd__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__radd__(other._array) | |
| return self.__class__._new(res) | |
| def __iand__(self: Array, other: Union[int, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __iand__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer or boolean", "__iand__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__iand__(other._array) | |
| return self | |
| def __rand__(self: Array, other: Union[int, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __rand__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer or boolean", "__rand__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rand__(other._array) | |
| return self.__class__._new(res) | |
| def __ifloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __ifloordiv__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__ifloordiv__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__ifloordiv__(other._array) | |
| return self | |
| def __rfloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __rfloordiv__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__rfloordiv__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rfloordiv__(other._array) | |
| return self.__class__._new(res) | |
| def __ilshift__(self: Array, other: Union[int, Array], /) -> Array: | |
| """ | |
| Performs the operation __ilshift__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer", "__ilshift__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__ilshift__(other._array) | |
| return self | |
| def __rlshift__(self: Array, other: Union[int, Array], /) -> Array: | |
| """ | |
| Performs the operation __rlshift__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer", "__rlshift__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rlshift__(other._array) | |
| return self.__class__._new(res) | |
| def __imatmul__(self: Array, other: Array, /) -> Array: | |
| """ | |
| Performs the operation __imatmul__. | |
| """ | |
| # matmul is not defined for scalars, but without this, we may get | |
| # the wrong error message from asarray. | |
| other = self._check_allowed_dtypes(other, "numeric", "__imatmul__") | |
| if other is NotImplemented: | |
| return other | |
| res = self._array.__imatmul__(other._array) | |
| return self.__class__._new(res) | |
| def __rmatmul__(self: Array, other: Array, /) -> Array: | |
| """ | |
| Performs the operation __rmatmul__. | |
| """ | |
| # matmul is not defined for scalars, but without this, we may get | |
| # the wrong error message from asarray. | |
| other = self._check_allowed_dtypes(other, "numeric", "__rmatmul__") | |
| if other is NotImplemented: | |
| return other | |
| res = self._array.__rmatmul__(other._array) | |
| return self.__class__._new(res) | |
| def __imod__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __imod__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__imod__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__imod__(other._array) | |
| return self | |
| def __rmod__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __rmod__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "real numeric", "__rmod__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rmod__(other._array) | |
| return self.__class__._new(res) | |
| def __imul__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __imul__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__imul__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__imul__(other._array) | |
| return self | |
| def __rmul__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __rmul__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__rmul__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rmul__(other._array) | |
| return self.__class__._new(res) | |
| def __ior__(self: Array, other: Union[int, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __ior__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer or boolean", "__ior__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__ior__(other._array) | |
| return self | |
| def __ror__(self: Array, other: Union[int, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __ror__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer or boolean", "__ror__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__ror__(other._array) | |
| return self.__class__._new(res) | |
| def __ipow__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __ipow__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__ipow__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__ipow__(other._array) | |
| return self | |
| def __rpow__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __rpow__. | |
| """ | |
| from ._elementwise_functions import pow | |
| other = self._check_allowed_dtypes(other, "numeric", "__rpow__") | |
| if other is NotImplemented: | |
| return other | |
| # Note: NumPy's __pow__ does not follow the spec type promotion rules | |
| # for 0-d arrays, so we use pow() here instead. | |
| return pow(other, self) | |
| def __irshift__(self: Array, other: Union[int, Array], /) -> Array: | |
| """ | |
| Performs the operation __irshift__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer", "__irshift__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__irshift__(other._array) | |
| return self | |
| def __rrshift__(self: Array, other: Union[int, Array], /) -> Array: | |
| """ | |
| Performs the operation __rrshift__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer", "__rrshift__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rrshift__(other._array) | |
| return self.__class__._new(res) | |
| def __isub__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __isub__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__isub__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__isub__(other._array) | |
| return self | |
| def __rsub__(self: Array, other: Union[int, float, Array], /) -> Array: | |
| """ | |
| Performs the operation __rsub__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "numeric", "__rsub__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rsub__(other._array) | |
| return self.__class__._new(res) | |
| def __itruediv__(self: Array, other: Union[float, Array], /) -> Array: | |
| """ | |
| Performs the operation __itruediv__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "floating-point", "__itruediv__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__itruediv__(other._array) | |
| return self | |
| def __rtruediv__(self: Array, other: Union[float, Array], /) -> Array: | |
| """ | |
| Performs the operation __rtruediv__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "floating-point", "__rtruediv__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rtruediv__(other._array) | |
| return self.__class__._new(res) | |
| def __ixor__(self: Array, other: Union[int, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __ixor__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer or boolean", "__ixor__") | |
| if other is NotImplemented: | |
| return other | |
| self._array.__ixor__(other._array) | |
| return self | |
| def __rxor__(self: Array, other: Union[int, bool, Array], /) -> Array: | |
| """ | |
| Performs the operation __rxor__. | |
| """ | |
| other = self._check_allowed_dtypes(other, "integer or boolean", "__rxor__") | |
| if other is NotImplemented: | |
| return other | |
| self, other = self._normalize_two_args(self, other) | |
| res = self._array.__rxor__(other._array) | |
| return self.__class__._new(res) | |
| def to_device(self: Array, device: Device, /, stream: None = None) -> Array: | |
| if stream is not None: | |
| raise ValueError("The stream argument to to_device() is not supported") | |
| if device == 'cpu': | |
| return self | |
| raise ValueError(f"Unsupported device {device!r}") | |
| def dtype(self) -> Dtype: | |
| """ | |
| Array API compatible wrapper for :py:meth:`np.ndarray.dtype <numpy.ndarray.dtype>`. | |
| See its docstring for more information. | |
| """ | |
| return self._array.dtype | |
| def device(self) -> Device: | |
| return "cpu" | |
| # Note: mT is new in array API spec (see matrix_transpose) | |
| def mT(self) -> Array: | |
| from .linalg import matrix_transpose | |
| return matrix_transpose(self) | |
| def ndim(self) -> int: | |
| """ | |
| Array API compatible wrapper for :py:meth:`np.ndarray.ndim <numpy.ndarray.ndim>`. | |
| See its docstring for more information. | |
| """ | |
| return self._array.ndim | |
| def shape(self) -> Tuple[int, ...]: | |
| """ | |
| Array API compatible wrapper for :py:meth:`np.ndarray.shape <numpy.ndarray.shape>`. | |
| See its docstring for more information. | |
| """ | |
| return self._array.shape | |
| def size(self) -> int: | |
| """ | |
| Array API compatible wrapper for :py:meth:`np.ndarray.size <numpy.ndarray.size>`. | |
| See its docstring for more information. | |
| """ | |
| return self._array.size | |
| def T(self) -> Array: | |
| """ | |
| Array API compatible wrapper for :py:meth:`np.ndarray.T <numpy.ndarray.T>`. | |
| See its docstring for more information. | |
| """ | |
| # Note: T only works on 2-dimensional arrays. See the corresponding | |
| # note in the specification: | |
| # https://data-apis.org/array-api/latest/API_specification/array_object.html#t | |
| if self.ndim != 2: | |
| raise ValueError("x.T requires x to have 2 dimensions. Use x.mT to transpose stacks of matrices and permute_dims() to permute dimensions.") | |
| return self.__class__._new(self._array.T) | |