From a5726e31d7958cb769230f7ebf2559bb80a4718b Mon Sep 17 00:00:00 2001 From: khaled Date: Sun, 7 May 2023 01:13:10 -0500 Subject: [PATCH] Update unit tests for dpnp array constructors --- numba_dpex/core/types/usm_ndarray_type.py | 20 +- numba_dpex/dpnp_iface/arrayobj.py | 54 ++++-- .../test_array_creation_errors.py | 0 .../tests/dpjit_tests/dpnp/test_dpnp_empty.py | 101 ++++++++-- .../dpjit_tests/dpnp/test_dpnp_empty_like.py | 168 +++++++++++++--- .../tests/dpjit_tests/dpnp/test_dpnp_full.py | 124 ++++++++++-- .../dpjit_tests/dpnp/test_dpnp_full_like.py | 182 +++++++++++++++--- .../tests/dpjit_tests/dpnp/test_dpnp_ones.py | 115 +++++++++-- .../dpjit_tests/dpnp/test_dpnp_ones_like.py | 174 +++++++++++++---- .../tests/dpjit_tests/dpnp/test_dpnp_zeros.py | 112 +++++++++-- .../dpjit_tests/dpnp/test_dpnp_zeros_like.py | 174 +++++++++++++---- 11 files changed, 1013 insertions(+), 211 deletions(-) rename numba_dpex/tests/{types/USMNdArray => core/types/USMNdAArray}/test_array_creation_errors.py (100%) diff --git a/numba_dpex/core/types/usm_ndarray_type.py b/numba_dpex/core/types/usm_ndarray_type.py index 0ec80d423f..dc3192e205 100644 --- a/numba_dpex/core/types/usm_ndarray_type.py +++ b/numba_dpex/core/types/usm_ndarray_type.py @@ -23,6 +23,7 @@ def __init__( ndim, layout="C", dtype=None, + is_fill_value_float=False, usm_type="device", device=None, queue=None, @@ -66,9 +67,22 @@ def __init__( self.device = self.queue.sycl_device.filter_string if not dtype: - dummy_tensor = dpctl.tensor.empty( - 1, order=layout, usm_type=usm_type, sycl_queue=self.queue - ) + if is_fill_value_float: + dummy_tensor = dpctl.tensor.empty( + 1, + dtype=dpctl.tensor.float64, + order=layout, + usm_type=usm_type, + sycl_queue=self.queue, + ) + else: + dummy_tensor = dpctl.tensor.empty( + 1, + dtype=dpctl.tensor.int64, + order=layout, + usm_type=usm_type, + sycl_queue=self.queue, + ) # convert dpnp type to numba/numpy type _dtype = dummy_tensor.dtype self.dtype = from_dtype(_dtype) diff --git a/numba_dpex/dpnp_iface/arrayobj.py b/numba_dpex/dpnp_iface/arrayobj.py index 9c49f9b2ce..db9a9a414f 100644 --- a/numba_dpex/dpnp_iface/arrayobj.py +++ b/numba_dpex/dpnp_iface/arrayobj.py @@ -4,6 +4,8 @@ import dpnp from numba import errors, types +from numba.core.types import scalars +from numba.core.types.containers import UniTuple from numba.core.typing.npydecl import parse_dtype as _ty_parse_dtype from numba.core.typing.npydecl import parse_shape as _ty_parse_shape from numba.extending import overload @@ -20,7 +22,6 @@ impl_dpnp_ones_like, impl_dpnp_zeros, impl_dpnp_zeros_like, - intrin_usm_alloc, ) # ========================================================================= @@ -28,7 +29,20 @@ # ========================================================================= -def _parse_dtype(dtype, data=None): +def _parse_dim(x1): + if hasattr(x1, "ndim") and x1.ndim: + return x1.ndim + elif isinstance(x1, scalars.Integer): + r = 1 + return r + elif isinstance(x1, UniTuple): + r = len(x1) + return r + else: + return 0 + + +def _parse_dtype(dtype): """Resolve dtype parameter. Resolves the dtype parameter based on the given value @@ -44,9 +58,8 @@ class for nd-arrays. Defaults to None. numba.core.types.functions.NumberClass: Resolved numba type class for number classes. """ + _dtype = None - if data and isinstance(data, types.Array): - _dtype = data.dtype if not is_nonelike(dtype): _dtype = _ty_parse_dtype(dtype) return _dtype @@ -60,6 +73,9 @@ def _parse_layout(layout): raise errors.NumbaValueError(msg) return layout_type_str elif isinstance(layout, str): + if layout not in ["C", "F", "A"]: + msg = f"Invalid layout specified: '{layout}'" + raise errors.NumbaValueError(msg) return layout else: raise TypeError( @@ -94,6 +110,9 @@ def _parse_usm_type(usm_type): raise errors.NumbaValueError(msg) return usm_type_str elif isinstance(usm_type, str): + if usm_type not in ["shared", "device", "host"]: + msg = f"Invalid usm_type specified: '{usm_type}'" + raise errors.NumbaValueError(msg) return usm_type else: raise TypeError( @@ -150,6 +169,7 @@ def build_dpnp_ndarray( ndim, layout="C", dtype=None, + is_fill_value_float=False, usm_type="device", device=None, sycl_queue=None, @@ -163,6 +183,8 @@ def build_dpnp_ndarray( Data type of the array. Can be typestring, a `numpy.dtype` object, `numpy` char string, or a numpy scalar type. Default: None. + is_fill_value_float (bool): Specify if the fill value is floating + point. usm_type (numba.core.types.misc.StringLiteral, optional): The type of SYCL USM allocation for the output array. Allowed values are "device"|"shared"|"host". @@ -198,6 +220,7 @@ def build_dpnp_ndarray( ndim=ndim, layout=layout, dtype=dtype, + is_fill_value_float=is_fill_value_float, usm_type=usm_type, device=device, queue=sycl_queue, @@ -280,6 +303,7 @@ def ol_dpnp_empty( _ndim, layout=_layout, dtype=_dtype, + is_fill_value_float=True, usm_type=_usm_type, device=_device, sycl_queue=_sycl_queue, @@ -384,6 +408,7 @@ def ol_dpnp_zeros( _ndim, layout=_layout, dtype=_dtype, + is_fill_value_float=True, usm_type=_usm_type, device=_device, sycl_queue=_sycl_queue, @@ -488,6 +513,7 @@ def ol_dpnp_ones( _ndim, layout=_layout, dtype=_dtype, + is_fill_value_float=True, usm_type=_usm_type, device=_device, sycl_queue=_sycl_queue, @@ -586,6 +612,7 @@ def ol_dpnp_full( _ndim = _ty_parse_shape(shape) _dtype = _parse_dtype(dtype) + _is_fill_value_float = isinstance(fill_value, scalars.Float) _layout = _parse_layout(order) _usm_type = _parse_usm_type(usm_type) if usm_type else "device" _device = _parse_device_filter_string(device) if device else None @@ -596,6 +623,7 @@ def ol_dpnp_full( _ndim, layout=_layout, dtype=_dtype, + is_fill_value_float=_is_fill_value_float, usm_type=_usm_type, device=_device, sycl_queue=_sycl_queue, @@ -699,8 +727,8 @@ def ol_dpnp_empty_like( + "inside overloaded dpnp.empty_like() function." ) - _ndim = x1.ndim if hasattr(x1, "ndim") and x1.ndim else 0 - _dtype = _parse_dtype(dtype, data=x1) + _ndim = _parse_dim(x1) + _dtype = x1.dtype if isinstance(x1, types.Array) else _parse_dtype(dtype) _order = x1.layout if order is None else order _usm_type = _parse_usm_type(usm_type) if usm_type else "device" _device = _parse_device_filter_string(device) if device else None @@ -812,8 +840,8 @@ def ol_dpnp_zeros_like( + "inside overloaded dpnp.zeros_like() function." ) - _ndim = x1.ndim if hasattr(x1, "ndim") and x1.ndim else 0 - _dtype = _parse_dtype(dtype, data=x1) + _ndim = _parse_dim(x1) + _dtype = x1.dtype if isinstance(x1, types.Array) else _parse_dtype(dtype) _order = x1.layout if order is None else order _usm_type = _parse_usm_type(usm_type) if usm_type else "device" _device = _parse_device_filter_string(device) if device else None @@ -924,8 +952,8 @@ def ol_dpnp_ones_like( + "inside overloaded dpnp.ones_like() function." ) - _ndim = x1.ndim if hasattr(x1, "ndim") and x1.ndim else 0 - _dtype = _parse_dtype(dtype, data=x1) + _ndim = _parse_dim(x1) + _dtype = x1.dtype if isinstance(x1, types.Array) else _parse_dtype(dtype) _order = x1.layout if order is None else order _usm_type = _parse_usm_type(usm_type) if usm_type else "device" _device = _parse_device_filter_string(device) if device else None @@ -1041,8 +1069,9 @@ def ol_dpnp_full_like( + "inside overloaded dpnp.full_like() function." ) - _ndim = x1.ndim if hasattr(x1, "ndim") and x1.ndim else 0 - _dtype = _parse_dtype(dtype, data=x1) + _ndim = _parse_dim(x1) + _dtype = x1.dtype if isinstance(x1, types.Array) else _parse_dtype(dtype) + _is_fill_value_float = isinstance(fill_value, scalars.Float) _order = x1.layout if order is None else order _usm_type = _parse_usm_type(usm_type) if usm_type else "device" _device = _parse_device_filter_string(device) if device else None @@ -1052,6 +1081,7 @@ def ol_dpnp_full_like( _ndim, layout=_order, dtype=_dtype, + is_fill_value_float=_is_fill_value_float, usm_type=_usm_type, device=_device, sycl_queue=_sycl_queue, diff --git a/numba_dpex/tests/types/USMNdArray/test_array_creation_errors.py b/numba_dpex/tests/core/types/USMNdAArray/test_array_creation_errors.py similarity index 100% rename from numba_dpex/tests/types/USMNdArray/test_array_creation_errors.py rename to numba_dpex/tests/core/types/USMNdAArray/test_array_creation_errors.py diff --git a/numba_dpex/tests/dpjit_tests/dpnp/test_dpnp_empty.py b/numba_dpex/tests/dpjit_tests/dpnp/test_dpnp_empty.py index aa52f19d90..313a3fd99c 100644 --- a/numba_dpex/tests/dpjit_tests/dpnp/test_dpnp_empty.py +++ b/numba_dpex/tests/dpjit_tests/dpnp/test_dpnp_empty.py @@ -2,25 +2,61 @@ # # SPDX-License-Identifier: Apache-2.0 -"""Tests for dpnp ndarray constructors.""" +"""Tests for the dpnp.empty overload.""" import dpctl import dpnp import pytest +from numba import errors from numba_dpex import dpjit shapes = [11, (2, 5)] dtypes = [dpnp.int32, dpnp.int64, dpnp.float32, dpnp.float64] usm_types = ["device", "shared", "host"] -devices = ["cpu", None] + + +@pytest.mark.parametrize("shape", shapes) +def test_dpnp_empty_default(shape): + """Test dpnp.empty() with default parameters inside dpjit.""" + + @dpjit + def func(shape): + c = dpnp.empty(shape) + return c + + try: + c = func(shape) + except Exception: + pytest.fail("Calling dpnp.empty() inside dpjit failed.") + + if len(c.shape) == 1: + assert c.shape[0] == shape + else: + assert c.shape == shape + + dummy = dpnp.empty(shape) + + assert c.dtype == dummy.dtype + assert c.usm_type == dummy.usm_type + assert c.sycl_device == dummy.sycl_device + assert c.sycl_queue == dummy.sycl_queue + if c.sycl_queue != dummy.sycl_queue: + pytest.xfail( + "Returned queue does not have the queue in the dummy array." + ) + assert c.sycl_queue == dpctl._sycl_queue_manager.get_device_cached_queue( + dummy.sycl_device + ) @pytest.mark.parametrize("shape", shapes) @pytest.mark.parametrize("dtype", dtypes) @pytest.mark.parametrize("usm_type", usm_types) -@pytest.mark.parametrize("device", devices) -def test_dpnp_empty(shape, dtype, usm_type, device): +def test_dpnp_empty_from_device(shape, dtype, usm_type): + """ "Use device only in dpnp.emtpy() inside dpjit.""" + device = dpctl.SyclDevice().filter_string + @dpjit def func(shape): c = dpnp.empty(shape, dtype=dtype, usm_type=usm_type, device=device) @@ -29,7 +65,7 @@ def func(shape): try: c = func(shape) except Exception: - pytest.fail("Calling dpnp.empty inside dpjit failed") + pytest.fail("Calling dpnp.empty() inside dpjit failed.") if len(c.shape) == 1: assert c.shape[0] == shape @@ -38,32 +74,61 @@ def func(shape): assert c.dtype == dtype assert c.usm_type == usm_type - if device is not None: - assert ( - c.sycl_device.filter_string - == dpctl.SyclDevice(device).filter_string + assert c.sycl_device.filter_string == device + if c.sycl_queue != dpctl._sycl_queue_manager.get_device_cached_queue( + device + ): + pytest.xfail( + "Returned queue does not have the queue cached against the device." ) - else: - c.sycl_device.filter_string == dpctl.SyclDevice().filter_string @pytest.mark.parametrize("shape", shapes) -def test_dpnp_empty_default_dtype(shape): +@pytest.mark.parametrize("dtype", dtypes) +@pytest.mark.parametrize("usm_type", usm_types) +def test_dpnp_empty_from_queue(shape, dtype, usm_type): + """ "Use queue only in dpnp.emtpy() inside dpjit.""" + @dpjit - def func(shape): - c = dpnp.empty(shape) + def func(shape, queue): + c = dpnp.empty(shape, dtype=dtype, usm_type=usm_type, sycl_queue=queue) return c + queue = dpctl.SyclQueue() + try: - c = func(shape) + c = func(shape, queue) except Exception: - pytest.fail("Calling dpnp.empty inside dpjit failed") + pytest.fail("Calling dpnp.empty() inside dpjit failed.") if len(c.shape) == 1: assert c.shape[0] == shape else: assert c.shape == shape - dummy_tensor = dpctl.tensor.empty(shape) + assert c.dtype == dtype + assert c.usm_type == usm_type + assert c.sycl_device == queue.sycl_device + + if c.sycl_queue != queue: + pytest.xfail( + "Returned queue does not have the queue passed to the dpnp function." + ) + + +def test_dpnp_empty_exceptions(): + """Test if exception is raised when both queue and device are specified.""" + device = dpctl.SyclDevice().filter_string - assert c.dtype == dummy_tensor.dtype + @dpjit + def func(shape, queue): + c = dpnp.empty(shape, sycl_queue=queue, device=device) + return c + + queue = dpctl.SyclQueue() + + try: + func(10, queue) + except Exception as e: + assert isinstance(e, errors.TypingError) + assert "`device` and `sycl_queue` are exclusive keywords" in str(e) diff --git a/numba_dpex/tests/dpjit_tests/dpnp/test_dpnp_empty_like.py b/numba_dpex/tests/dpjit_tests/dpnp/test_dpnp_empty_like.py index ef2dfc3e88..ada4143d68 100644 --- a/numba_dpex/tests/dpjit_tests/dpnp/test_dpnp_empty_like.py +++ b/numba_dpex/tests/dpjit_tests/dpnp/test_dpnp_empty_like.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: Apache-2.0 -"""Tests for dpnp ndarray constructors.""" +"""Tests for the dpnp.empty_like overload.""" import dpctl @@ -16,53 +16,136 @@ shapes = [10, (2, 5)] dtypes = [dpnp.int32, dpnp.int64, dpnp.float32, dpnp.float64] usm_types = ["device", "shared", "host"] -devices = ["cpu", None] + + +@pytest.mark.parametrize("shape", shapes) +def test_dpnp_empty_like_default(shape): + """Test dpnp.empty_like() with default parameters inside dpjit.""" + + @dpjit + def func(x): + y = dpnp.empty_like(x) + return y + + try: + a = dpnp.ones(shape) + c = func(a) + except Exception: + pytest.fail("Calling dpnp.empty_like() inside dpjit failed.") + + if len(c.shape) == 1: + assert c.shape[0] == a.shape[0] + else: + assert c.shape == a.shape + + dummy = dpnp.empty_like(a) + + assert c.dtype == dummy.dtype + assert c.usm_type == dummy.usm_type + assert c.sycl_device == dummy.sycl_device + if c.sycl_queue != dummy.sycl_queue: + pytest.xfail( + "Returned queue does not have the queue in the dummy array." + ) + assert c.sycl_queue == dpctl._sycl_queue_manager.get_device_cached_queue( + dummy.sycl_device + ) @pytest.mark.parametrize("shape", shapes) @pytest.mark.parametrize("dtype", dtypes) @pytest.mark.parametrize("usm_type", usm_types) -@pytest.mark.parametrize("device", devices) -def test_dpnp_empty_like(shape, dtype, usm_type, device): +def test_dpnp_empty_like_from_device(shape, dtype, usm_type): + """ "Use device only in dpnp.emtpy)like() inside dpjit.""" + device = dpctl.SyclDevice().filter_string + @dpjit - def func(a): - c = dpnp.empty_like(a, dtype=dtype, usm_type=usm_type, device=device) - return c + def func(x): + y = dpnp.empty_like(x, dtype=dtype, usm_type=usm_type, device=device) + return y - if isinstance(shape, int): - NZ = numpy.random.rand(shape) + try: + a = dpnp.ones(shape, dtype=dtype, usm_type=usm_type, device=device) + c = func(a) + except Exception: + pytest.fail("Calling dpnp.empty_like() inside dpjit failed.") + + if len(c.shape) == 1: + assert c.shape[0] == a.shape[0] else: - NZ = numpy.random.rand(*shape) + assert c.shape == a.shape + + assert c.dtype == a.dtype + assert c.usm_type == a.usm_type + assert c.sycl_device.filter_string == device + if c.sycl_queue != dpctl._sycl_queue_manager.get_device_cached_queue( + device + ): + pytest.xfail( + "Returned queue does not have the queue cached against the device." + ) + + +@pytest.mark.parametrize("shape", shapes) +@pytest.mark.parametrize("dtype", dtypes) +@pytest.mark.parametrize("usm_type", usm_types) +def test_dpnp_empty_like_from_queue(shape, dtype, usm_type): + """ "Use queue only in dpnp.emtpy_like() inside dpjit.""" + + @dpjit + def func(x, queue): + y = dpnp.empty_like(x, dtype=dtype, usm_type=usm_type, sycl_queue=queue) + return y + + queue = dpctl.SyclQueue() try: - c = func(NZ) + a = dpnp.ones(shape, dtype=dtype, usm_type=usm_type, sycl_queue=queue) + c = func(a, queue) except Exception: - pytest.fail("Calling dpnp.empty_like inside dpjit failed") + pytest.fail("Calling dpnp.empty_like() inside dpjit failed.") if len(c.shape) == 1: - assert c.shape[0] == NZ.shape[0] + assert c.shape[0] == a.shape[0] else: - assert c.shape == NZ.shape + assert c.shape == a.shape - assert c.dtype == dtype - assert c.usm_type == usm_type - if device is not None: - assert ( - c.sycl_device.filter_string - == dpctl.SyclDevice(device).filter_string + assert c.dtype == a.dtype + assert c.usm_type == a.usm_type + assert c.sycl_device == queue.sycl_device + + if c.sycl_queue != queue: + pytest.xfail( + "Returned queue does not have the queue passed to the dpnp function." ) - else: - c.sycl_device.filter_string == dpctl.SyclDevice().filter_string def test_dpnp_empty_like_exceptions(): + """Test if exception is raised when both queue and device are specified.""" + + device = dpctl.SyclDevice().filter_string + + @dpjit + def func1(x, queue): + y = dpnp.empty_like(x, sycl_queue=queue, device=device) + return y + + queue = dpctl.SyclQueue() + + try: + a = dpnp.ones(10) + func1(a, queue) + except Exception as e: + assert isinstance(e, errors.TypingError) + assert "`device` and `sycl_queue` are exclusive keywords" in str(e) + @dpjit - def func1(a): - c = dpnp.empty_like(a, shape=(3, 3)) - return c + def func2(x): + y = dpnp.empty_like(x, shape=(3, 3)) + return y try: - func1(numpy.random.rand(5, 5)) + func2(a) except Exception as e: assert isinstance(e, errors.TypingError) assert ( @@ -70,15 +153,36 @@ def func1(a): in str(e) ) - queue = dpctl.SyclQueue() + +@pytest.mark.xfail +def test_dpnp_empty_like_from_numpy(): + """Test if dpnp works with numpy array (it shouldn't)""" + + @dpjit + def func(x): + y = dpnp.empty_like(x) + return y + + a = numpy.empty(10) + + with pytest.raises(Exception): + func(a) + + +@pytest.mark.parametrize("shape", shapes) +def test_dpnp_empty_like_from_scalar(shape): + """Test if works with scalar argument in place of an array""" @dpjit - def func2(a, q): - c = dpnp.empty_like(a, sycl_queue=q, device="cpu") - return c + def func(shape): + x = dpnp.empty_like(shape) + return x try: - func2(numpy.random.rand(5, 5), queue) + func(shape) except Exception as e: assert isinstance(e, errors.TypingError) - assert "`device` and `sycl_queue` are exclusive keywords" in str(e) + assert ( + "No implementation of function Function(