8000 ENH: Make numpy.array_api more portable by asmeurer · Pull Request #25370 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: Make numpy.array_api more portable #25370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/release/upcoming_changes/25370.compatibility.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Make ``numpy.array_api`` more portable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep this release note; it can be cleaned up if and when we split off the whole numpy.array_api module into a standalone package.

--------------------------------------

``numpy.array_api`` no longer uses ``"cpu"`` as its "device", but rather a
separate ``CPU_DEVICE`` object (which is not accessible in the namespace).
This is because "cpu" is not part of the array API standard.

``numpy.array_api`` now uses separate wrapped objects for dtypes. Previously
it reused the ``numpy`` dtype objects. This makes it clear which behaviors on
dtypes are part of the array API standard (effectively, the standard only
requires ``==`` on dtype objects).

``numpy.array_api.nonzero`` now errors on zero-dimensional arrays, as required
by the array API standard.
28 changes: 26 additions & 2 deletions doc/source/reference/array_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,20 @@ Manipulation functions differences
- **Compatible**
- See https://github.com/numpy/numpy/issues/9818.

Searching functions differences
-------------------------------

.. list-table::
:header-rows: 1

* - Feature
- Type
- Notes
* - ``nonzero`` disallows 0-dimensional inputs
- **Breaking**
- This behavior is already deprecated for ``np.nonzero``. See
https://github.com/numpy/numpy/pull/13708.

Set functions differences
-------------------------

Expand All @@ -655,8 +669,8 @@ Set functions differences

.. _array_api-set-functions-differences:

Set functions differences
-------------------------
Sorting functions differences
-----------------------------

.. list-table::
:header-rows: 1
Expand Down Expand Up @@ -708,6 +722,16 @@ Other differences
- **Strictness**
- For example, ``numpy.array_api.asarray([0], dtype='int32')`` is not
allowed.
* - Dtype objects are wrapped so that they only implement the required
``__eq__`` method, which only compares against dtype objects.
- **Strictness**
- For example, ``float32 == 'float32'`` is not allowed.
* - ``arr.device`` always returns a ``CPU_DEVICE`` object (which is not
part of the namespace). This is the only valid non-default value for
``device`` keyword arguments to creation functions like ``asarray()``.
- **Compatible**
- CPU is the only device supported by NumPy. The standard does not
require device objects to be accessible other than via ``arr.device``.
* - ``asarray`` is not implicitly called in any function.
- **Strictness**
- The exception is Python operators, which accept Python scalars in
10000 Expand Down
30 changes: 22 additions & 8 deletions numpy/array_api/_array_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from enum import IntEnum
from ._creation_functions import asarray
from ._dtypes import (
_DType,
_all_dtypes,
_boolean_dtypes,
_integer_dtypes,
Expand All @@ -39,6 +40,13 @@

import numpy as np

# Placeholder object to represent the "cpu" device (the only device NumPy
# supports).
class _cpu_device:
def __repr__(self):
return "CPU_DEVICE"

CPU_DEVICE = _cpu_device()

class Array:
"""
Expand Down Expand Up @@ -75,11 +83,13 @@ def _new(cls, x, /):
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:
_dtype = _DType(x.dtype)
if _dtype not in _all_dtypes:
raise TypeError(
f"The array_api namespace does not support the dtype '{x.dtype}'"
)
obj._array = x
obj._dtype = _dtype
return obj

# Prevent Array() from working
Expand All @@ -101,7 +111,7 @@ def __repr__(self: Array, /) -> str:
"""
Performs the operation __repr__.
"""
suffix = f", dtype={self.dtype.name})"
suffix = f", dtype={self.dtype})"
if 0 in self.shape:
prefix = "empty("
mid = str(self.shape)
Expand Down Expand Up @@ -176,6 +186,8 @@ def _promote_scalar(self, scalar):
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.
"""
from ._data_type_functions import iinfo

# Note: Only Python scalar types that match the array dtype are
# allowed.
if isinstance(scalar, bool):
Expand All @@ -189,7 +201,7 @@ def _promote_scalar(self, scalar):
"Python int scalars cannot be promoted with bool arrays"
)
if self.dtype in _integer_dtypes:
info = np.iinfo(self.dtype)
info = 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"
Expand All @@ -215,7 +227,7 @@ def _promote_scalar(self, scalar):
# 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))
return Array._new(np.array(scalar, dtype=self.dtype._np_dtype))

@staticmethod
def _normalize_two_args(x1, x2) -> Tuple[Array, Array]:
Expand Down Expand Up @@ -325,7 +337,9 @@ def _validate_index(self, key):
for i in _key:
if i is not None:
nonexpanding_key.append(i)
if isinstance(i, Array) or isinstance(i, np.ndarray):
if isinstance(i, np.ndarray):
raise IndexError("Index arrays for np.array_api must be np.array_api arrays")
if isinstance(i, Array):
if i.dtype in _boolean_dtypes:
key_has_mask = True
single_axes.append(i)
Expand Down Expand Up @@ -1067,7 +1081,7 @@ def __rxor__(self: Array, other: Union[int, bool, Array], /) -> Array:
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':
if device == CPU_DEVICE:
return self
raise ValueError(f"Unsupported device {device!r}")

Expand All @@ -1078,11 +1092,11 @@ def dtype(self) -> Dtype:

See its docstring for more information.
"""
return self._array.dtype
return self._dtype

@property
def device(self) -> Device:
return "cpu"
return CPU_DEVICE

# Note: mT is new in array API spec (see matrix_transpose)
@property
Expand Down
Loading
0