diff --git a/dpctl/tensor/__init__.py b/dpctl/tensor/__init__.py index d54850045b..1026774947 100644 --- a/dpctl/tensor/__init__.py +++ b/dpctl/tensor/__init__.py @@ -22,7 +22,18 @@ """ from dpctl.tensor._copy_utils import asnumpy, astype, copy, from_numpy, to_numpy -from dpctl.tensor._ctors import arange, asarray, empty, zeros +from dpctl.tensor._ctors import ( + arange, + asarray, + empty, + empty_like, + full, + full_like, + ones, + ones_like, + zeros, + zeros_like, +) from dpctl.tensor._device import Device from dpctl.tensor._dlpack import from_dlpack from dpctl.tensor._manipulation_functions import ( @@ -46,6 +57,12 @@ "copy", "empty", "zeros", + "ones", + "full", + "empty_like", + "zeros_like", + "ones_like", + "full_like", "flip", "reshape", "roll", diff --git a/dpctl/tensor/_ctors.py b/dpctl/tensor/_ctors.py index 5d43792599..e96500ca8c 100644 --- a/dpctl/tensor/_ctors.py +++ b/dpctl/tensor/_ctors.py @@ -27,6 +27,26 @@ _host_set = frozenset([None]) +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) + elif ref_type in [bool, np.bool_]: + dtype = ti.default_device_bool_type(sycl_obj) + return np.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) + elif ref_type is complex or np.issubdtype(ref_type, np.complexfloating): + dtype = ti.default_device_complex_type(sycl_obj) + return np.dtype(dtype) + else: + raise ValueError(f"Reference type {ref_type} not recognized.") + else: + return np.dtype(dtype) + + def _array_info_dispatch(obj): if isinstance(obj, dpt.usm_ndarray): return obj.shape, obj.dtype, frozenset([obj.sycl_queue]) @@ -387,7 +407,7 @@ def asarray( def empty( - sh, dtype="f8", order="C", device=None, usm_type="device", sycl_queue=None + sh, dtype=None, order="C", device=None, usm_type="device", sycl_queue=None ): """ Creates `usm_ndarray` from uninitialized USM allocation. @@ -396,7 +416,7 @@ def empty( shape (tuple): Dimensions of the array to be created. dtype (optional): data type of the array. Can be typestring, a `numpy.dtype` object, `numpy` char string, or a numpy - scalar type. Default: "f8" + scalar type. Default: None order ("C", or F"): memory layout for the array. Default: "C" device (optional): array API concept of device where the output array is created. `device` can be `None`, a oneAPI filter selector string, @@ -414,7 +434,6 @@ def empty( `dpctl.SyclQueue()` is used for allocation and copying. Default: `None`. """ - dtype = np.dtype(dtype) if not isinstance(order, str) or len(order) == 0 or order[0] not in "CcFf": raise ValueError( "Unrecognized order keyword value, expecting 'F' or 'C'." @@ -423,6 +442,7 @@ def empty( order = order[0].upper() 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) res = dpt.usm_ndarray( sh, dtype=dtype, @@ -434,6 +454,7 @@ def empty( def _coerce_and_infer_dt(*args, dt): + "Deduce arange type from sequence spec" nd, seq_dt, d = _array_info_sequence(args) if d != _host_set or nd != (len(args),): raise ValueError("start, stop and step must be Python scalars") @@ -450,6 +471,24 @@ def _coerce_and_infer_dt(*args, dt): raise ValueError(f"Data type {dt} is not supported") +def _get_arange_length(start, stop, step): + "Compute length of arange sequence" + span = stop - start + if type(step) in [int, float] and type(span) in [int, float]: + offset = -1 if step > 0 else 1 + tmp = 1 + (span + offset) / step + return tmp + tmp = span / step + if type(tmp) is complex and tmp.imag == 0: + tmp = tmp.real + else: + return tmp + k = int(tmp) + if k > 0 and float(k) < tmp: + tmp = tmp + 1 + return tmp + + def arange( start, /, @@ -491,9 +530,7 @@ def arange( step, ), dt = _coerce_and_infer_dt(start, stop, step, dt=dtype) try: - tmp = 1 + (stop - start - 1) / step - if type(tmp) is complex and tmp.imag == 0.0: - tmp = tmp.real + tmp = _get_arange_length(start, stop, step) sh = int(tmp) if sh < 0: sh = 0 @@ -509,13 +546,14 @@ def arange( buffer_ctor_kwargs={"queue": sycl_queue}, ) _step = (start + step) - start + _step = dt.type(_step) hev, _ = ti._linspace_step(start, _step, res, sycl_queue) hev.wait() return res def zeros( - sh, dtype="f8", order="C", device=None, usm_type="device", sycl_queue=None + sh, dtype=None, order="C", device=None, usm_type="device", sycl_queue=None ): """ Creates `usm_ndarray` with zero elements. @@ -524,7 +562,7 @@ def zeros( shape (tuple): Dimensions of the array to be created. dtype (optional): data type of the array. Can be typestring, a `numpy.dtype` object, `numpy` char string, or a numpy - scalar type. Default: "f8" + scalar type. Default: None order ("C", or F"): memory layout for the array. Default: "C" device (optional): array API concept of device where the output array is created. `device` can be `None`, a oneAPI filter selector string, @@ -542,7 +580,6 @@ def zeros( `dpctl.SyclQueue()` is used for allocation and copying. Default: `None`. """ - dtype = np.dtype(dtype) if not isinstance(order, str) or len(order) == 0 or order[0] not in "CcFf": raise ValueError( "Unrecognized order keyword value, expecting 'F' or 'C'." @@ -551,6 +588,7 @@ def zeros( order = order[0].upper() 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) res = dpt.usm_ndarray( sh, dtype=dtype, @@ -560,3 +598,344 @@ def zeros( ) res.usm_data.memset() return res + + +def ones( + sh, dtype=None, order="C", device=None, usm_type="device", sycl_queue=None +): + """ + Creates `usm_ndarray` with elements of one. + + Args: + shape (tuple): Dimensions of the array to be created. + dtype (optional): data type of the array. Can be typestring, + a `numpy.dtype` object, `numpy` char string, or a numpy + scalar type. Default: None + order ("C", or F"): memory layout for the array. Default: "C" + device (optional): array API concept of device where the output array + is created. `device` can be `None`, a oneAPI filter selector string, + an instance of :class:`dpctl.SyclDevice` corresponding to a + non-partitioned SYCL device, an instance of + :class:`dpctl.SyclQueue`, or a `Device` object returnedby + `dpctl.tensor.usm_array.device`. Default: `None`. + usm_type ("device"|"shared"|"host", optional): The type of SYCL USM + allocation for the output array. Default: `"device"`. + sycl_queue (:class:`dpctl.SyclQueue`, optional): The SYCL queue to use + for output array allocation and copying. `sycl_queue` and `device` + are exclusive keywords, i.e. use one or another. If both are + specified, a `TypeError` is raised unless both imply the same + underlying SYCL queue to be used. If both a `None`, the + `dpctl.SyclQueue()` is used for allocation and copying. + Default: `None`. + """ + if not isinstance(order, str) or len(order) == 0 or order[0] not in "CcFf": + raise ValueError( + "Unrecognized order keyword value, expecting 'F' or 'C'." + ) + else: + order = order[0].upper() + 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) + res = dpt.usm_ndarray( + sh, + dtype=dtype, + buffer=usm_type, + order=order, + buffer_ctor_kwargs={"queue": sycl_queue}, + ) + hev, ev = ti._full_usm_ndarray(1, res, sycl_queue) + hev.wait() + return res + + +def full( + sh, + fill_value, + dtype=None, + order="C", + device=None, + usm_type="device", + sycl_queue=None, +): + """ + Creates `usm_ndarray` with elements of one. + + Args: + shape (tuple): Dimensions of the array to be created. + fill_value (int,float,complex): fill value + dtype (optional): data type of the array. Can be typestring, + a `numpy.dtype` object, `numpy` char string, or a numpy + scalar type. Default: None + order ("C", or F"): memory layout for the array. Default: "C" + device (optional): array API concept of device where the output array + is created. `device` can be `None`, a oneAPI filter selector string, + an instance of :class:`dpctl.SyclDevice` corresponding to a + non-partitioned SYCL device, an instance of + :class:`dpctl.SyclQueue`, or a `Device` object returnedby + `dpctl.tensor.usm_array.device`. Default: `None`. + usm_type ("device"|"shared"|"host", optional): The type of SYCL USM + allocation for the output array. Default: `"device"`. + sycl_queue (:class:`dpctl.SyclQueue`, optional): The SYCL queue to use + for output array allocation and copying. `sycl_queue` and `device` + are exclusive keywords, i.e. use one or another. If both are + specified, a `TypeError` is raised unless both imply the same + underlying SYCL queue to be used. If both a `None`, the + `dpctl.SyclQueue()` is used for allocation and copying. + Default: `None`. + """ + if not isinstance(order, str) or len(order) == 0 or order[0] not in "CcFf": + raise ValueError( + "Unrecognized order keyword value, expecting 'F' or 'C'." + ) + else: + order = order[0].upper() + 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, ref_type=type(fill_value)) + res = dpt.usm_ndarray( + sh, + dtype=dtype, + buffer=usm_type, + order=order, + buffer_ctor_kwargs={"queue": sycl_queue}, + ) + hev, ev = ti._full_usm_ndarray(fill_value, res, sycl_queue) + hev.wait() + return res + + +def empty_like( + x, dtype=None, order="C", device=None, usm_type=None, sycl_queue=None +): + """ + Creates `usm_ndarray` from uninitialized USM allocation. + + Args: + x (usm_ndarray): Input array from which to derive the output array + shape. + dtype (optional): data type of the array. Can be typestring, + a `numpy.dtype` object, `numpy` char string, or a numpy + scalar type. Default: None + order ("C", or F"): memory layout for the array. Default: "C" + device (optional): array API concept of device where the output array + is created. `device` can be `None`, a oneAPI filter selector string, + an instance of :class:`dpctl.SyclDevice` corresponding to a + non-partitioned SYCL device, an instance of + :class:`dpctl.SyclQueue`, or a `Device` object returnedby + `dpctl.tensor.usm_array.device`. Default: `None`. + usm_type ("device"|"shared"|"host", optional): The type of SYCL USM + allocation for the output array. Default: `"device"`. + sycl_queue (:class:`dpctl.SyclQueue`, optional): The SYCL queue to use + for output array allocation and copying. `sycl_queue` and `device` + are exclusive keywords, i.e. use one or another. If both are + specified, a `TypeError` is raised unless both imply the same + underlying SYCL queue to be used. If both a `None`, the + `dpctl.SyclQueue()` is used for allocation and copying. + Default: `None`. + """ + if not isinstance(x, dpt.usm_ndarray): + raise TypeError(f"Expected instance of dpt.usm_ndarray, got {type(x)}.") + if not isinstance(order, str) or len(order) == 0 or order[0] not in "CcFf": + raise ValueError( + "Unrecognized order keyword value, expecting 'F' or 'C'." + ) + else: + order = order[0].upper() + if dtype is None: + dtype = x.dtype + if usm_type is None: + usm_type = x.usm_type + dpctl.utils.validate_usm_type(usm_type, allow_none=False) + if device is None and sycl_queue is None: + device = x.device + sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) + sh = x.shape + dtype = np.dtype(dtype) + res = dpt.usm_ndarray( + sh, + dtype=dtype, + buffer=usm_type, + order=order, + buffer_ctor_kwargs={"queue": sycl_queue}, + ) + return res + + +def zeros_like( + x, dtype=None, order="C", device=None, usm_type=None, sycl_queue=None +): + """ + Creates `usm_ndarray` from USM allocation initialized with zeros. + + Args: + x (usm_ndarray): Input array from which to derive the output array + shape. + dtype (optional): data type of the array. Can be typestring, + a `numpy.dtype` object, `numpy` char string, or a numpy + scalar type. Default: None + order ("C", or F"): memory layout for the array. Default: "C" + device (optional): array API concept of device where the output array + is created. `device` can be `None`, a oneAPI filter selector string, + an instance of :class:`dpctl.SyclDevice` corresponding to a + non-partitioned SYCL device, an instance of + :class:`dpctl.SyclQueue`, or a `Device` object returnedby + `dpctl.tensor.usm_array.device`. Default: `None`. + usm_type ("device"|"shared"|"host", optional): The type of SYCL USM + allocation for the output array. Default: `"device"`. + sycl_queue (:class:`dpctl.SyclQueue`, optional): The SYCL queue to use + for output array allocation and copying. `sycl_queue` and `device` + are exclusive keywords, i.e. use one or another. If both are + specified, a `TypeError` is raised unless both imply the same + underlying SYCL queue to be used. If both a `None`, the + `dpctl.SyclQueue()` is used for allocation and copying. + Default: `None`. + """ + if not isinstance(x, dpt.usm_ndarray): + raise TypeError(f"Expected instance of dpt.usm_ndarray, got {type(x)}.") + if not isinstance(order, str) or len(order) == 0 or order[0] not in "CcFf": + raise ValueError( + "Unrecognized order keyword value, expecting 'F' or 'C'." + ) + else: + order = order[0].upper() + if dtype is None: + dtype = x.dtype + if usm_type is None: + usm_type = x.usm_type + dpctl.utils.validate_usm_type(usm_type, allow_none=False) + if device is None and sycl_queue is None: + device = x.device + sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) + sh = x.shape + dtype = np.dtype(dtype) + return zeros( + sh, + dtype=dtype, + order=order, + device=device, + usm_type=usm_type, + sycl_queue=sycl_queue, + ) + + +def ones_like( + x, dtype=None, order="C", device=None, usm_type=None, sycl_queue=None +): + """ + Creates `usm_ndarray` from USM allocation initialized with zeros. + + Args: + x (usm_ndarray): Input array from which to derive the output array + shape. + dtype (optional): data type of the array. Can be typestring, + a `numpy.dtype` object, `numpy` char string, or a numpy + scalar type. Default: None + order ("C", or F"): memory layout for the array. Default: "C" + device (optional): array API concept of device where the output array + is created. `device` can be `None`, a oneAPI filter selector string, + an instance of :class:`dpctl.SyclDevice` corresponding to a + non-partitioned SYCL device, an instance of + :class:`dpctl.SyclQueue`, or a `Device` object returnedby + `dpctl.tensor.usm_array.device`. Default: `None`. + usm_type ("device"|"shared"|"host", optional): The type of SYCL USM + allocation for the output array. Default: `"device"`. + sycl_queue (:class:`dpctl.SyclQueue`, optional): The SYCL queue to use + for output array allocation and copying. `sycl_queue` and `device` + are exclusive keywords, i.e. use one or another. If both are + specified, a `TypeError` is raised unless both imply the same + underlying SYCL queue to be used. If both a `None`, the + `dpctl.SyclQueue()` is used for allocation and copying. + Default: `None`. + """ + if not isinstance(x, dpt.usm_ndarray): + raise TypeError(f"Expected instance of dpt.usm_ndarray, got {type(x)}.") + if not isinstance(order, str) or len(order) == 0 or order[0] not in "CcFf": + raise ValueError( + "Unrecognized order keyword value, expecting 'F' or 'C'." + ) + else: + order = order[0].upper() + if dtype is None: + dtype = x.dtype + if usm_type is None: + usm_type = x.usm_type + dpctl.utils.validate_usm_type(usm_type, allow_none=False) + if device is None and sycl_queue is None: + device = x.device + sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) + sh = x.shape + dtype = np.dtype(dtype) + return ones( + sh, + dtype=dtype, + order=order, + device=device, + usm_type=usm_type, + sycl_queue=sycl_queue, + ) + + +def full_like( + x, + fill_value, + dtype=None, + order="C", + device=None, + usm_type=None, + sycl_queue=None, +): + """ + Creates `usm_ndarray` from USM allocation initialized with zeros. + + Args: + x (usm_ndarray): Input array from which to derive the output array + shape. + fill_value: the value to fill array with + dtype (optional): data type of the array. Can be typestring, + a `numpy.dtype` object, `numpy` char string, or a numpy + scalar type. Default: None + order ("C", or F"): memory layout for the array. Default: "C" + device (optional): array API concept of device where the output array + is created. `device` can be `None`, a oneAPI filter selector string, + an instance of :class:`dpctl.SyclDevice` corresponding to a + non-partitioned SYCL device, an instance of + :class:`dpctl.SyclQueue`, or a `Device` object returnedby + `dpctl.tensor.usm_array.device`. Default: `None`. + usm_type ("device"|"shared"|"host", optional): The type of SYCL USM + allocation for the output array. Default: `"device"`. + sycl_queue (:class:`dpctl.SyclQueue`, optional): The SYCL queue to use + for output array allocation and copying. `sycl_queue` and `device` + are exclusive keywords, i.e. use one or another. If both are + specified, a `TypeError` is raised unless both imply the same + underlying SYCL queue to be used. If both a `None`, the + `dpctl.SyclQueue()` is used for allocation and copying. + Default: `None`. + """ + if not isinstance(x, dpt.usm_ndarray): + raise TypeError(f"Expected instance of dpt.usm_ndarray, got {type(x)}.") + if not isinstance(order, str) or len(order) == 0 or order[0] not in "CcFf": + raise ValueError( + "Unrecognized order keyword value, expecting 'F' or 'C'." + ) + else: + order = order[0].upper() + if dtype is None: + dtype = x.dtype + if usm_type is None: + usm_type = x.usm_type + dpctl.utils.validate_usm_type(usm_type, allow_none=False) + if device is None and sycl_queue is None: + device = x.device + sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device) + sh = x.shape + dtype = np.dtype(dtype) + return full( + sh, + fill_value, + dtype=dtype, + order=order, + device=device, + usm_type=usm_type, + sycl_queue=sycl_queue, + ) diff --git a/dpctl/tensor/libtensor/source/tensor_py.cpp b/dpctl/tensor/libtensor/source/tensor_py.cpp index 454d83d6ea..4ec925e50f 100644 --- a/dpctl/tensor/libtensor/source/tensor_py.cpp +++ b/dpctl/tensor/libtensor/source/tensor_py.cpp @@ -1697,6 +1697,96 @@ usm_ndarray_linear_sequence_affine(py::object start, linspace_affine_event); } +/* ================ Full ================== */ + +typedef sycl::event (*full_contig_fn_ptr_t)(sycl::queue, + size_t, + py::object, + char *, + const std::vector &); + +static full_contig_fn_ptr_t full_contig_dispatch_vector[_ns::num_types]; + +template +sycl::event full_contig_impl(sycl::queue q, + size_t nelems, + py::object py_value, + char *dst_p, + const std::vector &depends) +{ + dstTy fill_v; + try { + fill_v = unbox_py_scalar(py_value); + } catch (const py::error_already_set &e) { + throw; + } + + sycl::event fill_ev = q.submit([&](sycl::handler &cgh) { + cgh.depends_on(depends); + dstTy *p = reinterpret_cast(dst_p); + cgh.fill(p, fill_v, nelems); + }); + + return fill_ev; +} + +template struct FullContigFactory +{ + fnT get() + { + fnT f = full_contig_impl; + return f; + } +}; + +std::pair +usm_ndarray_full(py::object py_value, + dpctl::tensor::usm_ndarray dst, + sycl::queue exec_q, + const std::vector &depends = {}) +{ + // start, end should be coercible into data type of dst + + py::ssize_t dst_nelems = dst.get_size(); + + if (dst_nelems == 0) { + // nothing to do + return std::make_pair(sycl::event(), sycl::event()); + } + + int dst_flags = dst.get_flags(); + + sycl::queue dst_q = dst.get_queue(); + if (dst_q != exec_q && dst_q.get_context() != exec_q.get_context()) { + throw py::value_error( + "Execution queue context is not the same as allocation context"); + } + + int dst_typenum = dst.get_typenum(); + int dst_typeid = array_types.typenum_to_lookup_id(dst_typenum); + + char *dst_data = dst.get_data(); + sycl::event full_event; + + if (dst_nelems == 1 || (dst_flags & USM_ARRAY_C_CONTIGUOUS) || + (dst_flags & USM_ARRAY_F_CONTIGUOUS)) + { + auto fn = full_contig_dispatch_vector[dst_typeid]; + + sycl::event full_contig_event = + fn(exec_q, static_cast(dst_nelems), py_value, dst_data, + depends); + + return std::make_pair( + keep_args_alive(exec_q, {dst}, {full_contig_event}), + full_contig_event); + } + else { + throw std::runtime_error( + "Only population of contiguous usm_ndarray objects is supported."); + } +} + // populate dispatch tables void init_copy_and_cast_dispatch_tables(void) { @@ -1747,9 +1837,43 @@ void init_copy_for_reshape_dispatch_vector(void) dvb2; dvb2.populate_dispatch_vector(lin_space_affine_dispatch_vector); + DispatchVectorBuilder + dvb3; + dvb3.populate_dispatch_vector(full_contig_dispatch_vector); + return; } +std::string get_default_device_fp_type(sycl::device d) +{ + if (d.has(sycl::aspect::fp64)) { + return "f8"; + } + else { + return "f4"; + } +} + +std::string get_default_device_int_type(sycl::device) +{ + return "i8"; +} + +std::string get_default_device_complex_type(sycl::device d) +{ + if (d.has(sycl::aspect::fp64)) { + return "c16"; + } + else { + return "c8"; + } +} + +std::string get_default_device_bool_type(sycl::device) +{ + return "b1"; +} + } // namespace PYBIND11_MODULE(_tensor_impl, m) @@ -1816,4 +1940,34 @@ PYBIND11_MODULE(_tensor_impl, m) "Copy fom numpy array `src` into usm_ndarray `dst` synchronously.", py::arg("src"), py::arg("dst"), py::arg("sycl_queue"), py::arg("depends") = py::list()); + + m.def("_full_usm_ndarray", &usm_ndarray_full, + "Populate usm_ndarray `dst` with given fill_value.", + py::arg("fill_value"), py::arg("dst"), py::arg("sycl_queue"), + py::arg("depends") = py::list()); + + m.def("default_device_fp_type", [](sycl::queue q) { + return get_default_device_fp_type(q.get_device()); + }); + m.def("default_device_fp_type", + [](sycl::device dev) { return get_default_device_fp_type(dev); }); + + m.def("default_device_int_type", [](sycl::queue q) { + return get_default_device_int_type(q.get_device()); + }); + m.def("default_device_int_type", + [](sycl::device dev) { return get_default_device_int_type(dev); }); + + m.def("default_device_bool_type", [](sycl::queue q) { + return get_default_device_bool_type(q.get_device()); + }); + m.def("default_device_bool_type", + [](sycl::device dev) { return get_default_device_bool_type(dev); }); + + m.def("default_device_complex_type", [](sycl::queue q) { + return get_default_device_complex_type(q.get_device()); + }); + m.def("default_device_complex_type", [](sycl::device dev) { + return get_default_device_complex_type(dev); + }); } diff --git a/dpctl/tests/test_usm_ndarray_ctor.py b/dpctl/tests/test_usm_ndarray_ctor.py index ace4ca807b..63e2958586 100644 --- a/dpctl/tests/test_usm_ndarray_ctor.py +++ b/dpctl/tests/test_usm_ndarray_ctor.py @@ -558,27 +558,30 @@ def test_pyx_capi_check_constants(): assert cdouble_typenum == np.dtype(np.cdouble).num +_all_dtypes = [ + "b1", + "i1", + "u1", + "i2", + "u2", + "i4", + "u4", + "i8", + "u8", + "f2", + "f4", + "f8", + "c8", + "c16", +] + + @pytest.mark.parametrize( "shape", [tuple(), (1,), (5,), (2, 3), (2, 3, 4), (2, 2, 2, 2, 2)] ) @pytest.mark.parametrize( "dtype", - [ - "b1", - "i1", - "u1", - "i2", - "u2", - "i4", - "u4", - "i8", - "u8", - "f2", - "f4", - "f8", - "c8", - "c16", - ], + _all_dtypes, ) @pytest.mark.parametrize("usm_type", ["device", "shared", "host"]) def test_tofrom_numpy(shape, dtype, usm_type): @@ -593,22 +596,7 @@ def test_tofrom_numpy(shape, dtype, usm_type): @pytest.mark.parametrize( "dtype", - [ - "b1", - "i1", - "u1", - "i2", - "u2", - "i4", - "u4", - "i8", - "u8", - "f2", - "f4", - "f8", - "c8", - "c16", - ], + _all_dtypes, ) @pytest.mark.parametrize("src_usm_type", ["device", "shared", "host"]) @pytest.mark.parametrize("dst_usm_type", ["device", "shared", "host"]) @@ -657,22 +645,7 @@ def test_setitem_same_dtype(dtype, src_usm_type, dst_usm_type): @pytest.mark.parametrize( "dtype", - [ - "b1", - "i1", - "u1", - "i2", - "u2", - "i4", - "u4", - "i8", - "u8", - "f2", - "f4", - "f8", - "c8", - "c16", - ], + _all_dtypes, ) @pytest.mark.parametrize("usm_type", ["device", "shared", "host"]) def test_setitem_scalar(dtype, usm_type): @@ -961,23 +934,230 @@ def test_real_imag_views(): @pytest.mark.parametrize( "dtype", - [ - "b1", - "i1", - "u1", - "i2", - "u2", - "i4", - "u4", - "i8", - "u8", - "f2", - "f4", - "f8", - "c8", - "c16", - ], + _all_dtypes, ) def test_zeros(dtype): X = dpt.zeros(10, dtype=dtype) assert np.array_equal(dpt.asnumpy(X), np.zeros(10, dtype=dtype)) + + +@pytest.mark.parametrize( + "dtype", + _all_dtypes, +) +def test_ones(dtype): + X = dpt.ones(10, dtype=dtype) + assert np.array_equal(dpt.asnumpy(X), np.ones(10, dtype=dtype)) + + +@pytest.mark.parametrize( + "dtype", + _all_dtypes, +) +def test_full(dtype): + X = dpt.full(10, 4, dtype=dtype) + assert np.array_equal(dpt.asnumpy(X), np.full(10, 4, dtype=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 np.issubdtype(dpt.full(10, 12.3).dtype, np.floating) + assert np.issubdtype(dpt.full(10, 0.3 - 2j).dtype, np.complexfloating) + + +@pytest.mark.parametrize( + "dt", + _all_dtypes[1:], +) +def test_arange(dt): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Queue could not be created") + + X = dpt.arange(0, 123, dtype=dt, sycl_queue=q) + dt = np.dtype(dt) + if np.issubdtype(dt, np.integer): + assert int(X[47]) == 47 + elif np.issubdtype(dt, np.floating): + assert float(X[47]) == 47.0 + elif np.issubdtype(dt, np.complexfloating): + assert complex(X[47]) == 47.0 + 0.0j + + X1 = dpt.arange(4, dtype=dt, sycl_queue=q) + assert X1.shape == (4,) + + X2 = dpt.arange(4, 0, -1, dtype=dt, sycl_queue=q) + assert X2.shape == (4,) + + +@pytest.mark.parametrize( + "dt", + _all_dtypes, +) +@pytest.mark.parametrize( + "usm_kind", + [ + "shared", + "device", + "host", + ], +) +def test_empty_like(dt, usm_kind): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Queue could not be created") + + X = dpt.empty((4, 5), dtype=dt, usm_type=usm_kind, sycl_queue=q) + Y = dpt.empty_like(X) + assert X.shape == Y.shape + assert X.dtype == Y.dtype + assert X.usm_type == Y.usm_type + assert X.sycl_queue == Y.sycl_queue + + X = dpt.empty(tuple(), dtype=dt, usm_type=usm_kind, sycl_queue=q) + Y = dpt.empty_like(X) + assert X.shape == Y.shape + assert X.dtype == Y.dtype + assert X.usm_type == Y.usm_type + assert X.sycl_queue == Y.sycl_queue + + +@pytest.mark.parametrize( + "dt", + _all_dtypes, +) +@pytest.mark.parametrize( + "usm_kind", + [ + "shared", + "device", + "host", + ], +) +def test_zeros_like(dt, usm_kind): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Queue could not be created") + + X = dpt.empty((4, 5), dtype=dt, usm_type=usm_kind, sycl_queue=q) + Y = dpt.zeros_like(X) + assert X.shape == Y.shape + assert X.dtype == Y.dtype + assert X.usm_type == Y.usm_type + assert X.sycl_queue == Y.sycl_queue + assert np.allclose(dpt.asnumpy(Y), np.zeros(X.shape, dtype=X.dtype)) + + X = dpt.empty(tuple(), dtype=dt, usm_type=usm_kind, sycl_queue=q) + Y = dpt.zeros_like(X) + assert X.shape == Y.shape + assert X.dtype == Y.dtype + assert X.usm_type == Y.usm_type + assert X.sycl_queue == Y.sycl_queue + assert np.array_equal(dpt.asnumpy(Y), np.zeros(X.shape, dtype=X.dtype)) + + +@pytest.mark.parametrize( + "dt", + _all_dtypes, +) +@pytest.mark.parametrize( + "usm_kind", + [ + "shared", + "device", + "host", + ], +) +def test_ones_like(dt, usm_kind): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Queue could not be created") + + X = dpt.empty((4, 5), dtype=dt, usm_type=usm_kind, sycl_queue=q) + Y = dpt.ones_like(X) + assert X.shape == Y.shape + assert X.dtype == Y.dtype + assert X.usm_type == Y.usm_type + assert X.sycl_queue == Y.sycl_queue + assert np.allclose(dpt.asnumpy(Y), np.ones(X.shape, dtype=X.dtype)) + + X = dpt.empty(tuple(), dtype=dt, usm_type=usm_kind, sycl_queue=q) + Y = dpt.ones_like(X) + assert X.shape == Y.shape + assert X.dtype == Y.dtype + assert X.usm_type == Y.usm_type + assert X.sycl_queue == Y.sycl_queue + assert np.array_equal(dpt.asnumpy(Y), np.ones(X.shape, dtype=X.dtype)) + + +@pytest.mark.parametrize( + "dt", + _all_dtypes, +) +@pytest.mark.parametrize( + "usm_kind", + [ + "shared", + "device", + "host", + ], +) +def test_full_like(dt, usm_kind): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Queue could not be created") + + fill_v = np.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 + assert X.dtype == Y.dtype + assert X.usm_type == Y.usm_type + assert X.sycl_queue == Y.sycl_queue + assert np.allclose(dpt.asnumpy(Y), np.ones(X.shape, dtype=X.dtype)) + + X = dpt.empty(tuple(), dtype=dt, usm_type=usm_kind, sycl_queue=q) + Y = dpt.full_like(X, fill_v) + assert X.shape == Y.shape + assert X.dtype == Y.dtype + assert X.usm_type == Y.usm_type + assert X.sycl_queue == Y.sycl_queue + assert np.array_equal(dpt.asnumpy(Y), np.ones(X.shape, dtype=X.dtype)) + + +def test_common_arg_validation(): + order = "I" + # invalid order must raise ValueError + with pytest.raises(ValueError): + dpt.empty(10, order=order) + with pytest.raises(ValueError): + dpt.zeros(10, order=order) + with pytest.raises(ValueError): + dpt.ones(10, order=order) + with pytest.raises(ValueError): + dpt.full(10, 1, order=order) + X = dpt.empty(10) + with pytest.raises(ValueError): + dpt.empty_like(X, order=order) + with pytest.raises(ValueError): + dpt.zeros_like(X, order=order) + with pytest.raises(ValueError): + dpt.ones_like(X, order=order) + with pytest.raises(ValueError): + dpt.full_like(X, 1, order=order) + X = dict() + # test for type validation + with pytest.raises(TypeError): + dpt.empty_like(X) + with pytest.raises(TypeError): + dpt.zeros_like(X) + with pytest.raises(TypeError): + dpt.ones_like(X) + with pytest.raises(TypeError): + dpt.full_like(X, 1) diff --git a/dpctl/tests/test_usm_ndarray_manipulation.py b/dpctl/tests/test_usm_ndarray_manipulation.py index b1daf87c79..1b3716846b 100644 --- a/dpctl/tests/test_usm_ndarray_manipulation.py +++ b/dpctl/tests/test_usm_ndarray_manipulation.py @@ -725,40 +725,3 @@ def test_roll_2d(data): Y = dpt.roll(X, sh, ax) Ynp = np.roll(Xnp, sh, ax) assert_array_equal(Ynp, dpt.asnumpy(Y)) - - -@pytest.mark.parametrize( - "dt", - [ - "u1", - "i1", - "u2", - "i2", - "u4", - "i4", - "u8", - "i8", - "f2", - "f4", - "f8", - "c8", - "c16", - ], -) -def test_arange(dt): - try: - q = dpctl.SyclQueue() - except dpctl.SyclQueueCreationError: - pytest.skip("Queue could not be created") - - X = dpt.arange(0, 123, dtype=dt, sycl_queue=q) - dt = np.dtype(dt) - if np.issubdtype(dt, np.integer): - assert int(X[47]) == 47 - elif np.issubdtype(dt, np.floating): - assert float(X[47]) == 47.0 - elif np.issubdtype(dt, np.complexfloating): - assert complex(X[47]) == 47.0 + 0.0j - - X1 = dpt.arange(4, dtype=dt, sycl_queue=q) - assert X1.shape == (4,)