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 1 commit
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 @@ -154,7 +154,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-05-29
hameerabbasi marked this conversation as resolved.
Show resolved Hide resolved
submodules: 'true'
path: 'array-api-tests'
- name: Set up Python
Expand Down
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]
12 changes: 3 additions & 9 deletions sparse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from ._version import __version__, __version_tuple__ # noqa: F401

__array_api_version__ = "2022.12"
__array_api_version__ = "2023.12"


class BackendType(Enum):
Expand Down Expand Up @@ -45,13 +45,7 @@ def get_backend_module():


def __getattr__(attr):
if attr == "numba_backend":
import sparse.numba_backend as backend_module

return backend_module
if attr == "finch_backend":
import sparse.finch_backend as backend_module

return backend_module
if attr == "numba_backend" or attr == "finch_backend":
raise AttributeError
hameerabbasi marked this conversation as resolved.
Show resolved Hide resolved

return getattr(Backend.get_backend_module(), attr)
25 changes: 25 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 @@ -157,10 +165,16 @@
where,
)
from ._dok import DOK
from ._info import capabilities, default_device, default_dtypes, devices, dtypes
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 we shouldn't import them to the main namespace. Let's keep them in __array_namespace_info__ only.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The current array API tests require these in the main namespace as well.

Copy link
Collaborator

@mtsokol mtsokol Jun 10, 2024

Choose a reason for hiding this comment

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

Hmm then I think it's a bug in the array-api-tests suite.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Array API standard doesn't say they need to be in the main namespace: https://data-apis.org/array-api/latest/API_specification/inspection.html#inspection-apis

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Demonstrated the failure in 42c9bfd, let's wait for array-api-tests fixes as you suggest.

from ._io import load_npz, save_npz
from ._umath import elemwise
from ._utils import random


def __array_namespace_info__():
return _info

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

View check run for this annotation

Codecov / codecov/patch

sparse/numba_backend/__init__.py#L175

Added line #L175 was not covered by tests


__all__ = [
"COO",
"DOK",
Expand Down Expand Up @@ -196,19 +210,25 @@
"broadcast_arrays",
"broadcast_to",
"can_cast",
"capabilities",
"ceil",
"clip",
"complex128",
"complex64",
"concat",
"concatenate",
"conj",
"copysign",
"cos",
"cosh",
"default_device",
"default_dtypes",
"devices",
"diagonal",
"diagonalize",
"divide",
"dot",
"dtypes",
"e",
"einsum",
"elemwise",
Expand All @@ -230,6 +250,7 @@
"full_like",
"greater",
"greater_equal",
"hypot",
"iinfo",
"imag",
"inf",
Expand Down Expand Up @@ -258,8 +279,10 @@
"matmul",
"matrix_transpose",
"max",
"maximum",
"mean",
"min",
"minimum",
"moveaxis",
"multiply",
"nan",
Expand Down Expand Up @@ -291,6 +314,7 @@
"round",
"save_npz",
"sign",
"signbit",
"sin",
"sinh",
"sort",
Expand All @@ -314,6 +338,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
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 we want to test these functions. Let's add simple tests, like in NumPy: https://github.com/numpy/numpy/pull/26572/files#diff-db073cec9b943fac08cf9720c471d90dcbb7a0e00f4717433314ec95bee60fe2

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
Loading
Loading