From 9d0050c9fcbd5287894bd7df0a50ff63e4189864 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 29 Oct 2021 14:40:17 -0500 Subject: [PATCH 1/5] Add initial implementation of dpctl.tensor.asarray, dpctl.tensor.empty ``` dpctl.tensor.asarray( obj, dtype=None, copy=None, order="C", device=None, usm_type=None, sycl_queue=None ) ``` ``` dpctl.tensor.empty( shape, dtype="f8", order="C", device=None, usm_type=None, sycl_queue=None ) ``` --- dpctl/tensor/__init__.py | 3 + dpctl/tensor/_ctors.py | 377 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 380 insertions(+) create mode 100644 dpctl/tensor/_ctors.py diff --git a/dpctl/tensor/__init__.py b/dpctl/tensor/__init__.py index 32e1e1ce46..6036099c62 100644 --- a/dpctl/tensor/__init__.py +++ b/dpctl/tensor/__init__.py @@ -33,13 +33,16 @@ from dpctl.tensor._copy_utils import copy_from_numpy as from_numpy from dpctl.tensor._copy_utils import copy_to_numpy as asnumpy from dpctl.tensor._copy_utils import copy_to_numpy as to_numpy +from dpctl.tensor._ctors import asarray, empty from dpctl.tensor._reshape import reshape from dpctl.tensor._usmarray import usm_ndarray __all__ = [ "usm_ndarray", + "asarray", "astype", "copy", + "empty", "reshape", "from_numpy", "to_numpy", diff --git a/dpctl/tensor/_ctors.py b/dpctl/tensor/_ctors.py new file mode 100644 index 0000000000..14e42f65a5 --- /dev/null +++ b/dpctl/tensor/_ctors.py @@ -0,0 +1,377 @@ +# Data Parallel Control (dpctl) +# +# Copyright 2020-2021 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +import dpctl +import dpctl.memory as dpm +import dpctl.tensor as dpt +import dpctl.utils + +_empty_tuple = tuple() +_host_set = frozenset([None]) + + +def _array_info_dispatch(obj): + if isinstance(obj, dpt.usm_ndarray): + return obj.shape, obj.dtype, frozenset([obj.sycl_queue]) + elif isinstance(obj, np.ndarray): + return obj.shape, obj.dtype, _host_set + elif isinstance(obj, range): + return (len(obj),), int, _host_set + elif isinstance(obj, float): + return _empty_tuple, float, _host_set + elif isinstance(obj, int): + return _empty_tuple, int, _host_set + elif isinstance(obj, complex): + return _empty_tuple, complex, _host_set + elif isinstance(obj, (list, tuple, range)): + return _array_info_sequence(obj) + else: + raise ValueError(type(obj)) + + +def _array_info_sequence(li): + assert isinstance(li, (list, tuple, range)) + n = len(li) + dim = None + dt = None + device = frozenset() + for el in li: + el_dim, el_dt, el_dev = _array_info_dispatch(el) + if dim is None: + dim = el_dim + dt = np.promote_types(el_dt, el_dt) + device = device.union(el_dev) + elif el_dim == dim: + dt = np.promote_types(dt, el_dt) + device = device.union(el_dev) + else: + raise ValueError( + "Inconsistent dimensions, {} and {}".format(dim, el_dim) + ) + return (n,) + dim, dt, device + + +def _normalize_queue_device(q=None, d=None): + if q is None: + d = dpt._device.Device.create_device(d) + return d.sycl_queue + else: + if not isinstance(q, dpctl.SyclQueue): + raise TypeError(f"Expected dpctl.SyclQueue, got {type(q)}") + if d is None: + return q + d = dpt._device.Device.create_device(d) + qq = dpctl.utils.get_execution_queue( + ( + q, + d.sycl_queue, + ) + ) + if qq is None: + raise TypeError( + "sycl_queue and device keywords can not be both specified" + ) + return qq + + +def _asarray_from_usm_ndarray( + usm_ndary, + dtype=None, + copy=None, + usm_type=None, + sycl_queue=None, + order="K", +): + if not isinstance(usm_ndary, dpt.usm_ndarray): + raise TypeError( + f"Expected dpctl.tensor.usm_ndarray, got {type(usm_ndary)}" + ) + if dtype is None: + dtype = usm_ndary.dtype + if usm_type is None: + usm_type = usm_ndary.usm_type + if sycl_queue is not None: + exec_q = dpctl.utils.get_execution_queue( + [usm_ndary.sycl_queue, sycl_queue] + ) + copy_q = _normalize_queue_device(q=sycl_queue, d=exec_q) + else: + copy_q = usm_ndary.sycl_queue + # Conditions for zero copy: + can_zero_copy = copy is not True + # dtype is unchanged + can_zero_copy = can_zero_copy and dtype == usm_ndary.dtype + # USM allocation type is unchanged + can_zero_copy = can_zero_copy and usm_type == usm_ndary.usm_type + # sycl_queue is unchanged + can_zero_copy = can_zero_copy and copy_q is usm_ndary.sycl_queue + # order is unchanged + c_contig = usm_ndary.flags & 1 + f_contig = usm_ndary.flags & 2 + fc_contig = usm_ndary.flags & 3 + if can_zero_copy: + if order == "C" and c_contig: + pass + elif order == "F" and f_contig: + pass + elif order == "A" and fc_contig: + pass + elif order == "K": + pass + else: + can_zero_copy = False + if copy is False and can_zero_copy is False: + raise ValueError("asarray(..., copy=False) is not possible") + if can_zero_copy: + return usm_ndary + if order == "A": + order = "F" if f_contig and not c_contig else "C" + if order == "K" and fc_contig: + order = "C" if c_contig else "F" + if order == "K": + # new USM allocation + res = dpt.usm_ndarray( + usm_ndary.shape, + dtype=dtype, + buffer=usm_type, + order="C", + buffer_ctor_kwargs={"queue": copy_q}, + ) + original_strides = usm_ndary.strides + ind = sorted( + range(usm_ndary.ndim), + key=lambda i: abs(original_strides[i]), + reverse=True, + ) + new_strides = tuple(res.strides[ind[i]] for i in ind) + # reuse previously made USM allocation + res = dpt.usm_ndarray( + usm_ndary.shape, + dtype=res.dtype, + buffer=res.usm_data, + strides=new_strides, + ) + else: + res = dpt.usm_ndarray( + usm_ndary.shape, + dtype=dtype, + buffer=usm_type, + order=order, + buffer_ctor_kwargs={"queue": copy_q}, + ) + # FIXME: call copy_to when implemented + res[(slice(None, None, None),) * res.ndim] = usm_ndary + return res + + +def _asarray_from_numpy_ndarray( + ary, dtype=None, usm_type=None, sycl_queue=None, order="K" +): + if not isinstance(ary, np.ndarray): + raise TypeError(f"Expected numpy.ndarray, got {type(ary)}") + if usm_type is None: + usm_type = "device" + if dtype is None: + dtype = ary.dtype + copy_q = _normalize_queue_device(q=None, d=sycl_queue) + f_contig = ary.flags["F"] + c_contig = ary.flags["C"] + fc_contig = f_contig or c_contig + if order == "A": + order = "F" if f_contig and not c_contig else "C" + if order == "K" and fc_contig: + order = "C" if c_contig else "F" + if order == "K": + # new USM allocation + res = dpt.usm_ndarray( + ary.shape, + dtype=dtype, + buffer=usm_type, + order="C", + buffer_ctor_kwargs={"queue": copy_q}, + ) + original_strides = ary.strides + ind = sorted( + range(ary.ndim), + key=lambda i: abs(original_strides[i]), + reverse=True, + ) + new_strides = tuple(res.strides[ind[i]] for i in ind) + # reuse previously made USM allocation + res = dpt.usm_ndarray( + res.shape, dtype=res.dtype, buffer=res.usm_data, strides=new_strides + ) + else: + res = dpt.usm_ndarray( + ary.shape, + dtype=dtype, + buffer=usm_type, + order=order, + buffer_ctor_kwargs={"queue": copy_q}, + ) + # FIXME: call copy_to when implemented + res[(slice(None, None, None),) * res.ndim] = ary + return res + + +def asarray( + obj, + dtype=None, + device=None, + copy=None, + usm_type=None, + sycl_queue=None, + order="K", +): + """Represents object `obj` as usm_ndarray""" + # 1. Check that copy is a valid keyword + if copy not in [None, True, False]: + raise TypeError( + "Recognized copy keyword values should be True, False, or None" + ) + # 2. Check that dtype is None, or a valid dtype + if dtype is not None: + dtype = np.dtype(dtype) + # 3. Validate order + if not isinstance(order, str): + raise TypeError( + f"Expected order keyword to be of type str, got {type(order)}" + ) + if len(order) == 0 or order[0] not in "KkAaCcFf": + raise ValueError( + "Unrecognized order keyword value, expecting 'K', 'A', 'F', or 'C'." + ) + else: + order = order[0].upper() + # 4. Check that usm_type is None, or a valid value + if usm_type is not None: + if isinstance(usm_type, str): + if usm_type not in ["device", "shared", "host"]: + raise ValueError( + f"Unrecognized value of usm_type={usm_type}, " + "expected 'device', 'shared', 'host', or None." + ) + else: + raise TypeError( + f"Expected usm_type to be a str or None, got {type(usm_type)}" + ) + # 5. Normalize device/sycl_queue [keep it None if was None] + if device is not None or sycl_queue is not None: + sycl_queue = _normalize_queue_device(q=sycl_queue, d=device) + + # handle instance(obj, usm_ndarray) + if isinstance(obj, dpt.usm_ndarray): + return _asarray_from_usm_ndarray( + obj, + dtype=dtype, + copy=copy, + usm_type=usm_type, + sycl_queue=sycl_queue, + order=order, + ) + elif hasattr(obj, "__sycl_usm_array_interface__"): + sua_iface = getattr(obj, "__sycl_usm_array_interface__") + membuf = dpm.as_usm_memory(obj) + ary = dpt.usm_ndarray( + sua_iface["shape"], + dtype=sua_iface["typestr"], + buffer=membuf, + strides=sua_iface.get("strides", None), + ) + return _asarray_from_usm_ndarray( + ary, + dtype=dtype, + copy=copy, + usm_type=usm_type, + sycl_queue=sycl_queue, + order=order, + ) + elif isinstance(obj, np.ndarray): + if copy is False: + raise ValueError( + "Converting numpy.ndarray to usm_ndarray requires a copy" + ) + return _asarray_from_numpy_ndarray( + obj, + dtype=dtype, + usm_type=usm_type, + sycl_queue=sycl_queue, + order=order, + ) + elif hasattr(obj, "__dlpack__"): + raise NotImplementedError( + "Support for __dlpack__ is not yet implemented" + ) + elif isinstance(obj, (list, tuple, range)): + if copy is False: + raise ValueError( + "Converting Python sequence to usm_ndarray requires a copy" + ) + _, dt, devs = _array_info_sequence(obj) + if devs == _host_set: + return _asarray_from_numpy_ndarray( + np.asarray(obj, dt, order=order), + dtype=dtype, + usm_type=usm_type, + sycl_queue=sycl_queue, + order=order, + ) + # for sequences + raise NotImplementedError( + "Converting Python sequences is not implemented" + ) + # obj is a scalar, create 0d array + return _asarray_from_numpy_ndarray( + np.asarray(obj), + dtype=dtype, + usm_type=usm_type, + sycl_queue=sycl_queue, + order="C", + ) + + +def empty( + sh, dtype="f8", order="C", device=None, usm_type="device", sycl_queue=None +): + """Creates empty usm_ndarray""" + dtype = np.dtype(dtype) + if 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 isinstance(usm_type, str): + if usm_type not in ["device", "shared", "host"]: + raise ValueError( + f"Unrecognized value of usm_type={usm_type}, " + "expected 'device', 'shared', or 'host'." + ) + else: + raise TypeError( + f"Expected usm_type to be of type str, got {type(usm_type)}" + ) + sycl_queue = _normalize_queue_device(q=sycl_queue, d=device) + res = dpt.usm_ndarray( + sh, + dtype=dtype, + buffer=usm_type, + order=order, + buffer_ctor_kwargs={"queue": sycl_queue}, + ) + return res From e4e7682adbf0094dc5b2a79f92ff0dad1d8f244a Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 29 Oct 2021 15:11:25 -0500 Subject: [PATCH 2/5] Adding test file for asarray and empty 1. Check validation of arguments is `asarray` 2. Check that `asarray` works with objects exposing `__sycl_usm_array_interface__`. Adding a test with scalar arguments, tests to cover input validation, tests for copy=False --- dpctl/tests/test_tensor_asarray.py | 192 +++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 dpctl/tests/test_tensor_asarray.py diff --git a/dpctl/tests/test_tensor_asarray.py b/dpctl/tests/test_tensor_asarray.py new file mode 100644 index 0000000000..a17a470b5f --- /dev/null +++ b/dpctl/tests/test_tensor_asarray.py @@ -0,0 +1,192 @@ +# Data Parallel Control (dpctl) +# +# Copyright 2020-2021 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +import pytest + +import dpctl +import dpctl.tensor as dpt + + +@pytest.mark.parametrize( + "src_usm_type, dst_usm_type", + [ + ("device", "shared"), + ("device", "host"), + ("shared", "device"), + ("shared", "host"), + ("host", "device"), + ("host", "shared"), + ], +) +def test_asarray_change_usm_type(src_usm_type, dst_usm_type): + d = dpctl.SyclDevice() + if d.is_host: + pytest.skip( + "Skip test of host device, which only " + "supports host USM allocations" + ) + X = dpt.empty(10, dtype="u1", usm_type=src_usm_type) + Y = dpt.asarray(X, usm_type=dst_usm_type) + assert X.shape == Y.shape + assert X.usm_type == src_usm_type + assert Y.usm_type == dst_usm_type + + with pytest.raises(ValueError): + # zero copy is not possible + dpt.asarray(X, usm_type=dst_usm_type, copy=False) + + Y = dpt.asarray(X, usm_type=dst_usm_type, sycl_queue=X.sycl_queue) + assert X.shape == Y.shape + assert Y.usm_type == dst_usm_type + + Y = dpt.asarray( + X, + usm_type=dst_usm_type, + sycl_queue=X.sycl_queue, + device=d.get_filter_string(), + ) + assert X.shape == Y.shape + assert Y.usm_type == dst_usm_type + + +def test_asarray_from_numpy(): + Xnp = np.arange(10) + Y = dpt.asarray(Xnp, usm_type="device") + assert type(Y) is dpt.usm_ndarray + assert Y.shape == (10,) + assert Y.dtype == Xnp.dtype + + +def test_asarray_from_sequence(): + X = [1, 2, 3] + Y = dpt.asarray(X, usm_type="device") + assert type(Y) is dpt.usm_ndarray + + X = [(1, 1), (2.0, 2.0 + 1.0j), range(4, 6), np.array([3, 4], dtype="c16")] + Y = dpt.asarray(X, usm_type="device") + assert type(Y) is dpt.usm_ndarray + assert Y.ndim == 2 + + +def test_asarray_from_object_with_suai(): + """Test that asarray can deal with opaque objects implementing SUAI""" + + class Dummy: + def __init__(self, obj, iface): + self.obj = obj + self.__sycl_usm_array_interface__ = iface + + X = dpt.empty((2, 3, 4), dtype="f4") + Y = dpt.asarray(Dummy(X, X.__sycl_usm_array_interface__)) + assert Y.shape == X.shape + assert X.usm_type == Y.usm_type + assert X.dtype == Y.dtype + assert X.sycl_device == Y.sycl_device + + +def test_asarray_input_validation(): + with pytest.raises(TypeError): + # copy keyword is not of right type + dpt.asarray([1], copy="invalid") + with pytest.raises(TypeError): + # order keyword is not valid + dpt.asarray([1], order=1) + with pytest.raises(TypeError): + # dtype is not valid + dpt.asarray([1], dtype="invalid") + with pytest.raises(ValueError): + # unexpected value of order + dpt.asarray([1], order="Z") + with pytest.raises(TypeError): + # usm_type is of wrong type + dpt.asarray([1], usm_type=dict()) + with pytest.raises(ValueError): + # usm_type has wrong value + dpt.asarray([1], usm_type="mistake") + with pytest.raises(TypeError): + # sycl_queue type is not right + dpt.asarray([1], sycl_queue=dpctl.SyclContext()) + with pytest.raises(ValueError): + # sequence is not rectangular + dpt.asarray([[1], 2]) + + +def test_asarray_input_validation2(): + d = dpctl.get_devices() + if len(d) < 2: + pytest.skip("Not enough SYCL devices available") + + d0, d1 = d[:2] + try: + q0 = dpctl.SyclQueue(d0) + except dpctl.SyclQueueCreationError: + pytest.skip(f"SyclQueue could not be created for {d0}") + try: + q1 = dpctl.SyclQueue(d1) + except dpctl.SyclQueueCreationError: + pytest.skip(f"SyclQueue could not be created for {d1}") + with pytest.raises(TypeError): + dpt.asarray([1, 2], sycl_queue=q0, device=q1) + + +def test_asarray_scalars(): + import ctypes + + Y = dpt.asarray(5) + assert Y.dtype == np.dtype(int) + Y = dpt.asarray(5.2) + assert Y.dtype == np.dtype(float) + Y = dpt.asarray(np.float32(2.3)) + assert Y.dtype == np.dtype(np.float32) + Y = dpt.asarray(1.0j) + assert Y.dtype == np.dtype(complex) + Y = dpt.asarray(ctypes.c_int(8)) + assert Y.dtype == np.dtype(ctypes.c_int) + + +def test_asarray_copy_false(): + try: + q = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Could not create a queue") + X = dpt.from_numpy(np.random.randn(10, 4), usm_type="device", queue=q) + Y1 = dpt.asarray(X, copy=False, order="K") + assert Y1 is X + Y1c = dpt.asarray(X, copy=True, order="K") + assert not (Y1c is X) + Y2 = dpt.asarray(X, copy=False, order="C") + assert Y2 is X + Y3 = dpt.asarray(X, copy=False, order="A") + assert Y3 is X + with pytest.raises(ValueError): + Y1 = dpt.asarray(X, copy=False, order="F") + Xf = dpt.empty( + X.shape, + dtype=X.dtype, + usm_type="device", + sycl_queue=X.sycl_queue, + order="F", + ) + Xf[:] = X + Y4 = dpt.asarray(Xf, copy=False, order="K") + assert Y4 is Xf + Y5 = dpt.asarray(Xf, copy=False, order="F") + assert Y5 is Xf + Y6 = dpt.asarray(Xf, copy=False, order="A") + assert Y6 is Xf + with pytest.raises(ValueError): + dpt.asarray(Xf, copy=False, order="C") From 9c49c5db67f5eeea0d3c66bec44587fcde5e3d51 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 18 Nov 2021 16:05:07 -0600 Subject: [PATCH 3/5] Added docstrings to asarray and empty Change default of asarray's order keyword from "K" to "C" Fixed issue pointed out by Denis (processing of order in empty) Added support for objects with buffer protocol in asarray. --- dpctl/tensor/_ctors.py | 99 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/dpctl/tensor/_ctors.py b/dpctl/tensor/_ctors.py index 14e42f65a5..d0f459d1c5 100644 --- a/dpctl/tensor/_ctors.py +++ b/dpctl/tensor/_ctors.py @@ -229,6 +229,17 @@ def _asarray_from_numpy_ndarray( return res +def _is_object_with_buffer_protocol(obj): + "Returns True if object support Python buffer protocol" + try: + # use context manager to ensure + # buffer is instantly released + with memoryview(obj): + return True + except TypeError: + return False + + def asarray( obj, dtype=None, @@ -238,7 +249,45 @@ def asarray( sycl_queue=None, order="K", ): - """Represents object `obj` as usm_ndarray""" + """asarray(obj, dtype=None, copy=None, order="K", + device=None, usm_type=None, sycl_queue=None) + + Converts `obj` to :class:`dpctl.tensor.usm_ndarray`. + + Args: + obj: Python object to convert. Can be an instance of `usm_ndarray`, + an object representing SYCL USM allocation and implementing + `__sycl_usm_array_interface__` protocol, an instance + of `numpy.ndarray`, an object supporting Python buffer protocol, + a Python scalar, or a (possibly nested) sequence of Python scalars. + dtype (data type, optional): output array data type. If `dtype` is + `None`, the output array data type is inferred from data types in + `obj`. Default: `None` + copy (`bool`, optional): boolean indicating whether or not to copy the + input. If `True`, always creates a copy. If `False`, need to copy + raises `ValueError`. If `None`, try to reuse existing memory + allocations if possible, but allowed to perform a copy otherwise. + Default: `None`. + order ("C","F","A","K", optional): memory layout of the output 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. For `usm_type=None` the allocation + type is inferred from the input if `obj` has USM allocation, or + `"device"` is used instead. Default: `None`. + 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`. + """ # 1. Check that copy is a valid keyword if copy not in [None, True, False]: raise TypeError( @@ -313,9 +362,17 @@ def asarray( sycl_queue=sycl_queue, order=order, ) - elif hasattr(obj, "__dlpack__"): - raise NotImplementedError( - "Support for __dlpack__ is not yet implemented" + elif _is_object_with_buffer_protocol(obj): + if copy is False: + raise ValueError( + f"Converting {type(obj)} to usm_ndarray requires a copy" + ) + return _asarray_from_numpy_ndarray( + np.array(obj), + dtype=dtype, + usm_type=usm_type, + sycl_queue=sycl_queue, + order=order, ) elif isinstance(obj, (list, tuple, range)): if copy is False: @@ -335,6 +392,10 @@ def asarray( raise NotImplementedError( "Converting Python sequences is not implemented" ) + if copy is False: + raise ValueError( + f"Converting {type(obj)} to usm_ndarray requires a copy" + ) # obj is a scalar, create 0d array return _asarray_from_numpy_ndarray( np.asarray(obj), @@ -348,9 +409,35 @@ def asarray( def empty( sh, dtype="f8", order="C", device=None, usm_type="device", sycl_queue=None ): - """Creates empty usm_ndarray""" + """dpctl.tensor.empty(shape, dtype="f8", order="C", device=None, + usm_type="device", sycl_queue=None) + + Creates `usm_ndarray` from uninitialized USM allocation. + + 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: "f8" + 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`. + """ dtype = np.dtype(dtype) - if len(order) == 0 or order[0] not in "CcFf": + 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'." ) From 1626363abd5568cddd9e47374f70558ca4a02db5 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Fri, 19 Nov 2021 13:53:37 -0600 Subject: [PATCH 4/5] Populate CHANGELOG/added list with functions from feature/asarray PR --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4af5ac2d79..c09fea7af3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Next Release] - TBD +### Added +- `dpctl.tensor.asarray`, `dpctl.tensor.empty` implemented (#646). + +### Changed - dpctl-capi is now renamed to `libsyclinterface` (#666). ## [0.11.1] - 11/10/2021 From 12d6693647d3817f5ea6eeec4883eaa0bdab28bb Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Sat, 20 Nov 2021 08:36:41 -0600 Subject: [PATCH 5/5] renamed keyword in dpctl.tensor.from_numpy, and its alias asnumpy from queue to sycl_queue --- dpctl/tensor/_copy_utils.py | 6 +++--- dpctl/tests/test_tensor_asarray.py | 2 +- dpctl/tests/test_usm_ndarray_ctor.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dpctl/tensor/_copy_utils.py b/dpctl/tensor/_copy_utils.py index 7a7ba918f6..9422210a6d 100644 --- a/dpctl/tensor/_copy_utils.py +++ b/dpctl/tensor/_copy_utils.py @@ -93,12 +93,12 @@ def copy_to_numpy(ary): ) -def copy_from_numpy(np_ary, usm_type="device", queue=None): +def copy_from_numpy(np_ary, usm_type="device", sycl_queue=None): "Copies numpy array `np_ary` into a new usm_ndarray" # This may peform a copy to meet stated requirements Xnp = np.require(np_ary, requirements=["A", "O", "C", "E"]) - if queue: - ctor_kwargs = {"queue": queue} + if sycl_queue: + ctor_kwargs = {"queue": sycl_queue} else: ctor_kwargs = dict() Xusm = dpt.usm_ndarray( diff --git a/dpctl/tests/test_tensor_asarray.py b/dpctl/tests/test_tensor_asarray.py index a17a470b5f..c7734b8194 100644 --- a/dpctl/tests/test_tensor_asarray.py +++ b/dpctl/tests/test_tensor_asarray.py @@ -163,7 +163,7 @@ def test_asarray_copy_false(): q = dpctl.SyclQueue() except dpctl.SyclQueueCreationError: pytest.skip("Could not create a queue") - X = dpt.from_numpy(np.random.randn(10, 4), usm_type="device", queue=q) + X = dpt.from_numpy(np.random.randn(10, 4), usm_type="device", sycl_queue=q) Y1 = dpt.asarray(X, copy=False, order="K") assert Y1 is X Y1c = dpt.asarray(X, copy=True, order="K") diff --git a/dpctl/tests/test_usm_ndarray_ctor.py b/dpctl/tests/test_usm_ndarray_ctor.py index b5fab57566..7a33e8d87c 100644 --- a/dpctl/tests/test_usm_ndarray_ctor.py +++ b/dpctl/tests/test_usm_ndarray_ctor.py @@ -562,7 +562,7 @@ def test_pyx_capi_check_constants(): def test_tofrom_numpy(shape, dtype, usm_type): q = dpctl.SyclQueue() Xnp = np.zeros(shape, dtype=dtype) - Xusm = dpt.from_numpy(Xnp, usm_type=usm_type, queue=q) + Xusm = dpt.from_numpy(Xnp, usm_type=usm_type, sycl_queue=q) Ynp = np.ones(shape, dtype=dtype) ind = (slice(None, None, None),) * Ynp.ndim Xusm[ind] = Ynp