diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4e57e7621543..dc9ef34db71d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -324,7 +324,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: data-apis/array-api-tests - ref: '827edd804bcace9d64176b8115138d29ae3e8dec' # Latest commit as of 2024-07-30 + ref: 'c48410f96fc58e02eea844e6b7f6cc01680f77ce' # Latest commit as of 2025-04-01 submodules: 'true' path: 'array-api-tests' persist-credentials: false diff --git a/doc/release/upcoming_changes/28615.change.rst b/doc/release/upcoming_changes/28615.change.rst new file mode 100644 index 000000000000..58b751e40704 --- /dev/null +++ b/doc/release/upcoming_changes/28615.change.rst @@ -0,0 +1,5 @@ +* NumPy's ``__array_api_version__`` was upgraded from ``2023.12`` to ``2024.12``. +* `numpy.count_nonzero` for ``axis=None`` (default) now returns a NumPy scalar + instead of a Python integer. +* The parameter ``axis`` in `numpy.take_along_axis` function has now a default + value of ``-1``. diff --git a/numpy/__init__.py b/numpy/__init__.py index f1eb956c5b8c..508fc0d8970b 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -289,7 +289,7 @@ # import with `from numpy import *`. __future_scalars__ = {"str", "bytes", "object"} - __array_api_version__ = "2023.12" + __array_api_version__ = "2024.12" from ._array_api_info import __array_namespace_info__ diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 5502d6e0755a..551921889bf5 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -965,7 +965,7 @@ _DTypeNum: TypeAlias = L[ ] _DTypeBuiltinKind: TypeAlias = L[0, 1, 2] -_ArrayAPIVersion: TypeAlias = L["2021.12", "2022.12", "2023.12"] +_ArrayAPIVersion: TypeAlias = L["2021.12", "2022.12", "2023.12", "2024.12"] _CastingKind: TypeAlias = L["no", "equiv", "safe", "same_kind", "unsafe"] @@ -1153,7 +1153,7 @@ __NUMPY_SETUP__: Final[L[False]] = False __numpy_submodules__: Final[set[LiteralString]] = ... __former_attrs__: Final[_FormerAttrsDict] = ... __future_scalars__: Final[set[L["bytes", "str", "object"]]] = ... -__array_api_version__: Final[L["2023.12"]] = "2023.12" +__array_api_version__: Final[L["2024.12"]] = "2024.12" test: Final[PytestTester] = ... @type_check_only diff --git a/numpy/_core/numeric.pyi b/numpy/_core/numeric.pyi index d60639b97687..416c4eec8785 100644 --- a/numpy/_core/numeric.pyi +++ b/numpy/_core/numeric.pyi @@ -416,7 +416,7 @@ def full_like( # @overload -def count_nonzero(a: ArrayLike, axis: None = None, *, keepdims: L[False] = False) -> int: ... +def count_nonzero(a: ArrayLike, axis: None = None, *, keepdims: L[False] = False) -> np.intp: ... @overload def count_nonzero(a: _ScalarLike_co, axis: _ShapeLike | None = None, *, keepdims: L[True]) -> np.intp: ... @overload diff --git a/numpy/_core/src/multiarray/array_api_standard.c b/numpy/_core/src/multiarray/array_api_standard.c index 76612cff36fb..317fd8a69bb4 100644 --- a/numpy/_core/src/multiarray/array_api_standard.c +++ b/numpy/_core/src/multiarray/array_api_standard.c @@ -60,7 +60,8 @@ array_array_namespace(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds return NULL; } else if (PyUnicode_CompareWithASCIIString(array_api_version, "2021.12") != 0 && PyUnicode_CompareWithASCIIString(array_api_version, "2022.12") != 0 && - PyUnicode_CompareWithASCIIString(array_api_version, "2023.12") != 0) + PyUnicode_CompareWithASCIIString(array_api_version, "2023.12") != 0 && + PyUnicode_CompareWithASCIIString(array_api_version, "2024.12") != 0) { PyErr_Format(PyExc_ValueError, "Version \"%U\" of the Array API Standard is not supported.", diff --git a/numpy/_core/src/multiarray/multiarraymodule.c b/numpy/_core/src/multiarray/multiarraymodule.c index a53dd0960ed0..8ba38b555edb 100644 --- a/numpy/_core/src/multiarray/multiarraymodule.c +++ b/numpy/_core/src/multiarray/multiarraymodule.c @@ -2280,14 +2280,18 @@ array_count_nonzero(PyObject *NPY_UNUSED(self), PyObject *const *args, Py_ssize_ return NULL; } - count = PyArray_CountNonzero(array); - + count = PyArray_CountNonzero(array); Py_DECREF(array); if (count == -1) { return NULL; } - return PyLong_FromSsize_t(count); + + PyArray_Descr *descr = PyArray_DescrFromType(NPY_INTP); + if (descr == NULL) { + return NULL; + } + return PyArray_Scalar(&count, descr, NULL); } static PyObject * diff --git a/numpy/_core/tests/test_regression.py b/numpy/_core/tests/test_regression.py index 8aca446b3920..63899899c8e8 100644 --- a/numpy/_core/tests/test_regression.py +++ b/numpy/_core/tests/test_regression.py @@ -2570,21 +2570,23 @@ def test__array_namespace__(self): assert xp is np xp = arr.__array_namespace__(api_version="2023.12") assert xp is np + xp = arr.__array_namespace__(api_version="2024.12") + assert xp is np xp = arr.__array_namespace__(api_version=None) assert xp is np with pytest.raises( ValueError, - match="Version \"2024.12\" of the Array API Standard " + match="Version \"2025.12\" of the Array API Standard " "is not supported." ): - arr.__array_namespace__(api_version="2024.12") + arr.__array_namespace__(api_version="2025.12") with pytest.raises( ValueError, match="Only None and strings are allowed as the Array API version" ): - arr.__array_namespace__(api_version=2023) + arr.__array_namespace__(api_version=2024) def test_isin_refcnt_bug(self): # gh-25295 diff --git a/numpy/lib/_shape_base_impl.py b/numpy/lib/_shape_base_impl.py index fe5dc38efe38..f9b41f77943c 100644 --- a/numpy/lib/_shape_base_impl.py +++ b/numpy/lib/_shape_base_impl.py @@ -50,12 +50,12 @@ def _make_along_axis_idx(arr_shape, indices, axis): return tuple(fancy_index) -def _take_along_axis_dispatcher(arr, indices, axis): +def _take_along_axis_dispatcher(arr, indices, axis=None): return (arr, indices) @array_function_dispatch(_take_along_axis_dispatcher) -def take_along_axis(arr, indices, axis): +def take_along_axis(arr, indices, axis=-1): """ Take values from the input array by matching 1d index and data slices. @@ -71,14 +71,17 @@ def take_along_axis(arr, indices, axis): arr : ndarray (Ni..., M, Nk...) Source array indices : ndarray (Ni..., J, Nk...) - Indices to take along each 1d slice of `arr`. This must match the - dimension of arr, but dimensions Ni and Nj only need to broadcast - against `arr`. - axis : int + Indices to take along each 1d slice of ``arr``. This must match the + dimension of ``arr``, but dimensions Ni and Nj only need to broadcast + against ``arr``. + axis : int or None, optional The axis to take 1d slices along. If axis is None, the input array is treated as if it had first been flattened to 1d, for consistency with `sort` and `argsort`. + .. versionchanged:: 2.3 + The default value is now ``-1``. + Returns ------- out: ndarray (Ni..., J, Nk...) diff --git a/numpy/lib/_shape_base_impl.pyi b/numpy/lib/_shape_base_impl.pyi index 09d67cc3a062..15cbef7e4773 100644 --- a/numpy/lib/_shape_base_impl.pyi +++ b/numpy/lib/_shape_base_impl.pyi @@ -70,7 +70,7 @@ class _SupportsArrayWrap(Protocol): def take_along_axis( arr: _ScalarT | NDArray[_ScalarT], indices: NDArray[integer], - axis: int | None, + axis: int | None = ..., ) -> NDArray[_ScalarT]: ... def put_along_axis( diff --git a/numpy/typing/tests/data/reveal/numeric.pyi b/numpy/typing/tests/data/reveal/numeric.pyi index bd94f4e7eede..7c1ea8958e3b 100644 --- a/numpy/typing/tests/data/reveal/numeric.pyi +++ b/numpy/typing/tests/data/reveal/numeric.pyi @@ -25,9 +25,9 @@ AR_O: npt.NDArray[np.object_] B: list[int] C: SubClass -assert_type(np.count_nonzero(i8), int) -assert_type(np.count_nonzero(AR_i8), int) -assert_type(np.count_nonzero(B), int) +assert_type(np.count_nonzero(i8), np.intp) +assert_type(np.count_nonzero(AR_i8), np.intp) +assert_type(np.count_nonzero(B), np.intp) assert_type(np.count_nonzero(AR_i8, keepdims=True), npt.NDArray[np.intp]) assert_type(np.count_nonzero(AR_i8, axis=0), Any) diff --git a/tools/ci/array-api-xfails.txt b/tools/ci/array-api-xfails.txt index c81b61c5740e..98c3895ced06 100644 --- a/tools/ci/array-api-xfails.txt +++ b/tools/ci/array-api-xfails.txt @@ -21,3 +21,30 @@ array_api_tests/test_signatures.py::test_func_signature[vecdot] # input is cast to min/max's dtype if they're different array_api_tests/test_operators_and_elementwise_functions.py::test_clip + +# missing 'dtype' keyword argument +array_api_tests/test_signatures.py::test_extension_func_signature[fft.fftfreq] +array_api_tests/test_signatures.py::test_extension_func_signature[fft.rfftfreq] + +# fails on np.repeat(np.array([]), np.array([])) test case +array_api_tests/test_manipulation_functions.py::test_repeat + +# NumPy matches Python behavior and it returns NaN and -1 in these cases +array_api_tests/test_special_cases.py::test_binary[floor_divide(x1_i is +infinity and isfinite(x2_i) and x2_i > 0) -> +infinity] +array_api_tests/test_special_cases.py::test_binary[floor_divide(x1_i is +infinity and isfinite(x2_i) and x2_i < 0) -> -infinity] +array_api_tests/test_special_cases.py::test_binary[floor_divide(x1_i is -infinity and isfinite(x2_i) and x2_i > 0) -> -infinity] +array_api_tests/test_special_cases.py::test_binary[floor_divide(x1_i is -infinity and isfinite(x2_i) and x2_i < 0) -> +infinity] +array_api_tests/test_special_cases.py::test_binary[__floordiv__(x1_i is +infinity and isfinite(x2_i) and x2_i > 0) -> +infinity] +array_api_tests/test_special_cases.py::test_binary[__floordiv__(x1_i is +infinity and isfinite(x2_i) and x2_i < 0) -> -infinity] +array_api_tests/test_special_cases.py::test_binary[__floordiv__(x1_i is -infinity and isfinite(x2_i) and x2_i > 0) -> -infinity] +array_api_tests/test_special_cases.py::test_binary[__floordiv__(x1_i is -infinity and isfinite(x2_i) and x2_i < 0) -> +infinity] +array_api_tests/test_special_cases.py::test_binary[floor_divide(isfinite(x1_i) and x1_i > 0 and x2_i is -infinity) -> -0] +array_api_tests/test_special_cases.py::test_binary[floor_divide(isfinite(x1_i) and x1_i < 0 and x2_i is +infinity) -> -0] +array_api_tests/test_special_cases.py::test_binary[__floordiv__(isfinite(x1_i) and x1_i > 0 and x2_i is -infinity) -> -0] +array_api_tests/test_special_cases.py::test_binary[__floordiv__(isfinite(x1_i) and x1_i < 0 and x2_i is +infinity) -> -0] +array_api_tests/test_special_cases.py::test_iop[__ifloordiv__(x1_i is +infinity and isfinite(x2_i) and x2_i > 0) -> +infinity] +array_api_tests/test_special_cases.py::test_iop[__ifloordiv__(x1_i is +infinity and isfinite(x2_i) and x2_i < 0) -> -infinity] +array_api_tests/test_special_cases.py::test_iop[__ifloordiv__(x1_i is -infinity and isfinite(x2_i) and x2_i > 0) -> -infinity] +array_api_tests/test_special_cases.py::test_iop[__ifloordiv__(x1_i is -infinity and isfinite(x2_i) and x2_i < 0) -> +infinity] +array_api_tests/test_special_cases.py::test_iop[__ifloordiv__(isfinite(x1_i) and x1_i > 0 and x2_i is -infinity) -> -0] +array_api_tests/test_special_cases.py::test_iop[__ifloordiv__(isfinite(x1_i) and x1_i < 0 and x2_i is +infinity) -> -0]