Skip to content
Open
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
6 changes: 6 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ jobs:
fi
fi

# mparray requires Python >=3.11 and NumPy >=2.
if [[ "${{ matrix.python-version }}" =~ ^3\.1[1-4]$ &&
"${{ matrix.numpy-version }}" =~ ^(latest|dev)$ ]]; then
python -m pip install mparray
fi

- name: Dump pip environment
run: pip freeze

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

This is a small wrapper around common array libraries that is compatible with
the [Array API standard](https://data-apis.org/array-api/latest/). Currently,
NumPy, CuPy, PyTorch, Dask, JAX, ndonnx, `sparse` and dpnp are supported. If you want
NumPy, CuPy, PyTorch, Dask, JAX, mparray, ndonnx, `sparse` and dpnp are
supported. If you want
support for other array libraries, or if you encounter any issues, please [open
an issue](https://github.com/data-apis/array-api-compat/issues).

Expand Down
5 changes: 3 additions & 2 deletions docs/dev/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ that is not strictly required by the standard. To run these tests, install the
dependencies from the `dev` optional group (array-api-compat has [no hard
runtime dependencies](no-dependencies)).

array-api-tests is run against all supported libraries are tested on CI
([except for JAX](jax-support) and [Sparse](sparse-support)). This is achieved
The array-api-tests suite is run against all supported libraries on CI
([except for JAX](jax-support), [mparray](mparray-support), and
[Sparse](sparse-support)). This is achieved
by a [reusable GitHub Actions
Workflow](https://github.com/data-apis/array-api-compat/blob/main/.github/workflows/array-api-tests.yml).
Most libraries have tests that must be xfailed or skipped for various reasons.
Expand Down
2 changes: 2 additions & 0 deletions docs/helper-functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ yet.
.. autofunction:: is_torch_array
.. autofunction:: is_dask_array
.. autofunction:: is_jax_array
.. autofunction:: is_mparray_array
.. autofunction:: is_pydata_sparse_array
.. autofunction:: is_ndonnx_array
.. autofunction:: is_dpnp_array
Expand All @@ -59,6 +60,7 @@ yet.
.. autofunction:: is_torch_namespace
.. autofunction:: is_dask_namespace
.. autofunction:: is_jax_namespace
.. autofunction:: is_mparray_namespace
.. autofunction:: is_pydata_sparse_namespace
.. autofunction:: is_ndonnx_namespace
.. autofunction:: is_dpnp_namespace
Expand Down
14 changes: 7 additions & 7 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

This is a small wrapper around common array libraries that is compatible with
the [Array API standard](https://data-apis.org/array-api/latest/). Currently,
NumPy, CuPy, PyTorch, Dask, JAX, ndonnx, Sparse and dpnp are supported. If you want
NumPy, CuPy, PyTorch, Dask, JAX, mparray, ndonnx, Sparse and dpnp are
supported. If you want
support for other array libraries, or if you encounter any issues, please
[open an issue](https://github.com/data-apis/array-api-compat/issues).

Expand Down Expand Up @@ -61,12 +62,11 @@ import array_api_compat.dask as da
```

```{note}
There are no `array_api_compat` submodules for JAX, sparse, or ndonnx. These
support for these libraries is contained in the libraries themselves (JAX
support is in the `jax.numpy` module in JAX v0.4.32 or newer, and in the
`jax.experimental.array_api` module for older JAX versions). The
array-api-compat support for these libraries consists of supporting them in
the [helper functions](helper-functions).
There are no `array_api_compat` submodules for JAX, mparray, sparse, or ndonnx.
These libraries provide their own Array API support. (JAX provides it through
`jax.numpy` in JAX v0.4.32+ or `jax.experimental.array_api` in older versions.)
array-api-compat supports these libraries through the
[helper functions](helper-functions).
```

Each will include all the functions from the normal NumPy/CuPy/PyTorch/dask.array
Expand Down
10 changes: 10 additions & 0 deletions docs/supported-array-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ Unlike the other libraries supported here, JAX array API support is contained
entirely in the JAX library. The JAX array API support is tracked at
https://github.com/google/jax/issues/18353.

(mparray-support)=
## [mparray](https://github.com/mdhaber/mparray)

mparray implements the Array API directly, so there is no
`array_api_compat.mparray` wrapper. `array_namespace()` returns the native
`mparray` namespace, and compatibility support consists of library
detection and generic helper functions. mparray currently supports the
`2025.12` version of the standard and only the `"cpu"` device, so
`to_device()` follows NumPy's CPU-only behavior.

## [Dask](https://www.dask.org/)

If you're using dask with numpy, many of the same limitations that apply to numpy
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ repository = "https://github.com/data-apis/array-api-compat/"
cupy = ["cupy"]
dask = ["dask>=2024.9.0"]
jax = ["jax"]
mparray = ["mparray"]
# Note: array-api-compat follows scikit-learn minimum dependencies, which support
# much older versions of NumPy than what SPEC0 recommends.
numpy = ["numpy>=1.22"]
Expand All @@ -50,6 +51,7 @@ dev = [
"array-api-strict",
"dask[array]>=2024.9.0",
"jax[cpu]",
"mparray; python_version >= '3.11'",
"ndonnx",
"numpy>=1.22",
"pytest",
Expand Down Expand Up @@ -105,7 +107,7 @@ warn_unused_ignores = true
warn_unreachable = true

[[tool.mypy.overrides]]
module = ["cupy.*", "cupy_backends.*", "dask.*", "jax.*", "ndonnx.*", "sparse.*", "torch.*"]
module = ["cupy.*", "cupy_backends.*", "dask.*", "jax.*", "mparray.*", "ndonnx.*", "sparse.*", "torch.*"]
ignore_missing_imports = true


Expand Down
2 changes: 1 addition & 1 deletion src/array_api_compat/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
NumPy Array API compatibility library

This is a small wrapper around NumPy, CuPy, JAX, sparse and others that are
This is a small wrapper around NumPy, CuPy, JAX, mparray, sparse and others that are
compatible with the Array API standard https://data-apis.org/array-api/latest/.
See also NEP 47 https://numpy.org/neps/nep-0047-array-api-standard.html.

Expand Down
72 changes: 71 additions & 1 deletion src/array_api_compat/common/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import dask.array as da
import dpnp
import jax
import mparray
import ndonnx as ndx
import numpy as np
import numpy.typing as npt
Expand All @@ -52,6 +53,7 @@
| dpnp.ndarray
| dpnp.tensor.usm_ndarray
| jax.Array
| mparray.MPArray
| ndx.Array
| sparse.SparseArray
| torch.Tensor
Expand Down Expand Up @@ -116,6 +118,7 @@ def is_numpy_array(x: object) -> TypeIs[npt.NDArray[Any]]:
is_jax_array
is_pydata_sparse_array
is_dpnp_array
is_mparray_array
"""
# TODO: Should we reject ndarray subclasses?
cls = cast(Hashable, type(x))
Expand Down Expand Up @@ -146,6 +149,7 @@ def is_cupy_array(x: object) -> bool:
is_jax_array
is_pydata_sparse_array
is_dpnp_array
is_mparray_array
"""
cls = cast(Hashable, type(x))
return _issubclass_fast(cls, "cupy", "ndarray")
Expand All @@ -169,6 +173,7 @@ def is_torch_array(x: object) -> TypeIs[torch.Tensor]:
is_jax_array
is_pydata_sparse_array
is_dpnp_array
is_mparray_array
"""
cls = cast(Hashable, type(x))
return _issubclass_fast(cls, "torch", "Tensor")
Expand All @@ -193,6 +198,7 @@ def is_ndonnx_array(x: object) -> TypeIs[ndx.Array]:
is_jax_array
is_pydata_sparse_array
is_dpnp_array
is_mparray_array
"""
cls = cast(Hashable, type(x))
return _issubclass_fast(cls, "ndonnx", "Array")
Expand All @@ -217,6 +223,7 @@ def is_dask_array(x: object) -> TypeIs[da.Array]:
is_jax_array
is_pydata_sparse_array
is_dpnp_array
is_mparray_array
"""
cls = cast(Hashable, type(x))
return _issubclass_fast(cls, "dask.array", "Array")
Expand All @@ -242,6 +249,7 @@ def is_jax_array(x: object) -> TypeIs[jax.Array]:
is_dask_array
is_pydata_sparse_array
is_dpnp_array
is_mparray_array
"""
cls = cast(Hashable, type(x))
# We test for jax.core.Tracer here to identify jax arrays during jit tracing. From jax 0.8.2 on,
Expand All @@ -257,6 +265,31 @@ def is_jax_array(x: object) -> TypeIs[jax.Array]:
)


def is_mparray_array(x: object) -> TypeIs[mparray.MPArray]:
"""
Return True if `x` is an mparray array.

This function does not import mparray if it has not already been imported
and is therefore cheap to use.

See Also
--------

array_namespace
is_array_api_obj
is_numpy_array
is_cupy_array
is_torch_array
is_ndonnx_array
is_dask_array
is_jax_array
is_pydata_sparse_array
is_dpnp_array
"""
cls = cast(Hashable, type(x))
return _issubclass_fast(cls, "mparray", "MPArray")


def is_pydata_sparse_array(x: object) -> TypeIs[sparse.SparseArray]:
"""
Return True if `x` is an array from the `sparse` package.
Expand All @@ -277,6 +310,7 @@ def is_pydata_sparse_array(x: object) -> TypeIs[sparse.SparseArray]:
is_dask_array
is_jax_array
is_dpnp_array
is_mparray_array
"""
# TODO: Account for other backends.
cls = cast(Hashable, type(x))
Expand All @@ -303,6 +337,7 @@ def is_dpnp_array(x: object) -> bool:
is_dask_array
is_jax_array
is_pydata_sparse_array
is_mparray_array
"""
cls = cast(Hashable, type(x))
return (
Expand All @@ -327,6 +362,7 @@ def is_array_api_obj(x: object) -> TypeGuard[_ArrayApiObj]:
is_jax_array
is_pydata_sparse_array
is_dpnp_array
is_mparray_array
"""
try:
# TODO: drop this check after np.matrix is gone
Expand Down Expand Up @@ -379,6 +415,7 @@ def is_numpy_namespace(xp: Namespace) -> bool:
is_jax_namespace
is_pydata_sparse_namespace
is_dpnp_namespace
is_mparray_namespace
is_array_api_strict_namespace
"""
return xp.__name__ in {"numpy", _compat_module_name() + ".numpy"}
Expand All @@ -402,6 +439,7 @@ def is_cupy_namespace(xp: Namespace) -> bool:
is_jax_namespace
is_pydata_sparse_namespace
is_dpnp_namespace
is_mparray_namespace
is_array_api_strict_namespace
"""
return xp.__name__ in {"cupy", _compat_module_name() + ".cupy"}
Expand All @@ -425,6 +463,7 @@ def is_torch_namespace(xp: Namespace) -> bool:
is_jax_namespace
is_pydata_sparse_namespace
is_dpnp_namespace
is_mparray_namespace
is_array_api_strict_namespace
"""
return xp.__name__ in {"torch", _compat_module_name() + ".torch"}
Expand All @@ -445,6 +484,7 @@ def is_ndonnx_namespace(xp: Namespace) -> bool:
is_jax_namespace
is_pydata_sparse_namespace
is_dpnp_namespace
is_mparray_namespace
is_array_api_strict_namespace
"""
return xp.__name__ == "ndonnx"
Expand All @@ -468,6 +508,7 @@ def is_dask_namespace(xp: Namespace) -> bool:
is_jax_namespace
is_pydata_sparse_namespace
is_dpnp_namespace
is_mparray_namespace
is_array_api_strict_namespace
"""
return xp.__name__ in {"dask.array", _compat_module_name() + ".dask.array"}
Expand All @@ -491,11 +532,33 @@ def is_jax_namespace(xp: Namespace) -> bool:
is_dask_namespace
is_pydata_sparse_namespace
is_dpnp_namespace
is_mparray_namespace
is_array_api_strict_namespace
"""
return xp.__name__ in {"jax.numpy", "jax.experimental.array_api"}


def is_mparray_namespace(xp: Namespace) -> bool:
"""
Returns True if `xp` is the mparray namespace.

See Also
--------

array_namespace
is_numpy_namespace
is_cupy_namespace
is_torch_namespace
is_ndonnx_namespace
is_dask_namespace
is_jax_namespace
is_pydata_sparse_namespace
is_dpnp_namespace
is_array_api_strict_namespace
"""
return xp.__name__ == "mparray"


def is_pydata_sparse_namespace(xp: Namespace) -> bool:
"""
Returns True if `xp` is a pydata/sparse namespace.
Expand All @@ -511,6 +574,7 @@ def is_pydata_sparse_namespace(xp: Namespace) -> bool:
is_dask_namespace
is_jax_namespace
is_dpnp_namespace
is_mparray_namespace
is_array_api_strict_namespace
"""
return xp.__name__ == "sparse"
Expand All @@ -533,6 +597,7 @@ def is_dpnp_namespace(xp: Namespace) -> bool:
is_dask_namespace
is_jax_namespace
is_pydata_sparse_namespace
is_mparray_namespace
is_array_api_strict_namespace
"""
return xp.__name__ in {"dpnp", "dpnp.tensor"}
Expand All @@ -554,6 +619,7 @@ def is_array_api_strict_namespace(xp: Namespace) -> bool:
is_jax_namespace
is_pydata_sparse_namespace
is_dpnp_namespace
is_mparray_namespace
"""
return xp.__name__ == "array_api_strict"

Expand Down Expand Up @@ -714,6 +780,7 @@ def your_function(x, y):
is_torch_array
is_dask_array
is_jax_array
is_mparray_array
is_pydata_sparse_array
is_dpnp_array

Expand Down Expand Up @@ -971,7 +1038,7 @@ def to_device(x: Array, device: Device, /, *, stream: int | Any | None = None) -
device : Hardware device the array data resides on.

"""
if is_numpy_array(x):
if is_numpy_array(x) or is_mparray_array(x):
if stream is not None:
raise ValueError("The stream argument to to_device() is not supported")
if device == "cpu":
Expand Down Expand Up @@ -1070,6 +1137,7 @@ def _is_lazy_cls(cls: type) -> bool | None:
or _issubclass_fast(cls, "cupy", "ndarray")
or _issubclass_fast(cls, "torch", "Tensor")
or _issubclass_fast(cls, "sparse", "SparseArray")
or _issubclass_fast(cls, "mparray", "MPArray")
):
return False
if (
Expand Down Expand Up @@ -1152,6 +1220,8 @@ def is_lazy_array(x: object) -> TypeGuard[_ArrayApiObj]:
"is_dpnp_namespace",
"is_jax_array",
"is_jax_namespace",
"is_mparray_array",
"is_mparray_namespace",
"is_numpy_array",
"is_numpy_namespace",
"is_torch_array",
Expand Down
2 changes: 1 addition & 1 deletion tests/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

wrapped_libraries = ["numpy", "cupy", "torch", "dask.array"]
all_libraries = wrapped_libraries + [
"array_api_strict", "jax.numpy", "ndonnx", "sparse", "dpnp"
"array_api_strict", "jax.numpy", "ndonnx", "sparse", "dpnp", "mparray"
]

def import_(library, wrapper=False):
Expand Down
Loading
Loading