From 135df759f93b1f4bc61bb7e749657e461cf4cab4 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Mon, 3 Feb 2025 17:05:38 -0800 Subject: [PATCH] add explicit shape comparison --- dpnp/dpnp_iface_linearalgebra.py | 4 +- dpnp/dpnp_utils/dpnp_utils_linearalgebra.py | 2 +- dpnp/tests/helper.py | 36 +++++++++++------ dpnp/tests/test_amin_amax.py | 44 ++++++++++----------- dpnp/tests/test_mathematical.py | 42 ++++++-------------- dpnp/tests/test_nanfunctions.py | 4 +- dpnp/tests/test_product.py | 5 ++- dpnp/tests/testing/array.py | 9 +++++ 8 files changed, 77 insertions(+), 69 deletions(-) diff --git a/dpnp/dpnp_iface_linearalgebra.py b/dpnp/dpnp_iface_linearalgebra.py index 24303ba8c70b..bffd2015a39e 100644 --- a/dpnp/dpnp_iface_linearalgebra.py +++ b/dpnp/dpnp_iface_linearalgebra.py @@ -1275,13 +1275,13 @@ def vdot(a, b): if b.size != 1: raise ValueError("The second array should be of size one.") a_conj = numpy.conj(a) - return _call_multiply(a_conj, b) + return dpnp.squeeze(_call_multiply(a_conj, b)) if dpnp.isscalar(b): if a.size != 1: raise ValueError("The first array should be of size one.") a_conj = dpnp.conj(a) - return _call_multiply(a_conj, b) + return dpnp.squeeze(_call_multiply(a_conj, b)) if a.ndim == 1 and b.ndim == 1: return dpnp_dot(a, b, out=None, conjugate=True) diff --git a/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py b/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py index 5d200b2ed0f7..c6047a59a3f0 100644 --- a/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py +++ b/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py @@ -1108,7 +1108,7 @@ def dpnp_multiplication( result = dpnp.moveaxis(result, (-2, -1), axes_res) elif len(axes_res) == 1: result = dpnp.moveaxis(result, (-1,), axes_res) - return dpnp.ascontiguousarray(result) + return result return dpnp.asarray(result, order=order) diff --git a/dpnp/tests/helper.py b/dpnp/tests/helper.py index de0c949f0508..fd5713233430 100644 --- a/dpnp/tests/helper.py +++ b/dpnp/tests/helper.py @@ -10,13 +10,21 @@ from . import config +def _assert_dtype(a_dt, b_dt, check_only_type_kind=False): + + if check_only_type_kind: + assert a_dt.kind == b_dt.kind, f"{a_dt.kind} != {b_dt.kind}" + else: + assert a_dt == b_dt, f"{a_dt} != {b_dt}" + + def assert_dtype_allclose( dpnp_arr, numpy_arr, check_type=True, check_only_type_kind=False, factor=8, - relative_factor=None, + check_shape=True, ): """ Assert DPNP and NumPy array based on maximum dtype resolution of input arrays @@ -40,7 +48,13 @@ def assert_dtype_allclose( """ - list_64bit_types = [numpy.float64, numpy.complex128] + if check_shape: + if hasattr(numpy_arr, "shape"): + assert dpnp_arr.shape == numpy_arr.shape + else: + # numpy output is scalar, then dpnp is 0-D array + assert dpnp_arr.shape == () + is_inexact = lambda x: hasattr(x, "dtype") and dpnp.issubdtype( x.dtype, dpnp.inexact ) @@ -57,34 +71,32 @@ def assert_dtype_allclose( else -dpnp.inf ) tol = factor * max(tol_dpnp, tol_numpy) - assert_allclose(dpnp_arr.asnumpy(), numpy_arr, atol=tol, rtol=tol) + assert_allclose(dpnp_arr, numpy_arr, atol=tol, rtol=tol) if check_type: + list_64bit_types = [numpy.float64, numpy.complex128] numpy_arr_dtype = numpy_arr.dtype dpnp_arr_dtype = dpnp_arr.dtype dpnp_arr_dev = dpnp_arr.sycl_device if check_only_type_kind: - assert dpnp_arr_dtype.kind == numpy_arr_dtype.kind + _assert_dtype(dpnp_arr_dtype, numpy_arr_dtype, True) else: is_np_arr_f2 = numpy_arr_dtype == numpy.float16 if is_np_arr_f2: if has_support_aspect16(dpnp_arr_dev): - assert dpnp_arr_dtype == numpy_arr_dtype + _assert_dtype(dpnp_arr_dtype, numpy_arr_dtype, False) elif ( numpy_arr_dtype not in list_64bit_types or has_support_aspect64(dpnp_arr_dev) ): - assert dpnp_arr_dtype == numpy_arr_dtype + _assert_dtype(dpnp_arr_dtype, numpy_arr_dtype, False) else: - assert dpnp_arr_dtype.kind == numpy_arr_dtype.kind + _assert_dtype(dpnp_arr_dtype, numpy_arr_dtype, True) else: - assert_array_equal(dpnp_arr.asnumpy(), numpy_arr) + assert_array_equal(dpnp_arr, numpy_arr) if check_type and hasattr(numpy_arr, "dtype"): - if check_only_type_kind: - assert dpnp_arr.dtype.kind == numpy_arr.dtype.kind - else: - assert dpnp_arr.dtype == numpy_arr.dtype + _assert_dtype(dpnp_arr.dtype, numpy_arr.dtype, check_only_type_kind) def get_integer_dtypes(all_int_types=False, no_unsigned=False): diff --git a/dpnp/tests/test_amin_amax.py b/dpnp/tests/test_amin_amax.py index 35f45cd40822..1741674b541f 100644 --- a/dpnp/tests/test_amin_amax.py +++ b/dpnp/tests/test_amin_amax.py @@ -9,7 +9,7 @@ @pytest.mark.parametrize("func", ["amax", "amin"]) @pytest.mark.parametrize("keepdims", [True, False]) -@pytest.mark.parametrize("dtype", get_all_dtypes()) +@pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) def test_amax_amin(func, keepdims, dtype): a = [ [[-2.0, 3.0], [9.1, 0.2]], @@ -22,14 +22,14 @@ def test_amax_amin(func, keepdims, dtype): for axis in range(len(a)): result = getattr(dpnp, func)(ia, axis=axis, keepdims=keepdims) expected = getattr(numpy, func)(a, axis=axis, keepdims=keepdims) - assert_allclose(expected, result) + assert_allclose(expected, result, strict=True) -def _get_min_max_input(type, shape): +def _get_min_max_input(dtype, shape): size = numpy.prod(shape) - a = numpy.arange(size, dtype=type) + a = numpy.arange(size, dtype=dtype) a[int(size / 2)] = size + 5 - if numpy.issubdtype(type, numpy.unsignedinteger): + if numpy.issubdtype(dtype, numpy.unsignedinteger): a[int(size / 3)] = size else: a[int(size / 3)] = -(size + 5) @@ -37,37 +37,35 @@ def _get_min_max_input(type, shape): return a.reshape(shape) -@pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) +@pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True, no_bool=True)) @pytest.mark.parametrize( - "shape", [(4,), (2, 3), (4, 5, 6)], ids=["(4,)", "(2, 3)", "(4, 5, 6)"] + "shape", [(4,), (2, 3), (4, 5, 6)], ids=["1D", "2D", "3D"] ) def test_amax_diff_shape(dtype, shape): a = _get_min_max_input(dtype, shape) - ia = dpnp.array(a) - np_res = numpy.amax(a) - dpnp_res = dpnp.amax(ia) - assert_array_equal(dpnp_res, np_res) + expected = numpy.amax(a) + result = dpnp.amax(ia) + assert_array_equal(result, expected, strict=True) - np_res = a.max() - dpnp_res = ia.max() - numpy.testing.assert_array_equal(dpnp_res, np_res) + expected = a.max() + result = ia.max() + assert_array_equal(result, expected, strict=True) -@pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) +@pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True, no_bool=True)) @pytest.mark.parametrize( - "shape", [(4,), (2, 3), (4, 5, 6)], ids=["(4,)", "(2, 3)", "(4, 5, 6)"] + "shape", [(4,), (2, 3), (4, 5, 6)], ids=["1D", "2D", "3D"] ) def test_amin_diff_shape(dtype, shape): a = _get_min_max_input(dtype, shape) - ia = dpnp.array(a) - np_res = numpy.amin(a) - dpnp_res = dpnp.amin(ia) - assert_array_equal(dpnp_res, np_res) + expected = numpy.amin(a) + result = dpnp.amin(ia) + assert_array_equal(result, expected, strict=True) - np_res = a.min() - dpnp_res = ia.min() - assert_array_equal(dpnp_res, np_res) + expected = a.min() + result = ia.min() + assert_array_equal(result, expected, strict=True) diff --git a/dpnp/tests/test_mathematical.py b/dpnp/tests/test_mathematical.py index 6fe2e70bb59d..3549a4093c3b 100644 --- a/dpnp/tests/test_mathematical.py +++ b/dpnp/tests/test_mathematical.py @@ -2091,14 +2091,12 @@ def test_discont(self, dt): @pytest.mark.usefixtures("suppress_divide_invalid_numpy_warnings") -@pytest.mark.parametrize( - "val_type", [bool, int, float], ids=["bool", "int", "float"] -) +@pytest.mark.parametrize("val_type", [bool, int, float]) @pytest.mark.parametrize("data_type", get_all_dtypes()) @pytest.mark.parametrize( "func", ["add", "divide", "multiply", "power", "subtract"] ) -@pytest.mark.parametrize("val", [0, 1, 5], ids=["0", "1", "5"]) +@pytest.mark.parametrize("val", [0, 1, 5]) @pytest.mark.parametrize( "array", [ @@ -2151,7 +2149,7 @@ def test_op_with_scalar(array, val, func, data_type, val_type): assert_allclose(result, expected, rtol=1e-6) -@pytest.mark.parametrize("shape", [(), (3, 2)], ids=["()", "(3, 2)"]) +@pytest.mark.parametrize("shape", [(), (3, 2)], ids=["0D", "2D"]) @pytest.mark.parametrize("dtype", get_all_dtypes()) def test_multiply_scalar(shape, dtype): np_a = numpy.ones(shape, dtype=dtype) @@ -2162,7 +2160,7 @@ def test_multiply_scalar(shape, dtype): assert_allclose(result, expected) -@pytest.mark.parametrize("shape", [(), (3, 2)], ids=["()", "(3, 2)"]) +@pytest.mark.parametrize("shape", [(), (3, 2)], ids=["0D", "2D"]) @pytest.mark.parametrize("dtype", get_all_dtypes()) def test_add_scalar(shape, dtype): np_a = numpy.ones(shape, dtype=dtype) @@ -2173,7 +2171,7 @@ def test_add_scalar(shape, dtype): assert_allclose(result, expected) -@pytest.mark.parametrize("shape", [(), (3, 2)], ids=["()", "(3, 2)"]) +@pytest.mark.parametrize("shape", [(), (3, 2)], ids=["0D", "2D"]) @pytest.mark.parametrize("dtype", get_all_dtypes()) def test_subtract_scalar(shape, dtype): np_a = numpy.ones(shape, dtype=dtype) @@ -2184,7 +2182,7 @@ def test_subtract_scalar(shape, dtype): assert_allclose(result, expected) -@pytest.mark.parametrize("shape", [(), (3, 2)], ids=["()", "(3, 2)"]) +@pytest.mark.parametrize("shape", [(), (3, 2)], ids=["0D", "2D"]) @pytest.mark.parametrize("dtype", get_all_dtypes()) def test_divide_scalar(shape, dtype): np_a = numpy.ones(shape, dtype=dtype) @@ -2196,9 +2194,7 @@ def test_divide_scalar(shape, dtype): @pytest.mark.parametrize( - "data", - [[[1.0, -1.0], [0.1, -0.1]], [-2, -1, 0, 1, 2]], - ids=["[[1., -1.], [0.1, -0.1]]", "[-2, -1, 0, 1, 2]"], + "data", [[[1.0, -1.0], [0.1, -0.1]], [-2, -1, 0, 1, 2]], ids=["2D", "1D"] ) @pytest.mark.parametrize( "dtype", get_all_dtypes(no_bool=True, no_unsigned=True) @@ -2231,9 +2227,7 @@ def test_negative_boolean(): @pytest.mark.parametrize( - "data", - [[[1.0, -1.0], [0.1, -0.1]], [-2, -1, 0, 1, 2]], - ids=["[[1., -1.], [0.1, -0.1]]", "[-2, -1, 0, 1, 2]"], + "data", [[[1.0, -1.0], [0.1, -0.1]], [-2, -1, 0, 1, 2]], ids=["2D", "1D"] ) @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) def test_positive(data, dtype): @@ -2399,13 +2393,13 @@ def test_projection_infinity(self, dtype): a = dpnp.array(X, dtype=dtype) result = dpnp.proj(a) expected = dpnp.array(Y, dtype=dtype) - assert_dtype_allclose(result, expected) + assert_array_equal(result, expected, strict=True) # out keyword dp_out = dpnp.empty(expected.shape, dtype=expected.dtype) result = dpnp.proj(a, out=dp_out) assert dp_out is result - assert_dtype_allclose(result, expected) + assert_array_equal(result, expected, strict=True) @pytest.mark.parametrize("dtype", get_all_dtypes()) def test_projection(self, dtype): @@ -2793,21 +2787,11 @@ def test_bitwise_1array_input(): @pytest.mark.parametrize( "x_shape", - [ - (), - (2), - (3, 4), - (3, 4, 5), - ], + [(), (2), (3, 4), (3, 4, 5)], ) @pytest.mark.parametrize( "y_shape", - [ - (), - (2), - (3, 4), - (3, 4, 5), - ], + [(), (2), (3, 4), (3, 4, 5)], ) def test_elemenwise_outer(x_shape, y_shape): x_np = numpy.random.random(x_shape) @@ -2830,4 +2814,4 @@ def test_elemenwise_outer_scalar(): y = dpnp.asarray(s) expected = dpnp.add.outer(x, y) result = dpnp.add.outer(x, s) - assert_dtype_allclose(result, expected) + assert_array_equal(result, expected, strict=True) diff --git a/dpnp/tests/test_nanfunctions.py b/dpnp/tests/test_nanfunctions.py index a0b5d808697a..4626c9e91f73 100644 --- a/dpnp/tests/test_nanfunctions.py +++ b/dpnp/tests/test_nanfunctions.py @@ -125,7 +125,9 @@ def test_allnans(self, dtype, array): result = getattr(dpnp, self.func)(ia) expected = getattr(numpy, self.func)(a) - assert_dtype_allclose(result, expected) + # for "0d" case, dpnp returns 0D array, numpy returns 1D array + # Array API indicates that the behavior is unspecified + assert_dtype_allclose(result, expected, check_shape=False) @pytest.mark.parametrize("axis", [None, 0, 1]) def test_empty(self, axis): diff --git a/dpnp/tests/test_product.py b/dpnp/tests/test_product.py index b7cbfcf61e6d..974c7070f3ff 100644 --- a/dpnp/tests/test_product.py +++ b/dpnp/tests/test_product.py @@ -902,10 +902,13 @@ def test_strided1(self, dtype, stride): expected = numpy.matmul(a, a) assert_dtype_allclose(result, expected, factor=16) - iOUT = dpnp.empty(shape, dtype=result.dtype) + OUT = numpy.empty(shape, dtype=result.dtype) + out = OUT[slices] + iOUT = dpnp.array(OUT) iout = iOUT[slices] result = dpnp.matmul(ia, ia, out=iout) assert result is iout + expected = numpy.matmul(a, a, out=out) assert_dtype_allclose(result, expected, factor=16) @pytest.mark.parametrize("dtype", _selected_dtypes) diff --git a/dpnp/tests/testing/array.py b/dpnp/tests/testing/array.py index df4db0855dd2..4ef823eed4ac 100644 --- a/dpnp/tests/testing/array.py +++ b/dpnp/tests/testing/array.py @@ -39,6 +39,15 @@ def _assert(assert_func, result, expected, *args, **kwargs): result = convert_item(result) expected = convert_item(expected) + # original versions of assert_equal, assert_array_equal, and assert_allclose + # (since NumPy 2.0) have `strict` parameter. Added here for + # assert_almost_equal, assert_array_almost_equal, and assert_allclose + # (NumPy < 2.0) + if kwargs.get("strict"): + assert result.dtype == expected.dtype + assert result.shape == expected.shape + kwargs.pop("strict") + assert_func(result, expected, *args, **kwargs)