Skip to content
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

Move to Array API version 2023.12. #696

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: data-apis/array-api-tests
ref: '33f2d2ea2f3dd2b3ceeeb4519d55e08096184149' # Latest commit as of 2024-05-29
ref: 'd295a0a66cd82a43e84c1b8d73ca198cc45e9d23' # Latest commit as of 2024-06-10
submodules: 'true'
path: 'array-api-tests'
- name: Set up Python
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,6 @@ sandbox.py

# Version file
sparse/_version.py

# Benchmarks
/results/
20 changes: 13 additions & 7 deletions ci/Numba-array-api-xfails.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,42 +29,48 @@ array_api_tests/test_has_names.py::test_has_names[linalg-tensordot]
array_api_tests/test_has_names.py::test_has_names[linalg-trace]
array_api_tests/test_has_names.py::test_has_names[linalg-vecdot]
array_api_tests/test_has_names.py::test_has_names[linalg-vector_norm]
array_api_tests/test_has_names.py::test_has_names[manipulation-repeat]
array_api_tests/test_has_names.py::test_has_names[manipulation-tile]
array_api_tests/test_has_names.py::test_has_names[set-unique_all]
array_api_tests/test_has_names.py::test_has_names[set-unique_inverse]
array_api_tests/test_has_names.py::test_has_names[creation-arange]
array_api_tests/test_has_names.py::test_has_names[creation-from_dlpack]
array_api_tests/test_has_names.py::test_has_names[creation-linspace]
array_api_tests/test_has_names.py::test_has_names[creation-meshgrid]
array_api_tests/test_has_names.py::test_has_names[searching-searchsorted]
array_api_tests/test_has_names.py::test_has_names[sorting-argsort]
array_api_tests/test_has_names.py::test_has_names[statistical-cumulative_sum]
array_api_tests/test_has_names.py::test_has_names[data_type-isdtype]
array_api_tests/test_has_names.py::test_has_names[array_method-__dlpack__]
array_api_tests/test_has_names.py::test_has_names[array_method-__dlpack_device__]
array_api_tests/test_has_names.py::test_has_names[array_method-__setitem__]
array_api_tests/test_indexing_functions.py::test_take
array_api_tests/test_linalg.py::test_vecdot
array_api_tests/test_manipulation_functions.py::test_repeat
array_api_tests/test_manipulation_functions.py::test_tile
array_api_tests/test_operators_and_elementwise_functions.py::test_ceil
array_api_tests/test_operators_and_elementwise_functions.py::test_trunc
array_api_tests/test_searching_functions.py::test_argmax
array_api_tests/test_searching_functions.py::test_argmin
array_api_tests/test_searching_functions.py::test_searchsorted
array_api_tests/test_set_functions.py::test_unique_all
array_api_tests/test_set_functions.py::test_unique_inverse
array_api_tests/test_statistical_functions.py::test_cumulative_sum
array_api_tests/test_signatures.py::test_func_signature[unique_all]
array_api_tests/test_signatures.py::test_func_signature[unique_inverse]
array_api_tests/test_signatures.py::test_func_signature[arange]
array_api_tests/test_signatures.py::test_func_signature[cumulative_sum]
array_api_tests/test_signatures.py::test_func_signature[from_dlpack]
array_api_tests/test_signatures.py::test_func_signature[linspace]
array_api_tests/test_signatures.py::test_func_signature[meshgrid]
array_api_tests/test_signatures.py::test_func_signature[repeat]
array_api_tests/test_signatures.py::test_func_signature[tile]
array_api_tests/test_signatures.py::test_func_signature[argsort]
array_api_tests/test_signatures.py::test_func_signature[searchsorted]
array_api_tests/test_signatures.py::test_func_signature[isdtype]
array_api_tests/test_signatures.py::test_array_method_signature[__dlpack__]
array_api_tests/test_signatures.py::test_array_method_signature[__dlpack_device__]
array_api_tests/test_signatures.py::test_array_method_signature[__setitem__]
array_api_tests/test_sorting_functions.py::test_argsort
array_api_tests/test_sorting_functions.py::test_sort
array_api_tests/test_special_cases.py::test_nan_propagation[max]
array_api_tests/test_special_cases.py::test_nan_propagation[mean]
array_api_tests/test_special_cases.py::test_nan_propagation[min]
array_api_tests/test_special_cases.py::test_nan_propagation[prod]
array_api_tests/test_special_cases.py::test_nan_propagation[std]
array_api_tests/test_special_cases.py::test_nan_propagation[sum]
array_api_tests/test_special_cases.py::test_nan_propagation[var]
array_api_tests/test_special_cases.py::test_nan_propagation[cumulative_sum]
2 changes: 0 additions & 2 deletions sparse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

from ._version import __version__, __version_tuple__ # noqa: F401

__array_api_version__ = "2022.12"


class BackendType(Enum):
Numba = "Numba"
Expand Down
23 changes: 23 additions & 0 deletions sparse/numba_backend/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import sparse.numba_backend._info as _info

from numpy import (
add,
bitwise_and,
Expand All @@ -9,6 +11,7 @@
complex64,
complex128,
conj,
copysign,
cos,
cosh,
divide,
Expand All @@ -23,6 +26,7 @@
floor_divide,
greater,
greater_equal,
hypot,
iinfo,
inf,
int8,
Expand All @@ -41,6 +45,8 @@
logical_not,
logical_or,
logical_xor,
maximum,
minimum,
multiply,
nan,
negative,
Expand All @@ -50,6 +56,7 @@
positive,
remainder,
sign,
signbit,
sin,
sinh,
sqrt,
Expand Down Expand Up @@ -119,6 +126,7 @@
std,
sum,
tensordot,
unstack,
var,
vecdot,
zeros,
Expand Down Expand Up @@ -161,7 +169,16 @@
from ._umath import elemwise
from ._utils import random


def __array_namespace_info__():
return _info

Check warning on line 174 in sparse/numba_backend/__init__.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/__init__.py#L174

Added line #L174 was not covered by tests


__array_api_version__ = "2023.12"


__all__ = [
"__array_api_version__",
"COO",
"DOK",
"GCXS",
Expand Down Expand Up @@ -203,6 +220,7 @@
"concat",
"concatenate",
"conj",
"copysign",
"cos",
"cosh",
"diagonal",
Expand Down Expand Up @@ -230,6 +248,7 @@
"full_like",
"greater",
"greater_equal",
"hypot",
"iinfo",
"imag",
"inf",
Expand Down Expand Up @@ -258,8 +277,10 @@
"matmul",
"matrix_transpose",
"max",
"maximum",
"mean",
"min",
"minimum",
"moveaxis",
"multiply",
"nan",
Expand Down Expand Up @@ -291,6 +312,7 @@
"round",
"save_npz",
"sign",
"signbit",
"sin",
"sinh",
"sort",
Expand All @@ -314,6 +336,7 @@
"uint8",
"unique_counts",
"unique_values",
"unstack",
"var",
"vecdot",
"where",
Expand Down
16 changes: 14 additions & 2 deletions sparse/numba_backend/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
bs = b.shape
ndb = b.ndim
equal = True
if nda == 0 or ndb == 0:
if not (builtins.all(-nda <= ax < nda for ax in axes_a) and builtins.all(-ndb <= ax < ndb for ax in axes_b)):
pos = int(nda != 0)
raise ValueError(f"Input {pos} operand does not have enough dimensions")
if na != nb:
Expand Down Expand Up @@ -2146,10 +2146,22 @@
return x.reshape(shape=shape)


def astype(x, dtype, /, *, copy=True):
@_check_device
def astype(x, dtype, /, *, copy=True, device=None):
return x.astype(dtype, copy=copy)


def unstack(x, /, *, axis=0):
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think unstack is new - let's add a test for it.

axis = normalize_axis(axis, x.ndim)
out = []

Check warning on line 2156 in sparse/numba_backend/_common.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_common.py#L2155-L2156

Added lines #L2155 - L2156 were not covered by tests

for i in range(x.shape[axis]):
idx = (slice(None),) * axis + (i,)
out.append(x[idx])

Check warning on line 2160 in sparse/numba_backend/_common.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_common.py#L2158-L2160

Added lines #L2158 - L2160 were not covered by tests

return tuple(out)

Check warning on line 2162 in sparse/numba_backend/_common.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_common.py#L2162

Added line #L2162 was not covered by tests


@_support_numpy
def squeeze(x, /, axis=None):
"""Remove singleton dimensions from array.
Expand Down
10 changes: 5 additions & 5 deletions sparse/numba_backend/_coo/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,7 @@ def _diagonal_idx(coordlist, axis1, axis2, offset):
return np.array([i for i in range(len(coordlist[axis1])) if coordlist[axis1][i] + offset == coordlist[axis2][i]])


def clip(a, a_min=None, a_max=None, out=None):
def clip(a, min=None, max=None, out=None):
"""
Clip (limit) the values in the array.

Expand Down Expand Up @@ -1042,19 +1042,19 @@ def clip(a, a_min=None, a_max=None, out=None):
--------
>>> import sparse
>>> x = sparse.COO.from_numpy([0, 0, 0, 1, 2, 3])
>>> sparse.clip(x, a_min=1).todense() # doctest: +NORMALIZE_WHITESPACE
>>> sparse.clip(x, min=1).todense() # doctest: +NORMALIZE_WHITESPACE
array([1, 1, 1, 1, 2, 3])
>>> sparse.clip(x, a_max=1).todense() # doctest: +NORMALIZE_WHITESPACE
>>> sparse.clip(x, max=1).todense() # doctest: +NORMALIZE_WHITESPACE
array([0, 0, 0, 1, 1, 1])
>>> sparse.clip(x, a_min=1, a_max=2).todense() # doctest: +NORMALIZE_WHITESPACE
>>> sparse.clip(x, min=1, max=2).todense() # doctest: +NORMALIZE_WHITESPACE
array([1, 1, 1, 1, 2, 2])

See Also
--------
numpy.clip : Equivalent NumPy function
"""
a = asCOO(a, name="clip")
return a.clip(a_min, a_max)
return a.clip(min, max)


def expand_dims(x, /, *, axis=0):
Expand Down
95 changes: 95 additions & 0 deletions sparse/numba_backend/_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import numpy as np

from ._common import _check_device

__all__ = [
"capabilities",
"default_device",
"default_dtypes",
"devices",
"dtypes",
]

_CAPABILITIES = {
"boolean indexing": True,
"data-dependent shapes": True,
}

_DEFAULT_DTYPES = {
"cpu": {
"real floating": np.dtype(np.float64),
"complex floating": np.dtype(np.complex128),
"integral": np.dtype(np.int64),
"indexing": np.dtype(np.int64),
}
}


def _get_dtypes_with_prefix(prefix: str):
out = set()
for a in np.__all__:
if not a.startswith(prefix):
continue
try:
dt = np.dtype(getattr(np, a))
out.add(dt)
except (ValueError, TypeError, AttributeError):
pass
return sorted(out)


_DTYPES = {
"cpu": {
"bool": [np.bool_],
"signed integer": _get_dtypes_with_prefix("int"),
"unsigned integer": _get_dtypes_with_prefix("uint"),
"real floating": _get_dtypes_with_prefix("float"),
"complex floating": _get_dtypes_with_prefix("complex"),
}
}

for _dtdict in _DTYPES.values():
_dtdict["integral"] = _dtdict["signed integer"] + _dtdict["unsigned integer"]
_dtdict["numeric"] = _dtdict["integral"] + _dtdict["real floating"] + _dtdict["complex floating"]

del _dtdict


def capabilities():
return _CAPABILITIES

Check warning on line 59 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L59

Added line #L59 was not covered by tests


def default_device():
return "cpu"

Check warning on line 63 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L63

Added line #L63 was not covered by tests


@_check_device
def default_dtypes(*, device=None):
if device is None:
device = default_device()
return _DEFAULT_DTYPES[device]

Check warning on line 70 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L68-L70

Added lines #L68 - L70 were not covered by tests


def devices():
return ["cpu"]

Check warning on line 74 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L74

Added line #L74 was not covered by tests


@_check_device
def dtypes(*, device=None, kind=None):
if device is None:
device = default_device()

Check warning on line 80 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L79-L80

Added lines #L79 - L80 were not covered by tests

device_dtypes = _DTYPES[device]

Check warning on line 82 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L82

Added line #L82 was not covered by tests

if kind is None:
return device_dtypes

Check warning on line 85 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L84-L85

Added lines #L84 - L85 were not covered by tests

if isinstance(kind, str):
return device_dtypes[kind]

Check warning on line 88 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L87-L88

Added lines #L87 - L88 were not covered by tests

out = {}

Check warning on line 90 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L90

Added line #L90 was not covered by tests

for k in kind:
out[k] = device_dtypes[k]

Check warning on line 93 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L92-L93

Added lines #L92 - L93 were not covered by tests

return out

Check warning on line 95 in sparse/numba_backend/_info.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_info.py#L95

Added line #L95 was not covered by tests
6 changes: 4 additions & 2 deletions sparse/numba_backend/_sparse_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,10 +607,12 @@
sparse.clip : For full documentation and more details.
numpy.clip : Equivalent NumPy function.
"""
if min is None and max is None:
raise ValueError("One of max or min must be given.")
if out is not None and not isinstance(out, tuple):
out = (out,)
if min is None and max is None:
if out is not None:
return self.__array_ufunc__(np.identity, "__call__", self, out=out)
return self

Check warning on line 615 in sparse/numba_backend/_sparse_array.py

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/_sparse_array.py#L613-L615

Added lines #L613 - L615 were not covered by tests
return self.__array_ufunc__(np.clip, "__call__", self, a_min=min, a_max=max, out=out)

def astype(self, dtype, casting="unsafe", copy=True):
Expand Down
3 changes: 0 additions & 3 deletions sparse/numba_backend/tests/test_coo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1200,9 +1200,6 @@ def test_clip():

assert_eq(np.clip(s, 1, 3), np.clip(x, 1, 3))

with pytest.raises(ValueError):
s.clip()

out = sparse.COO.from_numpy(np.zeros_like(x))
out2 = s.clip(min=1, max=3, out=out)
assert out is out2
Expand Down
Loading
Loading