# Data Parallel Control (dpctl)
#
# Copyright 2020-2024 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 operator
import numpy as np
import dpctl
import dpctl.memory as dpm
import dpctl.tensor as dpt
import dpctl.tensor._tensor_impl as ti
import dpctl.utils
from dpctl.tensor._copy_utils import _empty_like_orderK
from dpctl.tensor._data_types import _get_dtype
from dpctl.tensor._device import normalize_queue_device
from dpctl.tensor._usmarray import _is_object_with_buffer_protocol
__doc__ = "Implementation of creation functions in :module:`dpctl.tensor`"
_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])
if isinstance(obj, np.ndarray):
return obj.shape, obj.dtype, _host_set
if isinstance(obj, range):
return (len(obj),), int, _host_set
if isinstance(obj, bool):
return _empty_tuple, bool, _host_set
if isinstance(obj, float):
return _empty_tuple, float, _host_set
if isinstance(obj, int):
return _empty_tuple, int, _host_set
if isinstance(obj, complex):
return _empty_tuple, complex, _host_set
if isinstance(
obj,
(
list,
tuple,
),
):
return _array_info_sequence(obj)
if _is_object_with_buffer_protocol(obj):
np_obj = np.array(obj)
return np_obj.shape, np_obj.dtype, _host_set
if hasattr(obj, "__sycl_usm_array_interface__"):
usm_ar = _usm_ndarray_from_suai(obj)
return usm_ar.shape, usm_ar.dtype, frozenset([usm_ar.sycl_queue])
raise ValueError(type(obj))
def _array_info_sequence(li):
if not isinstance(li, (list, tuple, range)):
raise TypeError(f"Expected list, tuple, or range, got {type(li)}")
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(f"Inconsistent dimensions, {dim} and {el_dim}")
if dim is None:
dim = tuple()
dt = float
device = _host_set
return (n,) + dim, dt, device
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(sycl_queue=sycl_queue, device=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.c_contiguous
f_contig = usm_ndary.flags.f_contiguous
fc_contig = usm_ndary.flags.forc
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":
_ensure_native_dtype_device_support(dtype, copy_q.sycl_device)
res = _empty_like_orderK(usm_ndary, dtype, usm_type, copy_q)
else:
_ensure_native_dtype_device_support(dtype, copy_q.sycl_device)
res = dpt.usm_ndarray(
usm_ndary.shape,
dtype=dtype,
buffer=usm_type,
order=order,
buffer_ctor_kwargs={"queue": copy_q},
)
eq = dpctl.utils.get_execution_queue([usm_ndary.sycl_queue, copy_q])
if eq is not None:
_manager = dpctl.utils.SequentialOrderManager[eq]
dep_evs = _manager.submitted_events
hev, cpy_ev = ti._copy_usm_ndarray_into_usm_ndarray(
src=usm_ndary, dst=res, sycl_queue=eq, depends=dep_evs
)
_manager.add_event_pair(hev, cpy_ev)
else:
tmp = dpt.asnumpy(usm_ndary)
res[...] = tmp
return res
def _map_to_device_dtype(dt, q):
dtc = dt.char
if dtc == "?" or np.issubdtype(dt, np.integer):
return dt
d = q.sycl_device
if np.issubdtype(dt, np.floating):
if dtc == "f":
return dt
if dtc == "d" and d.has_aspect_fp64:
return dt
if dtc == "e" and d.has_aspect_fp16:
return dt
return dpt.dtype("f4")
if np.issubdtype(dt, np.complexfloating):
if dtc == "F":
return dt
if dtc == "D" and d.has_aspect_fp64:
return dt
return dpt.dtype("c8")
raise RuntimeError(f"Unrecognized data type '{dt}' encountered.")
def _usm_ndarray_from_suai(obj):
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),
)
_data_field = sua_iface["data"]
if isinstance(_data_field, tuple) and len(_data_field) > 1:
ro_field = _data_field[1]
else:
ro_field = False
if ro_field:
ary.flags["W"] = False
return ary
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"
copy_q = normalize_queue_device(sycl_queue=None, device=sycl_queue)
if ary.dtype.char not in "?bBhHiIlLqQefdFD":
raise TypeError(
f"Numpy array of data type {ary.dtype} is not supported. "
"Please convert the input to an array with numeric data type."
)
if dtype is None:
# deduce device-representable output data type
dtype = _map_to_device_dtype(ary.dtype, copy_q)
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
_ensure_native_dtype_device_support(dtype, copy_q.sycl_device)
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:
_ensure_native_dtype_device_support(dtype, copy_q.sycl_device)
res = dpt.usm_ndarray(
ary.shape,
dtype=dtype,
buffer=usm_type,
order=order,
buffer_ctor_kwargs={"queue": copy_q},
)
res[...] = ary
return res
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`):
The device about which the query is being made.
Returns:
None
Raise:
ValueError:
if 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 _usm_types_walker(o, usm_types_list):
if isinstance(o, dpt.usm_ndarray):
usm_types_list.append(o.usm_type)
return
if hasattr(o, "__sycl_usm_array_interface__"):
usm_ar = _usm_ndarray_from_suai(o)
usm_types_list.append(usm_ar.usm_type)
return
if _is_object_with_buffer_protocol(o):
return
if isinstance(o, (int, bool, float, complex)):
return
if isinstance(o, (list, tuple, range)):
for el in o:
_usm_types_walker(el, usm_types_list)
return
raise TypeError
def _device_copy_walker(seq_o, res, _manager):
if isinstance(seq_o, dpt.usm_ndarray):
exec_q = res.sycl_queue
deps = _manager.submitted_events
ht_ev, cpy_ev = ti._copy_usm_ndarray_into_usm_ndarray(
src=seq_o, dst=res, sycl_queue=exec_q, depends=deps
)
_manager.add_event_pair(ht_ev, cpy_ev)
return
if hasattr(seq_o, "__sycl_usm_array_interface__"):
usm_ar = _usm_ndarray_from_suai(seq_o)
exec_q = res.sycl_queue
deps = _manager.submitted_events
ht_ev, cpy_ev = ti._copy_usm_ndarray_into_usm_ndarray(
src=usm_ar, dst=res, sycl_queue=exec_q, depends=deps
)
_manager.add_event_pair(ht_ev, cpy_ev)
return
if isinstance(seq_o, (list, tuple)):
for i, el in enumerate(seq_o):
_device_copy_walker(el, res[i], _manager)
return
raise TypeError
def _copy_through_host_walker(seq_o, usm_res):
if isinstance(seq_o, dpt.usm_ndarray):
if (
dpctl.utils.get_execution_queue(
(
usm_res.sycl_queue,
seq_o.sycl_queue,
)
)
is None
):
usm_res[...] = dpt.asnumpy(seq_o).copy()
return
else:
usm_res[...] = seq_o
if hasattr(seq_o, "__sycl_usm_array_interface__"):
usm_ar = _usm_ndarray_from_suai(seq_o)
if (
dpctl.utils.get_execution_queue(
(
usm_res.sycl_queue,
usm_ar.sycl_queue,
)
)
is None
):
usm_res[...] = dpt.asnumpy(usm_ar).copy()
else:
usm_res[...] = usm_ar
return
if _is_object_with_buffer_protocol(seq_o):
np_ar = np.asarray(seq_o)
usm_res[...] = np_ar
return
if isinstance(seq_o, (list, tuple)):
for i, el in enumerate(seq_o):
_copy_through_host_walker(el, usm_res[i])
return
usm_res[...] = np.asarray(seq_o)
def _asarray_from_seq(
seq_obj,
seq_shape,
seq_dt,
alloc_q,
exec_q,
dtype=None,
usm_type=None,
order="C",
):
"`seq_obj` is a sequence"
if usm_type is None:
usm_types_in_seq = []
_usm_types_walker(seq_obj, usm_types_in_seq)
usm_type = dpctl.utils.get_coerced_usm_type(usm_types_in_seq)
dpctl.utils.validate_usm_type(usm_type)
if dtype is None:
dtype = _map_to_device_dtype(seq_dt, alloc_q)
else:
_mapped_dt = _map_to_device_dtype(dtype, alloc_q)
if _mapped_dt != dtype:
raise ValueError(
f"Device {alloc_q.sycl_device} "
f"does not support {dtype} natively."
)
dtype = _mapped_dt
if order in "KA":
order = "C"
if isinstance(exec_q, dpctl.SyclQueue):
res = dpt.empty(
seq_shape,
dtype=dtype,
usm_type=usm_type,
sycl_queue=alloc_q,
order=order,
)
_manager = dpctl.utils.SequentialOrderManager[exec_q]
_device_copy_walker(seq_obj, res, _manager)
return res
else:
res = dpt.empty(
seq_shape,
dtype=dtype,
usm_type=usm_type,
sycl_queue=alloc_q,
order=order,
)
_copy_through_host_walker(seq_obj, res)
return res
def _asarray_from_seq_single_device(
obj,
seq_shape,
seq_dt,
seq_dev,
dtype=None,
usm_type=None,
sycl_queue=None,
order="C",
):
if sycl_queue is None:
exec_q = seq_dev
alloc_q = seq_dev
else:
exec_q = dpctl.utils.get_execution_queue(
(
sycl_queue,
seq_dev,
)
)
alloc_q = sycl_queue
return _asarray_from_seq(
obj,
seq_shape,
seq_dt,
alloc_q,
exec_q,
dtype=dtype,
usm_type=usm_type,
order=order,
)
[docs]def asarray(
obj,
/,
*,
dtype=None,
device=None,
copy=None,
usm_type=None,
sycl_queue=None,
order="K",
):
"""
Converts input object to :class:`dpctl.tensor.usm_ndarray`.
Args:
obj: Python object to convert. Can be an instance of
:class:`dpctl.tensor.usm_ndarray`,
an object representing SYCL USM allocation and implementing
``__sycl_usm_array_interface__`` protocol, an instance
of :class:`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``, the
need to copy raises :exc:`ValueError`. If ``None``, tries to reuse
existing memory allocations if possible, but allows to perform
a copy otherwise. Default: ``None``
order (``"C"``, ``"F"``, ``"A"``, ``"K"``, optional):
memory layout of the output array. Default: ``"K"``
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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
Array created from input object.
"""
# 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 = dpt.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'."
)
order = order[0].upper()
# 4. Check that usm_type is None, or a valid value
dpctl.utils.validate_usm_type(usm_type, allow_none=True)
# 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(
sycl_queue=sycl_queue, device=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,
)
if hasattr(obj, "__sycl_usm_array_interface__"):
ary = _usm_ndarray_from_suai(obj)
return _asarray_from_usm_ndarray(
ary,
dtype=dtype,
copy=copy,
usm_type=usm_type,
sycl_queue=sycl_queue,
order=order,
)
if 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,
)
if _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,
)
if isinstance(obj, (list, tuple, range)):
if copy is False:
raise ValueError(
"Converting Python sequence to usm_ndarray requires a copy"
)
seq_shape, seq_dt, devs = _array_info_sequence(obj)
if devs == _host_set:
return _asarray_from_numpy_ndarray(
np.asarray(obj, dtype=dtype, order=order),
dtype=dtype,
usm_type=usm_type,
sycl_queue=sycl_queue,
order=order,
)
elif len(devs) == 1:
seq_dev = list(devs)[0]
return _asarray_from_seq_single_device(
obj,
seq_shape,
seq_dt,
seq_dev,
dtype=dtype,
usm_type=usm_type,
sycl_queue=sycl_queue,
order=order,
)
elif len(devs) > 1:
devs = [dev for dev in devs if dev is not None]
if sycl_queue is None:
if len(devs) == 1:
alloc_q = devs[0]
else:
raise dpctl.utils.ExecutionPlacementError(
"Please specify `device` or `sycl_queue` keyword "
"argument to determine where to allocate the "
"resulting array."
)
else:
alloc_q = sycl_queue
return _asarray_from_seq(
obj,
seq_shape,
seq_dt,
alloc_q,
# force copying via host
None,
dtype=dtype,
usm_type=usm_type,
order=order,
)
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, dtype=dtype),
dtype=dtype,
usm_type=usm_type,
sycl_queue=sycl_queue,
order="C",
)
[docs]def empty(
shape,
*,
dtype=None,
order="C",
device=None,
usm_type="device",
sycl_queue=None,
):
"""
Creates :class:`dpctl.tensor.usm_ndarray` from uninitialized
USM allocation.
Args:
shape (Tuple[int], int):
Dimensions of the array to be created.
dtype (optional):
data type of the array. Can be typestring,
a :class:`numpy.dtype` object, :mod:`numpy` char string,
or a NumPy scalar type. The ``None`` value creates an
array of floating point data 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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
Created empty array.
"""
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'."
)
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)
_ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device)
res = dpt.usm_ndarray(
shape,
dtype=dtype,
buffer=usm_type,
order=order,
buffer_ctor_kwargs={"queue": sycl_queue},
)
return res
def _coerce_and_infer_dt(*args, dt, sycl_queue, err_msg, allow_bool=False):
"Deduce arange type from sequence spec"
nd, seq_dt, d = _array_info_sequence(args)
if d != _host_set or nd != (len(args),):
raise ValueError(err_msg)
dt = _get_dtype(dt, sycl_queue, ref_type=seq_dt)
if np.issubdtype(dt, np.integer):
return tuple(int(v) for v in args), dt
if np.issubdtype(dt, np.floating):
return tuple(float(v) for v in args), dt
if np.issubdtype(dt, np.complexfloating):
return tuple(complex(v) for v in args), dt
if allow_bool and dt.char == "?":
return tuple(bool(v) for v in args), dt
raise ValueError(f"Data type {dt} is not supported")
def _round_for_arange(tmp):
k = int(tmp)
if k >= 0 and float(k) < tmp:
tmp = tmp + 1
return tmp
def _get_arange_length(start, stop, step):
"Compute length of arange sequence"
span = stop - start
if hasattr(step, "__float__") and hasattr(span, "__float__"):
return _round_for_arange(span / step)
tmp = span / step
if hasattr(tmp, "__complex__"):
tmp = complex(tmp)
tmp = tmp.real
else:
tmp = float(tmp)
return _round_for_arange(tmp)
def _to_scalar(obj, sc_ty):
"""A way to convert object to NumPy scalar type.
Raises OverflowError if obj can not be represented
using the requested scalar type.
"""
zd_arr = np.asarray(obj, dtype=sc_ty)
return zd_arr[tuple()]
[docs]def arange(
start,
/,
stop=None,
step=1,
*,
dtype=None,
device=None,
usm_type="device",
sycl_queue=None,
):
"""
Returns evenly spaced values within the half-open interval [start, stop)
as a one-dimensional array.
Args:
start:
Starting point of the interval
stop:
Ending point of the interval. Default: ``None``
step: Increment of the returned sequence. Default: ``1``
dtype: Output array data type. Default: ``None``
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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
Array populated with evenly spaced values.
"""
if stop is None:
stop = start
start = 0
if step is None:
step = 1
dpctl.utils.validate_usm_type(usm_type, allow_none=False)
sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device)
is_bool = False
if dtype:
is_bool = (dtype is bool) or (dpt.dtype(dtype) == dpt.bool)
_, dt = _coerce_and_infer_dt(
start,
stop,
step,
dt=dpt.int8 if is_bool else dtype,
sycl_queue=sycl_queue,
err_msg="start, stop, and step must be Python scalars",
allow_bool=False,
)
try:
tmp = _get_arange_length(start, stop, step)
sh = max(int(tmp), 0)
except TypeError:
sh = 0
if is_bool and sh > 2:
raise ValueError("no fill-function for boolean data type")
res = dpt.usm_ndarray(
(sh,),
dtype=dt,
buffer=usm_type,
order="C",
buffer_ctor_kwargs={"queue": sycl_queue},
)
sc_ty = dt.type
_first = _to_scalar(start, sc_ty)
if sh > 1:
_second = _to_scalar(start + step, sc_ty)
if dt in [dpt.uint8, dpt.uint16, dpt.uint32, dpt.uint64]:
int64_ty = dpt.int64.type
_step = int64_ty(_second) - int64_ty(_first)
else:
_step = _second - _first
_step = sc_ty(_step)
else:
_step = sc_ty(1)
_start = _first
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
# populating newly allocated array, no task dependencies
hev, lin_ev = ti._linspace_step(_start, _step, res, sycl_queue)
_manager.add_event_pair(hev, lin_ev)
if is_bool:
res_out = dpt.usm_ndarray(
(sh,),
dtype=dpt.bool,
buffer=usm_type,
order="C",
buffer_ctor_kwargs={"queue": sycl_queue},
)
hev_cpy, cpy_ev = ti._copy_usm_ndarray_into_usm_ndarray(
src=res, dst=res_out, sycl_queue=sycl_queue, depends=[lin_ev]
)
_manager.add_event_pair(hev_cpy, cpy_ev)
return res_out
return res
[docs]def zeros(
shape,
*,
dtype=None,
order="C",
device=None,
usm_type="device",
sycl_queue=None,
):
"""
Returns a new :class:`dpctl.tensor.usm_ndarray` having a specified
shape and filled with zeros.
Args:
shape (Tuple[int], int):
Dimensions of the array to be created.
dtype (optional):
data type of the array. Can be typestring,
a :class:`numpy.dtype` object, :mod:`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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
Constructed array initialized with zeros.
"""
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'."
)
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)
_ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device)
res = dpt.usm_ndarray(
shape,
dtype=dtype,
buffer=usm_type,
order=order,
buffer_ctor_kwargs={"queue": sycl_queue},
)
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
# populating new allocation, no dependent events
hev, zeros_ev = ti._zeros_usm_ndarray(res, sycl_queue)
_manager.add_event_pair(hev, zeros_ev)
return res
[docs]def ones(
shape,
*,
dtype=None,
order="C",
device=None,
usm_type="device",
sycl_queue=None,
):
""" ones(shape, dtype=None, order="C", \
device=None, usm_type="device", sycl_queue=None)
Returns a new :class:`dpctl.tensor.usm_ndarray` having a specified
shape and filled with ones.
Args:
shape (Tuple[int], int):
Dimensions of the array to be created.
dtype (optional):
data type of the array. Can be typestring,
a :class:`numpy.dtype` object, :mod:`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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
Created array initialized with ones.
"""
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'."
)
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(
shape,
dtype=dtype,
buffer=usm_type,
order=order,
buffer_ctor_kwargs={"queue": sycl_queue},
)
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
# populating new allocation, no dependent events
hev, full_ev = ti._full_usm_ndarray(1, res, sycl_queue)
_manager.add_event_pair(hev, full_ev)
return res
def _cast_fill_val(fill_val, dt):
"""
Casts the Python scalar `fill_val` to another Python type coercible to the
requested data type `dt`, if necessary.
"""
val_type = type(fill_val)
if val_type in [float, complex] and np.issubdtype(dt, np.integer):
return int(fill_val.real)
elif val_type is complex and np.issubdtype(dt, np.floating):
return fill_val.real
elif val_type is int and np.issubdtype(dt, np.integer):
return _to_scalar(fill_val, dt)
else:
return fill_val
[docs]def full(
shape,
fill_value,
*,
dtype=None,
order="C",
device=None,
usm_type=None,
sycl_queue=None,
):
"""
Returns a new :class:`dpctl.tensor.usm_ndarray` having a specified
shape and filled with `fill_value`.
Args:
shape (tuple):
Dimensions of the array to be created.
fill_value (int,float,complex,usm_ndarray):
fill value
dtype (optional): data type of the array. Can be typestring,
a :class:`numpy.dtype` object, :mod:`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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
New array initialized with given value.
"""
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'."
)
order = order[0].upper()
dpctl.utils.validate_usm_type(usm_type, allow_none=True)
if isinstance(fill_value, (dpt.usm_ndarray, np.ndarray, tuple, list)):
if (
isinstance(fill_value, dpt.usm_ndarray)
and sycl_queue is None
and device is None
):
sycl_queue = fill_value.sycl_queue
else:
sycl_queue = normalize_queue_device(
sycl_queue=sycl_queue, device=device
)
X = dpt.asarray(
fill_value,
dtype=dtype,
order=order,
usm_type=usm_type,
sycl_queue=sycl_queue,
)
return dpt.copy(dpt.broadcast_to(X, shape), order=order)
sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device)
usm_type = usm_type if usm_type is not None else "device"
dtype = _get_dtype(dtype, sycl_queue, ref_type=type(fill_value))
res = dpt.usm_ndarray(
shape,
dtype=dtype,
buffer=usm_type,
order=order,
buffer_ctor_kwargs={"queue": sycl_queue},
)
fill_value = _cast_fill_val(fill_value, dtype)
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
# populating new allocation, no dependent events
hev, full_ev = ti._full_usm_ndarray(fill_value, res, sycl_queue)
_manager.add_event_pair(hev, full_ev)
return res
def _normalize_order(order, arr):
"""
Utility function for processing the `order` keyword of array-like
constructors, which support `"K"` and `"A"` orders.
"""
arr_flags = arr.flags
f_contig = arr_flags["F"]
c_contig = arr_flags["C"]
if order == "A":
order = "F" if f_contig and not c_contig else "C"
if order == "K" and (f_contig or c_contig):
order = "C" if c_contig else "F"
return order
[docs]def empty_like(
x, /, *, dtype=None, order="K", device=None, usm_type=None, sycl_queue=None
):
"""
Returns an uninitialized :class:`dpctl.tensor.usm_ndarray` with the
same `shape` as the input array `x`.
Args:
x (usm_ndarray):
Input array from which to derive the output array shape.
dtype (optional):
data type of the array. Can be a typestring,
a :class:`numpy.dtype` object, NumPy char string,
or a NumPy scalar type. Default: ``None``
order ("C", "F", "A", or "K"):
memory layout for the array. Default: ``"K"``
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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation. Default: ``None``
Returns:
usm_ndarray:
Created empty array with uninitialized memory.
"""
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 "CcFfAaKk"
):
raise ValueError(
"Unrecognized order keyword value, expecting 'C', 'F', 'A', or 'K'."
)
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)
dtype = dpt.dtype(dtype)
order = _normalize_order(order, x)
if order == "K":
_ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device)
return _empty_like_orderK(x, dtype, usm_type, sycl_queue)
else:
shape = x.shape
_ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device)
res = dpt.usm_ndarray(
shape,
dtype=dtype,
buffer=usm_type,
order=order,
buffer_ctor_kwargs={"queue": sycl_queue},
)
return res
[docs]def zeros_like(
x, /, *, dtype=None, order="K", device=None, usm_type=None, sycl_queue=None
):
"""
Creates :class:`dpctl.tensor.usm_ndarray` from USM allocation
initialized with zeros.
Args:
x (usm_ndarray):
Input array from which to derive the shape of the
output array.
dtype (optional):
data type of the array. Can be typestring,
a :class:`numpy.dtype` object, :mod:`numpy` char string, or a
NumPy scalar type. If `None`, output array has the same data
type as the input array. 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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
New array initialized with zeros.
"""
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 "CcFfAaKk"
):
raise ValueError(
"Unrecognized order keyword value, expecting 'C', 'F', 'A', or 'K'."
)
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)
dtype = dpt.dtype(dtype)
order = _normalize_order(order, x)
if order == "K":
_ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device)
res = _empty_like_orderK(x, dtype, usm_type, sycl_queue)
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
# populating new allocation, no dependent events
hev, full_ev = ti._full_usm_ndarray(0, res, sycl_queue)
_manager.add_event_pair(hev, full_ev)
return res
else:
_ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device)
sh = x.shape
return zeros(
sh,
dtype=dtype,
order=order,
device=device,
usm_type=usm_type,
sycl_queue=sycl_queue,
)
[docs]def ones_like(
x, /, *, dtype=None, order="K", device=None, usm_type=None, sycl_queue=None
):
"""
Returns a new :class:`dpctl.tensor.usm_ndarray` filled with ones and
having the same `shape` as the input array `x`.
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 :class:`numpy.dtype` object, :mod:`numpy` char string,
or a NumPy scalar type. Default: `None`
order ("C", "F", "A", or "K"):
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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
New array initialized with ones.
"""
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 "CcFfAaKk"
):
raise ValueError(
"Unrecognized order keyword value, expecting 'C', 'F', 'A', or 'K'."
)
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)
dtype = dpt.dtype(dtype)
order = _normalize_order(order, x)
if order == "K":
_ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device)
res = _empty_like_orderK(x, dtype, usm_type, sycl_queue)
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
# populating new allocation, no dependent events
hev, full_ev = ti._full_usm_ndarray(1, res, sycl_queue)
_manager.add_event_pair(hev, full_ev)
return res
else:
sh = x.shape
return ones(
sh,
dtype=dtype,
order=order,
device=device,
usm_type=usm_type,
sycl_queue=sycl_queue,
)
[docs]def full_like(
x,
/,
fill_value,
*,
dtype=None,
order="K",
device=None,
usm_type=None,
sycl_queue=None,
):
""" full_like(x, fill_value, dtype=None, order="K", \
device=None, usm_type=None, sycl_queue=None)
Returns a new :class:`dpctl.tensor.usm_ndarray` filled with `fill_value`
and having the same `shape` as the input array `x`.
Args:
x (usm_ndarray): Input array from which to derive the output array
shape.
fill_value: the value to fill output array with
dtype (optional):
data type of the array. Can be typestring,
a :class:`numpy.dtype` object, :mod:`numpy` char string, or a
NumPy scalar type. If ``dtype`` is ``None``, the output array data
type is inferred from ``x``. Default: ``None``
order ("C", "F", "A", or "K"):
memory layout for the array. Default: ``"K"``
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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
New array initialized with given value.
"""
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 "CcFfAaKk"
):
raise ValueError(
"Unrecognized order keyword value, expecting 'C', 'F', 'A', or 'K'."
)
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 = dpt.dtype(dtype)
order = _normalize_order(order, x)
if order == "K":
_ensure_native_dtype_device_support(dtype, sycl_queue.sycl_device)
if isinstance(fill_value, (dpt.usm_ndarray, np.ndarray, tuple, list)):
X = dpt.asarray(
fill_value,
dtype=dtype,
order=order,
usm_type=usm_type,
sycl_queue=sycl_queue,
)
X = dpt.broadcast_to(X, sh)
res = _empty_like_orderK(x, dtype, usm_type, sycl_queue)
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
# order copy after tasks populating X
dep_evs = _manager.submitted_events
hev, copy_ev = ti._copy_usm_ndarray_into_usm_ndarray(
src=X, dst=res, sycl_queue=sycl_queue, depends=dep_evs
)
_manager.add_event_pair(hev, copy_ev)
return res
dtype = _get_dtype(dtype, sycl_queue, ref_type=type(fill_value))
res = _empty_like_orderK(x, dtype, usm_type, sycl_queue)
fill_value = _cast_fill_val(fill_value, dtype)
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
# populating new allocation, no dependent events
hev, full_ev = ti._full_usm_ndarray(fill_value, res, sycl_queue)
_manager.add_event_pair(hev, full_ev)
return res
else:
return full(
sh,
fill_value,
dtype=dtype,
order=order,
device=device,
usm_type=usm_type,
sycl_queue=sycl_queue,
)
[docs]def linspace(
start,
stop,
/,
num,
*,
dtype=None,
device=None,
endpoint=True,
sycl_queue=None,
usm_type="device",
):
"""
linspace(start, stop, num, dtype=None, device=None, endpoint=True, \
sycl_queue=None, usm_type="device")
Returns :class:`dpctl.tensor.usm_ndarray` array populated with
evenly spaced numbers of specified interval.
Args:
start:
the start of the interval.
stop:
the end of the interval. If the ``endpoint`` is ``False``, the
function generates ``num+1`` evenly spaced points starting
with ``start`` and ending with ``stop`` and exclude the
``stop`` from the returned array such that the returned array
consists of evenly spaced numbers over the half-open interval
``[start, stop)``. If ``endpoint`` is ``True``, the output
array consists of evenly spaced numbers over the closed
interval ``[start, stop]``. Default: ``True``
num (int):
number of samples. Must be a non-negative integer; otherwise,
the function raises ``ValueError`` exception.
dtype:
output array data type. Should be a floating data type.
If ``dtype`` is ``None``, the output array must be the default
floating point data type for target device.
Default: ``None``
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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
endpoint: boolean indicating whether to include ``stop`` in the
interval. Default: ``True``
Returns:
usm_ndarray:
Array populated with evenly spaced numbers in the requested
interval.
"""
sycl_queue = normalize_queue_device(sycl_queue=sycl_queue, device=device)
dpctl.utils.validate_usm_type(usm_type, allow_none=False)
if endpoint not in [True, False]:
raise TypeError("endpoint keyword argument must be of boolean type")
num = operator.index(num)
if num < 0:
raise ValueError("Number of points must be non-negative")
_, dt = _coerce_and_infer_dt(
start,
stop,
dt=dtype,
sycl_queue=sycl_queue,
err_msg="start and stop must be Python scalars.",
allow_bool=True,
)
int_dt = None
if np.issubdtype(dt, np.integer):
if dtype is not None:
int_dt = dt
dt = ti.default_device_fp_type(sycl_queue)
dt = dpt.dtype(dt)
start = float(start)
stop = float(stop)
res = dpt.empty(num, dtype=dt, usm_type=usm_type, sycl_queue=sycl_queue)
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
hev, la_ev = ti._linspace_affine(
start, stop, dst=res, include_endpoint=endpoint, sycl_queue=sycl_queue
)
_manager.add_event_pair(hev, la_ev)
return res if int_dt is None else dpt.astype(res, int_dt)
[docs]def eye(
n_rows,
n_cols=None,
/,
*,
k=0,
dtype=None,
order="C",
device=None,
usm_type="device",
sycl_queue=None,
):
"""
eye(n_rows, n_cols=None, /, *, k=0, dtype=None, \
device=None, usm_type="device", sycl_queue=None)
Creates :class:`dpctl.tensor.usm_ndarray` with ones on the `k`-th
diagonal.
Args:
n_rows (int):
number of rows in the output array.
n_cols (int, optional):
number of columns in the output array. If ``None``,
``n_cols = n_rows``. Default: ``None``
k (int):
index of the diagonal, with ``0`` as the main diagonal.
A positive value of ``k`` is a superdiagonal, a negative value
is a subdiagonal.
Raises :exc:`TypeError` if ``k`` is not an integer.
Default: ``0``
dtype (optional):
data type of the array. Can be typestring,
a :class:`numpy.dtype` object, :mod:`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 :class:`dpctl.tensor.Device` object
returned by :attr:`dpctl.tensor.usm_ndarray.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 complementary arguments, i.e. use one or another.
If both are specified, a :exc:`TypeError` is raised unless both
imply the same underlying SYCL queue to be used. If both are
``None``, a cached queue targeting default-selected device is
used for allocation and population. Default: ``None``
Returns:
usm_ndarray:
A diagonal matrix.
"""
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'."
)
order = order[0].upper()
n_rows = operator.index(n_rows)
n_cols = n_rows if n_cols is None else operator.index(n_cols)
k = operator.index(k)
if k >= n_cols or -k >= n_rows:
return dpt.zeros(
(n_rows, n_cols),
dtype=dtype,
order=order,
device=device,
usm_type=usm_type,
sycl_queue=sycl_queue,
)
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,
buffer=usm_type,
order=order,
buffer_ctor_kwargs={"queue": sycl_queue},
)
if n_rows != 0 and n_cols != 0:
_manager = dpctl.utils.SequentialOrderManager[sycl_queue]
hev, eye_ev = ti._eye(k, dst=res, sycl_queue=sycl_queue)
_manager.add_event_pair(hev, eye_ev)
return res
[docs]def tril(x, /, *, k=0):
"""
Returns the lower triangular part of a matrix (or a stack of matrices)
``x``.
The lower triangular part of the matrix is defined as the elements on and
below the specified diagonal ``k``.
Args:
x (usm_ndarray):
Input array
k (int, optional):
Specifies the diagonal above which to set
elements to zero. If ``k = 0``, the diagonal is the main diagonal.
If ``k < 0``, the diagonal is below the main diagonal.
If ``k > 0``, the diagonal is above the main diagonal.
Default: ``0``
Returns:
usm_ndarray:
A lower-triangular array or a stack of lower-triangular arrays.
"""
if not isinstance(x, dpt.usm_ndarray):
raise TypeError(
"Expected argument of type dpctl.tensor.usm_ndarray, "
f"got {type(x)}."
)
k = operator.index(k)
order = "F" if (x.flags.f_contiguous) else "C"
shape = x.shape
nd = x.ndim
if nd < 2:
raise ValueError("Array dimensions less than 2.")
q = x.sycl_queue
if k >= shape[nd - 1] - 1:
res = dpt.empty(
x.shape,
dtype=x.dtype,
order=order,
usm_type=x.usm_type,
sycl_queue=q,
)
_manager = dpctl.utils.SequentialOrderManager[q]
dep_evs = _manager.submitted_events
hev, cpy_ev = ti._copy_usm_ndarray_into_usm_ndarray(
src=x, dst=res, sycl_queue=q, depends=dep_evs
)
_manager.add_event_pair(hev, cpy_ev)
elif k < -shape[nd - 2]:
res = dpt.zeros(
x.shape,
dtype=x.dtype,
order=order,
usm_type=x.usm_type,
sycl_queue=q,
)
else:
res = dpt.empty(
x.shape,
dtype=x.dtype,
order=order,
usm_type=x.usm_type,
sycl_queue=q,
)
_manager = dpctl.utils.SequentialOrderManager[q]
dep_evs = _manager.submitted_events
hev, tril_ev = ti._tril(
src=x, dst=res, k=k, sycl_queue=q, depends=dep_evs
)
_manager.add_event_pair(hev, tril_ev)
return res
[docs]def triu(x, /, *, k=0):
"""
Returns the upper triangular part of a matrix (or a stack of matrices)
``x``.
The upper triangular part of the matrix is defined as the elements on and
above the specified diagonal ``k``.
Args:
x (usm_ndarray):
Input array
k (int, optional):
Specifies the diagonal below which to set
elements to zero. If ``k = 0``, the diagonal is the main diagonal.
If ``k < 0``, the diagonal is below the main diagonal.
If ``k > 0``, the diagonal is above the main diagonal.
Default: ``0``
Returns:
usm_ndarray:
An upper-triangular array or a stack of upper-triangular arrays.
"""
if not isinstance(x, dpt.usm_ndarray):
raise TypeError(
"Expected argument of type dpctl.tensor.usm_ndarray, "
f"got {type(x)}."
)
k = operator.index(k)
order = "F" if (x.flags.f_contiguous) else "C"
shape = x.shape
nd = x.ndim
if nd < 2:
raise ValueError("Array dimensions less than 2.")
q = x.sycl_queue
if k > shape[nd - 1]:
res = dpt.zeros(
x.shape,
dtype=x.dtype,
order=order,
usm_type=x.usm_type,
sycl_queue=q,
)
elif k <= -shape[nd - 2] + 1:
res = dpt.empty(
x.shape,
dtype=x.dtype,
order=order,
usm_type=x.usm_type,
sycl_queue=q,
)
_manager = dpctl.utils.SequentialOrderManager[q]
dep_evs = _manager.submitted_events
hev, cpy_ev = ti._copy_usm_ndarray_into_usm_ndarray(
src=x, dst=res, sycl_queue=q, depends=dep_evs
)
_manager.add_event_pair(hev, cpy_ev)
else:
res = dpt.empty(
x.shape,
dtype=x.dtype,
order=order,
usm_type=x.usm_type,
sycl_queue=q,
)
_manager = dpctl.utils.SequentialOrderManager[q]
dep_evs = _manager.submitted_events
hev, triu_ev = ti._triu(
src=x, dst=res, k=k, sycl_queue=q, depends=dep_evs
)
_manager.add_event_pair(hev, triu_ev)
return res
[docs]def meshgrid(*arrays, indexing="xy"):
"""
Creates list of :class:`dpctl.tensor.usm_ndarray` coordinate matrices
from vectors.
Args:
arrays (usm_ndarray):
an arbitrary number of one-dimensional arrays
representing grid coordinates. Each array should have the same
numeric data type.
indexing (``"xy"``, or ``"ij"``):
Cartesian (``"xy"``) or matrix (``"ij"``) indexing of output.
If provided zero or one one-dimensional vector(s) (i.e., the
zero- and one-dimensional cases, respectively), the ``indexing``
keyword has no effect and should be ignored. Default: ``"xy"``
Returns:
List[array]:
list of ``N`` arrays, where ``N`` is the number of
provided one-dimensional input arrays. Each returned array must
have rank ``N``.
For a set of ``n`` vectors with lengths ``N0``, ``N1``, ``N2``, ...
The cartesian indexing results in arrays of shape
``(N1, N0, N2, ...)``, while the
matrix indexing results in arrays of shape
``(N0, N1, N2, ...)``.
Default: ``"xy"``.
Raises:
ValueError: If vectors are not of the same data type, or are not
one-dimensional.
"""
ref_dt = None
ref_unset = True
for array in arrays:
if not isinstance(array, dpt.usm_ndarray):
raise TypeError(
f"Expected instance of dpt.usm_ndarray, got {type(array)}."
)
if array.ndim != 1:
raise ValueError("All arrays must be one-dimensional.")
if ref_unset:
ref_unset = False
ref_dt = array.dtype
else:
if not ref_dt == array.dtype:
raise ValueError(
"All arrays must be of the same numeric data type."
)
if indexing not in ["xy", "ij"]:
raise ValueError(
"Unrecognized indexing keyword value, expecting 'xy' or 'ij.'"
)
n = len(arrays)
if n == 0:
return []
sh = (-1,) + (1,) * (n - 1)
res = []
if n > 1 and indexing == "xy":
res.append(dpt.reshape(arrays[0], (1, -1) + sh[2:], copy=True))
res.append(dpt.reshape(arrays[1], sh, copy=True))
arrays, sh = arrays[2:], sh[-2:] + sh[:-2]
for array in arrays:
res.append(dpt.reshape(array, sh, copy=True))
sh = sh[-1:] + sh[:-1]
output = dpt.broadcast_arrays(*res)
return output