From ef6df2f976e4c264854a256102712523dd49167c Mon Sep 17 00:00:00 2001
From: Vahid Tavanashad <vahid.tavanashad@intel.com>
Date: Mon, 3 Feb 2025 17:05:38 -0800
Subject: [PATCH] add explicit shape comparison

---
 dpnp/dpnp_iface_linearalgebra.py              | 36 ++++++++++-----
 dpnp/dpnp_utils/dpnp_utils_linearalgebra.py   |  2 +-
 dpnp/tests/helper.py                          | 40 ++++++++++++-----
 dpnp/tests/test_arraycreation.py              |  4 --
 dpnp/tests/test_arraypad.py                   | 11 ++---
 dpnp/tests/test_fill.py                       | 14 +++---
 dpnp/tests/test_histogram.py                  | 43 +++++++++++++-----
 dpnp/tests/test_indexing.py                   |  2 +-
 dpnp/tests/test_linalg.py                     |  8 ++--
 dpnp/tests/test_logic.py                      | 10 ++++-
 dpnp/tests/test_mathematical.py               |  7 +--
 dpnp/tests/test_nanfunctions.py               |  5 ++-
 dpnp/tests/test_random_state.py               | 44 +++++++++----------
 dpnp/tests/test_statistics.py                 |  2 -
 dpnp/tests/test_strides.py                    |  2 +-
 dpnp/tests/test_sum.py                        |  3 +-
 dpnp/tests/test_umath.py                      |  6 ++-
 dpnp/tests/testing/array.py                   | 28 ++++++++++++
 dpnp/tests/third_party/cupy/testing/_array.py |  5 +++
 19 files changed, 176 insertions(+), 96 deletions(-)

diff --git a/dpnp/dpnp_iface_linearalgebra.py b/dpnp/dpnp_iface_linearalgebra.py
index fa2cb6136d67..8c381f9ec854 100644
--- a/dpnp/dpnp_iface_linearalgebra.py
+++ b/dpnp/dpnp_iface_linearalgebra.py
@@ -69,16 +69,31 @@
 
 
 # TODO: implement a specific scalar-array kernel
-def _call_multiply(a, b, out=None):
-    """Call multiply function for special cases of scalar-array dots."""
+def _call_multiply(a, b, out=None, outer_calc=False):
+    """
+    Call multiply function for special cases of scalar-array dots.
+
+    if `sc` is an scalar and `a` is an array of type float32, we have
+    dpnp.multiply(a, sc).dtype == dpnp.float32 and
+    numpy.multiply(a, sc).dtype == dpnp.float32.
+
+    However, for scalar-array dots such as dot function we have
+    dpnp.dot(a, sc).dtype == dpnp.float32 while
+    numpy.dot(a, sc).dtype == dpnp.float64.
+
+    We need to adjust the behavior of the multiply function when it is
+    used for special cases of scalar-array dots.
+
+    """
 
     sc, arr = (a, b) if dpnp.isscalar(a) else (b, a)
     sc_dtype = map_dtype_to_device(type(sc), arr.sycl_device)
     res_dtype = dpnp.result_type(sc_dtype, arr)
+    multiply_func = dpnp.multiply.outer if outer_calc else dpnp.multiply
     if out is not None and out.dtype == arr.dtype:
-        res = dpnp.multiply(a, b, out=out)
+        res = multiply_func(a, b, out=out)
     else:
-        res = dpnp.multiply(a, b, dtype=res_dtype)
+        res = multiply_func(a, b, dtype=res_dtype)
     return dpnp.get_result_array(res, out, casting="no")
 
 
@@ -1109,16 +1124,15 @@ def outer(a, b, out=None):
 
     dpnp.check_supported_arrays_type(a, b, scalar_type=True, all_scalars=False)
     if dpnp.isscalar(a):
-        x1 = a
         x2 = dpnp.ravel(b)[None, :]
+        result = _call_multiply(a, x2, out=out, outer_calc=True)
     elif dpnp.isscalar(b):
         x1 = dpnp.ravel(a)[:, None]
-        x2 = b
+        result = _call_multiply(x1, b, out=out, outer_calc=True)
     else:
-        x1 = dpnp.ravel(a)
-        x2 = dpnp.ravel(b)
+        result = dpnp.multiply.outer(dpnp.ravel(a), dpnp.ravel(b), out=out)
 
-    return dpnp.multiply.outer(x1, x2, out=out)
+    return result
 
 
 def tensordot(a, b, axes=2):
@@ -1288,13 +1302,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 71314c90272c..60a4375843fb 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 56d8dd5ac2d2..a98310b3a0c1 100644
--- a/dpnp/tests/helper.py
+++ b/dpnp/tests/helper.py
@@ -10,13 +10,28 @@
 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_shape(a, b):
+    if hasattr(b, "shape"):
+        assert a.shape == b.shape, f"{a.shape} != {b.shape}"
+    else:
+        # numpy output is scalar, then dpnp is 0-D array
+        assert a.shape == (), f"{a.shape} != ()"
+
+
 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
@@ -37,10 +52,13 @@ def assert_dtype_allclose(
     for all data types supported by DPNP when set to True.
     It is effective only when 'check_type' is also set to True.
     The parameter `factor` scales the resolution used for comparing the arrays.
+    The parameter `check_shape`, when True (default), asserts the shape of input arrays is the same.
 
     """
 
-    list_64bit_types = [numpy.float64, numpy.complex128]
+    if check_shape:
+        _assert_shape(dpnp_arr, numpy_arr)
+
     is_inexact = lambda x: hasattr(x, "dtype") and dpnp.issubdtype(
         x.dtype, dpnp.inexact
     )
@@ -57,34 +75,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, strict=False)
         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)
                 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)
                 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, strict=False)
         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 generate_random_numpy_array(
diff --git a/dpnp/tests/test_arraycreation.py b/dpnp/tests/test_arraycreation.py
index 36a320c8cfd5..c801b769a7e2 100644
--- a/dpnp/tests/test_arraycreation.py
+++ b/dpnp/tests/test_arraycreation.py
@@ -952,7 +952,6 @@ def test_ascontiguousarray1(data):
     result = dpnp.ascontiguousarray(data)
     expected = numpy.ascontiguousarray(data)
     assert_dtype_allclose(result, expected)
-    assert result.shape == expected.shape
 
 
 @pytest.mark.parametrize("data", [(), 1, (2, 3), [4]])
@@ -960,7 +959,6 @@ def test_ascontiguousarray2(data):
     result = dpnp.ascontiguousarray(dpnp.array(data))
     expected = numpy.ascontiguousarray(numpy.array(data))
     assert_dtype_allclose(result, expected)
-    assert result.shape == expected.shape
 
 
 @pytest.mark.parametrize(
@@ -970,7 +968,6 @@ def test_asfortranarray1(data):
     result = dpnp.asfortranarray(data)
     expected = numpy.asfortranarray(data)
     assert_dtype_allclose(result, expected)
-    assert result.shape == expected.shape
 
 
 @pytest.mark.parametrize("data", [(), 1, (2, 3), [4]])
@@ -978,7 +975,6 @@ def test_asfortranarray2(data):
     result = dpnp.asfortranarray(dpnp.array(data))
     expected = numpy.asfortranarray(numpy.array(data))
     assert_dtype_allclose(result, expected)
-    assert result.shape == expected.shape
 
 
 def test_meshgrid_raise_error():
diff --git a/dpnp/tests/test_arraypad.py b/dpnp/tests/test_arraypad.py
index 16b7c23c75ef..795311105562 100644
--- a/dpnp/tests/test_arraypad.py
+++ b/dpnp/tests/test_arraypad.py
@@ -42,7 +42,6 @@ def test_basic(self, mode):
         result = dpnp.pad(a_dp, (25, 20), mode=mode)
         if mode == "empty":
             # omit uninitialized "empty" boundary from the comparison
-            assert result.shape == expected.shape
             assert_equal(result[25:-20], expected[25:-20])
         else:
             assert_array_equal(result, expected)
@@ -70,7 +69,6 @@ def test_non_contiguous_array(self, mode):
         result = dpnp.pad(a_dp, (2, 3), mode=mode)
         if mode == "empty":
             # omit uninitialized "empty" boundary from the comparison
-            assert result.shape == expected.shape
             assert_equal(result[2:-3, 2:-3], expected[2:-3, 2:-3])
         else:
             assert_array_equal(result, expected)
@@ -287,10 +285,10 @@ def test_linear_ramp_end_values(self):
         """Ensure that end values are exact."""
         a_dp = dpnp.ones(10).reshape(2, 5)
         a = dpnp.pad(a_dp, (223, 123), mode="linear_ramp")
-        assert_equal(a[:, 0], 0.0)
-        assert_equal(a[:, -1], 0.0)
-        assert_equal(a[0, :], 0.0)
-        assert_equal(a[-1, :], 0.0)
+        assert_equal(a[:, 0], 0.0, strict=False)
+        assert_equal(a[:, -1], 0.0, strict=False)
+        assert_equal(a[0, :], 0.0, strict=False)
+        assert_equal(a[-1, :], 0.0, strict=False)
 
     @pytest.mark.parametrize(
         "dtype", [numpy.uint32, numpy.uint64] + get_all_dtypes(no_none=True)
@@ -426,7 +424,6 @@ def test_empty(self):
         expected = numpy.pad(a_np, [(2, 3), (3, 1)], "empty")
         result = dpnp.pad(a_dp, [(2, 3), (3, 1)], "empty")
         # omit uninitialized "empty" boundary from the comparison
-        assert result.shape == expected.shape
         assert_equal(result[2:-3, 3:-1], expected[2:-3, 3:-1])
 
     # Check how padding behaves on arrays with an empty dimension.
diff --git a/dpnp/tests/test_fill.py b/dpnp/tests/test_fill.py
index b5b82e4cf052..3102de395d93 100644
--- a/dpnp/tests/test_fill.py
+++ b/dpnp/tests/test_fill.py
@@ -38,7 +38,7 @@ def test_fill_strided_array():
     expected = dpnp.tile(dpnp.asarray([0, 1], dtype=a.dtype), 50)
 
     b.fill(1)
-    assert_array_equal(b, 1)
+    assert_array_equal(b, 1, strict=False)
     assert_array_equal(a, expected)
 
 
@@ -51,7 +51,7 @@ def test_fill_strided_2d_array(order):
     expected[::-2, ::2] = 1
 
     b.fill(1)
-    assert_array_equal(b, 1)
+    assert_array_equal(b, 1, strict=False)
     assert_array_equal(a, expected)
 
 
@@ -60,27 +60,27 @@ def test_fill_memset(order):
     a = dpnp.ones((10, 10), dtype="i4", order=order)
     a.fill(0)
 
-    assert_array_equal(a, 0)
+    assert_array_equal(a, 0, strict=False)
 
 
 def test_fill_float_complex_to_int():
     a = dpnp.ones((10, 10), dtype="i4")
 
     a.fill(complex(2, 0))
-    assert_array_equal(a, 2)
+    assert_array_equal(a, 2, strict=False)
 
     a.fill(float(3))
-    assert_array_equal(a, 3)
+    assert_array_equal(a, 3, strict=False)
 
 
 def test_fill_complex_to_float():
     a = dpnp.ones((10, 10), dtype="f4")
 
     a.fill(complex(2, 0))
-    assert_array_equal(a, 2)
+    assert_array_equal(a, 2, strict=False)
 
 
 def test_fill_bool():
     a = dpnp.full(5, fill_value=7, dtype="i4")
     a.fill(True)
-    assert_array_equal(a, 1)
+    assert_array_equal(a, 1, strict=False)
diff --git a/dpnp/tests/test_histogram.py b/dpnp/tests/test_histogram.py
index 06ec6f3fde67..a00312af3a5a 100644
--- a/dpnp/tests/test_histogram.py
+++ b/dpnp/tests/test_histogram.py
@@ -733,7 +733,8 @@ def test_bins(self, bins):
         expected_hist, expected_edges = numpy.histogramdd(v, bins)
         result_hist, result_edges = dpnp.histogramdd(iv, bins_dpnp)
         assert_allclose(result_hist, expected_hist)
-        assert_allclose(result_edges, expected_edges)
+        for x, y in zip(result_edges, expected_edges):
+            assert_allclose(x, y)
 
     def test_no_side_effects(self):
         v = dpnp.array([[1.3, 2.5, 2.3]])
@@ -752,7 +753,8 @@ def test_01d(self, data):
         result_hist, result_edges = dpnp.histogramdd(ia)
 
         assert_allclose(result_hist, expected_hist)
-        assert_allclose(result_edges, expected_edges)
+        for x, y in zip(result_edges, expected_edges):
+            assert_allclose(x, y)
 
     def test_3d(self):
         a = dpnp.ones((10, 10, 10))
@@ -822,7 +824,10 @@ def test_nan_values(self):
         )
         result_hist, result_edges = dpnp.histogramdd(ione_nan, bins=[[0, 1]])
         assert_allclose(result_hist, expected_hist)
-        assert_allclose(result_edges, expected_edges)
+        # dpnp returns both result_hist and result_edges as float64 while
+        # numpy returns result_hist as float64 but result_edges as int64
+        for x, y in zip(result_edges, expected_edges):
+            assert_allclose(x, y, strict=False)
 
         # NaN is not counted
         expected_hist, expected_edges = numpy.histogramdd(
@@ -830,7 +835,10 @@ def test_nan_values(self):
         )
         result_hist, result_edges = dpnp.histogramdd(iall_nan, bins=[[0, 1]])
         assert_allclose(result_hist, expected_hist)
-        assert_allclose(result_edges, expected_edges)
+        # dpnp returns both result_hist and result_edges as float64 while
+        # numpy returns result_hist as float64 but result_edges as int64
+        for x, y in zip(result_edges, expected_edges):
+            assert_allclose(x, y, strict=False)
 
     def test_bins_another_sycl_queue(self):
         v = dpnp.arange(7, 12, sycl_queue=dpctl.SyclQueue())
@@ -866,7 +874,10 @@ def test_different_bins_amount(self, bins_count):
         expected_hist, expected_edges = numpy.histogramdd(v, bins=[bins_count])
         result_hist, result_edges = dpnp.histogramdd(iv, bins=[bins_count])
         assert_array_equal(result_hist, expected_hist)
-        assert_allclose(result_edges, expected_edges)
+        # dpnp returns both result_hist and result_edges as float64 while
+        # numpy returns result_hist as float64 but result_edges as float32
+        for x, y in zip(result_edges, expected_edges):
+            assert_allclose(x, y, strict=False)
 
 
 class TestHistogram2d:
@@ -1045,8 +1056,10 @@ def test_nan_values(self):
             ione_nan, ione_nan, bins=[[0, 1]] * 2
         )
         assert_allclose(result_hist, expected_hist)
-        assert_allclose(result_edges_x, expected_edges_x)
-        assert_allclose(result_edges_y, expected_edges_y)
+        # dpnp returns both result_hist and result_edges as float64 while
+        # numpy returns result_hist as float64 but result_edges as int64
+        assert_allclose(result_edges_x, expected_edges_x, strict=False)
+        assert_allclose(result_edges_y, expected_edges_y, strict=False)
 
         # NaN is not counted
         expected_hist, expected_edges_x, expected_edges_y = numpy.histogram2d(
@@ -1056,8 +1069,10 @@ def test_nan_values(self):
             iall_nan, iall_nan, bins=[[0, 1]] * 2
         )
         assert_allclose(result_hist, expected_hist)
-        assert_allclose(result_edges_x, expected_edges_x)
-        assert_allclose(result_edges_y, expected_edges_y)
+        # dpnp returns both result_hist and result_edges as float64 while
+        # numpy returns result_hist as float64 but result_edges as int64
+        assert_allclose(result_edges_x, expected_edges_x, strict=False)
+        assert_allclose(result_edges_y, expected_edges_y, strict=False)
 
     def test_bins_another_sycl_queue(self):
         x = y = dpnp.arange(7, 12, sycl_queue=dpctl.SyclQueue())
@@ -1107,5 +1122,11 @@ def test_different_bins_amount(self, bins_count):
             ix, iy, bins=bins_count
         )
         assert_array_equal(result_hist, expected_hist)
-        assert_allclose(result_edges_x, expected_edges_x, rtol=1e-6)
-        assert_allclose(result_edges_y, expected_edges_y, rtol=1e-6)
+        # dpnp returns both result_hist and result_edges as float64 while
+        # numpy returns result_hist as float64 but result_edges as float32
+        assert_allclose(
+            result_edges_x, expected_edges_x, rtol=1e-6, strict=False
+        )
+        assert_allclose(
+            result_edges_y, expected_edges_y, rtol=1e-6, strict=False
+        )
diff --git a/dpnp/tests/test_indexing.py b/dpnp/tests/test_indexing.py
index 5439b4dc484e..7c69319f31b8 100644
--- a/dpnp/tests/test_indexing.py
+++ b/dpnp/tests/test_indexing.py
@@ -292,7 +292,7 @@ def test_indexing_array_negative_strides(self):
 
         slices = (slice(None), dpnp.array([0, 1, 2, 3]))
         arr[slices] = 10
-        assert_array_equal(arr, 10.0)
+        assert_equal(arr, 10.0, strict=False)
 
 
 class TestIx:
diff --git a/dpnp/tests/test_linalg.py b/dpnp/tests/test_linalg.py
index 600a1b658514..33652c8cf5f7 100644
--- a/dpnp/tests/test_linalg.py
+++ b/dpnp/tests/test_linalg.py
@@ -2114,7 +2114,7 @@ def test_empty(self, shape, ord, axis, keepdims):
                 # TODO: when similar changes in numpy are available, instead
                 # of assert_equal with zero, we should compare with numpy
                 # ord in [None, 1, 2]
-                assert_equal(dpnp.linalg.norm(ia, **kwarg), 0)
+                assert_equal(dpnp.linalg.norm(ia, **kwarg), 0.0)
                 assert_raises(ValueError, numpy.linalg.norm, a, **kwarg)
         else:
             result = dpnp.linalg.norm(ia, **kwarg)
@@ -2323,7 +2323,8 @@ def test_matrix_norm(self, ord, keepdims):
     @pytest.mark.parametrize("ord", [None, "fro", "nuc", 1, 2, dpnp.inf])
     def test_matrix_norm_empty(self, xp, dtype, shape, axis, ord):
         x = xp.zeros(shape, dtype=dtype)
-        assert_equal(xp.linalg.norm(x, axis=axis, ord=ord), 0)
+        sc = dtype(0.0) if dtype == dpnp.float32 else 0.0
+        assert_equal(xp.linalg.norm(x, axis=axis, ord=ord), sc)
 
     @pytest.mark.parametrize(
         "xp",
@@ -2343,7 +2344,8 @@ def test_matrix_norm_empty(self, xp, dtype, shape, axis, ord):
     @pytest.mark.parametrize("ord", [None, 1, 2, dpnp.inf])
     def test_vector_norm_empty(self, xp, dtype, axis, ord):
         x = xp.zeros(0, dtype=dtype)
-        assert_equal(xp.linalg.vector_norm(x, axis=axis, ord=ord), 0)
+        sc = dtype(0.0) if dtype == dpnp.float32 else 0.0
+        assert_equal(xp.linalg.vector_norm(x, axis=axis, ord=ord), sc)
 
     @testing.with_requires("numpy>=2.0")
     @pytest.mark.parametrize(
diff --git a/dpnp/tests/test_logic.py b/dpnp/tests/test_logic.py
index 3e6f88ee4442..4281279da450 100644
--- a/dpnp/tests/test_logic.py
+++ b/dpnp/tests/test_logic.py
@@ -1,6 +1,11 @@
 import numpy
 import pytest
-from numpy.testing import assert_allclose, assert_equal, assert_raises
+from numpy.testing import (
+    assert_allclose,
+    assert_array_equal,
+    assert_equal,
+    assert_raises,
+)
 
 import dpnp
 
@@ -36,7 +41,8 @@ def test_all_any_out(self, func, a_dtype, out_dtype):
         out = dpnp.empty(expected.shape, dtype=out_dtype)
         result = getattr(dpnp, func)(dp_array, out=out)
         assert out is result
-        assert_allclose(result, expected)
+        # out kwarg is not used with NumPy, dtype may differ
+        assert_array_equal(result, expected, strict=False)
 
     @pytest.mark.parametrize("func", ["all", "any"])
     @pytest.mark.parametrize("axis", [None, 0, 1, (0, 1)])
diff --git a/dpnp/tests/test_mathematical.py b/dpnp/tests/test_mathematical.py
index 7e917b665ee0..ee875823b6e4 100644
--- a/dpnp/tests/test_mathematical.py
+++ b/dpnp/tests/test_mathematical.py
@@ -353,13 +353,10 @@ def test_dtype(self, func, in_dt, dt):
         result = getattr(ia, func)(dtype=dt)
         assert_dtype_allclose(result, expected)
 
-    # TODO: include boolean dtype when dpctl-0.20.0 is being used in Internal CI
     @pytest.mark.usefixtures("suppress_complex_warning")
     @pytest.mark.parametrize("func", ["cumprod", "cumsum"])
     @pytest.mark.parametrize("in_dt", get_all_dtypes(no_none=True))
-    @pytest.mark.parametrize(
-        "out_dt", get_all_dtypes(no_none=True, no_bool=True)
-    )
+    @pytest.mark.parametrize("out_dt", get_all_dtypes(no_none=True))
     def test_out(self, func, in_dt, out_dt):
         a = generate_random_numpy_array(5, dtype=in_dt, low=-5, high=5)
         out = numpy.zeros_like(a, dtype=out_dt)
@@ -1370,8 +1367,6 @@ def test_basic(self, axis, keepdims, dtype):
 
         expected = numpy.prod(a, axis=axis, keepdims=keepdims)
         result = dpnp.prod(ia, axis=axis, keepdims=keepdims)
-
-        assert result.shape == expected.shape
         assert_dtype_allclose(result, expected)
 
     @pytest.mark.parametrize("axis", [None, 0, 1, -1, 2, -2, (1, 2), (0, -2)])
diff --git a/dpnp/tests/test_nanfunctions.py b/dpnp/tests/test_nanfunctions.py
index ed0d95d2c7af..d92cee045a72 100644
--- a/dpnp/tests/test_nanfunctions.py
+++ b/dpnp/tests/test_nanfunctions.py
@@ -110,7 +110,9 @@ def test_allnans(self, func, dtype, array):
 
         result = getattr(dpnp, func)(ia)
         expected = getattr(numpy, 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, func, axis):
@@ -511,7 +513,6 @@ def test_basic(self, func, axis, keepdims, dtype):
         expected = getattr(numpy, func)(a, axis=axis, keepdims=keepdims)
         result = getattr(dpnp, func)(ia, axis=axis, keepdims=keepdims)
 
-        assert result.shape == expected.shape
         assert_allclose(result, expected, rtol=1e-5)
 
     @pytest.mark.usefixtures("suppress_complex_warning")
diff --git a/dpnp/tests/test_random_state.py b/dpnp/tests/test_random_state.py
index fb483692a49a..ecc76611377a 100644
--- a/dpnp/tests/test_random_state.py
+++ b/dpnp/tests/test_random_state.py
@@ -100,7 +100,7 @@ def test_distr(self, dtype, usm_type):
     @pytest.mark.parametrize("dtype", [dpnp.float32, dpnp.float64, None])
     @pytest.mark.parametrize("usm_type", list_of_usm_types)
     def test_scale(self, dtype, usm_type):
-        mean = 7.0
+        mean = dtype(7.0) if dtype == dpnp.float32 else 7.0
         rs = RandomState(39567)
         func = lambda scale: rs.normal(
             loc=mean, scale=scale, dtype=dtype, usm_type=usm_type
@@ -135,7 +135,7 @@ def test_scale(self, dtype, usm_type):
     )
     def test_inf_loc(self, loc):
         a = RandomState(6531).normal(loc=loc, scale=1, size=1000)
-        assert_equal(a, get_default_floating()(loc))
+        assert_equal(a, get_default_floating()(loc), strict=False)
 
     def test_inf_scale(self):
         a = RandomState().normal(0, numpy.inf, size=1000)
@@ -427,8 +427,8 @@ def test_negative_interval(self):
         assert -5 <= rs.randint(-5, -1) < -1
 
         x = rs.randint(-7, -1, 5)
-        assert_equal(-7 <= x, True)
-        assert_equal(x < -1, True)
+        assert_equal(-7 <= x, True, strict=False)
+        assert_equal(x < -1, True, strict=False)
 
     def test_bounds_checking(self):
         dtype = dpnp.int32
@@ -466,13 +466,13 @@ def test_rng_zero_and_extremes(self):
             )
 
         tgt = high - 1
-        assert_equal(func(tgt, tgt + 1, size=1000), tgt)
+        assert_equal(func(tgt, tgt + 1, size=1000), tgt, strict=False)
 
         tgt = low
-        assert_equal(func(tgt, tgt + 1, size=1000), tgt)
+        assert_equal(func(tgt, tgt + 1, size=1000), tgt, strict=False)
 
         tgt = (low + high) // 2
-        assert_equal(func(tgt, tgt + 1, size=1000), tgt)
+        assert_equal(func(tgt, tgt + 1, size=1000), tgt, strict=False)
 
     def test_full_range(self):
         dtype = dpnp.int32
@@ -972,31 +972,29 @@ def test_distr(self, bounds, dtype, usm_type):
         dtype = get_default_floating() if dtype is None else dtype
         if sycl_queue.sycl_device.is_cpu:
             if dtype != dpnp.int32:
-                expected = numpy.array(
-                    [
-                        [4.023770128630567, 8.87456423597643],
-                        [2.888630247435067, 4.823004481580574],
-                        [2.030351535445079, 4.533497077834326],
-                    ]
-                )
+                data = [
+                    [4.023770128630567, 8.87456423597643],
+                    [2.888630247435067, 4.823004481580574],
+                    [2.030351535445079, 4.533497077834326],
+                ]
+                expected = numpy.array(data, dtype=dtype)
                 precision = dpnp.finfo(dtype).precision
                 assert_array_almost_equal(actual, expected, decimal=precision)
             else:
-                expected = numpy.array([[3, 8], [2, 4], [1, 4]])
+                expected = numpy.array([[3, 8], [2, 4], [1, 4]], dtype=dtype)
                 assert_array_equal(actual, expected)
         else:
             if dtype != dpnp.int32:
-                expected = numpy.array(
-                    [
-                        [1.230000000452886, 4.889115418092382],
-                        [6.084098950993071, 1.682066500463302],
-                        [3.316473517549554, 8.428297791221597],
-                    ]
-                )
+                data = [
+                    [1.230000000452886, 4.889115418092382],
+                    [6.084098950993071, 1.682066500463302],
+                    [3.316473517549554, 8.428297791221597],
+                ]
+                expected = numpy.array(data, dtype=dtype)
                 precision = dpnp.finfo(dtype).precision
                 assert_array_almost_equal(actual, expected, decimal=precision)
             else:
-                expected = numpy.array([[1, 4], [5, 1], [3, 7]])
+                expected = numpy.array([[1, 4], [5, 1], [3, 7]], dtype=dtype)
                 assert_array_equal(actual, expected)
 
         # check if compute follows data isn't broken
diff --git a/dpnp/tests/test_statistics.py b/dpnp/tests/test_statistics.py
index efcc0ae20437..cf436087b607 100644
--- a/dpnp/tests/test_statistics.py
+++ b/dpnp/tests/test_statistics.py
@@ -348,7 +348,6 @@ def test_corrcoef_dtype(self, dt_in, dt_out):
 
         expected = numpy.corrcoef(a, dtype=dt_out)
         result = dpnp.corrcoef(ia, dtype=dt_out)
-        assert expected.dtype == result.dtype
         assert_allclose(result, expected, rtol=1e-6)
 
     @pytest.mark.usefixtures(
@@ -1132,7 +1131,6 @@ def test_dtype(self, func, dt_in, dt_out):
 
         expected = getattr(numpy, func)(a, dtype=dt_out)
         result = getattr(dpnp, func)(ia, dtype=dt_out)
-        assert expected.dtype == result.dtype
         assert_allclose(result, expected, rtol=1e-6)
 
     @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True))
diff --git a/dpnp/tests/test_strides.py b/dpnp/tests/test_strides.py
index 3665fb05c069..712bc7c91df3 100644
--- a/dpnp/tests/test_strides.py
+++ b/dpnp/tests/test_strides.py
@@ -251,7 +251,7 @@ def test_bitwise(func, dtype):
 
     result = getattr(dpnp, func)(ia, ib)
     expected = getattr(numpy, func)(a, b)
-    assert_array_equal(result, expected, strict=True)
+    assert_array_equal(result, expected)
 
 
 @pytest.mark.parametrize("dtype", get_integer_float_dtypes())
diff --git a/dpnp/tests/test_sum.py b/dpnp/tests/test_sum.py
index 68dd14ab7fdf..eaee7efae8e1 100644
--- a/dpnp/tests/test_sum.py
+++ b/dpnp/tests/test_sum.py
@@ -126,7 +126,8 @@ def test_sum_out(dtype, axis):
     expected = numpy.sum(a_np, axis=axis)
     res = dpnp.empty(expected.shape, dtype=dtype)
     a.sum(axis=axis, out=res)
-    assert_array_equal(expected, res)
+    # NumPy result is without out keyword, dtype may differ
+    assert_array_equal(res, expected, strict=False)
 
 
 @pytest.mark.usefixtures("suppress_complex_warning")
diff --git a/dpnp/tests/test_umath.py b/dpnp/tests/test_umath.py
index 53a5d8d37bc9..56f55de2f1c7 100644
--- a/dpnp/tests/test_umath.py
+++ b/dpnp/tests/test_umath.py
@@ -264,7 +264,8 @@ def test_negative_base_value(self, dt):
 
         result = dpnp.float_power(ia, 1.5)
         expected = numpy.float_power(a, 1.5)
-        assert_allclose(result, expected)
+        # numpy and dpnp promote the result differently
+        assert_allclose(result, expected, strict=False)
 
     @pytest.mark.parametrize(
         "dt", get_all_dtypes(no_none=True, no_unsigned=True)
@@ -290,7 +291,8 @@ def test_nan_infs_base(self, exp_val, dtype):
 
         result = dpnp.float_power(ia, exp_val)
         expected = numpy.float_power(a, exp_val)
-        assert_allclose(result, expected)
+        # numpy and dpnp promote the result differently
+        assert_allclose(result, expected, strict=False)
 
 
 class TestLogAddExp:
diff --git a/dpnp/tests/testing/array.py b/dpnp/tests/testing/array.py
index df4db0855dd2..cd6f53e3260c 100644
--- a/dpnp/tests/testing/array.py
+++ b/dpnp/tests/testing/array.py
@@ -24,6 +24,7 @@
 # THE POSSIBILITY OF SUCH DAMAGE.
 # *****************************************************************************
 
+import dpctl
 import numpy
 
 from dpnp.dpnp_utils import convert_item
@@ -39,6 +40,33 @@ 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
+    flag = assert_func in [
+        assert_almost_equal_orig,
+        assert_array_almost_equal_orig,
+    ]
+    # For numpy < 2.0, some tests will fail for dtype mismatch
+    dev = dpctl.select_default_device()
+    if numpy.__version__ >= "2.0.0" and dev.has_aspect_fp64:
+        kwargs.setdefault("strict", True)
+        if flag:
+            if kwargs.get("strict"):
+                if hasattr(expected, "dtype"):
+                    assert (
+                        result.dtype == expected.dtype
+                    ), f"{result.dtype} != {expected.dtype}"
+                    assert (
+                        result.shape == expected.shape
+                    ), f"{result.shape} != {expected.shape}"
+                else:
+                    # numpy output is scalar, then dpnp is 0-D array
+                    assert result.shape == (), f"{result.shape} != ()"
+            kwargs.pop("strict")
+    else:
+        kwargs.pop("strict", None)
+
     assert_func(result, expected, *args, **kwargs)
 
 
diff --git a/dpnp/tests/third_party/cupy/testing/_array.py b/dpnp/tests/third_party/cupy/testing/_array.py
index bda9ac22eedb..c290b0bc7707 100644
--- a/dpnp/tests/third_party/cupy/testing/_array.py
+++ b/dpnp/tests/third_party/cupy/testing/_array.py
@@ -29,6 +29,7 @@ def assert_allclose(
         atol=atol,
         err_msg=err_msg,
         verbose=verbose,
+        strict=False,
     )
 
 
@@ -51,6 +52,7 @@ def assert_array_almost_equal(x, y, decimal=6, err_msg="", verbose=True):
         decimal=decimal,
         err_msg=err_msg,
         verbose=verbose,
+        strict=False,
     )
 
 
@@ -105,11 +107,14 @@ def assert_array_equal(
 
     .. seealso:: :func:`numpy.testing.assert_array_equal`
     """
+
+    strict = kwargs.get("strict", False)
     numpy.testing.assert_array_equal(
         cupy.asnumpy(x),
         cupy.asnumpy(y),
         err_msg=err_msg,
         verbose=verbose,
+        strict=strict,
         **kwargs,
     )