diff --git a/dpctl/tensor/_ctors.py b/dpctl/tensor/_ctors.py index ee09c0d785..db49be1603 100644 --- a/dpctl/tensor/_ctors.py +++ b/dpctl/tensor/_ctors.py @@ -153,6 +153,7 @@ def _asarray_from_usm_ndarray( if order == "K" and fc_contig: order = "C" if c_contig else "F" if order == "K": + _ensure_native_dtype_device_support(dtype, copy_q.sycl_device) # new USM allocation res = dpt.usm_ndarray( usm_ndary.shape, @@ -176,6 +177,7 @@ def _asarray_from_usm_ndarray( strides=new_strides, ) else: + _ensure_native_dtype_device_support(dtype, copy_q.sycl_device) res = dpt.usm_ndarray( usm_ndary.shape, dtype=dtype, @@ -242,6 +244,7 @@ def _asarray_from_numpy_ndarray( order = "C" if c_contig else "F" if order == "K": # new USM allocation + _ensure_native_dtype_device_support(dtype, copy_q.sycl_device) res = dpt.usm_ndarray( ary.shape, dtype=dtype, @@ -261,6 +264,7 @@ def _asarray_from_numpy_ndarray( res.shape, dtype=res.dtype, buffer=res.usm_data, strides=new_strides ) else: + _ensure_native_dtype_device_support(dtype, copy_q.sycl_device) res = dpt.usm_ndarray( ary.shape, dtype=dtype, @@ -283,6 +287,35 @@ def _is_object_with_buffer_protocol(obj): return False +def _ensure_native_dtype_device_support(dtype, dev) -> None: + """Check that dtype is natively supported by device. + + Arg: + dtype: elemental data-type + dev: :class:`dpctl.SyclDevice` + Return: + None + Raise: + ValueError is device does not natively support this dtype. + """ + if dtype in [dpt.float64, dpt.complex128] and not dev.has_aspect_fp64: + raise ValueError( + f"Device {dev.name} does not provide native support " + "for double-precision floating point type." + ) + if ( + dtype + in [ + dpt.float16, + ] + and not dev.has_aspect_fp16 + ): + raise ValueError( + f"Device {dev.name} does not provide native support " + "for half-precision floating point type." + ) + + def asarray( obj, dtype=None, @@ -474,6 +507,7 @@ def empty( dpctl.utils.validate_usm_type(usm_type, allow_none=False) sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) dtype = _get_dtype(dtype, sycl_queue) + _ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device) res = dpt.usm_ndarray( sh, dtype=dtype, @@ -651,6 +685,7 @@ def zeros( dpctl.utils.validate_usm_type(usm_type, allow_none=False) sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) dtype = _get_dtype(dtype, sycl_queue) + _ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device) res = dpt.usm_ndarray( sh, dtype=dtype, @@ -839,6 +874,7 @@ def empty_like( sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) sh = x.shape dtype = dpt.dtype(dtype) + _ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device) res = dpt.usm_ndarray( sh, dtype=dtype, @@ -1171,6 +1207,7 @@ def eye( dpctl.utils.validate_usm_type(usm_type, allow_none=False) sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) dtype = _get_dtype(dtype, sycl_queue) + _ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device) res = dpt.usm_ndarray( (n_rows, n_cols), dtype=dtype, diff --git a/dpctl/tensor/_usmarray.pyx b/dpctl/tensor/_usmarray.pyx index 261385a3b3..8c383b72c6 100644 --- a/dpctl/tensor/_usmarray.pyx +++ b/dpctl/tensor/_usmarray.pyx @@ -178,6 +178,8 @@ cdef class usm_ndarray: cdef Py_ssize_t _offset = offset cdef Py_ssize_t ary_min_displacement = 0 cdef Py_ssize_t ary_max_displacement = 0 + cdef bint is_fp64 = False + cdef bint is_fp16 = False self._reset() if (not isinstance(shape, (list, tuple)) @@ -253,6 +255,16 @@ cdef class usm_ndarray: self._cleanup() raise ValueError(("buffer='{}' can not accomodate " "the requested array.").format(buffer)) + is_fp64 = (typenum == UAR_DOUBLE or typenum == UAR_CDOUBLE) + is_fp16 = (typenum == UAR_HALF) + if (is_fp64 or is_fp16): + if ((is_fp64 and not _buffer.sycl_device.has_aspect_fp64) or + (is_fp16 and not _buffer.sycl_device.has_aspect_fp16) + ): + raise ValueError( + f"Device {_buffer.sycl_device.name} does" + f" not support {dtype} natively." + ) self.base_ = _buffer self.data_ = ( ( _buffer._pointer)) + itemsize * _offset self.shape_ = shape_ptr diff --git a/dpctl/tests/test_usm_ndarray_ctor.py b/dpctl/tests/test_usm_ndarray_ctor.py index 46f4a23ca9..e3f772086e 100644 --- a/dpctl/tests/test_usm_ndarray_ctor.py +++ b/dpctl/tests/test_usm_ndarray_ctor.py @@ -45,9 +45,9 @@ def test_allocate_usm_ndarray(shape, usm_type): q = get_queue_or_skip() X = dpt.usm_ndarray( - shape, dtype="d", buffer=usm_type, buffer_ctor_kwargs={"queue": q} + shape, dtype="i8", buffer=usm_type, buffer_ctor_kwargs={"queue": q} ) - Xnp = np.ndarray(shape, dtype="d") + Xnp = np.ndarray(shape, dtype="i8") assert X.usm_type == usm_type assert X.sycl_context == q.sycl_context assert X.sycl_device == q.sycl_device @@ -57,13 +57,17 @@ def test_allocate_usm_ndarray(shape, usm_type): def test_usm_ndarray_flags(): - assert dpt.usm_ndarray((5,)).flags.fc - assert dpt.usm_ndarray((5, 2)).flags.c_contiguous - assert dpt.usm_ndarray((5, 2), order="F").flags.f_contiguous - assert dpt.usm_ndarray((5, 1, 2), order="F").flags.f_contiguous - assert dpt.usm_ndarray((5, 1, 2), strides=(2, 0, 1)).flags.c_contiguous - assert dpt.usm_ndarray((5, 1, 2), strides=(1, 0, 5)).flags.f_contiguous - assert dpt.usm_ndarray((5, 1, 1), strides=(1, 0, 1)).flags.fc + assert dpt.usm_ndarray((5,), dtype="i4").flags.fc + assert dpt.usm_ndarray((5, 2), dtype="i4").flags.c_contiguous + assert dpt.usm_ndarray((5, 2), dtype="i4", order="F").flags.f_contiguous + assert dpt.usm_ndarray((5, 1, 2), dtype="i4", order="F").flags.f_contiguous + assert dpt.usm_ndarray( + (5, 1, 2), dtype="i4", strides=(2, 0, 1) + ).flags.c_contiguous + assert dpt.usm_ndarray( + (5, 1, 2), dtype="i4", strides=(1, 0, 5) + ).flags.f_contiguous + assert dpt.usm_ndarray((5, 1, 1), dtype="i4", strides=(1, 0, 1)).flags.fc @pytest.mark.parametrize( @@ -88,6 +92,8 @@ def test_usm_ndarray_flags(): ], ) def test_dtypes(dtype): + q = get_queue_or_skip() + skip_if_dtype_not_supported(dtype, q) Xusm = dpt.usm_ndarray((1,), dtype=dtype) assert Xusm.itemsize == dpt.dtype(dtype).itemsize expected_fmt = (dpt.dtype(dtype).str)[1:] @@ -169,7 +175,7 @@ def test_copy_scalar_with_method(method, shape, dtype): @pytest.mark.parametrize("func", [bool, float, int, complex]) @pytest.mark.parametrize("shape", [(2,), (1, 2), (3, 4, 5), (0,)]) def test_copy_scalar_invalid_shape(func, shape): - X = dpt.usm_ndarray(shape) + X = dpt.usm_ndarray(shape, dtype="i8") with pytest.raises(ValueError): func(X) @@ -177,7 +183,7 @@ def test_copy_scalar_invalid_shape(func, shape): def test_index_noninteger(): import operator - X = dpt.usm_ndarray(1, "d") + X = dpt.usm_ndarray(1, "f4") with pytest.raises(IndexError): operator.index(X) @@ -283,7 +289,7 @@ def test_slice_suai(usm_type): def test_slicing_basic(): - Xusm = dpt.usm_ndarray((10, 5), dtype="c16") + Xusm = dpt.usm_ndarray((10, 5), dtype="c8") Xusm[None] Xusm[...] Xusm[8] @@ -318,20 +324,20 @@ def test_ctor_invalid_order(): def test_ctor_buffer_kwarg(): - dpt.usm_ndarray(10, buffer=b"device") + dpt.usm_ndarray(10, dtype="i8", buffer=b"device") with pytest.raises(ValueError): dpt.usm_ndarray(10, buffer="invalid_param") - Xusm = dpt.usm_ndarray((10, 5), dtype="c16") + Xusm = dpt.usm_ndarray((10, 5), dtype="c8") X2 = dpt.usm_ndarray(Xusm.shape, buffer=Xusm, dtype=Xusm.dtype) assert np.array_equal( Xusm.usm_data.copy_to_host(), X2.usm_data.copy_to_host() ) with pytest.raises(ValueError): - dpt.usm_ndarray(10, buffer=dict()) + dpt.usm_ndarray(10, dtype="i4", buffer=dict()) def test_usm_ndarray_props(): - Xusm = dpt.usm_ndarray((10, 5), dtype="c16", order="F") + Xusm = dpt.usm_ndarray((10, 5), dtype="c8", order="F") Xusm.ndim repr(Xusm) Xusm.flags @@ -348,7 +354,7 @@ def test_usm_ndarray_props(): def test_datapi_device(): - X = dpt.usm_ndarray(1) + X = dpt.usm_ndarray(1, dtype="i4") dev_t = type(X.device) with pytest.raises(TypeError): dev_t() @@ -387,7 +393,7 @@ def _pyx_capi_fnptr_to_callable( def test_pyx_capi_get_data(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="i8")[1::2] get_data_fn = _pyx_capi_fnptr_to_callable( X, "UsmNDArray_GetData", @@ -400,7 +406,7 @@ def test_pyx_capi_get_data(): def test_pyx_capi_get_shape(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="u4")[1::2] get_shape_fn = _pyx_capi_fnptr_to_callable( X, "UsmNDArray_GetShape", @@ -413,7 +419,7 @@ def test_pyx_capi_get_shape(): def test_pyx_capi_get_strides(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="f4")[1::2] get_strides_fn = _pyx_capi_fnptr_to_callable( X, "UsmNDArray_GetStrides", @@ -429,7 +435,7 @@ def test_pyx_capi_get_strides(): def test_pyx_capi_get_ndim(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="?")[1::2] get_ndim_fn = _pyx_capi_fnptr_to_callable( X, "UsmNDArray_GetNDim", @@ -440,7 +446,7 @@ def test_pyx_capi_get_ndim(): def test_pyx_capi_get_typenum(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="c8")[1::2] get_typenum_fn = _pyx_capi_fnptr_to_callable( X, "UsmNDArray_GetTypenum", @@ -453,7 +459,7 @@ def test_pyx_capi_get_typenum(): def test_pyx_capi_get_elemsize(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="u8")[1::2] get_elemsize_fn = _pyx_capi_fnptr_to_callable( X, "UsmNDArray_GetElementSize", @@ -466,7 +472,7 @@ def test_pyx_capi_get_elemsize(): def test_pyx_capi_get_flags(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="i8")[1::2] get_flags_fn = _pyx_capi_fnptr_to_callable( X, "UsmNDArray_GetFlags", @@ -478,7 +484,7 @@ def test_pyx_capi_get_flags(): def test_pyx_capi_get_offset(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="u2")[1::2] get_offset_fn = _pyx_capi_fnptr_to_callable( X, "UsmNDArray_GetOffset", @@ -491,7 +497,7 @@ def test_pyx_capi_get_offset(): def test_pyx_capi_get_queue_ref(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="i2")[1::2] get_queue_ref_fn = _pyx_capi_fnptr_to_callable( X, "UsmNDArray_GetQueueRef", @@ -521,7 +527,7 @@ def _pyx_capi_int(X, pyx_capi_name, caps_name=b"int", val_restype=ctypes.c_int): def test_pyx_capi_check_constants(): - X = dpt.usm_ndarray(17)[1::2] + X = dpt.usm_ndarray(17, dtype="i1")[1::2] cc_flag = _pyx_capi_int(X, "USM_ARRAY_C_CONTIGUOUS") assert cc_flag > 0 and 0 == (cc_flag & (cc_flag - 1)) fc_flag = _pyx_capi_int(X, "USM_ARRAY_F_CONTIGUOUS") @@ -598,6 +604,7 @@ def test_pyx_capi_check_constants(): @pytest.mark.parametrize("usm_type", ["device", "shared", "host"]) def test_tofrom_numpy(shape, dtype, usm_type): q = get_queue_or_skip() + skip_if_dtype_not_supported(dtype, q) Xnp = np.zeros(shape, dtype=dtype) Xusm = dpt.from_numpy(Xnp, usm_type=usm_type, sycl_queue=q) Ynp = np.ones(shape, dtype=dtype) @@ -733,7 +740,7 @@ def relaxed_strides_equal(st1, st2, sh): 4, 5, ) - X = dpt.usm_ndarray(sh_s, dtype="d") + X = dpt.usm_ndarray(sh_s, dtype="i8") X.shape = sh_f assert X.shape == sh_f assert relaxed_strides_equal(X.strides, cc_strides(sh_f), sh_f) @@ -750,27 +757,27 @@ def relaxed_strides_equal(st1, st2, sh): 4, 5, ) - X = dpt.usm_ndarray(sh_s, dtype="d", order="C") + X = dpt.usm_ndarray(sh_s, dtype="u4", order="C") X.shape = sh_f assert X.shape == sh_f assert relaxed_strides_equal(X.strides, cc_strides(sh_f), sh_f) sh_s = (2, 3, 4, 5) sh_f = (4, 3, 2, 5) - X = dpt.usm_ndarray(sh_s, dtype="d") + X = dpt.usm_ndarray(sh_s, dtype="f4") X.shape = sh_f assert relaxed_strides_equal(X.strides, cc_strides(sh_f), sh_f) sh_s = (2, 3, 4, 5) sh_f = (4, 3, 1, 2, 5) - X = dpt.usm_ndarray(sh_s, dtype="d") + X = dpt.usm_ndarray(sh_s, dtype="?") X.shape = sh_f assert relaxed_strides_equal(X.strides, cc_strides(sh_f), sh_f) - X = dpt.usm_ndarray(sh_s, dtype="d") + X = dpt.usm_ndarray(sh_s, dtype="u4") with pytest.raises(TypeError): X.shape = "abcbe" - X = dpt.usm_ndarray((4, 4), dtype="d")[::2, ::2] + X = dpt.usm_ndarray((4, 4), dtype="u1")[::2, ::2] with pytest.raises(AttributeError): X.shape = (4,) X = dpt.usm_ndarray((0,), dtype="i4") @@ -814,7 +821,7 @@ def test_dlpack(): def test_to_device(): - X = dpt.usm_ndarray(1, "d") + X = dpt.usm_ndarray(1, "f4") for dev in dpctl.get_devices(): if dev.default_selector_score > 0: Y = X.to_device(dev) @@ -900,7 +907,7 @@ def test_reshape(): W = dpt.reshape(Z, (-1,), order="C") assert W.shape == (Z.size,) - X = dpt.usm_ndarray((1,)) + X = dpt.usm_ndarray((1,), dtype="i8") Y = dpt.reshape(X, X.shape) assert Y.flags == X.flags @@ -970,7 +977,9 @@ def test_real_imag_views(): _all_dtypes, ) def test_zeros(dtype): - X = dpt.zeros(10, dtype=dtype) + q = get_queue_or_skip() + skip_if_dtype_not_supported(dtype, q) + X = dpt.zeros(10, dtype=dtype, sycl_queue=q) assert np.array_equal(dpt.asnumpy(X), np.zeros(10, dtype=dtype)) @@ -1197,6 +1206,7 @@ def test_linspace_fp_max(dtype): ) def test_empty_like(dt, usm_kind): q = get_queue_or_skip() + skip_if_dtype_not_supported(dt, q) X = dpt.empty((4, 5), dtype=dt, usm_type=usm_kind, sycl_queue=q) Y = dpt.empty_like(X) @@ -1232,6 +1242,7 @@ def test_empty_unexpected_data_type(): ) def test_zeros_like(dt, usm_kind): q = get_queue_or_skip() + skip_if_dtype_not_supported(dt, q) X = dpt.empty((4, 5), dtype=dt, usm_type=usm_kind, sycl_queue=q) Y = dpt.zeros_like(X) diff --git a/dpctl/tests/test_usm_ndarray_dlpack.py b/dpctl/tests/test_usm_ndarray_dlpack.py index a329fecc80..786f2f3398 100644 --- a/dpctl/tests/test_usm_ndarray_dlpack.py +++ b/dpctl/tests/test_usm_ndarray_dlpack.py @@ -17,6 +17,7 @@ import ctypes import pytest +from helper import skip_if_dtype_not_supported import dpctl import dpctl.tensor as dpt @@ -71,6 +72,7 @@ def test_dlpack_exporter(typestr, usm_type): caps_fn.argtypes = [ctypes.py_object, ctypes.c_char_p] all_root_devices = dpctl.get_devices() for sycl_dev in all_root_devices: + skip_if_dtype_not_supported(typestr, sycl_dev) X = dpt.empty((64,), dtype=typestr, usm_type=usm_type, device=sycl_dev) caps = X.__dlpack__() assert caps_fn(caps, b"dltensor") @@ -95,6 +97,7 @@ def test_dlpack_exporter_stream(): def test_from_dlpack(shape, typestr, usm_type): all_root_devices = dpctl.get_devices() for sycl_dev in all_root_devices: + skip_if_dtype_not_supported(typestr, sycl_dev) X = dpt.empty(shape, dtype=typestr, usm_type=usm_type, device=sycl_dev) Y = dpt.from_dlpack(X) assert X.shape == Y.shape