Skip to content

Commit 2072436

Browse files
authored
ENH: add mparray backend (#963)
* ENH: add mparray backend [skip ci] * Fixups
1 parent 565b1d2 commit 2072436

11 files changed

Lines changed: 2970 additions & 1484 deletions

File tree

pixi.lock

Lines changed: 2951 additions & 1483 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pixi.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ exclude-newer = "7d"
1717

1818
[exclude-newer]
1919
pixi-build-python = "5d" # for compatibility with pixi-build-api-version 7
20+
mparray = "0d"
2021

2122
### array-api-extra package definition ###
2223

@@ -372,6 +373,7 @@ numpy = "=1.24.1"
372373
pytorch = ">=2.12.0"
373374
dask-core = ">=2026.7.1" # No distributed, tornado, etc.
374375
sparse = ">=0.19.2"
376+
mparray = ">=0.2.2"
375377

376378
[feature.backends.target.unix.dependencies]
377379
jax = ">=0.10.2" # waiting for conda-forge/jaxlib-feedstock#326

src/array_api_extra/_lib/_backends.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class Backend(Enum): # numpydoc ignore=PR02
2828
ARRAY_API_STRICTEST = "array_api_strict:strictest"
2929
NUMPY = "numpy"
3030
NUMPY_READONLY = "numpy:readonly"
31+
MPARRAY = "mparray"
3132
CUPY = "cupy"
3233
TORCH = "torch"
3334
TORCH_GPU = "torch:gpu"

src/array_api_extra/_lib/_helpers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"eager_shape",
3535
"in1d",
3636
"is_jax_jit_enabled",
37+
"is_mparray_namespace",
3738
"is_python_scalar",
3839
"jax_autojit",
3940
"meta_namespace",
@@ -616,3 +617,8 @@ def is_jax_jit_enabled(xp: ArrayNamespace) -> bool: # numpydoc ignore=PR01,RT01
616617
return bool(x)
617618
except jax.errors.TracerBoolConversionError:
618619
return True
620+
621+
622+
def is_mparray_namespace(xp: ArrayNamespace) -> bool: # numpydoc ignore=PR01,RT01
623+
"""Return True if the argument is the MPArray namespace."""
624+
return xp.__name__ == "mparray"

src/array_api_extra/_set.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def nunique(x: Array, /, *, xp: ArrayNamespace | None = None) -> Array:
101101
if (
102102
_compat.is_numpy_namespace(xp)
103103
or _compat.is_cupy_namespace(xp)
104+
or _helpers.is_mparray_namespace(xp)
104105
or (
105106
_compat.is_torch_namespace(xp)
106107
and _helpers.capabilities(xp, x)["data-dependent shapes"]

src/array_api_extra/testing/_testing.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,9 @@ def _as_numpy_array( # numpydoc ignore=PR01,RT01
695695
cpu = typing.cast(Device, jax.devices("cpu")[0])
696696
array = _compat.to_device(array, cpu)
697697

698+
if _helpers.is_mparray_namespace(xp):
699+
return np.asarray(array._data, dtype=array.dtype) # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue]
700+
698701
if hasattr(array, "__dlpack__"):
699702
try:
700703
return np.from_dlpack(array)

tests/main/test_at.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ def test_incompatible_dtype(
246246
with pytest.warns(FutureWarning, match="cannot safely cast"):
247247
z = at_op(x, idx, op, 1.1, copy=copy)
248248

249-
elif library.like(Backend.DASK):
249+
# MPArray mutation is currently a little too flexible; see mdhaber/mparray#21
250+
elif library.like(Backend.DASK) or (library == Backend.MPARRAY):
250251
z = at_op(x, idx, op, 1.1, copy=copy)
251252

252253
elif library.like(Backend.ARRAY_API_STRICT):

tests/main/test_elementwise.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ def test_basic(self, xp: ArrayNamespace):
662662
atol=1e-11,
663663
)
664664

665+
@pytest.mark.skip_xp_backend(Backend.MPARRAY, reason="negative zero not supported")
665666
def test_real(self, xp: ArrayNamespace):
666667
x = xp.asarray([0.0, -0.0, 1.0, -1.0])
667668
expected = xp.asarray([0.0, xp.pi, 0.0, xp.pi], dtype=x.dtype)

tests/main/test_lazy.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
Backend.TORCH_GPU, reason="device->host copy"
2828
),
2929
pytest.mark.skip_xp_backend(Backend.SPARSE, reason="densification"),
30+
pytest.mark.skip_xp_backend(Backend.MPARRAY, reason="precision loss"),
3031
],
3132
),
3233
],

tests/main/test_searching.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ def xp_searchsorted(
144144

145145
@pytest.mark.skip_xp_backend(Backend.DASK, reason="no take_along_axis")
146146
@pytest.mark.skip_xp_backend(Backend.SPARSE, reason="no searchsorted")
147+
@pytest.mark.skip_xp_backend(Backend.MPARRAY, reason="see mdhaber/mparray#20")
147148
class TestSearchsorted:
148149
def test_input_validation(self, xp: ArrayNamespace):
149150
message = "`side` must be either 'left' or 'right'."

0 commit comments

Comments
 (0)