• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IntelPython / dpctl / 12752491468

13 Jan 2025 05:16PM UTC coverage: 87.716% (+0.001%) from 87.715%
12752491468

Pull #1964

github

web-flow
Merge 2ec86ffc7 into c354cd886
Pull Request #1964: Usm ndrray array protocol must raise

3138 of 3658 branches covered (85.78%)

Branch coverage included in aggregate %.

3 of 3 new or added lines in 1 file covered. (100.0%)

9 existing lines in 1 file now uncovered.

11871 of 13453 relevant lines covered (88.24%)

7185.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.36
/dpctl/tensor/_usmarray.pyx
1
#                       Data Parallel Control (dpctl)
1✔
2
#
3
#  Copyright 2020-2025 Intel Corporation
4
#
5
#  Licensed under the Apache License, Version 2.0 (the "License");
6
#  you may not use this file except in compliance with the License.
7
#  You may obtain a copy of the License at
8
#
9
#     http://www.apache.org/licenses/LICENSE-2.0
10
#
11
#  Unless required by applicable law or agreed to in writing, software
12
#  distributed under the License is distributed on an "AS IS" BASIS,
13
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
#  See the License for the specific language governing permissions and
15
#  limitations under the License.
16

17
# distutils: language = c++
18
# cython: language_level=3
19
# cython: linetrace=True
20

21
import sys
1✔
22

23
import numpy as np
1✔
24

25
import dpctl
1✔
26
import dpctl.memory as dpmem
1✔
27

28
from .._backend cimport DPCTLSyclUSMRef
29
from .._sycl_device_factory cimport _cached_default_device
30

31
from ._data_types import bool as dpt_bool
1✔
32
from ._device import Device
1✔
33
from ._print import usm_ndarray_repr, usm_ndarray_str
1✔
34

35
from cpython.mem cimport PyMem_Free
36
from cpython.tuple cimport PyTuple_New, PyTuple_SetItem
37

38
cimport dpctl as c_dpctl
39
cimport dpctl.memory as c_dpmem
40
cimport dpctl.tensor._dlpack as c_dlpack
41

42
from ._dlpack import get_build_dlpack_version
1✔
43

44
from .._sycl_device_factory cimport _cached_default_device
45

46
from enum import IntEnum
1✔
47

48
import dpctl.tensor._flags as _flags
1✔
49
from dpctl.tensor._tensor_impl import default_device_fp_type
1✔
50

51
include "_stride_utils.pxi"
52
include "_types.pxi"
53
include "_slicing.pxi"
54

55

56
class DLDeviceType(IntEnum):
1✔
57
    """
58
    An :class:`enum.IntEnum` for the types of DLDevices supported by the DLPack protocol.
59

60
        ``kDLCPU``:
61
            CPU (host) device
62
        ``kDLCUDA``:
63
            CUDA GPU device
64
        ``kDLCUDAHost``:
65
            Pinned CUDA CPU memory by cudaMallocHost
66
        ``kDLOpenCL``:
67
            OpenCL device
68
        ``kDLVulkan``:
69
            Vulkan buffer
70
        ``kDLMetal``:
71
            Metal for Apple GPU
72
        ``kDLVPI``:
73
            Verilog simulator buffer
74
        ``kDLROCM``:
75
            ROCm GPU device
76
        ``kDLROCMHost``:
77
            Pinned ROCm CPU memory allocated by hipMallocHost
78
        ``kDLExtDev``:
79
            Reserved extension device type used to test new devices
80
        ``kDLCUDAManaged``:
81
            CUDA managed/unified memory allocated by cudaMallocManaged
82
        ``kDLOneAPI``:
83
            Unified shared memory allocated on a oneAPI non-partitioned device
84
        ``kDLWebGPU``:
85
            Device support for WebGPU standard
86
        ``kDLHexagon``:
87
            Qualcomm Hexagon DSP
88
        ``kDLMAIA``:
89
            Microsoft MAIA device
90
    """
91
    kDLCPU = c_dlpack.device_CPU
1✔
92
    kDLCUDA = c_dlpack.device_CUDA
1✔
93
    kDLCUDAHost = c_dlpack.device_CUDAHost
1✔
94
    kDLCUDAManaged = c_dlpack.device_CUDAManaged
1✔
95
    kDLROCM = c_dlpack.device_DLROCM
1✔
96
    kDLROCMHost = c_dlpack.device_ROCMHost
1✔
97
    kDLOpenCL = c_dlpack.device_OpenCL
1✔
98
    kDLVulkan = c_dlpack.device_Vulkan
1✔
99
    kDLMetal = c_dlpack.device_Metal
1✔
100
    kDLVPI = c_dlpack.device_VPI
1✔
101
    kDLOneAPI = c_dlpack.device_OneAPI
1✔
102
    kDLWebGPU = c_dlpack.device_WebGPU
1✔
103
    kDLHexagon = c_dlpack.device_Hexagon
1✔
104
    kDLMAIA = c_dlpack.device_MAIA
1✔
105

106

107
cdef class InternalUSMArrayError(Exception):
108
    """
109
    An InternalUSMArrayError exception is raised when internal
110
    inconsistency has been detected in :class:`.usm_ndarray`.
111
    """
112
    pass
113

114

115
cdef object _as_zero_dim_ndarray(object usm_ary):
1✔
116
    "Convert size-1 array to NumPy 0d array"
117
    mem_view = dpmem.as_usm_memory(usm_ary)
1✔
118
    usm_ary.sycl_queue.wait()
1✔
119
    host_buf = mem_view.copy_to_host()
1✔
120
    view = host_buf.view(usm_ary.dtype)
1✔
121
    view.shape = tuple()
1✔
122
    return view
1✔
123

124

125
cdef int _copy_writable(int lhs_flags, int rhs_flags):
1✔
126
    "Copy the WRITABLE flag to lhs_flags from rhs_flags"
127
    return (lhs_flags & ~USM_ARRAY_WRITABLE) | (rhs_flags & USM_ARRAY_WRITABLE)
1✔
128

129

130
cdef bint _is_host_cpu(object dl_device):
1✔
131
    "Check if dl_device denotes (kDLCPU, 0)"
132
    cdef object dl_type
133
    cdef object dl_id
134
    cdef Py_ssize_t n_elems = -1
1✔
135

136
    try:
1✔
137
        n_elems = len(dl_device)
1✔
138
    except TypeError:
×
139
        pass
140

141
    if n_elems != 2:
1✔
142
        return False
×
143

144
    dl_type = dl_device[0]
1✔
145
    dl_id = dl_device[1]
1✔
146
    if isinstance(dl_type, str):
1✔
147
        return (dl_type == "kDLCPU" and dl_id == 0)
1✔
148

149
    return (dl_type == DLDeviceType.kDLCPU) and (dl_id == 0)
1✔
150

151

152
cdef class usm_ndarray:
153
    """ usm_ndarray(shape, dtype=None, strides=None, buffer="device", \
154
           offset=0, order="C", buffer_ctor_kwargs=dict(), \
155
           array_namespace=None)
156

157
    An array object represents a multidimensional tensor of numeric
158
    elements stored in a USM allocation on a SYCL device.
159

160
    Arg:
161
        shape (int, tuple):
162
            Shape of the array to be created.
163
        dtype (str, dtype):
164
            Array data type, i.e. the type of array elements.
165
            If ``dtype`` has the value ``None``, it is determined by default
166
            floating point type supported by target device.
167
            The supported types are
168

169
                ``bool``:
170
                    boolean type
171
                ``int8``, ``int16``, ``int32``, ``int64``:
172
                    signed integer types
173
                ``uint8``, ``uint16``, ``uint32``, ``uint64``:
174
                    unsigned integer types
175
                ``float16``:
176
                    half-precision floating type,
177
                    supported if target device's property
178
                    ``has_aspect_fp16`` is ``True``
179
                ``float32``, ``complex64``:
180
                    single-precision real and complex floating types
181
                ``float64``, ``complex128``:
182
                    double-precision real and complex floating
183
                    types, supported if target device's property
184
                    ``has_aspect_fp64`` is ``True``.
185

186
            Default: ``None``.
187
        strides (tuple, optional):
188
            Strides of the array to be created in elements.
189
            If ``strides`` has the value ``None``, it is determined by the
190
            ``shape`` of the array and the requested ``order``.
191
            Default: ``None``.
192
        buffer (str, object, optional):
193
            A string corresponding to the type of USM allocation to make,
194
            or a Python object representing a USM memory allocation, i.e.
195
            :class:`dpctl.memory.MemoryUSMDevice`,
196
            :class:`dpctl.memory.MemoryUSMShared`, or
197
            :class:`dpctl.memory.MemoryUSMHost`. Recognized strings are
198
            ``"device"``, ``"shared"``, or ``"host"``. Additional arguments to
199
            the USM memory allocators can be passed in a dictionary specified
200
            via ``buffer_ctor_kwrds`` keyword parameter.
201
            Default: ``"device"``.
202
        offset (int, optional):
203
            Offset of the array element with all zero indexes relative to the
204
            start of the provided `buffer` in elements. The argument is ignored
205
            if the ``buffer`` value is a string and the memory is allocated by
206
            the constructor. Default: ``0``.
207
        order ({"C", "F"}, optional):
208
            The memory layout of the array when constructing using a new
209
            allocation. Value ``"C"`` corresponds to C-contiguous, or row-major
210
            memory layout, while value ``"F"`` corresponds to F-contiguous, or
211
            column-major layout. Default: ``"C"``.
212
        buffer_ctor_kwargs (dict, optional):
213
            Dictionary with keyword parameters to use when creating a new USM
214
            memory allocation. See :class:`dpctl.memory.MemoryUSMShared` for
215
            supported keyword arguments.
216
        array_namespace (module, optional):
217
            Array namespace module associated with this array.
218
            Default: ``None``.
219

220
    ``buffer`` can be ``"shared"``, ``"host"``, ``"device"`` to allocate
221
    new device memory by calling respective constructor with
222
    the specified ``buffer_ctor_kwrds``; ``buffer`` can be an
223
    instance of :class:`dpctl.memory.MemoryUSMShared`,
224
    :class:`dpctl.memory.MemoryUSMDevice`, or
225
    :class:`dpctl.memory.MemoryUSMHost`; ``buffer`` can also be
226
    another :class:`dpctl.tensor.usm_ndarray` instance, in which case its
227
    underlying ``MemoryUSM*`` buffer is used.
228
    """
229

230
    cdef void _reset(usm_ndarray self):
1✔
231
        """
232
        Initializes member fields
233
        """
234
        self.base_ = None
1✔
235
        self.array_namespace_ = None
1✔
236
        self.nd_ = -1
1✔
237
        self.data_ = <char *>0
1✔
238
        self.shape_ = <Py_ssize_t *>0
1✔
239
        self.strides_ = <Py_ssize_t *>0
1✔
240
        self.flags_ = 0
1✔
241

242
    cdef void _cleanup(usm_ndarray self):
1✔
243
        if (self.shape_):
1✔
244
            PyMem_Free(self.shape_)
1✔
245
        if (self.strides_):
1✔
246
            PyMem_Free(self.strides_)
1✔
247
        self._reset()
1✔
248

249
    def __cinit__(self, shape, dtype=None, strides=None, buffer='device',
250
                  Py_ssize_t offset=0, order='C',
251
                  buffer_ctor_kwargs=dict(),
1✔
252
                  array_namespace=None):
253
        """
254
        strides and offset must be given in units of array elements.
255
        buffer can be strings ('device'|'shared'|'host' to allocate new memory)
256
        or ``dpctl.memory.MemoryUSM*`` buffers, or ``usm_ndarray`` instances.
257
        """
258
        cdef int nd = 0
1✔
259
        cdef int typenum = 0
1✔
260
        cdef int itemsize = 0
1✔
261
        cdef int err = 0
1✔
262
        cdef int contig_flag = 0
1✔
263
        cdef int writable_flag = USM_ARRAY_WRITABLE
1✔
264
        cdef Py_ssize_t *shape_ptr = NULL
1✔
265
        cdef Py_ssize_t ary_nelems = 0
1✔
266
        cdef Py_ssize_t ary_nbytes = 0
1✔
267
        cdef Py_ssize_t *strides_ptr = NULL
1✔
268
        cdef Py_ssize_t _offset = offset
1✔
269
        cdef Py_ssize_t ary_min_displacement = 0
1✔
270
        cdef Py_ssize_t ary_max_displacement = 0
1✔
271
        cdef bint is_fp64 = False
1✔
272
        cdef bint is_fp16 = False
1✔
273

274
        self._reset()
1✔
275
        if not isinstance(shape, (list, tuple)):
1✔
276
            if hasattr(shape, 'tolist'):
1✔
277
                fn = getattr(shape, 'tolist')
1✔
278
                if callable(fn):
1✔
279
                    shape = shape.tolist()
1✔
280
            if not isinstance(shape, (list, tuple)):
1✔
281
                try:
1✔
282
                    <Py_ssize_t> shape
1✔
283
                    shape = [shape, ]
1✔
284
                except Exception as e:
1✔
285
                    raise TypeError(
1✔
286
                        "Argument shape must a non-negative integer, "
287
                        "or a list/tuple of such integers."
288
                    ) from e
1✔
289
        nd = len(shape)
1✔
290
        if dtype is None:
1✔
291
            if isinstance(buffer, (dpmem._memory._Memory, usm_ndarray)):
1✔
292
                q = buffer.sycl_queue
1✔
293
            else:
294
                q = buffer_ctor_kwargs.get("queue")
1✔
295
            if q is not None:
1✔
296
                dtype = default_device_fp_type(q)
1✔
297
            else:
298
                dev = _cached_default_device()
1✔
299
                dtype = "f8" if dev.has_aspect_fp64 else "f4"
1✔
300
        typenum = dtype_to_typenum(dtype)
1✔
301
        if (typenum < 0):
1✔
302
            if typenum == -2:
1✔
303
                raise ValueError("Data type '" + str(dtype) + "' can only have native byteorder.")
1✔
304
            elif typenum == -1:
305
                raise ValueError("Data type '" + str(dtype) + "' is not understood.")
1✔
306
            raise TypeError(f"Expected string or a dtype object, got {type(dtype)}")
1✔
307
        itemsize = type_bytesize(typenum)
1✔
308
        if (itemsize < 1):
1✔
309
            raise TypeError("dtype=" + np.dtype(dtype).name + " is not supported.")
1✔
310
        # allocate host C-arrays for shape, strides
311
        err = _from_input_shape_strides(
1✔
312
            nd, shape, strides, itemsize, <char> ord(order),
1✔
313
            &shape_ptr, &strides_ptr, &ary_nelems,
314
            &ary_min_displacement, &ary_max_displacement, &contig_flag
315
        )
316
        if (err):
1✔
317
            self._cleanup()
1✔
318
            if err == ERROR_MALLOC:
1✔
319
                raise MemoryError("Memory allocation for shape/strides "
×
320
                                  "array failed.")
321
            elif err == ERROR_INCORRECT_ORDER:
1✔
322
                raise ValueError(
1✔
323
                    "Unsupported order='{}' given. "
324
                    "Supported values are 'C' or 'F'.".format(order))
1✔
325
            elif err == ERROR_UNEXPECTED_STRIDES:
1✔
326
                raise ValueError(
1✔
327
                    "strides={} is not understood".format(strides))
1✔
328
            else:
329
                raise InternalUSMArrayError(
×
330
                    " .. while processing shape and strides.")
331
        ary_nbytes = (ary_max_displacement -
332
                      ary_min_displacement + 1) * itemsize
1✔
333
        if isinstance(buffer, dpmem._memory._Memory):
1✔
334
            _buffer = buffer
1✔
335
        elif isinstance(buffer, (str, bytes)):
1✔
336
            if isinstance(buffer, bytes):
1✔
337
                buffer = buffer.decode("UTF-8")
1✔
338
            _offset = -ary_min_displacement
1✔
339
            if (buffer == "shared"):
1✔
340
                _buffer = dpmem.MemoryUSMShared(ary_nbytes,
1✔
341
                                                **buffer_ctor_kwargs)
1✔
342
            elif (buffer == "device"):
1✔
343
                _buffer = dpmem.MemoryUSMDevice(ary_nbytes,
1✔
344
                                                **buffer_ctor_kwargs)
1✔
345
            elif (buffer == "host"):
1✔
346
                _buffer = dpmem.MemoryUSMHost(ary_nbytes,
1✔
347
                                              **buffer_ctor_kwargs)
1✔
348
            else:
349
                self._cleanup()
1✔
350
                raise ValueError(
1✔
351
                    ("buffer='{}' is not understood. "
352
                    "Recognized values are 'device', 'shared',  'host', "
353
                    "an instance of `MemoryUSM*` object, or a usm_ndarray"
354
                     "").format(buffer))
1✔
355
        elif isinstance(buffer, usm_ndarray):
1✔
356
            if not buffer.flags.writable:
1✔
357
                writable_flag = 0
1✔
358
            _buffer = buffer.usm_data
1✔
359
        else:
360
            self._cleanup()
1✔
361
            raise ValueError("buffer='{}' was not understood.".format(buffer))
1✔
362
        if (_offset + ary_min_displacement < 0 or
1✔
363
           (_offset + ary_max_displacement + 1) * itemsize > _buffer.nbytes):
1✔
364
            self._cleanup()
1✔
365
            raise ValueError(("buffer='{}' can not accommodate "
1✔
366
                              "the requested array.").format(buffer))
1✔
367
        is_fp64 = (typenum == UAR_DOUBLE or typenum == UAR_CDOUBLE)
1✔
368
        is_fp16 = (typenum == UAR_HALF)
1✔
369
        if (is_fp64 or is_fp16):
1✔
370
            if ((is_fp64 and not _buffer.sycl_device.has_aspect_fp64) or
1✔
371
                (is_fp16 and not _buffer.sycl_device.has_aspect_fp16)
1✔
372
            ):
373
                raise ValueError(
×
374
                    f"Device {_buffer.sycl_device.name} does"
×
375
                    f" not support {dtype} natively."
×
376
                )
377
        self.base_ = _buffer
1✔
378
        self.data_ = (<char *> (<size_t> _buffer._pointer)) + itemsize * _offset
1✔
379
        self.shape_ = shape_ptr
1✔
380
        self.strides_ = strides_ptr
1✔
381
        self.typenum_ = typenum
1✔
382
        self.flags_ = (contig_flag | writable_flag)
1✔
383
        self.nd_ = nd
1✔
384
        self.array_namespace_ = array_namespace
1✔
385

386
    def __dealloc__(self):
387
        self._cleanup()
1✔
388

389
    @property
390
    def _pointer(self):
391
        """
392
        Returns USM pointer to the start of array (element with zero
393
        multi-index) encoded as integer.
394
        """
395
        return <size_t> self.get_data()
1✔
396

397
    cdef Py_ssize_t get_offset(self) except *:
1✔
398
        cdef char *mem_ptr = NULL
1✔
399
        cdef char *ary_ptr = self.get_data()
1✔
400
        mem_ptr = <char *>(<size_t> self.base_._pointer)
1✔
401
        byte_offset = ary_ptr - mem_ptr
1✔
402
        item_size = self.get_itemsize()
1✔
403
        if (byte_offset % item_size):
1✔
404
            raise InternalUSMArrayError(
×
405
                "byte_offset is not a multiple of item_size.")
406
        return byte_offset // item_size
1✔
407

408
    @property
409
    def _element_offset(self):
410
        """Returns the offset of the zero-index element of the array, in elements,
411
        relative to the start of memory allocation"""
412
        return self.get_offset()
1✔
413

414
    @property
415
    def _byte_bounds(self):
416
        """Returns a 2-tuple with pointers to the end-points of the array
417

418
        :Example:
419

420
            .. code-block:: python
421

422
                from dpctl import tensor
423

424
                x = tensor.ones((3, 10, 7))
425
                y = tensor.flip(x[:, 1::2], axis=1)
426

427
                beg_p, end_p = y._byte_bounds
428
                # Bytes taken to store this array
429
                bytes_extent = end_p - beg_p
430

431
                # C-contiguous copy is more compact
432
                yc = tensor.copy(y, order="C")
433
                beg_pc, end_pc = yc._byte_bounds
434
                assert bytes_extent < end_pc - beg_pc
435
        """
436
        cdef Py_ssize_t min_disp = 0
1✔
437
        cdef Py_ssize_t max_disp = 0
1✔
438
        cdef Py_ssize_t step_ = 0
1✔
439
        cdef Py_ssize_t dim_ = 0
1✔
440
        cdef int it = 0
1✔
441
        cdef Py_ssize_t _itemsize = self.get_itemsize()
1✔
442

443
        if ((self.flags_ & USM_ARRAY_C_CONTIGUOUS) or (self.flags_ & USM_ARRAY_F_CONTIGUOUS)):
1✔
444
            return (
1✔
445
                self._pointer,
1✔
446
                self._pointer + shape_to_elem_count(self.nd_, self.shape_) * _itemsize
1✔
447
            )
448

449
        for it in range(self.nd_):
1✔
450
           dim_ = self.shape[it]
1✔
451
           if dim_ > 0:
1✔
452
               step_ = self.strides[it]
1✔
453
               if step_ > 0:
1✔
454
                   max_disp += step_ * (dim_ - 1)
1✔
455
               else:
456
                   min_disp += step_ * (dim_ - 1)
1✔
457

458
        return (
1✔
459
            self._pointer + min_disp * _itemsize,
1✔
460
            self._pointer + (max_disp + 1) * _itemsize
1✔
461
        )
462

463

464
    cdef char* get_data(self):
1✔
465
        """Returns the USM pointer for this array."""
466
        return self.data_
1✔
467

468
    cdef int get_ndim(self):
1✔
469
        """
470
        Returns the number of indices needed to address
471
        an element of this array.
472
        """
473
        return self.nd_
1✔
474

475
    cdef Py_ssize_t* get_shape(self):
1✔
476
        """
477
        Returns pointer to shape C-array for this array.
478

479
        C-array has at least ``ndim`` non-negative elements,
480
        which determine the range of permissible indices
481
        addressing individual elements of this array.
482
        """
483
        return self.shape_
1✔
484

485
    cdef Py_ssize_t* get_strides(self):
1✔
486
        """
487
        Returns pointer to strides C-array for this array.
488

489
        The pointer can be NULL (contiguous array), or the
490
        array size is at least ``ndim`` elements
491
        """
492
        return self.strides_
1✔
493

494
    cdef int get_typenum(self):
1✔
495
        """Returns typenum corresponding to values of this array"""
496
        return self.typenum_
1✔
497

498
    cdef int get_itemsize(self):
1✔
499
        """
500
        Returns itemsize of this arrays in bytes
501
        """
502
        return type_bytesize(self.typenum_)
1✔
503

504
    cdef int get_flags(self):
1✔
505
        """Returns flags of this array"""
506
        return self.flags_
1✔
507

508
    cdef object get_base(self):
1✔
509
        """Returns the object owning the USM data addressed by this array"""
510
        return self.base_
1✔
511

512
    cdef c_dpctl.SyclQueue get_sycl_queue(self):
1✔
513
        cdef c_dpmem._Memory mem
514
        if not isinstance(self.base_, dpctl.memory._Memory):
1✔
515
            raise InternalUSMArrayError(
×
516
                "This array has unexpected memory owner"
517
            )
518
        mem = <c_dpmem._Memory> self.base_
1✔
519
        return mem.queue
1✔
520

521
    cdef c_dpctl.DPCTLSyclQueueRef get_queue_ref(self) except *:
1✔
522
        """
523
        Returns a copy of DPCTLSyclQueueRef associated with array
524
        """
525
        cdef c_dpctl.SyclQueue q = self.get_sycl_queue()
1✔
526
        cdef c_dpctl.DPCTLSyclQueueRef QRef = q.get_queue_ref()
1✔
527
        cdef c_dpctl.DPCTLSyclQueueRef QRefCopy = NULL
1✔
528
        if QRef is not NULL:
1✔
529
            QRefCopy = c_dpctl.DPCTLQueue_Copy(QRef)
1✔
530
            return QRefCopy
1✔
531
        else:
532
            raise InternalUSMArrayError(
×
533
                "Memory owner of this array is corrupted"
534
            )
535

536
    @property
537
    def __sycl_usm_array_interface__(self):
538
        """
539
        Gives ``__sycl_usm_array_interface__`` dictionary describing
540
        the array.
541
        """
542
        cdef Py_ssize_t byte_offset = -1
1✔
543
        cdef int item_size = -1
1✔
544
        cdef Py_ssize_t elem_offset = -1
1✔
545
        cdef char *mem_ptr = NULL
1✔
546
        cdef char *ary_ptr = NULL
1✔
547
        if (not isinstance(self.base_, dpmem._memory._Memory)):
1✔
548
            raise InternalUSMArrayError(
×
549
                "Invalid instance of usm_ndarray encountered. "
550
                "Private field base_ has an unexpected type {}.".format(
×
551
                    type(self.base_)
×
552
                )
553
            )
554
        ary_iface = self.base_.__sycl_usm_array_interface__
1✔
555
        mem_ptr = <char *>(<size_t> ary_iface['data'][0])
1✔
556
        ary_ptr = <char *>(<size_t> self.data_)
1✔
557
        ro_flag = False if (self.flags_ & USM_ARRAY_WRITABLE) else True
1✔
558
        ary_iface['data'] = (<size_t> mem_ptr, ro_flag)
1✔
559
        ary_iface['shape'] = self.shape
1✔
560
        if (self.strides_):
1✔
561
            ary_iface['strides'] = _make_int_tuple(self.nd_, self.strides_)
1✔
562
        else:
563
            if (self.flags_ & USM_ARRAY_C_CONTIGUOUS):
1✔
564
                ary_iface['strides'] = None
1✔
565
            elif (self.flags_ & USM_ARRAY_F_CONTIGUOUS):
1✔
566
                ary_iface['strides'] = _f_contig_strides(self.nd_, self.shape_)
1✔
567
            else:
568
                raise InternalUSMArrayError(
×
569
                    "USM Array is not contiguous and has empty strides"
570
                )
571
        ary_iface['typestr'] = _make_typestr(self.typenum_)
1✔
572
        byte_offset = ary_ptr - mem_ptr
1✔
573
        item_size = self.get_itemsize()
1✔
574
        if (byte_offset % item_size):
1✔
575
            raise InternalUSMArrayError(
×
576
                "byte_offset is not a multiple of item_size.")
577
        elem_offset = byte_offset // item_size
1✔
578
        ary_iface['offset'] = elem_offset
1✔
579
        # must wait for content of the memory to finalize
580
        self.sycl_queue.wait()
1✔
581
        return ary_iface
1✔
582

583
    @property
584
    def ndim(self):
585
        """
586
        Gives the number of indices needed to address elements of this array.
587
        """
588
        return self.nd_
1✔
589

590
    @property
591
    def usm_data(self):
592
        """
593
        Gives USM memory object underlying :class:`.usm_ndarray` instance.
594
        """
595
        return self.get_base()
1✔
596

597
    @property
598
    def shape(self):
599
        """
600
        Elements of the shape tuple give the lengths of the
601
        respective array dimensions.
602

603
        Setting shape is allowed only when reshaping to the requested
604
        dimensions can be returned as view, otherwise :exc:`AttributeError`
605
        is raised. Use :func:`dpctl.tensor.reshape` to reshape the array
606
        in all cases.
607

608
        :Example:
609

610
            .. code-block:: python
611

612
                from dpctl import tensor
613

614
                x = tensor.arange(899)
615
                x.shape = (29, 31)
616
        """
617
        if self.nd_ > 0:
1✔
618
            return _make_int_tuple(self.nd_, self.shape_)
1✔
619
        else:
620
            return tuple()
1✔
621

622
    @shape.setter
623
    def shape(self, new_shape):
624
        """
625
        Modifies usm_ndarray instance in-place by changing its metadata
626
        about the shape and the strides of the array, or raises
627
        `AttributeError` exception if in-place change is not possible.
628

629
        Args:
630
            new_shape: (tuple, int)
631
                New shape. Only non-negative values are supported.
632
                The new shape may not lead to the change in the
633
                number of elements in the array.
634

635
        Whether the array can be reshape in-place depends on its
636
        strides. Use :func:`dpctl.tensor.reshape` function which
637
        always succeeds to reshape the array by performing a copy
638
        if necessary.
639
        """
640
        cdef int new_nd = -1
1✔
641
        cdef Py_ssize_t nelems = -1
1✔
642
        cdef int err = 0
1✔
643
        cdef Py_ssize_t min_disp = 0
1✔
644
        cdef Py_ssize_t max_disp = 0
1✔
645
        cdef int contig_flag = 0
1✔
646
        cdef Py_ssize_t *shape_ptr = NULL
1✔
647
        cdef Py_ssize_t *strides_ptr = NULL
1✔
648
        cdef Py_ssize_t size = -1
1✔
649
        import operator
1✔
650

651
        from ._reshape import reshaped_strides
1✔
652

653
        try:
1✔
654
            new_nd = len(new_shape)
1✔
655
        except TypeError:
1✔
656
            new_nd = 1
1✔
657
            new_shape = (new_shape,)
1✔
658
        try:
1✔
659
            new_shape = tuple(operator.index(dim) for dim in new_shape)
1✔
660
        except TypeError:
1✔
661
            raise TypeError(
1✔
662
                "Target shape must be a finite iterable of integers"
663
            )
664
        size = shape_to_elem_count(self.nd_, self.shape_)
1✔
665
        if not np.prod(new_shape) == size:
1✔
666
            raise TypeError(
×
667
                f"Can not reshape array of size {self.size} into {new_shape}"
×
668
            )
669
        if size > 0:
1✔
670
            new_strides = reshaped_strides(
671
               self.shape,
1✔
672
               self.strides,
1✔
673
               new_shape
1✔
674
            )
675
        else:
676
            new_strides = (1,) * len(new_shape)
1✔
677
        if new_strides is None:
1✔
678
            raise AttributeError(
1✔
679
                "Incompatible shape for in-place modification. "
680
                "Use `reshape()` to make a copy with the desired shape."
681
            )
682
        err = _from_input_shape_strides(
1✔
683
            new_nd, new_shape, new_strides,
684
            self.get_itemsize(),
1✔
685
            b"C",
686
            &shape_ptr, &strides_ptr,
687
            &nelems, &min_disp, &max_disp, &contig_flag
688
        )
689
        if (err == 0):
1✔
690
            if (self.shape_):
1✔
691
                PyMem_Free(self.shape_)
1✔
692
            if (self.strides_):
1✔
693
                PyMem_Free(self.strides_)
1✔
694
            self.flags_ = (contig_flag | (self.flags_ & USM_ARRAY_WRITABLE))
1✔
695
            self.nd_ = new_nd
1✔
696
            self.shape_ = shape_ptr
1✔
697
            self.strides_ = strides_ptr
1✔
698
        else:
699
            raise InternalUSMArrayError(
×
700
                "Encountered in shape setter, error code {err}".format(err)
×
701
            )
702

703
    @property
704
    def strides(self):
705
        """
706
        Returns memory displacement in array elements, upon unit
707
        change of respective index.
708

709
        For example, for strides ``(s1, s2, s3)`` and multi-index
710
        ``(i1, i2, i3)`` position of the respective element relative
711
        to zero multi-index element is ``s1*s1 + s2*i2 + s3*i3``.
712

713
        :Example:
714

715
            .. code-block:: python
716

717
                from dpctl import tensor
718

719
                x = tensor.zeros((20, 30))
720
                xv = x[10:, :15]
721

722
                multi_id = (3, 5)
723
                byte_displacement = xv[multi_id]._pointer - xv[0, 0]._pointer
724
                element_displacement = sum(
725
                    i * s for i, s in zip(multi_id, xv.strides)
726
                )
727
                assert byte_displacement == element_displacement * xv.itemsize
728
        """
729
        if (self.strides_):
1✔
730
            return _make_int_tuple(self.nd_, self.strides_)
1✔
731
        else:
732
            if (self.flags_ & USM_ARRAY_C_CONTIGUOUS):
1✔
733
                return _c_contig_strides(self.nd_, self.shape_)
1✔
734
            elif (self.flags_ & USM_ARRAY_F_CONTIGUOUS):
1✔
735
                return _f_contig_strides(self.nd_, self.shape_)
1✔
736
            else:
737
                raise ValueError("Inconsistent usm_ndarray data")
×
738

739
    @property
740
    def flags(self):
741
        """
742
        Returns :class:`dpctl.tensor._flags.Flags` object.
743
        """
744
        return _flags.Flags(self, self.flags_)
1✔
745

746
    cdef _set_writable_flag(self, int flag):
1✔
747
        cdef int mask = (USM_ARRAY_WRITABLE if flag else 0)
1✔
748
        self.flags_ = _copy_writable(self.flags_, mask)
1✔
749

750
    @property
751
    def usm_type(self):
752
        """
753
        USM type of underlying memory. Possible values are:
754

755
            * ``"device"``
756
                USM-device allocation in device memory, only accessible
757
                to kernels executed on the device
758
            * ``"shared"``
759
                USM-shared allocation in device memory, accessible both
760
                from the device and from host
761
            * ``"host"``
762
                USM-host allocation in host memory, accessible both
763
                from the device and from host
764

765
        See: https://docs.oneapi.com/versions/latest/dpcpp/iface/usm.html
766
        """
767
        return self.base_.get_usm_type()
1✔
768

769
    @property
770
    def itemsize(self):
771
        """
772
        Size of array element in bytes.
773
        """
774
        return self.get_itemsize()
1✔
775

776
    @property
777
    def nbytes(self):
778
        """
779
        Total bytes consumed by the elements of the array.
780
        """
781
        return (
1✔
782
            shape_to_elem_count(self.nd_, self.shape_) *
1✔
783
            self.get_itemsize())
1✔
784

785
    @property
786
    def size(self):
787
        """
788
        Number of elements in the array.
789
        """
790
        return shape_to_elem_count(self.nd_, self.shape_)
1✔
791

792
    @property
793
    def dtype(self):
794
        """
795
        Returns NumPy's dtype corresponding to the type of the array elements.
796
        """
797
        return np.dtype(_make_typestr(self.typenum_))
1✔
798

799
    @property
800
    def sycl_queue(self):
801
        """
802
        Returns :class:`dpctl.SyclQueue` object associated with USM data.
803
        """
804
        return self.get_sycl_queue()
1✔
805

806
    @property
807
    def sycl_device(self):
808
        """
809
        Returns :class:`dpctl.SyclDevice` object on which USM data was allocated.
810
        """
811
        q = self.sycl_queue
1✔
812
        return q.sycl_device
1✔
813

814
    @property
815
    def device(self):
816
        """
817
        Returns :class:`dpctl.tensor.Device` object representing
818
        residence of the array data.
819

820
        The ``Device`` object represents Array API notion of the
821
        device, and contains :class:`dpctl.SyclQueue` associated
822
        with this array. Hence, ``.device`` property provides
823
        information distinct from ``.sycl_device`` property.
824

825
        :Example:
826

827
            .. code-block:: python
828

829
                >>> from dpctl import tensor
830
                >>> x = tensor.ones(10)
831
                >>> x.device
832
                Device(level_zero:gpu:0)
833
        """
834
        return Device.create_device(self.sycl_queue)
1✔
835

836
    @property
837
    def sycl_context(self):
838
        """
839
        Returns :class:`dpctl.SyclContext` object to which USM data is bound.
840
        """
841
        q = self.sycl_queue
1✔
842
        return q.sycl_context
1✔
843

844
    @property
845
    def T(self):
846
        """Returns transposed array for 2D array, raises ``ValueError``
847
        otherwise.
848
        """
849
        if self.nd_ == 2:
1✔
850
            return _transpose(self)
1✔
851
        else:
852
            raise ValueError(
1✔
853
                "array.T requires array to have 2 dimensions. "
854
                "Use array.mT to transpose stacks of matrices and "
855
                "dpctl.tensor.permute_dims() to permute dimensions."
856
            )
857

858
    @property
859
    def mT(self):
860
        """ Returns array (a view) where the last two dimensions are
861
        transposed.
862
        """
863
        if self.nd_ < 2:
1✔
864
            raise ValueError(
1✔
865
                "array.mT requires array to have at least 2 dimensions."
866
            )
867
        return _m_transpose(self)
1✔
868

869
    @property
870
    def real(self):
871
        """
872
        Returns view into real component for arrays with
873
        complex data-types and returns itself for all other
874
        data-types.
875

876
        :Example:
877

878
            .. code-block:: python
879

880
                from dpctl import tensor
881

882
                # Create complex array from
883
                # arrays of real and imaginary parts
884

885
                re = tensor.linspace(-1, 1, num=100, dtype="f4")
886
                im = tensor.full_like(re, fill_value=tensor.pi)
887

888
                z = tensor.empty_like(re, dtype="c8")
889
                z.real[:] = re
890
                z.imag[:] = im
891
        """
892
        # explicitly check for UAR_HALF, which is greater than UAR_CFLOAT
893
        if (self.typenum_ < UAR_CFLOAT or self.typenum_ == UAR_HALF):
1✔
894
            # elements are real
895
            return self
1✔
896
        if (self.typenum_ < UAR_TYPE_SENTINEL):
1✔
897
            return _real_view(self)
1✔
898

899
    @property
900
    def imag(self):
901
        """ Returns view into imaginary component for arrays with
902
        complex data-types and returns new zero array for all other
903
        data-types.
904

905
        :Example:
906

907
            .. code-block:: python
908

909
                from dpctl import tensor
910

911
                # Reset imaginary part of complex array
912

913
                z = tensor.ones(100, dtype="c8")
914
                z.imag[:] = dpt.pi/2
915
        """
916
        # explicitly check for UAR_HALF, which is greater than UAR_CFLOAT
917
        if (self.typenum_ < UAR_CFLOAT or self.typenum_ == UAR_HALF):
1✔
918
            # elements are real
919
            return _zero_like(self)
1✔
920
        if (self.typenum_ < UAR_TYPE_SENTINEL):
1✔
921
            return _imag_view(self)
1✔
922

923
    def __getitem__(self, ind):
924
        cdef tuple _meta = _basic_slice_meta(
1✔
925
            ind, (<object>self).shape, (<object> self).strides,
1✔
926
            self.get_offset())
1✔
927
        cdef usm_ndarray res
928
        cdef int i = 0
1✔
929
        cdef bint matching = 1
1✔
930

931
        if len(_meta) < 5:
1✔
932
            raise RuntimeError
×
933

934
        res = usm_ndarray.__new__(
1✔
935
            usm_ndarray,
936
            _meta[0],
1✔
937
            dtype=_make_typestr(self.typenum_),
1✔
938
            strides=_meta[1],
1✔
939
            buffer=self.base_,
1✔
940
            offset=_meta[2]
1✔
941
        )
942
        res.array_namespace_ = self.array_namespace_
1✔
943

944
        adv_ind = _meta[3]
1✔
945
        adv_ind_start_p = _meta[4]
1✔
946

947
        if adv_ind_start_p < 0:
1✔
948
            res.flags_ = _copy_writable(res.flags_, self.flags_)
1✔
949
            return res
1✔
950

951
        from ._copy_utils import _extract_impl, _nonzero_impl, _take_multi_index
1✔
952
        if len(adv_ind) == 1 and adv_ind[0].dtype == dpt_bool:
1✔
953
            key_ = adv_ind[0]
1✔
954
            adv_ind_end_p = key_.ndim + adv_ind_start_p
1✔
955
            if adv_ind_end_p > res.ndim:
1✔
956
                raise IndexError("too many indices for the array")
1✔
957
            key_shape = key_.shape
1✔
958
            arr_shape = res.shape[adv_ind_start_p:adv_ind_end_p]
1✔
959
            for i in range(key_.ndim):
1✔
960
                if matching:
1✔
961
                    if not key_shape[i] == arr_shape[i] and key_shape[i] > 0:
1✔
962
                        matching = 0
1✔
963
            if not matching:
1✔
964
                raise IndexError("boolean index did not match indexed array in dimensions")
1✔
965
            res = _extract_impl(res, key_, axis=adv_ind_start_p)
1✔
966
            res.flags_ = _copy_writable(res.flags_, self.flags_)
1✔
967
            return res
1✔
968

969
        if any(ind.dtype == dpt_bool for ind in adv_ind):
1✔
970
            adv_ind_int = list()
1✔
971
            for ind in adv_ind:
1✔
972
                if ind.dtype == dpt_bool:
1✔
973
                    adv_ind_int.extend(_nonzero_impl(ind))
1✔
974
                else:
975
                    adv_ind_int.append(ind)
1✔
976
            res = _take_multi_index(res, tuple(adv_ind_int), adv_ind_start_p)
1✔
977
            res.flags_ = _copy_writable(res.flags_, self.flags_)
1✔
978
            return res
1✔
979

980
        res = _take_multi_index(res, adv_ind, adv_ind_start_p)
1✔
981
        res.flags_ = _copy_writable(res.flags_, self.flags_)
1✔
982
        return res
1✔
983

984
    def to_device(self, target_device, stream=None):
1✔
985
        """ to_device(target_device)
986

987
        Transfers this array to specified target device.
988

989
        :Example:
990
            .. code-block:: python
991

992
                import dpctl
993
                import dpctl.tensor as dpt
994

995
                x = dpt.full(10**6, 2, dtype="int64")
996
                q_prof = dpctl.SyclQueue(
997
                    x.sycl_device, property="enable_profiling")
998
                # return a view with profile-enabled queue
999
                y = x.to_device(q_prof)
1000
                timer = dpctl.SyclTimer()
1001
                with timer(q_prof):
1002
                    z = y * y
1003
                print(timer.dt)
1004

1005
        Args:
1006
            target_device (object):
1007
                Array API concept of target device.
1008
                It can be a oneAPI filter selector string,
1009
                an instance of :class:`dpctl.SyclDevice` corresponding to a
1010
                non-partitioned SYCL device, an instance of
1011
                :class:`dpctl.SyclQueue`, or a :class:`dpctl.tensor.Device`
1012
                object returned by :attr:`dpctl.tensor.usm_ndarray.device`.
1013
            stream (:class:`dpctl.SyclQueue`, optional):
1014
                Execution queue to synchronize with. If ``None``,
1015
                synchronization is not performed.
1016

1017
        Returns:
1018
            usm_ndarray:
1019
                A view if data copy is not required, and a copy otherwise.
1020
                If copying is required, it is done by copying from the original
1021
                allocation device to the host, followed by copying from host
1022
                to the target device.
1023
        """
1024
        cdef c_dpctl.DPCTLSyclQueueRef QRef = NULL
1✔
1025
        cdef c_dpmem._Memory arr_buf
1026
        d = Device.create_device(target_device)
1✔
1027

1028
        if (stream is None or not isinstance(stream, dpctl.SyclQueue) or
1✔
1029
            stream == self.sycl_queue):
×
1030
            pass
1031
        else:
1032
            ev = self.sycl_queue.submit_barrier()
×
1033
            stream.submit_barrier(dependent_events=[ev])
×
1034

1035
        if (d.sycl_context == self.sycl_context):
1✔
1036
            arr_buf = <c_dpmem._Memory> self.usm_data
1✔
1037
            QRef = (<c_dpctl.SyclQueue> d.sycl_queue).get_queue_ref()
1✔
1038
            view_buffer = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1039
                <DPCTLSyclUSMRef>arr_buf.get_data_ptr(),
1✔
1040
                arr_buf.nbytes,
1041
                QRef,
1042
                memory_owner=arr_buf
1043
            )
1044
            res = usm_ndarray(
1✔
1045
                self.shape,
1✔
1046
                self.dtype,
1✔
1047
                buffer=view_buffer,
1✔
1048
                strides=self.strides,
1✔
1049
                offset=self.get_offset()
1✔
1050
            )
1051
            res.flags_ = self.flags_
1✔
1052
            return res
1✔
1053
        else:
1054
            nbytes = self.usm_data.nbytes
×
1055
            copy_buffer = type(self.usm_data)(
×
1056
                nbytes, queue=d.sycl_queue
×
1057
            )
1058
            copy_buffer.copy_from_device(self.usm_data)
×
1059
            res = usm_ndarray(
×
1060
                self.shape,
×
1061
                self.dtype,
×
1062
                buffer=copy_buffer,
×
1063
                strides=self.strides,
×
1064
                offset=self.get_offset()
×
1065
            )
1066
            res.flags_ = self.flags_
×
1067
            return res
×
1068

1069
    def _set_namespace(self, mod):
1✔
1070
        """ Sets array namespace to given module `mod`. """
1071
        self.array_namespace_ = mod
1✔
1072

1073
    def __array_namespace__(self, api_version=None):
1✔
1074
        """
1075
        Returns array namespace, member functions of which
1076
        implement data API.
1077

1078
        Args:
1079
            api_version (str, optional)
1080
                Request namespace compliant with given version of
1081
                array API. If ``None``, namespace for the most
1082
                recent supported version is returned.
1083
                Default: ``None``.
1084
        """
1085
        if api_version is not None:
1✔
1086
            from ._array_api import __array_api_version__
×
1087
            if not isinstance(api_version, str):
×
1088
                raise TypeError(f"Expected type str, got {type(api_version)}")
×
1089
            if api_version != __array_api_version__:
×
1090
                raise ValueError(f"Only {__array_api_version__} is supported")
×
1091
        return self.array_namespace_ if self.array_namespace_ is not None else dpctl.tensor
1✔
1092

1093
    def __bool__(self):
1094
        if self.size == 1:
1✔
1095
            view = _as_zero_dim_ndarray(self)
1✔
1096
            return view.__bool__()
1✔
1097

1098
        if self.size == 0:
1✔
1099
            raise ValueError(
1✔
1100
                "The truth value of an empty array is ambiguous"
1101
            )
1102

1103
        raise ValueError(
1✔
1104
            "The truth value of an array with more than one element is "
1105
            "ambiguous. Use dpctl.tensor.any() or dpctl.tensor.all()"
1106
        )
1107

1108
    def __float__(self):
1109
        if self.size == 1:
1✔
1110
            view = _as_zero_dim_ndarray(self)
1✔
1111
            return view.__float__()
1✔
1112

1113
        raise ValueError(
1✔
1114
            "only size-1 arrays can be converted to Python scalars"
1115
        )
1116

1117
    def __complex__(self):
1✔
1118
        if self.size == 1:
1✔
1119
            view = _as_zero_dim_ndarray(self)
1✔
1120
            return view.__complex__()
1✔
1121

1122
        raise ValueError(
1✔
1123
            "only size-1 arrays can be converted to Python scalars"
1124
        )
1125

1126
    def __int__(self):
1127
        if self.size == 1:
1✔
1128
            view = _as_zero_dim_ndarray(self)
1✔
1129
            return view.__int__()
1✔
1130

1131
        raise ValueError(
1✔
1132
            "only size-1 arrays can be converted to Python scalars"
1133
        )
1134

1135
    def __index__(self):
1136
        if np.issubdtype(self.dtype, np.integer):
1✔
1137
            return int(self)
1✔
1138

1139
        raise IndexError("only integer arrays are valid indices")
1✔
1140

1141
    def __abs__(self):
1142
        return dpctl.tensor.abs(self)
1✔
1143

1144
    def __add__(self, other):
1145
        """
1146
        Implementation for operator.add
1147
        """
1148
        return dpctl.tensor.add(self, other)
1✔
1149

1150
    def __and__(self, other):
1151
        "Implementation for operator.and"
1152
        return dpctl.tensor.bitwise_and(self, other)
1✔
1153

1154
    def __dlpack__(self, *, stream=None, max_version=None, dl_device=None, copy=None):
1✔
1155
        """
1156
        Produces DLPack capsule.
1157

1158
        Args:
1159
            stream (:class:`dpctl.SyclQueue`, optional):
1160
                Execution queue to synchronize with.
1161
                If ``None``, synchronization is not performed.
1162
                Default: ``None``.
1163
            max_version (tuple[int, int], optional):
1164
                The maximum DLPack version the consumer (caller of
1165
                ``__dlpack__``) supports. As ``__dlpack__`` may not
1166
                always return a DLPack capsule with version
1167
                `max_version`, the consumer must verify the version
1168
                even if this argument is passed.
1169
                Default: ``None``.
1170
            dl_device (tuple[enum.Enum, int], optional):
1171
                The device the returned DLPack capsule will be
1172
                placed on.
1173
                The device must be a 2-tuple matching the format of
1174
                ``__dlpack_device__`` method, an integer enumerator
1175
                representing the device type followed by an integer
1176
                representing the index of the device.
1177
                Default: ``None``.
1178
            copy (bool, optional):
1179
                Boolean indicating whether or not to copy the input.
1180

1181
                * If ``copy`` is ``True``, the input will always be
1182
                  copied.
1183
                * If ``False``, a ``BufferError`` will be raised if a
1184
                  copy is deemed necessary.
1185
                * If ``None``, a copy will be made only if deemed
1186
                  necessary, otherwise, the existing memory buffer will
1187
                  be reused.
1188

1189
                Default: ``None``.
1190

1191
        Raises:
1192
            MemoryError:
1193
                when host memory can not be allocated.
1194
            DLPackCreationError:
1195
                when array is allocated on a partitioned
1196
                SYCL device, or with a non-default context.
1197
            BufferError:
1198
                when a copy is deemed necessary but ``copy``
1199
                is ``False`` or when the provided ``dl_device``
1200
                cannot be handled.
1201
        """
1202
        if max_version is None:
1✔
1203
            # legacy path for DLManagedTensor
1204
            # copy kwarg ignored because copy flag can't be set
1205
            _caps = c_dlpack.to_dlpack_capsule(self)
1✔
1206
            if (stream is None or type(stream) is not dpctl.SyclQueue or
1✔
1207
                stream == self.sycl_queue):
1✔
1208
                pass
1209
            else:
1210
                ev = self.sycl_queue.submit_barrier()
1✔
1211
                stream.submit_barrier(dependent_events=[ev])
1✔
1212
            return _caps
1✔
1213
        else:
1214
            if not isinstance(max_version, tuple) or len(max_version) != 2:
1✔
1215
                raise TypeError(
1✔
1216
                    "`__dlpack__` expects `max_version` to be a "
1✔
1217
                    "2-tuple of integers `(major, minor)`, instead "
1218
                    f"got {max_version}"
1✔
1219
                )
1220
            dpctl_dlpack_version = get_build_dlpack_version()
1✔
1221
            if max_version[0] >= dpctl_dlpack_version[0]:
1✔
1222
                # DLManagedTensorVersioned path
1223
                if dl_device is not None:
1✔
1224
                    if not isinstance(dl_device, tuple) or len(dl_device) != 2:
1✔
1225
                        raise TypeError(
1✔
1226
                            "`__dlpack__` expects `dl_device` to be a 2-tuple "
1✔
1227
                            "of `(device_type, device_id)`, instead "
1228
                            f"got {dl_device}"
1✔
1229
                        )
1230
                    if dl_device != self.__dlpack_device__():
1✔
1231
                        if copy == False:
1✔
1232
                            raise BufferError(
×
1233
                                "array cannot be placed on the requested device without a copy"
1234
                            )
1235
                        if _is_host_cpu(dl_device):
1✔
1236
                            if stream is not None:
1✔
1237
                                raise ValueError(
×
1238
                                    "`stream` must be `None` when `dl_device` is of type `kDLCPU`"
1239
                                )
1240
                            from ._copy_utils import _copy_to_numpy
1✔
1241
                            _arr = _copy_to_numpy(self)
1✔
1242
                            _arr.flags["W"] = self.flags["W"]
1✔
1243
                            return c_dlpack.numpy_to_dlpack_versioned_capsule(_arr, True)
1✔
1244
                        else:
1245
                            raise BufferError(
×
1246
                                f"targeting `dl_device` {dl_device} with `__dlpack__` is not "
×
1247
                                "yet implemented"
1248
                            )
1249
                if copy is None:
1✔
1250
                    copy = False
1✔
1251
                # TODO: strategy for handling stream on different device from dl_device
1252
                if copy:
1✔
1253
                    if (stream is None or type(stream) is not dpctl.SyclQueue or
1✔
1254
                        stream == self.sycl_queue):
1✔
1255
                        pass
1256
                    else:
1257
                        ev = self.sycl_queue.submit_barrier()
1✔
1258
                        stream.submit_barrier(dependent_events=[ev])
1✔
1259
                    nbytes = self.usm_data.nbytes
1✔
1260
                    copy_buffer = type(self.usm_data)(
1✔
1261
                        nbytes, queue=self.sycl_queue
1✔
1262
                    )
1263
                    copy_buffer.copy_from_device(self.usm_data)
1✔
1264
                    _copied_arr = usm_ndarray(
1✔
1265
                        self.shape,
1✔
1266
                        self.dtype,
1✔
1267
                        buffer=copy_buffer,
1✔
1268
                        strides=self.strides,
1✔
1269
                        offset=self.get_offset()
1✔
1270
                    )
1271
                    _copied_arr.flags_ = self.flags_
1✔
1272
                    _caps = c_dlpack.to_dlpack_versioned_capsule(_copied_arr, copy)
1✔
1273
                else:
1274
                    _caps = c_dlpack.to_dlpack_versioned_capsule(self, copy)
1✔
1275
                    if (stream is None or type(stream) is not dpctl.SyclQueue or
1✔
1276
                        stream == self.sycl_queue):
1✔
1277
                        pass
1278
                    else:
1279
                        ev = self.sycl_queue.submit_barrier()
1✔
1280
                        stream.submit_barrier(dependent_events=[ev])
1✔
1281
                return _caps
1✔
1282
            else:
1283
                # legacy path for DLManagedTensor
1284
                _caps = c_dlpack.to_dlpack_capsule(self)
1✔
1285
                if (stream is None or type(stream) is not dpctl.SyclQueue or
1✔
1286
                    stream == self.sycl_queue):
1✔
1287
                    pass
1288
                else:
1289
                    ev = self.sycl_queue.submit_barrier()
1✔
1290
                    stream.submit_barrier(dependent_events=[ev])
1✔
1291
                return _caps
1✔
1292

1293
    def __dlpack_device__(self):
1✔
1294
        """
1295
        Gives a tuple (``device_type``, ``device_id``) corresponding to
1296
        ``DLDevice`` entry in ``DLTensor`` in DLPack protocol.
1297

1298
        The tuple describes the non-partitioned device where the array has been allocated,
1299
        or the non-partitioned parent device of the allocation device.
1300

1301
        See ``DLDeviceType`` for a list of devices supported by the DLPack protocol.
1302

1303
        Raises:
1304
            DLPackCreationError:
1305
                when the ``device_id`` could not be determined.
1306
        """
1307
        cdef int dev_id = c_dlpack.get_parent_device_ordinal_id(<c_dpctl.SyclDevice>self.sycl_device)
1✔
1308
        if dev_id < 0:
1✔
1309
            raise c_dlpack.DLPackCreationError(
×
1310
                "Could not determine id of the device where array was allocated."
1311
            )
1312
        else:
1313
            return (
1✔
1314
                DLDeviceType.kDLOneAPI,
1✔
1315
                dev_id,
1✔
1316
            )
1317

1318
    def __eq__(self, other):
1319
        return dpctl.tensor.equal(self, other)
1✔
1320

1321
    def __floordiv__(self, other):
1322
        return dpctl.tensor.floor_divide(self, other)
1✔
1323

1324
    def __ge__(self, other):
1325
        return dpctl.tensor.greater_equal(self, other)
1✔
1326

1327
    def __gt__(self, other):
1328
        return dpctl.tensor.greater(self, other)
1✔
1329

1330
    def __invert__(self):
1331
        return dpctl.tensor.bitwise_invert(self)
1✔
1332

1333
    def __le__(self, other):
1334
        return dpctl.tensor.less_equal(self, other)
1✔
1335

1336
    def __len__(self):
1337
        if (self.nd_):
1✔
1338
            return self.shape[0]
1✔
1339
        else:
1340
            raise TypeError("len() of unsized object")
1✔
1341

1342
    def __lshift__(self, other):
1343
        return dpctl.tensor.bitwise_left_shift(self, other)
1✔
1344

1345
    def __lt__(self, other):
1346
        return dpctl.tensor.less(self, other)
1✔
1347

1348
    def __matmul__(self, other):
1349
        return dpctl.tensor.matmul(self, other)
1✔
1350

1351
    def __mod__(self, other):
1352
        return dpctl.tensor.remainder(self, other)
1✔
1353

1354
    def __mul__(self, other):
1355
        return dpctl.tensor.multiply(self, other)
1✔
1356

1357
    def __ne__(self, other):
1358
        return dpctl.tensor.not_equal(self, other)
1✔
1359

1360
    def __neg__(self):
1361
        return dpctl.tensor.negative(self)
1✔
1362

1363
    def __or__(self, other):
1364
        return dpctl.tensor.bitwise_or(self, other)
1✔
1365

1366
    def __pos__(self):
1367
        return dpctl.tensor.positive(self)
1✔
1368

1369
    def __pow__(self, other):
1370
        return dpctl.tensor.pow(self, other)
1✔
1371

1372
    def __rshift__(self, other):
1373
        return dpctl.tensor.bitwise_right_shift(self, other)
1✔
1374

1375
    def __setitem__(self, key, rhs):
1376
        cdef tuple _meta
1377
        cdef usm_ndarray Xv
1378

1379
        if (self.flags_ & USM_ARRAY_WRITABLE) == 0:
1✔
1380
            raise ValueError("Can not modify read-only array.")
1✔
1381

1382
        _meta = _basic_slice_meta(
1✔
1383
            key, (<object>self).shape, (<object> self).strides,
1✔
1384
            self.get_offset()
1✔
1385
        )
1386

1387
        if len(_meta) < 5:
1✔
1388
            raise RuntimeError
×
1389

1390
        Xv = usm_ndarray.__new__(
1✔
1391
            usm_ndarray,
1392
            _meta[0],
1✔
1393
            dtype=_make_typestr(self.typenum_),
1✔
1394
            strides=_meta[1],
1✔
1395
            buffer=self.base_,
1✔
1396
            offset=_meta[2],
1✔
1397
        )
1398
        # set namespace
1399
        Xv.array_namespace_ = self.array_namespace_
1✔
1400

1401
        from ._copy_utils import (
1✔
1402
            _copy_from_numpy_into,
1✔
1403
            _copy_from_usm_ndarray_to_usm_ndarray,
1404
            _nonzero_impl,
1405
            _place_impl,
1406
            _put_multi_index,
1407
        )
1408

1409
        adv_ind = _meta[3]
1✔
1410
        adv_ind_start_p = _meta[4]
1✔
1411

1412
        if adv_ind_start_p < 0:
1✔
1413
            # basic slicing
1414
            if isinstance(rhs, usm_ndarray):
1✔
1415
                _copy_from_usm_ndarray_to_usm_ndarray(Xv, rhs)
1✔
1416
            else:
1417
                if hasattr(rhs, "__sycl_usm_array_interface__"):
1✔
1418
                    from dpctl.tensor import asarray
×
1419
                    try:
×
1420
                        rhs_ar = asarray(rhs)
×
1421
                        _copy_from_usm_ndarray_to_usm_ndarray(Xv, rhs_ar)
×
1422
                    except Exception:
×
1423
                        raise ValueError(
×
1424
                            f"Input of type {type(rhs)} could not be "
×
1425
                            "converted to usm_ndarray"
1426
                        )
1427
                else:
1428
                    rhs_np = np.asarray(rhs)
1✔
1429
                    if type_bytesize(rhs_np.dtype.num) < 0:
1✔
1430
                        raise ValueError(
×
1431
                            f"Input of type {type(rhs)} can not be "
×
1432
                            "assigned to usm_ndarray because of "
1433
                            f"unsupported data type '{rhs_np.dtype}'"
×
1434
                        )
1435
                    try:
1✔
1436
                        _copy_from_numpy_into(Xv, rhs_np)
1✔
1437
                    except Exception:
×
1438
                        raise ValueError(
×
1439
                            f"Input of type {type(rhs)} could not be "
×
1440
                            "copied into dpctl.tensor.usm_ndarray"
1441
                        )
1442
            return
1✔
1443

1444
        if len(adv_ind) == 1 and adv_ind[0].dtype == dpt_bool:
1✔
1445
            _place_impl(Xv, adv_ind[0], rhs, axis=adv_ind_start_p)
1✔
1446
            return
1✔
1447

1448
        if any(ind.dtype == dpt_bool for ind in adv_ind):
1✔
1449
            adv_ind_int = list()
1✔
1450
            for ind in adv_ind:
1✔
1451
                if ind.dtype == dpt_bool:
1✔
1452
                    adv_ind_int.extend(_nonzero_impl(ind))
1✔
1453
                else:
1454
                    adv_ind_int.append(ind)
1✔
1455
            _put_multi_index(Xv, tuple(adv_ind_int), adv_ind_start_p, rhs)
1✔
1456
            return
1✔
1457

1458
        _put_multi_index(Xv, adv_ind, adv_ind_start_p, rhs)
1✔
1459
        return
1✔
1460

1461

1462
    def __sub__(self, other):
1463
        return dpctl.tensor.subtract(self, other)
1✔
1464

1465
    def __truediv__(self, other):
1466
        return dpctl.tensor.divide(self, other)
1✔
1467

1468
    def __xor__(self, other):
1469
        return dpctl.tensor.bitwise_xor(self, other)
1✔
1470

1471
    def __radd__(self, other):
1472
        return dpctl.tensor.add(other, self)
1✔
1473

1474
    def __rand__(self, other):
1475
        return dpctl.tensor.bitwise_and(other, self)
1✔
1476

1477
    def __rfloordiv__(self, other):
1478
        return dpctl.tensor.floor_divide(other, self)
1✔
1479

1480
    def __rlshift__(self, other):
1481
        return dpctl.tensor.bitwise_left_shift(other, self)
1✔
1482

1483
    def __rmatmul__(self, other):
1484
        return dpctl.tensor.matmul(other, self)
1✔
1485

1486
    def __rmod__(self, other):
1487
        return dpctl.tensor.remainder(other, self)
1✔
1488

1489
    def __rmul__(self, other):
1490
        return dpctl.tensor.multiply(other, self)
1✔
1491

1492
    def __ror__(self, other):
1493
        return dpctl.tensor.bitwise_or(other, self)
1✔
1494

1495
    def __rpow__(self, other):
1496
        return dpctl.tensor.pow(other, self)
1✔
1497

1498
    def __rrshift__(self, other):
1499
        return dpctl.tensor.bitwise_right_shift(other, self)
1✔
1500

1501
    def __rsub__(self, other):
1502
        return dpctl.tensor.subtract(other, self)
1✔
1503

1504
    def __rtruediv__(self, other):
1505
        return dpctl.tensor.divide(other, self)
1✔
1506

1507
    def __rxor__(self, other):
1508
        return dpctl.tensor.bitwise_xor(other, self)
1✔
1509

1510
    def __iadd__(self, other):
1511
        return dpctl.tensor.add._inplace_op(self, other)
1✔
1512

1513
    def __iand__(self, other):
1514
        return dpctl.tensor.bitwise_and._inplace_op(self, other)
1✔
1515

1516
    def __ifloordiv__(self, other):
1517
        return dpctl.tensor.floor_divide._inplace_op(self, other)
1✔
1518

1519
    def __ilshift__(self, other):
1520
        return dpctl.tensor.bitwise_left_shift._inplace_op(self, other)
1✔
1521

1522
    def __imatmul__(self, other):
1523
        return dpctl.tensor.matmul(self, other, out=self, dtype=self.dtype)
1✔
1524

1525
    def __imod__(self, other):
1526
        return dpctl.tensor.remainder._inplace_op(self, other)
1✔
1527

1528
    def __imul__(self, other):
1529
        return dpctl.tensor.multiply._inplace_op(self, other)
1✔
1530

1531
    def __ior__(self, other):
1532
        return dpctl.tensor.bitwise_or._inplace_op(self, other)
1✔
1533

1534
    def __ipow__(self, other):
1535
        return dpctl.tensor.pow._inplace_op(self, other)
1✔
1536

1537
    def __irshift__(self, other):
1538
        return dpctl.tensor.bitwise_right_shift._inplace_op(self, other)
1✔
1539

1540
    def __isub__(self, other):
1541
        return dpctl.tensor.subtract._inplace_op(self, other)
1✔
1542

1543
    def __itruediv__(self, other):
1544
        return dpctl.tensor.divide._inplace_op(self, other)
1✔
1545

1546
    def __ixor__(self, other):
1547
        return dpctl.tensor.bitwise_xor._inplace_op(self, other)
1✔
1548

1549
    def __str__(self):
1550
        return usm_ndarray_str(self)
1✔
1551

1552
    def __repr__(self):
1553
        return usm_ndarray_repr(self)
1✔
1554

1555
    @property
1556
    def __array__(self):
1557
        "NumPy array protocol"
1558
        raise TypeError(
1✔
1559
            "Implicit conversion to a NumPy array is not allowed. "
1560
            "Use `dpctl.tensor.asnumpy` to copy data from this "
1561
            "`dpctl.tensor.usm_ndarray` instance to NumPy array"
1562
        )
1563

1564

1565
cdef usm_ndarray _real_view(usm_ndarray ary):
1✔
1566
    """
1567
    View into real parts of a complex type array
1568
    """
1569
    cdef int r_typenum_ = -1
1✔
1570
    cdef usm_ndarray r = None
1✔
1571
    cdef Py_ssize_t offset_elems = 0
1✔
1572

1573
    if (ary.typenum_ == UAR_CFLOAT):
1✔
1574
        r_typenum_ = UAR_FLOAT
1✔
1575
    elif (ary.typenum_ == UAR_CDOUBLE):
1✔
1576
        r_typenum_ = UAR_DOUBLE
1✔
1577
    else:
UNCOV
1578
        raise InternalUSMArrayError(
×
1579
            "_real_view call on array of non-complex type.")
1580

1581
    offset_elems = ary.get_offset() * 2
1✔
1582
    r = usm_ndarray.__new__(
1✔
1583
        usm_ndarray,
1584
        _make_int_tuple(ary.nd_, ary.shape_) if ary.nd_ > 0 else tuple(),
1✔
1585
        dtype=_make_typestr(r_typenum_),
1✔
1586
        strides=tuple(2 * si for si in ary.strides),
1✔
1587
        buffer=ary.base_,
1✔
1588
        offset=offset_elems,
1✔
1589
        order=('C' if (ary.flags_ & USM_ARRAY_C_CONTIGUOUS) else 'F')
1✔
1590
    )
1591
    r.flags_ = _copy_writable(r.flags_, ary.flags_)
1✔
1592
    r.array_namespace_ = ary.array_namespace_
1✔
1593
    return r
1✔
1594

1595

1596
cdef usm_ndarray _imag_view(usm_ndarray ary):
1✔
1597
    """
1598
    View into imaginary parts of a complex type array
1599
    """
1600
    cdef int r_typenum_ = -1
1✔
1601
    cdef usm_ndarray r = None
1✔
1602
    cdef Py_ssize_t offset_elems = 0
1✔
1603

1604
    if (ary.typenum_ == UAR_CFLOAT):
1✔
1605
        r_typenum_ = UAR_FLOAT
1✔
1606
    elif (ary.typenum_ == UAR_CDOUBLE):
1✔
1607
        r_typenum_ = UAR_DOUBLE
1✔
1608
    else:
UNCOV
1609
        raise InternalUSMArrayError(
×
1610
            "_imag_view call on array of non-complex type.")
1611

1612
    # displace pointer to imaginary part
1613
    offset_elems = 2 * ary.get_offset() + 1
1✔
1614
    r = usm_ndarray.__new__(
1✔
1615
        usm_ndarray,
1616
        _make_int_tuple(ary.nd_, ary.shape_) if ary.nd_ > 0 else tuple(),
1✔
1617
        dtype=_make_typestr(r_typenum_),
1✔
1618
        strides=tuple(2 * si for si in ary.strides),
1✔
1619
        buffer=ary.base_,
1✔
1620
        offset=offset_elems,
1✔
1621
        order=('C' if (ary.flags_ & USM_ARRAY_C_CONTIGUOUS) else 'F')
1✔
1622
    )
1623
    r.flags_ = _copy_writable(r.flags_, ary.flags_)
1✔
1624
    r.array_namespace_ = ary.array_namespace_
1✔
1625
    return r
1✔
1626

1627

1628
cdef usm_ndarray _transpose(usm_ndarray ary):
1✔
1629
    """
1630
    Construct transposed array without copying the data
1631
    """
1632
    cdef usm_ndarray r = usm_ndarray.__new__(
1✔
1633
        usm_ndarray,
1634
        _make_reversed_int_tuple(ary.nd_, ary.shape_),
1✔
1635
        dtype=_make_typestr(ary.typenum_),
1✔
1636
        strides=(
1637
            _make_reversed_int_tuple(ary.nd_, ary.strides_)
1✔
1638
            if (ary.strides_) else None),
1✔
1639
        buffer=ary.base_,
1✔
1640
        order=('F' if (ary.flags_ & USM_ARRAY_C_CONTIGUOUS) else 'C'),
1✔
1641
        offset=ary.get_offset()
1✔
1642
    )
1643
    r.flags_ = _copy_writable(r.flags_, ary.flags_)
1✔
1644
    return r
1✔
1645

1646

1647
cdef usm_ndarray _m_transpose(usm_ndarray ary):
1✔
1648
    """
1649
    Construct matrix transposed array
1650
    """
1651
    cdef usm_ndarray r = usm_ndarray.__new__(
1✔
1652
        usm_ndarray,
1653
        _swap_last_two(_make_int_tuple(ary.nd_, ary.shape_)),
1✔
1654
        dtype=_make_typestr(ary.typenum_),
1✔
1655
        strides=_swap_last_two(ary.strides),
1✔
1656
        buffer=ary.base_,
1✔
1657
        order=('F' if (ary.flags_ & USM_ARRAY_C_CONTIGUOUS) else 'C'),
1✔
1658
        offset=ary.get_offset()
1✔
1659
    )
1660
    r.flags_ = _copy_writable(r.flags_, ary.flags_)
1✔
1661
    return r
1✔
1662

1663

1664
cdef usm_ndarray _zero_like(usm_ndarray ary):
1✔
1665
    """
1666
    Make C-contiguous array of zero elements with same shape,
1667
    type, device, and sycl_queue as ary.
1668
    """
1669
    cdef dt = _make_typestr(ary.typenum_)
1✔
1670
    cdef usm_ndarray r = usm_ndarray(
1✔
1671
        _make_int_tuple(ary.nd_, ary.shape_) if ary.nd_ > 0 else tuple(),
1✔
1672
        dtype=dt,
1✔
1673
        buffer=ary.base_.get_usm_type(),
1✔
1674
        buffer_ctor_kwargs={"queue": ary.get_sycl_queue()},
1✔
1675
    )
1676
    r.base_.memset()
1✔
1677
    return r
1✔
1678

1679

1680
cdef api char* UsmNDArray_GetData(usm_ndarray arr):
1✔
1681
    """Get allocation pointer of zero index element of array """
1682
    return arr.get_data()
1✔
1683

1684

1685
cdef api int UsmNDArray_GetNDim(usm_ndarray arr):
1✔
1686
    """Get array rank: length of its shape"""
1687
    return arr.get_ndim()
1✔
1688

1689

1690
cdef api Py_ssize_t* UsmNDArray_GetShape(usm_ndarray arr):
1✔
1691
    """Get host pointer to shape vector"""
1692
    return arr.get_shape()
1✔
1693

1694

1695
cdef api Py_ssize_t* UsmNDArray_GetStrides(usm_ndarray arr):
1✔
1696
    """Get host pointer to strides vector"""
1697
    return arr.get_strides()
1✔
1698

1699

1700
cdef api int UsmNDArray_GetTypenum(usm_ndarray arr):
1✔
1701
    """Get type number for data type of array elements"""
1702
    return arr.get_typenum()
1✔
1703

1704

1705
cdef api int UsmNDArray_GetElementSize(usm_ndarray arr):
1✔
1706
    """Get array element size in bytes"""
1707
    return arr.get_itemsize()
1✔
1708

1709

1710
cdef api int UsmNDArray_GetFlags(usm_ndarray arr):
1✔
1711
    """Get flags of array"""
1712
    return arr.get_flags()
1✔
1713

1714

1715
cdef api c_dpctl.DPCTLSyclQueueRef UsmNDArray_GetQueueRef(usm_ndarray arr):
1✔
1716
    """Get DPCTLSyclQueueRef for queue associated with the array"""
1717
    return arr.get_queue_ref()
1✔
1718

1719

1720
cdef api Py_ssize_t UsmNDArray_GetOffset(usm_ndarray arr):
1✔
1721
    """Get offset of zero-index array element from the beginning of the USM
1722
    allocation"""
1723
    return arr.get_offset()
1✔
1724

1725

1726
cdef api object UsmNDArray_GetUSMData(usm_ndarray arr):
1✔
1727
    """Get USM data object underlying the array"""
1728
    return arr.get_base()
1✔
1729

1730

1731
cdef api void UsmNDArray_SetWritableFlag(usm_ndarray arr, int flag):
1✔
1732
    """Set/unset USM_ARRAY_WRITABLE in the given array `arr`."""
1733
    arr._set_writable_flag(flag)
1✔
1734

1735

1736
cdef api object UsmNDArray_MakeSimpleFromMemory(
1✔
1737
    int nd, const Py_ssize_t *shape, int typenum,
1738
    c_dpmem._Memory mobj, Py_ssize_t offset, char order
1739
):
1740
    """Create contiguous usm_ndarray.
1741

1742
    Args:
1743
        nd: number of dimensions (non-negative)
1744
        shape: array of nd non-negative array's sizes along each dimension
1745
        typenum: array elemental type number
1746
        ptr: pointer to the start of allocation
1747
        QRef: DPCTLSyclQueueRef associated with the allocation
1748
        offset: distance between element with zero multi-index and the
1749
                start of allocation
1750
        order: Memory layout of the array. Use 'C' for C-contiguous or
1751
               row-major layout; 'F' for F-contiguous or column-major layout
1752
    Returns:
1753
        Created usm_ndarray instance
1754
    """
1755
    cdef object shape_tuple = _make_int_tuple(nd, <Py_ssize_t *>shape)
1✔
1756
    cdef usm_ndarray arr = usm_ndarray(
1✔
1757
        shape_tuple,
1758
        dtype=_make_typestr(typenum),
1✔
1759
        buffer=mobj,
1✔
1760
        offset=offset,
1✔
1761
        order=<bytes>(order)
1✔
1762
    )
1763
    return arr
1✔
1764

1765

1766
cdef api object UsmNDArray_MakeSimpleFromPtr(
1✔
1767
    size_t nelems,
1768
    int typenum,
1769
    c_dpctl.DPCTLSyclUSMRef ptr,
1770
    c_dpctl.DPCTLSyclQueueRef QRef,
1771
    object owner
1772
):
1773
    """Create 1D contiguous usm_ndarray from pointer.
1774

1775
    Args:
1776
        nelems: number of elements in array
1777
        typenum: array elemental type number
1778
        ptr: pointer to the start of allocation
1779
        QRef: DPCTLSyclQueueRef associated with the allocation
1780
        owner: Python object managing lifetime of USM allocation.
1781
               Value None implies transfer of USM allocation ownership
1782
               to the created array object.
1783
    Returns:
1784
        Created usm_ndarray instance
1785
    """
1786
    cdef size_t itemsize = type_bytesize(typenum)
1✔
1787
    cdef size_t nbytes = itemsize * nelems
1✔
1788
    cdef c_dpmem._Memory mobj = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1789
        ptr, nbytes, QRef, memory_owner=owner
1790
    )
1791
    cdef usm_ndarray arr = usm_ndarray(
1✔
1792
        (nelems,),
1✔
1793
        dtype=_make_typestr(typenum),
1✔
1794
        buffer=mobj
1✔
1795
    )
1796
    return arr
1✔
1797

1798
cdef api object UsmNDArray_MakeFromPtr(
1✔
1799
    int nd,
1800
    const Py_ssize_t *shape,
1801
    int typenum,
1802
    const Py_ssize_t *strides,
1803
    c_dpctl.DPCTLSyclUSMRef ptr,
1804
    c_dpctl.DPCTLSyclQueueRef QRef,
1805
    Py_ssize_t offset,
1806
    object owner
1807
):
1808
    """
1809
    General usm_ndarray constructor from externally made USM-allocation.
1810

1811
    Args:
1812
        nd: number of dimensions (non-negative)
1813
        shape: array of nd non-negative array's sizes along each dimension
1814
        typenum: array elemental type number
1815
        strides: array of nd strides along each dimension in elements
1816
        ptr: pointer to the start of allocation
1817
        QRef: DPCTLSyclQueueRef associated with the allocation
1818
        offset: distance between element with zero multi-index and the
1819
                start of allocation
1820
        owner: Python object managing lifetime of USM allocation.
1821
               Value None implies transfer of USM allocation ownership
1822
               to the created array object.
1823
    Returns:
1824
        Created usm_ndarray instance
1825
    """
1826
    cdef size_t itemsize = type_bytesize(typenum)
1✔
1827
    cdef int err = 0
1✔
1828
    cdef size_t nelems = 1
1✔
1829
    cdef Py_ssize_t min_disp = 0
1✔
1830
    cdef Py_ssize_t max_disp = 0
1✔
1831
    cdef Py_ssize_t step_ = 0
1✔
1832
    cdef Py_ssize_t dim_ = 0
1✔
1833
    cdef it = 0
1✔
1834
    cdef c_dpmem._Memory mobj
1835
    cdef usm_ndarray arr
1836
    cdef object obj_shape
1837
    cdef object obj_strides
1838

1839
    if (nd < 0):
1✔
UNCOV
1840
        raise ValueError("Dimensionality must be non-negative")
×
1841
    if (ptr is NULL or QRef is NULL):
1✔
UNCOV
1842
        raise ValueError(
×
1843
            "Non-null USM allocation pointer and QRef are expected"
1844
        )
1845
    if (nd == 0):
1✔
1846
        # case of 0d scalars
1847
        mobj = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1848
            ptr, itemsize, QRef, memory_owner=owner
1849
        )
1850
        arr = usm_ndarray(
1✔
1851
            tuple(),
1✔
1852
            dtype=_make_typestr(typenum),
1✔
1853
            buffer=mobj
1✔
1854
        )
1855
        return arr
1✔
1856
    if (shape is NULL or strides is NULL):
1✔
UNCOV
1857
        raise ValueError("Both shape and stride vectors are required")
×
1858
    for it in range(nd):
1✔
1859
        dim_ = shape[it]
1✔
1860
        if dim_ < 0:
1✔
UNCOV
1861
            raise ValueError(
×
UNCOV
1862
                f"Dimension along axis {it} must be non-negative"
×
1863
            )
1864
        nelems *= dim_
1✔
1865
        if dim_ > 0:
1✔
1866
            step_ = strides[it]
1✔
1867
            if step_ > 0:
1✔
1868
                max_disp += step_ * (dim_ - 1)
1✔
1869
            else:
UNCOV
1870
                min_disp += step_ * (dim_ - 1)
×
1871

1872
    obj_shape = _make_int_tuple(nd, shape)
1✔
1873
    obj_strides = _make_int_tuple(nd, strides)
1✔
1874
    if nelems == 0:
1✔
1875
        mobj = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1876
            ptr, itemsize, QRef, memory_owner=owner
1877
        )
1878
        arr = usm_ndarray(
1✔
1879
            obj_shape,
1880
            dtype=_make_typestr(typenum),
1✔
1881
            strides=obj_strides,
1✔
1882
            buffer=mobj,
1✔
1883
            offset=0
1884
        )
1885
        return arr
1✔
1886
    if offset + min_disp < 0:
1✔
UNCOV
1887
        raise ValueError(
×
1888
            "Given shape, strides and offset reference out-of-bound memory"
1889
        )
1890
    nbytes = itemsize * (offset + max_disp + 1)
1✔
1891
    mobj = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1892
        ptr, nbytes, QRef, memory_owner=owner
1893
    )
1894
    arr = usm_ndarray(
1✔
1895
        obj_shape,
1896
        dtype=_make_typestr(typenum),
1✔
1897
        strides=obj_strides,
1✔
1898
        buffer=mobj,
1✔
1899
        offset=offset
1✔
1900
    )
1901
    return arr
1✔
1902

1903

1904
def _is_object_with_buffer_protocol(o):
1✔
1905
   "Returns True if object supports Python buffer protocol"
1906
   return _is_buffer(o)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc