diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c58796ea578..f995d1c9a419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.21.0] - MM/DD/2026 +## [0.21.0] - 2026-MM-DD + +This release is compatible with NumPy 2.4.5. ### Added * Added C API functions for `dpnp.tensor.usm_ndarray` setters and getters to avoid ABI breakage if `dpnp.tensor.usm_ndarray` is modified [gh-2866](https://github.com/IntelPython/dpnp/pull/2866) ### Changed -* Changed `dpnp.meshgrid` and `dpnp.tensor.meshgrid` to return a tuple instead of a list, aligning with NumPy 2.5+ behavior and 2025.12 version of the Python array API standard [#2854](https://github.com/IntelPython/dpnp/pull/2854) +* Changed `dpnp.meshgrid` and `dpnp.tensor.meshgrid` to return a tuple instead of a list, aligning with NumPy 2.5+ behavior and 2025.12 version of the Python array API standard [#2854](https://github.com/IntelPython/dpnp/pull/2854) * Updated `searchsorted` implementations to align with the 2025.12 array API spec [gh-2902](https://github.com/IntelPython/dpnp/pull/2902) +* Updated tests to align with NumPy 2.4.5 compatibility [gh-2920](https://github.com/IntelPython/dpnp/pull/2920) ### Deprecated diff --git a/dpnp/tests/test_binary_ufuncs.py b/dpnp/tests/test_binary_ufuncs.py index 0de83c5b99ce..f638fbf2ac1a 100644 --- a/dpnp/tests/test_binary_ufuncs.py +++ b/dpnp/tests/test_binary_ufuncs.py @@ -1014,16 +1014,21 @@ def test_broadcasting(self, func, dt): result = getattr(dpnp, func)(ia, b) assert_array_equal(result, expected) - @pytest.mark.parametrize("dt", [numpy.int32, numpy.int64]) - def test_gcd_overflow(self, dt): - a = dt(numpy.iinfo(dt).min) # negative power of two - ia = dpnp.array(a) - q = -(a // 4) + @pytest.mark.parametrize("sign", [1, -1]) + @pytest.mark.parametrize("dt", get_integer_dtypes(no_unsigned=True)) + def test_gcd_overflow(self, sign, dt): + a = dt(numpy.iinfo(dt).min) # INT_MIN + q = (a // 4) * sign + ia, iq = dpnp.array(a), dpnp.array(q) # verify that we don't overflow when taking abs(x) # not relevant for lcm, where the result is unrepresentable anyway - expected = numpy.gcd(a, q) - result = dpnp.gcd(ia, q) + expected = numpy.gcd(a, q * 3) + result = dpnp.gcd(ia, iq * 3) + assert_array_equal(result, expected) + + expected = numpy.gcd(q * 3, a) + result = dpnp.gcd(iq * 3, ia) assert_array_equal(result, expected) def test_lcm_overflow(self): diff --git a/dpnp/tests/test_indexing.py b/dpnp/tests/test_indexing.py index 2edc8214f3e6..29939740a40c 100644 --- a/dpnp/tests/test_indexing.py +++ b/dpnp/tests/test_indexing.py @@ -661,10 +661,11 @@ def test_empty(self, dtype, mode): dpnp.put(ia, [1, 2, 3], [], mode=mode) assert_array_equal(ia, a) - # TODO: enable test for numpy also since 2.0 + @testing.with_requires("numpy>=2.0") + @pytest.mark.parametrize("xp", [dpnp, numpy]) @pytest.mark.parametrize("mode", ["clip", "wrap"]) - def test_empty_input(self, mode): - empty = dpnp.asarray(list()) + def test_empty_input(self, xp, mode): + empty = xp.asarray(list()) with pytest.raises(IndexError): empty.put(1, 1, mode=mode) diff --git a/dpnp/tests/test_linalg.py b/dpnp/tests/test_linalg.py index e0225ec50608..be8987ab066e 100644 --- a/dpnp/tests/test_linalg.py +++ b/dpnp/tests/test_linalg.py @@ -290,8 +290,7 @@ def test_empty(self, shape, p): expected = numpy.linalg.cond(a, p=p) assert_dtype_allclose(result, expected) - # TODO: uncomment once numpy 2.3.3 release is published - # @testing.with_requires("numpy>=2.3.3") + @testing.with_requires("numpy>=2.3.3") @pytest.mark.parametrize( "dtype", get_all_dtypes(no_none=True, no_bool=True) ) @@ -305,9 +304,6 @@ def test_basic(self, dtype, shape, p): result = dpnp.linalg.cond(ia, p=p) expected = numpy.linalg.cond(a, p=p) - # TODO: remove when numpy#29333 is released - if numpy_version() < "2.3.3": - expected = expected.real assert_dtype_allclose(result, expected, factor=16) @pytest.mark.parametrize("p", _norms) @@ -3238,8 +3234,7 @@ def test_errors(self): ValueError, dpnp.linalg.matrix_rank, a_dp, tol=1e-06, rtol=1e-04 ) - # TODO: use below fixture when NumPy 2.5 is released - # @testing.with_requires("numpy>=2.5") + @testing.with_requires("numpy>=2.4.5") @pytest.mark.parametrize( "shape", [ @@ -3258,14 +3253,7 @@ def test_empty(self, shape): ia = dpnp.array(a) result = dpnp.linalg.matrix_rank(ia) - if numpy_version() < "2.5.0": # TODO: remove - # Expected behavior: rank of empty matrix is 0 - # For stacked matrices, return array of zeros - expected = numpy.zeros(shape[:-2], dtype=numpy.intp) - if expected.ndim == 0: - expected = numpy.array(0) - else: - expected = numpy.linalg.matrix_rank(a) + expected = numpy.linalg.matrix_rank(a) assert_array_equal(result, expected, strict=True) # Also test with hermitian=True diff --git a/dpnp/tests/test_manipulation.py b/dpnp/tests/test_manipulation.py index 4fc4b8cb1619..d09f19f1d77d 100644 --- a/dpnp/tests/test_manipulation.py +++ b/dpnp/tests/test_manipulation.py @@ -23,7 +23,6 @@ get_integer_float_dtypes, get_unsigned_dtypes, has_support_aspect64, - numpy_version, ) from .third_party.cupy import testing @@ -90,16 +89,15 @@ def test_size(self): assert dpnp.size(ia, 1) == numpy.size(a, 1) - # TODO: include commented code in the test when numpy-2.4 is released - # @testing.with_requires("numpy>=2.4") - def test_size_tuple(self): + @testing.with_requires("numpy>=2.4.0") + @pytest.mark.parametrize("axis", [(), (0,), (1,), (0, 1)]) + def test_size_tuple(self, axis): a = [[1, 2, 3], [4, 5, 6]] ia = dpnp.array(a) - assert dpnp.size(ia, ()) == 1 # numpy.size(a, ()) - assert dpnp.size(ia, (0,)) == 2 # numpy.size(a, (0,)) - assert dpnp.size(ia, (1,)) == 3 # numpy.size(a, (1,)) - assert dpnp.size(ia, (0, 1)) == 6 # numpy.size(a, (0, 1)) + result = dpnp.size(ia, axis=axis) + expected = numpy.size(a, axis=axis) + assert result == expected class TestAppend: @@ -1891,8 +1889,7 @@ def test_equal_nan(self, eq_nan_kwd): expected = numpy.unique(a, **eq_nan_kwd) assert_array_equal(result, expected) - # TODO: uncomment once numpy 2.4.0 release is published - # @testing.with_requires("numpy>=2.4.0") + @testing.with_requires("numpy>=2.4.0") @pytest.mark.parametrize("axis", [0, -1]) def test_1d_equal_nan_axis(self, axis): a = numpy.array([numpy.nan, 0, 0, numpy.nan]) @@ -1900,16 +1897,11 @@ def test_1d_equal_nan_axis(self, axis): result = dpnp.unique(ia, axis=axis, equal_nan=True) expected = numpy.unique(a, axis=axis, equal_nan=True) - # TODO: remove when numpy#29372 is released - if numpy_version() < "2.4.0": - expected = numpy.array([0.0, numpy.nan]) assert_array_equal(result, expected) - # TODO: uncomment once numpy 2.4.0 release is published - # @testing.with_requires("numpy>=2.4.0") + @testing.with_requires("numpy>=2.4.0") @pytest.mark.parametrize("equal_nan", [True, False]) - # @pytest.mark.parametrize("xp", [numpy, dpnp]) - @pytest.mark.parametrize("xp", [dpnp]) + @pytest.mark.parametrize("xp", [numpy, dpnp]) def test_1d_axis_float_raises_typeerror(self, xp, equal_nan): a = xp.array([xp.nan, 0, 0, xp.nan]) with pytest.raises(TypeError, match="integer argument expected"): diff --git a/dpnp/tests/test_mathematical.py b/dpnp/tests/test_mathematical.py index 8de7ec2ed80d..3dcae2ac2693 100644 --- a/dpnp/tests/test_mathematical.py +++ b/dpnp/tests/test_mathematical.py @@ -72,6 +72,7 @@ def test_angle_complex(self, dtype, deg): class TestConj: + @testing.with_requires("numpy!=2.4.5") @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) def test_conj(self, dtype): a = generate_random_numpy_array(20, dtype) @@ -1674,15 +1675,13 @@ def test_zero(self, dt): flag = dt in [numpy.int8, numpy.int16, numpy.uint8, numpy.uint16] assert_dtype_allclose(result, expected, check_only_type_kind=flag) - # TODO: add a proper NumPy version once resolved - @testing.with_requires("numpy>=2.0.0") + @testing.with_requires("numpy>=2.3.0") def test_zero_fp16(self): a = numpy.array([0.0], dtype=numpy.float16) ia = dpnp.array(a) result = dpnp.sinc(ia) - # expected = numpy.sinc(a) # numpy returns NaN, but expected 1.0 - expected = numpy.ones_like(a) + expected = numpy.sinc(a) assert_dtype_allclose(result, expected) @pytest.mark.usefixtures("suppress_invalid_numpy_warnings") diff --git a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_complex_ops.py b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_complex_ops.py index a2fe0e2f256c..b13e326c7452 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_complex_ops.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_complex_ops.py @@ -13,6 +13,8 @@ class TestConj: @testing.numpy_cupy_array_almost_equal() def test_conj(self, xp, dtype): x = testing.shaped_arange((2, 3), xp, dtype) + if xp is numpy and numpy.__version__ == "2.4.5" and x.dtype == bool: + return x # NumPy 2.4.5 had a regression for bool_arr.conj() return x.conj() @testing.for_all_dtypes(no_complex=True) @@ -20,6 +22,8 @@ def test_conj(self, xp, dtype): def test_conj_pass(self, xp, dtype): x = testing.shaped_arange((2, 3), xp, dtype) y = x.conj() + if xp is numpy and numpy.__version__ == "2.4.5": + return x # NumPy 2.4.5 had a regression for bool_arr.conj() assert x is y return y @@ -27,6 +31,8 @@ def test_conj_pass(self, xp, dtype): @testing.numpy_cupy_array_almost_equal() def test_conjugate(self, xp, dtype): x = testing.shaped_arange((2, 3), xp, dtype) + if xp is numpy and numpy.__version__ == "2.4.5" and x.dtype == bool: + return x # NumPy 2.4.5 had a regression for bool_arr.conj() return x.conjugate() @testing.for_all_dtypes(no_complex=True) @@ -34,6 +40,8 @@ def test_conjugate(self, xp, dtype): def test_conjugate_pass(self, xp, dtype): x = testing.shaped_arange((2, 3), xp, dtype) y = x.conjugate() + if xp is numpy and numpy.__version__ == "2.4.5": + return x # NumPy 2.4.5 had a regression for bool_arr.conj() assert x is y return y diff --git a/dpnp/tests/third_party/cupy/core_tests/test_nep50_examples.py b/dpnp/tests/third_party/cupy/core_tests/test_nep50_examples.py index 44f5433281e4..4e7e7b99dc5d 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_nep50_examples.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_nep50_examples.py @@ -1,15 +1,8 @@ -import numpy import pytest -import dpnp as cp from dpnp.tests.helper import has_support_aspect64 from dpnp.tests.third_party.cupy import testing -# TODO: remove once all dtype aliases added -cp.int8 = numpy.int8 -cp.uint8 = numpy.uint8 -cp.int16 = numpy.int16 - # "example string" or # ("example string", "xfail message") examples = [ diff --git a/dpnp/tests/third_party/cupy/indexing_tests/test_indexing.py b/dpnp/tests/third_party/cupy/indexing_tests/test_indexing.py index f28d7647f148..bec8f4204b33 100644 --- a/dpnp/tests/third_party/cupy/indexing_tests/test_indexing.py +++ b/dpnp/tests/third_party/cupy/indexing_tests/test_indexing.py @@ -204,10 +204,6 @@ class TestChoose(unittest.TestCase): @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_choose(self, xp, dtype): - # TODO: include additional dtype when dpnp#2201 is merged - dtype_list = [xp.int8, xp.int16] - if dtype in dtype_list or xp.issubdtype(dtype, xp.unsignedinteger): - pytest.skip("dpnp.choose() does not support new integer dtypes.") a = xp.array([0, 2, 1, 2]) c = testing.shaped_arange((3, 4), xp, dtype) return a.choose(c) diff --git a/dpnp/tests/third_party/cupy/statistics_tests/test_correlation.py b/dpnp/tests/third_party/cupy/statistics_tests/test_correlation.py index 604e545e0785..4eaaf0ddf036 100644 --- a/dpnp/tests/third_party/cupy/statistics_tests/test_correlation.py +++ b/dpnp/tests/third_party/cupy/statistics_tests/test_correlation.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import unittest import numpy @@ -226,7 +228,6 @@ def test_correlate_diff_types(self, xp, dtype1, dtype2, mode): @testing.parameterize(*testing.product({"mode": ["valid", "same", "full"]})) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") class TestCorrelateInvalid(unittest.TestCase): @testing.with_requires("numpy>=1.18")