diff --git a/dpctl/tensor/__init__.py b/dpctl/tensor/__init__.py index 05778d7436..3f1d093fe1 100644 --- a/dpctl/tensor/__init__.py +++ b/dpctl/tensor/__init__.py @@ -21,6 +21,8 @@ """ +from numpy import dtype + from dpctl.tensor._copy_utils import asnumpy, astype, copy, from_numpy, to_numpy from dpctl.tensor._ctors import ( arange, @@ -44,10 +46,14 @@ from dpctl.tensor._manipulation_functions import ( broadcast_arrays, broadcast_to, + can_cast, concat, expand_dims, + finfo, flip, + iinfo, permute_dims, + result_type, roll, squeeze, stack, @@ -55,6 +61,21 @@ from dpctl.tensor._reshape import reshape from dpctl.tensor._usmarray import usm_ndarray +bool = dtype("bool") +int8 = dtype("int8") +int16 = dtype("int16") +int32 = dtype("int32") +int64 = dtype("int64") +uint8 = dtype("uint8") +uint16 = dtype("uint16") +uint32 = dtype("uint32") +uint64 = dtype("uint64") +float16 = dtype("float16") +float32 = dtype("float32") +float64 = dtype("float64") +complex64 = dtype("complex64") +complex128 = dtype("complex128") + __all__ = [ "Device", "usm_ndarray", @@ -88,5 +109,24 @@ "from_dlpack", "tril", "triu", + "dtype", + "bool", + "int8", + "uint8", + "int16", + "uint16", + "int32", + "uint32", + "int64", + "uint64", + "float16", + "float32", + "float64", + "complex64", + "complex128", + "iinfo", + "finfo", + "can_cast", + "result_type", "meshgrid", ] diff --git a/dpctl/tensor/_copy_utils.py b/dpctl/tensor/_copy_utils.py index af4266e783..a4c1b44f57 100644 --- a/dpctl/tensor/_copy_utils.py +++ b/dpctl/tensor/_copy_utils.py @@ -64,7 +64,7 @@ def _copy_from_numpy(np_ary, usm_type="device", sycl_queue=None): dt = Xnp.dtype if dt.char in "dD" and alloc_q.sycl_device.has_aspect_fp64 is False: Xusm_dtype = ( - np.dtype("float32") if dt.char == "d" else np.dtype("complex64") + dpt.dtype("float32") if dt.char == "d" else dpt.dtype("complex64") ) else: Xusm_dtype = dt @@ -318,8 +318,8 @@ def astype(usm_ary, newdtype, order="K", casting="unsafe", copy=True): "Recognized values are 'A', 'C', 'F', or 'K'" ) ary_dtype = usm_ary.dtype - target_dtype = np.dtype(newdtype) - if not np.can_cast(ary_dtype, target_dtype, casting=casting): + target_dtype = dpt.dtype(newdtype) + if not dpt.can_cast(ary_dtype, target_dtype, casting=casting): raise TypeError( "Can not cast from {} to {} according to rule {}".format( ary_dtype, newdtype, casting diff --git a/dpctl/tensor/_ctors.py b/dpctl/tensor/_ctors.py index a21d8d7459..15b5356454 100644 --- a/dpctl/tensor/_ctors.py +++ b/dpctl/tensor/_ctors.py @@ -33,20 +33,20 @@ def _get_dtype(dtype, sycl_obj, ref_type=None): if dtype is None: if ref_type in [None, float] or np.issubdtype(ref_type, np.floating): dtype = ti.default_device_fp_type(sycl_obj) - return np.dtype(dtype) + return dpt.dtype(dtype) elif ref_type in [bool, np.bool_]: dtype = ti.default_device_bool_type(sycl_obj) - return np.dtype(dtype) + return dpt.dtype(dtype) elif ref_type is int or np.issubdtype(ref_type, np.integer): dtype = ti.default_device_int_type(sycl_obj) - return np.dtype(dtype) + return dpt.dtype(dtype) elif ref_type is complex or np.issubdtype(ref_type, np.complexfloating): dtype = ti.default_device_complex_type(sycl_obj) - return np.dtype(dtype) + return dpt.dtype(dtype) else: raise TypeError(f"Reference type {ref_type} not recognized.") else: - return np.dtype(dtype) + return dpt.dtype(dtype) def _array_info_dispatch(obj): @@ -313,7 +313,7 @@ def asarray( ) # 2. Check that dtype is None, or a valid dtype if dtype is not None: - dtype = np.dtype(dtype) + dtype = dpt.dtype(dtype) # 3. Validate order if not isinstance(order, str): raise TypeError( @@ -768,7 +768,7 @@ def empty_like( device = x.device sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) sh = x.shape - dtype = np.dtype(dtype) + dtype = dpt.dtype(dtype) res = dpt.usm_ndarray( sh, dtype=dtype, @@ -825,7 +825,7 @@ def zeros_like( device = x.device sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) sh = x.shape - dtype = np.dtype(dtype) + dtype = dpt.dtype(dtype) return zeros( sh, dtype=dtype, @@ -882,7 +882,7 @@ def ones_like( device = x.device sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) sh = x.shape - dtype = np.dtype(dtype) + dtype = dpt.dtype(dtype) return ones( sh, dtype=dtype, @@ -946,7 +946,7 @@ def full_like( device = x.device sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) sh = x.shape - dtype = np.dtype(dtype) + dtype = dpt.dtype(dtype) return full( sh, fill_value, @@ -1026,7 +1026,7 @@ def linspace( ) if dtype is None and np.issubdtype(dt, np.integer): dt = ti.default_device_fp_type(sycl_queue) - dt = np.dtype(dt) + dt = dpt.dtype(dt) start = float(start) stop = float(stop) res = dpt.empty(num, dtype=dt, sycl_queue=sycl_queue) diff --git a/dpctl/tensor/_manipulation_functions.py b/dpctl/tensor/_manipulation_functions.py index 3de47faa23..f4e75ad348 100644 --- a/dpctl/tensor/_manipulation_functions.py +++ b/dpctl/tensor/_manipulation_functions.py @@ -309,8 +309,7 @@ def _arrays_validation(arrays): raise ValueError("All the input arrays must have usm_type") X0 = arrays[0] - if not all(Xi.dtype.char in "?bBhHiIlLqQefdFD" for Xi in arrays): - raise ValueError("Unsupported dtype encountered.") + _supported_dtype(Xi.dtype for Xi in arrays) res_dtype = X0.dtype for i in range(1, n): @@ -421,3 +420,73 @@ def stack(arrays, axis=0): dpctl.SyclEvent.wait_for(hev_list) return res + + +def can_cast(from_, to, casting="safe"): + """ + can_cast(from: usm_ndarray or dtype, to: dtype) -> bool + + Determines if one data type can be cast to another data type according \ + to Type Promotion Rules rules. + """ + if isinstance(to, dpt.usm_ndarray): + raise TypeError("Expected dtype type.") + + dtype_to = dpt.dtype(to) + + dtype_from = ( + from_.dtype if isinstance(from_, dpt.usm_ndarray) else dpt.dtype(from_) + ) + + _supported_dtype([dtype_from, dtype_to]) + + return np.can_cast(dtype_from, dtype_to, casting) + + +def result_type(*arrays_and_dtypes): + """ + result_type(arrays_and_dtypes: an arbitrary number usm_ndarrays or dtypes)\ + -> dtype + + Returns the dtype that results from applying the Type Promotion Rules to \ + the arguments. + """ + dtypes = [ + X.dtype if isinstance(X, dpt.usm_ndarray) else dpt.dtype(X) + for X in arrays_and_dtypes + ] + + _supported_dtype(dtypes) + + return np.result_type(*dtypes) + + +def iinfo(type): + """ + iinfo(type: integer data-type) -> iinfo_object + + Returns machine limits for integer data types. + """ + if isinstance(type, dpt.usm_ndarray): + raise TypeError("Expected dtype type, get {to}.") + _supported_dtype([dpt.dtype(type)]) + return np.iinfo(type) + + +def finfo(type): + """ + finfo(type: float data-type) -> finfo_object + + Returns machine limits for float data types. + """ + if isinstance(type, dpt.usm_ndarray): + raise TypeError("Expected dtype type, get {to}.") + _supported_dtype([dpt.dtype(type)]) + return np.finfo(type) + + +def _supported_dtype(dtypes): + for dtype in dtypes: + if dtype.char not in "?bBhHiIlLqQefdFD": + raise ValueError(f"Dpctl doesn't support dtype {dtype}.") + return True diff --git a/dpctl/tests/test_sycl_kernel_submit.py b/dpctl/tests/test_sycl_kernel_submit.py index aeb0e2f809..c97583c8ee 100644 --- a/dpctl/tests/test_sycl_kernel_submit.py +++ b/dpctl/tests/test_sycl_kernel_submit.py @@ -31,13 +31,13 @@ @pytest.mark.parametrize( "ctype_str,dtype,ctypes_ctor", [ - ("short", np.dtype("i2"), ctypes.c_short), - ("int", np.dtype("i4"), ctypes.c_int), - ("unsigned int", np.dtype("u4"), ctypes.c_uint), - ("long", np.dtype(np.longlong), ctypes.c_longlong), - ("unsigned long", np.dtype(np.ulonglong), ctypes.c_ulonglong), - ("float", np.dtype("f4"), ctypes.c_float), - ("double", np.dtype("f8"), ctypes.c_double), + ("short", dpt.dtype("i2"), ctypes.c_short), + ("int", dpt.dtype("i4"), ctypes.c_int), + ("unsigned int", dpt.dtype("u4"), ctypes.c_uint), + ("long", dpt.dtype(np.longlong), ctypes.c_longlong), + ("unsigned long", dpt.dtype(np.ulonglong), ctypes.c_ulonglong), + ("float", dpt.dtype("f4"), ctypes.c_float), + ("double", dpt.dtype("f8"), ctypes.c_double), ], ) def test_create_program_from_source(ctype_str, dtype, ctypes_ctor): @@ -45,7 +45,7 @@ def test_create_program_from_source(ctype_str, dtype, ctypes_ctor): q = dpctl.SyclQueue("opencl", property="enable_profiling") except dpctl.SyclQueueCreationError: pytest.skip("OpenCL queue could not be created") - if dtype == np.dtype("f8") and q.sycl_device.has_aspect_fp64 is False: + if dtype == dpt.dtype("f8") and q.sycl_device.has_aspect_fp64 is False: pytest.skip( "Device does not support double precision floating point type" ) diff --git a/dpctl/tests/test_tensor_asarray.py b/dpctl/tests/test_tensor_asarray.py index 09cb1a04b7..f4f35d44dd 100644 --- a/dpctl/tests/test_tensor_asarray.py +++ b/dpctl/tests/test_tensor_asarray.py @@ -175,21 +175,21 @@ def test_asarray_scalars(): import ctypes Y = dpt.asarray(5) - assert Y.dtype == np.dtype(int) + assert Y.dtype == dpt.dtype(int) Y = dpt.asarray(5.2) if Y.sycl_device.has_aspect_fp64: - assert Y.dtype == np.dtype(float) + assert Y.dtype == dpt.dtype(float) else: - assert Y.dtype == np.dtype(np.float32) + assert Y.dtype == dpt.dtype(dpt.float32) Y = dpt.asarray(np.float32(2.3)) - assert Y.dtype == np.dtype(np.float32) + assert Y.dtype == dpt.dtype(dpt.float32) Y = dpt.asarray(1.0j) if Y.sycl_device.has_aspect_fp64: - assert Y.dtype == np.dtype(complex) + assert Y.dtype == dpt.dtype(complex) else: - assert Y.dtype == np.dtype(np.complex64) + assert Y.dtype == dpt.dtype(dpt.complex64) Y = dpt.asarray(ctypes.c_int(8)) - assert Y.dtype == np.dtype(ctypes.c_int) + assert Y.dtype == dpt.dtype(ctypes.c_int) def test_asarray_copy_false(): diff --git a/dpctl/tests/test_usm_ndarray_ctor.py b/dpctl/tests/test_usm_ndarray_ctor.py index 5a1fca5470..1079674f52 100644 --- a/dpctl/tests/test_usm_ndarray_ctor.py +++ b/dpctl/tests/test_usm_ndarray_ctor.py @@ -85,14 +85,14 @@ def test_usm_ndarray_flags(): "c8", "c16", b"float32", - np.dtype("d"), + dpt.dtype("d"), np.half, ], ) def test_dtypes(dtype): Xusm = dpt.usm_ndarray((1,), dtype=dtype) - assert Xusm.itemsize == np.dtype(dtype).itemsize - expected_fmt = (np.dtype(dtype).str)[1:] + assert Xusm.itemsize == dpt.dtype(dtype).itemsize + expected_fmt = (dpt.dtype(dtype).str)[1:] actual_fmt = Xusm.__sycl_usm_array_interface__["typestr"][1:] assert expected_fmt == actual_fmt @@ -112,7 +112,7 @@ def test_properties(dt): assert isinstance(X.sycl_queue, dpctl.SyclQueue) assert isinstance(X.sycl_device, dpctl.SyclDevice) assert isinstance(X.sycl_context, dpctl.SyclContext) - assert isinstance(X.dtype, np.dtype) + assert isinstance(X.dtype, dpt.dtype) assert isinstance(X.__sycl_usm_array_interface__, dict) assert isinstance(X.mT, dpt.usm_ndarray) assert isinstance(X.imag, dpt.usm_ndarray) @@ -521,44 +521,44 @@ def test_pyx_capi_check_constants(): assert w_flag > 0 and 0 == (w_flag & (w_flag - 1)) bool_typenum = _pyx_capi_int(X, "UAR_BOOL") - assert bool_typenum == np.dtype("bool_").num + assert bool_typenum == dpt.dtype("bool_").num byte_typenum = _pyx_capi_int(X, "UAR_BYTE") - assert byte_typenum == np.dtype(np.byte).num + assert byte_typenum == dpt.dtype(np.byte).num ubyte_typenum = _pyx_capi_int(X, "UAR_UBYTE") - assert ubyte_typenum == np.dtype(np.ubyte).num + assert ubyte_typenum == dpt.dtype(np.ubyte).num short_typenum = _pyx_capi_int(X, "UAR_SHORT") - assert short_typenum == np.dtype(np.short).num + assert short_typenum == dpt.dtype(np.short).num ushort_typenum = _pyx_capi_int(X, "UAR_USHORT") - assert ushort_typenum == np.dtype(np.ushort).num + assert ushort_typenum == dpt.dtype(np.ushort).num int_typenum = _pyx_capi_int(X, "UAR_INT") - assert int_typenum == np.dtype(np.intc).num + assert int_typenum == dpt.dtype(np.intc).num uint_typenum = _pyx_capi_int(X, "UAR_UINT") - assert uint_typenum == np.dtype(np.uintc).num + assert uint_typenum == dpt.dtype(np.uintc).num long_typenum = _pyx_capi_int(X, "UAR_LONG") - assert long_typenum == np.dtype(np.int_).num + assert long_typenum == dpt.dtype(np.int_).num ulong_typenum = _pyx_capi_int(X, "UAR_ULONG") - assert ulong_typenum == np.dtype(np.uint).num + assert ulong_typenum == dpt.dtype(np.uint).num longlong_typenum = _pyx_capi_int(X, "UAR_LONGLONG") - assert longlong_typenum == np.dtype(np.longlong).num + assert longlong_typenum == dpt.dtype(np.longlong).num ulonglong_typenum = _pyx_capi_int(X, "UAR_ULONGLONG") - assert ulonglong_typenum == np.dtype(np.ulonglong).num + assert ulonglong_typenum == dpt.dtype(np.ulonglong).num half_typenum = _pyx_capi_int(X, "UAR_HALF") - assert half_typenum == np.dtype(np.half).num + assert half_typenum == dpt.dtype(np.half).num float_typenum = _pyx_capi_int(X, "UAR_FLOAT") - assert float_typenum == np.dtype(np.single).num + assert float_typenum == dpt.dtype(np.single).num double_typenum = _pyx_capi_int(X, "UAR_DOUBLE") - assert double_typenum == np.dtype(np.double).num + assert double_typenum == dpt.dtype(np.double).num cfloat_typenum = _pyx_capi_int(X, "UAR_CFLOAT") - assert cfloat_typenum == np.dtype(np.csingle).num + assert cfloat_typenum == dpt.dtype(np.csingle).num cdouble_typenum = _pyx_capi_int(X, "UAR_CDOUBLE") - assert cdouble_typenum == np.dtype(np.cdouble).num + assert cdouble_typenum == dpt.dtype(np.cdouble).num _all_dtypes = [ @@ -720,12 +720,12 @@ def test_setitem_wingaps(): q = dpctl.SyclQueue() except dpctl.SyclQueueCreationError: pytest.skip("Default queue could not be created") - if np.dtype("intc").itemsize == np.dtype("int32").itemsize: + if dpt.dtype("intc").itemsize == dpt.dtype("int32").itemsize: dpt_dst = dpt.empty(4, dtype="int32", sycl_queue=q) np_src = np.arange(4, dtype="intc") dpt_dst[:] = np_src # should not raise exceptions assert np.array_equal(dpt.asnumpy(dpt_dst), np_src) - if np.dtype("long").itemsize == np.dtype("longlong").itemsize: + if dpt.dtype("long").itemsize == dpt.dtype("longlong").itemsize: dpt_dst = dpt.empty(4, dtype="longlong", sycl_queue=q) np_src = np.arange(4, dtype="long") dpt_dst[:] = np_src # should not raise exceptions @@ -1027,7 +1027,7 @@ def test_full(dtype): def test_full_dtype_inference(): assert np.issubdtype(dpt.full(10, 4).dtype, np.integer) - assert dpt.full(10, True).dtype is np.dtype(np.bool_) + assert dpt.full(10, True).dtype is dpt.dtype(np.bool_) assert np.issubdtype(dpt.full(10, 12.3).dtype, np.floating) assert np.issubdtype(dpt.full(10, 0.3 - 2j).dtype, np.complexfloating) @@ -1047,7 +1047,7 @@ def test_arange(dt): "Device does not support double precision floating point type" ) X = dpt.arange(0, 123, dtype=dt, sycl_queue=q) - dt = np.dtype(dt) + dt = dpt.dtype(dt) if np.issubdtype(dt, np.integer): assert int(X[47]) == 47 elif np.issubdtype(dt, np.floating): @@ -1056,7 +1056,7 @@ def test_arange(dt): assert complex(X[47]) == 47.0 + 0.0j # choose size larger than maximal value that u1/u2 can accomodate - sz = int(np.iinfo(np.int16).max) + 1 + sz = int(dpt.iinfo(dpt.int16).max) + 1 X1 = dpt.arange(sz, dtype=dt, sycl_queue=q) assert X1.shape == (sz,) @@ -1101,9 +1101,9 @@ def test_linspace_fp(): n = 16 X = dpt.linspace(0, n - 1, num=n, sycl_queue=q) if q.sycl_device.has_aspect_fp64: - assert X.dtype == np.dtype("float64") + assert X.dtype == dpt.dtype("float64") else: - assert X.dtype == np.dtype("float32") + assert X.dtype == dpt.dtype("float32") assert X.shape == (n,) assert X.strides == (1,) @@ -1238,7 +1238,7 @@ def test_full_like(dt, usm_kind): "Device does not support double precision floating point type" ) - fill_v = np.dtype(dt).type(1) + fill_v = dpt.dtype(dt).type(1) X = dpt.empty((4, 5), dtype=dt, usm_type=usm_kind, sycl_queue=q) Y = dpt.full_like(X, fill_v) assert X.shape == Y.shape diff --git a/dpctl/tests/test_usm_ndarray_manipulation.py b/dpctl/tests/test_usm_ndarray_manipulation.py index 895f2c0ec3..967f58cc21 100644 --- a/dpctl/tests/test_usm_ndarray_manipulation.py +++ b/dpctl/tests/test_usm_ndarray_manipulation.py @@ -999,3 +999,32 @@ def test_stack_3arrays(data): R = dpt.stack([X, Y, Z], axis=axis) assert_array_equal(Rnp, dpt.asnumpy(R)) + + +def test_can_cast(): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Queue could not be created") + + # incorrect input + X = dpt.ones((2, 2), dtype=dpt.int64, sycl_queue=q) + pytest.raises(TypeError, dpt.can_cast, X, 1) + pytest.raises(TypeError, dpt.can_cast, X, X) + X_np = np.ones((2, 2), dtype=np.int64) + + assert dpt.can_cast(X, "float32") == np.can_cast(X_np, "float32") + assert dpt.can_cast(X, dpt.int32) == np.can_cast(X_np, np.int32) + assert dpt.can_cast(X, dpt.int64) == np.can_cast(X_np, np.int64) + + +def test_result_type(): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Queue could not be created") + + X = [dpt.ones((2), dtype=dpt.int64, sycl_queue=q), dpt.int32, "float16"] + X_np = [np.ones((2), dtype=np.int64), np.int32, "float16"] + + assert dpt.result_type(*X) == np.result_type(*X_np)