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

IntelPython / dpctl / 14537256782

18 Apr 2025 03:10PM UTC coverage: 86.41% (+0.001%) from 86.409%
14537256782

Pull #2056

github

web-flow
Merge f63bdbb79 into f57963e87
Pull Request #2056: extend pre-commit hooks with cython-lint

3014 of 3710 branches covered (81.24%)

Branch coverage included in aggregate %.

205 of 263 new or added lines in 18 files covered. (77.95%)

4 existing lines in 3 files now uncovered.

12189 of 13884 relevant lines covered (87.79%)

7003.4 hits per line

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

92.17
/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 numpy as np
1✔
22

23
import dpctl
1✔
24
import dpctl.memory as dpmem
1✔
25

26
from .._backend cimport DPCTLSyclUSMRef
27
from .._sycl_device_factory cimport _cached_default_device
28

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

33
from cpython.mem cimport PyMem_Free
34
from cpython.tuple cimport PyTuple_New, PyTuple_SetItem
35

36
cimport dpctl as c_dpctl
37
cimport dpctl.memory as c_dpmem
38
cimport dpctl.tensor._dlpack as c_dlpack
39

40
from ._dlpack import get_build_dlpack_version
1✔
41

42
from .._sycl_device_factory cimport _cached_default_device
43

44
from enum import IntEnum
1✔
45

46
import dpctl.tensor._flags as _flags
1✔
47
from dpctl.tensor._tensor_impl import default_device_fp_type
1✔
48

49
include "_stride_utils.pxi"
50
include "_types.pxi"
51
include "_slicing.pxi"
52

53

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

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

105

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

113

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

123

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

128

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

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

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

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

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

150

151
cdef void _validate_and_use_stream(
1✔
152
    object stream, c_dpctl.SyclQueue self_queue
153
) except *:
154
    if (stream is None or stream == self_queue):
1✔
155
        pass
156
    else:
157
        if not isinstance(stream, dpctl.SyclQueue):
1✔
158
            raise TypeError(
1✔
159
                "stream argument type was expected to be dpctl.SyclQueue,"
1✔
160
                f" got {type(stream)} instead"
1✔
161
            )
162
        ev = self_queue.submit_barrier()
1✔
163
        stream.submit_barrier(dependent_events=[ev])
1✔
164

165
cdef class usm_ndarray:
166
    """ usm_ndarray(shape, dtype=None, strides=None, buffer="device", \
167
           offset=0, order="C", buffer_ctor_kwargs=dict(), \
168
           array_namespace=None)
169

170
    An array object represents a multidimensional tensor of numeric
171
    elements stored in a USM allocation on a SYCL device.
172

173
    Arg:
174
        shape (int, tuple):
175
            Shape of the array to be created.
176
        dtype (str, dtype):
177
            Array data type, i.e. the type of array elements.
178
            If ``dtype`` has the value ``None``, it is determined by default
179
            floating point type supported by target device.
180
            The supported types are
181

182
                ``bool``:
183
                    boolean type
184
                ``int8``, ``int16``, ``int32``, ``int64``:
185
                    signed integer types
186
                ``uint8``, ``uint16``, ``uint32``, ``uint64``:
187
                    unsigned integer types
188
                ``float16``:
189
                    half-precision floating type,
190
                    supported if target device's property
191
                    ``has_aspect_fp16`` is ``True``
192
                ``float32``, ``complex64``:
193
                    single-precision real and complex floating types
194
                ``float64``, ``complex128``:
195
                    double-precision real and complex floating
196
                    types, supported if target device's property
197
                    ``has_aspect_fp64`` is ``True``.
198

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

233
    ``buffer`` can be ``"shared"``, ``"host"``, ``"device"`` to allocate
234
    new device memory by calling respective constructor with
235
    the specified ``buffer_ctor_kwrds``; ``buffer`` can be an
236
    instance of :class:`dpctl.memory.MemoryUSMShared`,
237
    :class:`dpctl.memory.MemoryUSMDevice`, or
238
    :class:`dpctl.memory.MemoryUSMHost`; ``buffer`` can also be
239
    another :class:`dpctl.tensor.usm_ndarray` instance, in which case its
240
    underlying ``MemoryUSM*`` buffer is used.
241
    """
242

243
    cdef void _reset(usm_ndarray self):
1✔
244
        """
245
        Initializes member fields
246
        """
247
        self.base_ = None
1✔
248
        self.array_namespace_ = None
1✔
249
        self.nd_ = -1
1✔
250
        self.data_ = <char *>0
1✔
251
        self.shape_ = <Py_ssize_t *>0
1✔
252
        self.strides_ = <Py_ssize_t *>0
1✔
253
        self.flags_ = 0
1✔
254

255
    cdef void _cleanup(usm_ndarray self):
1✔
256
        if (self.shape_):
1✔
257
            PyMem_Free(self.shape_)
1✔
258
        if (self.strides_):
1✔
259
            PyMem_Free(self.strides_)
1✔
260
        self._reset()
1✔
261

262
    def __cinit__(self, shape, dtype=None, strides=None, buffer="device",
263
                  Py_ssize_t offset=0, order="C",
264
                  buffer_ctor_kwargs=dict(),
1✔
265
                  array_namespace=None):
266
        """
267
        strides and offset must be given in units of array elements.
268
        buffer can be strings ('device'|'shared'|'host' to allocate new memory)
269
        or ``dpctl.memory.MemoryUSM*`` buffers, or ``usm_ndarray`` instances.
270
        """
271
        cdef int nd = 0
1✔
272
        cdef int typenum = 0
1✔
273
        cdef int itemsize = 0
1✔
274
        cdef int err = 0
1✔
275
        cdef int contig_flag = 0
1✔
276
        cdef int writable_flag = USM_ARRAY_WRITABLE
1✔
277
        cdef Py_ssize_t *shape_ptr = NULL
1✔
278
        cdef Py_ssize_t ary_nelems = 0
1✔
279
        cdef Py_ssize_t ary_nbytes = 0
1✔
280
        cdef Py_ssize_t *strides_ptr = NULL
1✔
281
        cdef Py_ssize_t _offset = offset
1✔
282
        cdef Py_ssize_t ary_min_displacement = 0
1✔
283
        cdef Py_ssize_t ary_max_displacement = 0
1✔
284
        cdef bint is_fp64 = False
1✔
285
        cdef bint is_fp16 = False
1✔
286

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

410
    def __dealloc__(self):
411
        self._cleanup()
1✔
412

413
    @property
414
    def _pointer(self):
415
        """
416
        Returns USM pointer to the start of array (element with zero
417
        multi-index) encoded as integer.
418
        """
419
        return <size_t> self.get_data()
1✔
420

421
    cdef Py_ssize_t get_offset(self) except *:
1✔
422
        cdef char *mem_ptr = NULL
1✔
423
        cdef char *ary_ptr = self.get_data()
1✔
424
        mem_ptr = <char *>(<size_t> self.base_._pointer)
1✔
425
        byte_offset = ary_ptr - mem_ptr
1✔
426
        item_size = self.get_itemsize()
1✔
427
        if (byte_offset % item_size):
1✔
428
            raise InternalUSMArrayError(
×
429
                "byte_offset is not a multiple of item_size.")
430
        return byte_offset // item_size
1✔
431

432
    @property
433
    def _element_offset(self):
434
        """Returns the offset of the zero-index element of the array, in
435
        elements, relative to the start of memory allocation"""
436
        return self.get_offset()
1✔
437

438
    @property
439
    def _byte_bounds(self):
440
        """Returns a 2-tuple with pointers to the end-points of the array
441

442
        :Example:
443

444
            .. code-block:: python
445

446
                from dpctl import tensor
447

448
                x = tensor.ones((3, 10, 7))
449
                y = tensor.flip(x[:, 1::2], axis=1)
450

451
                beg_p, end_p = y._byte_bounds
452
                # Bytes taken to store this array
453
                bytes_extent = end_p - beg_p
454

455
                # C-contiguous copy is more compact
456
                yc = tensor.copy(y, order="C")
457
                beg_pc, end_pc = yc._byte_bounds
458
                assert bytes_extent < end_pc - beg_pc
459
        """
460
        cdef Py_ssize_t min_disp = 0
1✔
461
        cdef Py_ssize_t max_disp = 0
1✔
462
        cdef Py_ssize_t step_ = 0
1✔
463
        cdef Py_ssize_t dim_ = 0
1✔
464
        cdef int it = 0
1✔
465
        cdef Py_ssize_t _itemsize = self.get_itemsize()
1✔
466

467
        if (
1✔
468
            (self.flags_ & USM_ARRAY_C_CONTIGUOUS)
1✔
469
            or (self.flags_ & USM_ARRAY_F_CONTIGUOUS)
1✔
470
        ):
471
            return (
1✔
472
                self._pointer,
1✔
473
                self._pointer + shape_to_elem_count(
1✔
474
                    self.nd_, self.shape_
1✔
475
                ) * _itemsize
1✔
476
            )
477

478
        for it in range(self.nd_):
1✔
479
            dim_ = self.shape[it]
1✔
480
            if dim_ > 0:
1✔
481
                step_ = self.strides[it]
1✔
482
                if step_ > 0:
1✔
483
                    max_disp += step_ * (dim_ - 1)
1✔
484
                else:
485
                    min_disp += step_ * (dim_ - 1)
1✔
486

487
        return (
1✔
488
            self._pointer + min_disp * _itemsize,
1✔
489
            self._pointer + (max_disp + 1) * _itemsize
1✔
490
        )
491

492
    cdef char* get_data(self):
1✔
493
        """Returns the USM pointer for this array."""
494
        return self.data_
1✔
495

496
    cdef int get_ndim(self):
1✔
497
        """
498
        Returns the number of indices needed to address
499
        an element of this array.
500
        """
501
        return self.nd_
1✔
502

503
    cdef Py_ssize_t* get_shape(self):
1✔
504
        """
505
        Returns pointer to shape C-array for this array.
506

507
        C-array has at least ``ndim`` non-negative elements,
508
        which determine the range of permissible indices
509
        addressing individual elements of this array.
510
        """
511
        return self.shape_
1✔
512

513
    cdef Py_ssize_t* get_strides(self):
1✔
514
        """
515
        Returns pointer to strides C-array for this array.
516

517
        The pointer can be NULL (contiguous array), or the
518
        array size is at least ``ndim`` elements
519
        """
520
        return self.strides_
1✔
521

522
    cdef int get_typenum(self):
1✔
523
        """Returns typenum corresponding to values of this array"""
524
        return self.typenum_
1✔
525

526
    cdef int get_itemsize(self):
1✔
527
        """
528
        Returns itemsize of this arrays in bytes
529
        """
530
        return type_bytesize(self.typenum_)
1✔
531

532
    cdef int get_flags(self):
1✔
533
        """Returns flags of this array"""
534
        return self.flags_
1✔
535

536
    cdef object get_base(self):
1✔
537
        """Returns the object owning the USM data addressed by this array"""
538
        return self.base_
1✔
539

540
    cdef c_dpctl.SyclQueue get_sycl_queue(self):
1✔
541
        cdef c_dpmem._Memory mem
542
        if not isinstance(self.base_, dpctl.memory._Memory):
1✔
543
            raise InternalUSMArrayError(
×
544
                "This array has unexpected memory owner"
545
            )
546
        mem = <c_dpmem._Memory> self.base_
1✔
547
        return mem.queue
1✔
548

549
    cdef c_dpctl.DPCTLSyclQueueRef get_queue_ref(self) except *:
1✔
550
        """
551
        Returns a copy of DPCTLSyclQueueRef associated with array
552
        """
553
        cdef c_dpctl.SyclQueue q = self.get_sycl_queue()
1✔
554
        cdef c_dpctl.DPCTLSyclQueueRef QRef = q.get_queue_ref()
1✔
555
        cdef c_dpctl.DPCTLSyclQueueRef QRefCopy = NULL
1✔
556
        if QRef is not NULL:
1✔
557
            QRefCopy = c_dpctl.DPCTLQueue_Copy(QRef)
1✔
558
            return QRefCopy
1✔
559
        else:
560
            raise InternalUSMArrayError(
×
561
                "Memory owner of this array is corrupted"
562
            )
563

564
    @property
565
    def __sycl_usm_array_interface__(self):
566
        """
567
        Gives ``__sycl_usm_array_interface__`` dictionary describing
568
        the array.
569
        """
570
        cdef Py_ssize_t byte_offset = -1
1✔
571
        cdef int item_size = -1
1✔
572
        cdef Py_ssize_t elem_offset = -1
1✔
573
        cdef char *mem_ptr = NULL
1✔
574
        cdef char *ary_ptr = NULL
1✔
575
        if (not isinstance(self.base_, dpmem._memory._Memory)):
1✔
576
            raise InternalUSMArrayError(
×
577
                "Invalid instance of usm_ndarray encountered. "
578
                "Private field base_ has an unexpected type {}.".format(
×
579
                    type(self.base_)
×
580
                )
581
            )
582
        ary_iface = self.base_.__sycl_usm_array_interface__
1✔
583
        mem_ptr = <char *>(<size_t> ary_iface["data"][0])
1✔
584
        ary_ptr = <char *>(<size_t> self.data_)
1✔
585
        ro_flag = False if (self.flags_ & USM_ARRAY_WRITABLE) else True
1✔
586
        ary_iface["data"] = (<size_t> mem_ptr, ro_flag)
1✔
587
        ary_iface["shape"] = self.shape
1✔
588
        if (self.strides_):
1✔
589
            ary_iface["strides"] = _make_int_tuple(self.nd_, self.strides_)
1✔
590
        else:
591
            if (self.flags_ & USM_ARRAY_C_CONTIGUOUS):
1✔
592
                ary_iface["strides"] = None
1✔
593
            elif (self.flags_ & USM_ARRAY_F_CONTIGUOUS):
1✔
594
                ary_iface["strides"] = _f_contig_strides(self.nd_, self.shape_)
1✔
595
            else:
596
                raise InternalUSMArrayError(
×
597
                    "USM Array is not contiguous and has empty strides"
598
                )
599
        ary_iface["typestr"] = _make_typestr(self.typenum_)
1✔
600
        byte_offset = ary_ptr - mem_ptr
1✔
601
        item_size = self.get_itemsize()
1✔
602
        if (byte_offset % item_size):
1✔
603
            raise InternalUSMArrayError(
×
604
                "byte_offset is not a multiple of item_size.")
605
        elem_offset = byte_offset // item_size
1✔
606
        ary_iface["offset"] = elem_offset
1✔
607
        # must wait for content of the memory to finalize
608
        self.sycl_queue.wait()
1✔
609
        return ary_iface
1✔
610

611
    @property
612
    def ndim(self):
613
        """
614
        Gives the number of indices needed to address elements of this array.
615
        """
616
        return self.nd_
1✔
617

618
    @property
619
    def usm_data(self):
620
        """
621
        Gives USM memory object underlying :class:`.usm_ndarray` instance.
622
        """
623
        return self.get_base()
1✔
624

625
    @property
626
    def shape(self):
627
        """
628
        Elements of the shape tuple give the lengths of the
629
        respective array dimensions.
630

631
        Setting shape is allowed only when reshaping to the requested
632
        dimensions can be returned as view, otherwise :exc:`AttributeError`
633
        is raised. Use :func:`dpctl.tensor.reshape` to reshape the array
634
        in all cases.
635

636
        :Example:
637

638
            .. code-block:: python
639

640
                from dpctl import tensor
641

642
                x = tensor.arange(899)
643
                x.shape = (29, 31)
644
        """
645
        if self.nd_ > 0:
1✔
646
            return _make_int_tuple(self.nd_, self.shape_)
1✔
647
        else:
648
            return tuple()
1✔
649

650
    @shape.setter
651
    def shape(self, new_shape):
652
        """
653
        Modifies usm_ndarray instance in-place by changing its metadata
654
        about the shape and the strides of the array, or raises
655
        `AttributeError` exception if in-place change is not possible.
656

657
        Args:
658
            new_shape: (tuple, int)
659
                New shape. Only non-negative values are supported.
660
                The new shape may not lead to the change in the
661
                number of elements in the array.
662

663
        Whether the array can be reshape in-place depends on its
664
        strides. Use :func:`dpctl.tensor.reshape` function which
665
        always succeeds to reshape the array by performing a copy
666
        if necessary.
667
        """
668
        cdef int new_nd = -1
1✔
669
        cdef Py_ssize_t nelems = -1
1✔
670
        cdef int err = 0
1✔
671
        cdef Py_ssize_t min_disp = 0
1✔
672
        cdef Py_ssize_t max_disp = 0
1✔
673
        cdef int contig_flag = 0
1✔
674
        cdef Py_ssize_t *shape_ptr = NULL
1✔
675
        cdef Py_ssize_t *strides_ptr = NULL
1✔
676
        cdef Py_ssize_t size = -1
1✔
677
        import operator
1✔
678

679
        from ._reshape import reshaped_strides
1✔
680

681
        try:
1✔
682
            new_nd = len(new_shape)
1✔
683
        except TypeError:
1✔
684
            new_nd = 1
1✔
685
            new_shape = (new_shape,)
1✔
686
        try:
1✔
687
            new_shape = tuple(operator.index(dim) for dim in new_shape)
1✔
688
        except TypeError:
1✔
689
            raise TypeError(
1✔
690
                "Target shape must be a finite iterable of integers"
691
            )
692
        size = shape_to_elem_count(self.nd_, self.shape_)
1✔
693
        if not np.prod(new_shape) == size:
1✔
694
            raise TypeError(
×
695
                f"Can not reshape array of size {self.size} into {new_shape}"
×
696
            )
697
        if size > 0:
1✔
698
            new_strides = reshaped_strides(
699
               self.shape,
1✔
700
               self.strides,
1✔
701
               new_shape
1✔
702
            )
703
        else:
704
            new_strides = (1,) * len(new_shape)
1✔
705
        if new_strides is None:
1✔
706
            raise AttributeError(
1✔
707
                "Incompatible shape for in-place modification. "
708
                "Use `reshape()` to make a copy with the desired shape."
709
            )
710
        err = _from_input_shape_strides(
1✔
711
            new_nd, new_shape, new_strides,
712
            self.get_itemsize(),
1✔
713
            b"C",
714
            &shape_ptr, &strides_ptr,
715
            &nelems, &min_disp, &max_disp, &contig_flag
716
        )
717
        if (err == 0):
1✔
718
            if (self.shape_):
1✔
719
                PyMem_Free(self.shape_)
1✔
720
            if (self.strides_):
1✔
721
                PyMem_Free(self.strides_)
1✔
722
            self.flags_ = (contig_flag | (self.flags_ & USM_ARRAY_WRITABLE))
1✔
723
            self.nd_ = new_nd
1✔
724
            self.shape_ = shape_ptr
1✔
725
            self.strides_ = strides_ptr
1✔
726
        else:
727
            raise InternalUSMArrayError(
×
728
                "Encountered in shape setter, error code {err}".format(err)
×
729
            )
730

731
    @property
732
    def strides(self):
733
        """
734
        Returns memory displacement in array elements, upon unit
735
        change of respective index.
736

737
        For example, for strides ``(s1, s2, s3)`` and multi-index
738
        ``(i1, i2, i3)`` position of the respective element relative
739
        to zero multi-index element is ``s1*s1 + s2*i2 + s3*i3``.
740

741
        :Example:
742

743
            .. code-block:: python
744

745
                from dpctl import tensor
746

747
                x = tensor.zeros((20, 30))
748
                xv = x[10:, :15]
749

750
                multi_id = (3, 5)
751
                byte_displacement = xv[multi_id]._pointer - xv[0, 0]._pointer
752
                element_displacement = sum(
753
                    i * s for i, s in zip(multi_id, xv.strides)
754
                )
755
                assert byte_displacement == element_displacement * xv.itemsize
756
        """
757
        if (self.strides_):
1✔
758
            return _make_int_tuple(self.nd_, self.strides_)
1✔
759
        else:
760
            if (self.flags_ & USM_ARRAY_C_CONTIGUOUS):
1✔
761
                return _c_contig_strides(self.nd_, self.shape_)
1✔
762
            elif (self.flags_ & USM_ARRAY_F_CONTIGUOUS):
1✔
763
                return _f_contig_strides(self.nd_, self.shape_)
1✔
764
            else:
765
                raise ValueError("Inconsistent usm_ndarray data")
×
766

767
    @property
768
    def flags(self):
769
        """
770
        Returns :class:`dpctl.tensor._flags.Flags` object.
771
        """
772
        return _flags.Flags(self, self.flags_)
1✔
773

774
    cdef _set_writable_flag(self, int flag):
1✔
775
        cdef int mask = (USM_ARRAY_WRITABLE if flag else 0)
1✔
776
        self.flags_ = _copy_writable(self.flags_, mask)
1✔
777

778
    @property
779
    def usm_type(self):
780
        """
781
        USM type of underlying memory. Possible values are:
782

783
            * ``"device"``
784
                USM-device allocation in device memory, only accessible
785
                to kernels executed on the device
786
            * ``"shared"``
787
                USM-shared allocation in device memory, accessible both
788
                from the device and from host
789
            * ``"host"``
790
                USM-host allocation in host memory, accessible both
791
                from the device and from host
792

793
        See: https://docs.oneapi.com/versions/latest/dpcpp/iface/usm.html
794
        """
795
        return self.base_.get_usm_type()
1✔
796

797
    @property
798
    def itemsize(self):
799
        """
800
        Size of array element in bytes.
801
        """
802
        return self.get_itemsize()
1✔
803

804
    @property
805
    def nbytes(self):
806
        """
807
        Total bytes consumed by the elements of the array.
808
        """
809
        return (
1✔
810
            shape_to_elem_count(self.nd_, self.shape_) *
1✔
811
            self.get_itemsize())
1✔
812

813
    @property
814
    def size(self):
815
        """
816
        Number of elements in the array.
817
        """
818
        return shape_to_elem_count(self.nd_, self.shape_)
1✔
819

820
    @property
821
    def dtype(self):
822
        """
823
        Returns NumPy's dtype corresponding to the type of the array elements.
824
        """
825
        return np.dtype(_make_typestr(self.typenum_))
1✔
826

827
    @property
828
    def sycl_queue(self):
829
        """
830
        Returns :class:`dpctl.SyclQueue` object associated with USM data.
831
        """
832
        return self.get_sycl_queue()
1✔
833

834
    @property
835
    def sycl_device(self):
836
        """
837
        Returns :class:`dpctl.SyclDevice` object on which USM data
838
        was allocated.
839
        """
840
        q = self.sycl_queue
1✔
841
        return q.sycl_device
1✔
842

843
    @property
844
    def device(self):
845
        """
846
        Returns :class:`dpctl.tensor.Device` object representing
847
        residence of the array data.
848

849
        The ``Device`` object represents Array API notion of the
850
        device, and contains :class:`dpctl.SyclQueue` associated
851
        with this array. Hence, ``.device`` property provides
852
        information distinct from ``.sycl_device`` property.
853

854
        :Example:
855

856
            .. code-block:: python
857

858
                >>> from dpctl import tensor
859
                >>> x = tensor.ones(10)
860
                >>> x.device
861
                Device(level_zero:gpu:0)
862
        """
863
        return Device.create_device(self.sycl_queue)
1✔
864

865
    @property
866
    def sycl_context(self):
867
        """
868
        Returns :class:`dpctl.SyclContext` object to which USM data is bound.
869
        """
870
        q = self.sycl_queue
1✔
871
        return q.sycl_context
1✔
872

873
    @property
874
    def T(self):
875
        """Returns transposed array for 2D array, raises ``ValueError``
876
        otherwise.
877
        """
878
        if self.nd_ == 2:
1✔
879
            return _transpose(self)
1✔
880
        else:
881
            raise ValueError(
1✔
882
                "array.T requires array to have 2 dimensions. "
883
                "Use array.mT to transpose stacks of matrices and "
884
                "dpctl.tensor.permute_dims() to permute dimensions."
885
            )
886

887
    @property
888
    def mT(self):
889
        """ Returns array (a view) where the last two dimensions are
890
        transposed.
891
        """
892
        if self.nd_ < 2:
1✔
893
            raise ValueError(
1✔
894
                "array.mT requires array to have at least 2 dimensions."
895
            )
896
        return _m_transpose(self)
1✔
897

898
    @property
899
    def real(self):
900
        """
901
        Returns view into real component for arrays with
902
        complex data-types and returns itself for all other
903
        data-types.
904

905
        :Example:
906

907
            .. code-block:: python
908

909
                from dpctl import tensor
910

911
                # Create complex array from
912
                # arrays of real and imaginary parts
913

914
                re = tensor.linspace(-1, 1, num=100, dtype="f4")
915
                im = tensor.full_like(re, fill_value=tensor.pi)
916

917
                z = tensor.empty_like(re, dtype="c8")
918
                z.real[:] = re
919
                z.imag[:] = im
920
        """
921
        # explicitly check for UAR_HALF, which is greater than UAR_CFLOAT
922
        if (self.typenum_ < UAR_CFLOAT or self.typenum_ == UAR_HALF):
1✔
923
            # elements are real
924
            return self
1✔
925
        if (self.typenum_ < UAR_TYPE_SENTINEL):
1✔
926
            return _real_view(self)
1✔
927

928
    @property
929
    def imag(self):
930
        """ Returns view into imaginary component for arrays with
931
        complex data-types and returns new zero array for all other
932
        data-types.
933

934
        :Example:
935

936
            .. code-block:: python
937

938
                from dpctl import tensor
939

940
                # Reset imaginary part of complex array
941

942
                z = tensor.ones(100, dtype="c8")
943
                z.imag[:] = dpt.pi/2
944
        """
945
        # explicitly check for UAR_HALF, which is greater than UAR_CFLOAT
946
        if (self.typenum_ < UAR_CFLOAT or self.typenum_ == UAR_HALF):
1✔
947
            # elements are real
948
            return _zero_like(self)
1✔
949
        if (self.typenum_ < UAR_TYPE_SENTINEL):
1✔
950
            return _imag_view(self)
1✔
951

952
    def __getitem__(self, ind):
953
        cdef tuple _meta = _basic_slice_meta(
1✔
954
            ind, (<object>self).shape, (<object> self).strides,
1✔
955
            self.get_offset())
1✔
956
        cdef usm_ndarray res
957
        cdef int i = 0
1✔
958
        cdef bint matching = 1
1✔
959

960
        if len(_meta) < 5:
1✔
961
            raise RuntimeError
×
962

963
        res = usm_ndarray.__new__(
1✔
964
            usm_ndarray,
965
            _meta[0],
1✔
966
            dtype=_make_typestr(self.typenum_),
1✔
967
            strides=_meta[1],
1✔
968
            buffer=self.base_,
1✔
969
            offset=_meta[2]
1✔
970
        )
971
        res.array_namespace_ = self.array_namespace_
1✔
972

973
        adv_ind = _meta[3]
1✔
974
        adv_ind_start_p = _meta[4]
1✔
975

976
        if adv_ind_start_p < 0:
1✔
977
            res.flags_ = _copy_writable(res.flags_, self.flags_)
1✔
978
            return res
1✔
979

980
        from ._copy_utils import _extract_impl, _nonzero_impl, _take_multi_index
1✔
981

982
        # if len(adv_ind == 1), the (only) element is always an array
983
        if len(adv_ind) == 1 and adv_ind[0].dtype == dpt_bool:
1✔
984
            key_ = adv_ind[0]
1✔
985
            adv_ind_end_p = key_.ndim + adv_ind_start_p
1✔
986
            if adv_ind_end_p > res.ndim:
1✔
987
                raise IndexError("too many indices for the array")
1✔
988
            key_shape = key_.shape
1✔
989
            arr_shape = res.shape[adv_ind_start_p:adv_ind_end_p]
1✔
990
            for i in range(key_.ndim):
1✔
991
                if matching:
1✔
992
                    if not key_shape[i] == arr_shape[i] and key_shape[i] > 0:
1✔
993
                        matching = 0
1✔
994
            if not matching:
1✔
995
                raise IndexError(
1✔
996
                    "boolean index did not match indexed array in dimensions"
997
                )
998
            res = _extract_impl(res, key_, axis=adv_ind_start_p)
1✔
999
            res.flags_ = _copy_writable(res.flags_, self.flags_)
1✔
1000
            return res
1✔
1001

1002
        if any(
1✔
1003
            (
1004
                isinstance(ind, usm_ndarray) and ind.dtype == dpt_bool
1✔
1005
            ) for ind in adv_ind
1✔
1006
        ):
1007
            adv_ind_int = list()
1✔
1008
            for ind in adv_ind:
1✔
1009
                if isinstance(ind, usm_ndarray) and ind.dtype == dpt_bool:
1✔
1010
                    adv_ind_int.extend(_nonzero_impl(ind))
1✔
1011
                else:
1012
                    adv_ind_int.append(ind)
1✔
1013
            res = _take_multi_index(res, tuple(adv_ind_int), adv_ind_start_p)
1✔
1014
            res.flags_ = _copy_writable(res.flags_, self.flags_)
1✔
1015
            return res
1✔
1016

1017
        res = _take_multi_index(res, adv_ind, adv_ind_start_p)
1✔
1018
        res.flags_ = _copy_writable(res.flags_, self.flags_)
1✔
1019
        return res
1✔
1020

1021
    def to_device(self, target_device, /, *, stream=None):
1✔
1022
        """ to_device(target_device, /, *, stream=None)
1023

1024
        Transfers this array to specified target device.
1025

1026
        :Example:
1027
            .. code-block:: python
1028

1029
                import dpctl
1030
                import dpctl.tensor as dpt
1031

1032
                x = dpt.full(10**6, 2, dtype="int64")
1033
                q_prof = dpctl.SyclQueue(
1034
                    x.sycl_device, property="enable_profiling")
1035
                # return a view with profile-enabled queue
1036
                y = x.to_device(q_prof)
1037
                timer = dpctl.SyclTimer()
1038
                with timer(q_prof):
1039
                    z = y * y
1040
                print(timer.dt)
1041

1042
        Args:
1043
            target_device (object):
1044
                Array API concept of target device.
1045
                It can be a oneAPI filter selector string,
1046
                an instance of :class:`dpctl.SyclDevice` corresponding to a
1047
                non-partitioned SYCL device, an instance of
1048
                :class:`dpctl.SyclQueue`, or a :class:`dpctl.tensor.Device`
1049
                object returned by :attr:`dpctl.tensor.usm_ndarray.device`.
1050
            stream (:class:`dpctl.SyclQueue`, optional):
1051
                Execution queue to synchronize with. If ``None``,
1052
                synchronization is not performed.
1053

1054
        Returns:
1055
            usm_ndarray:
1056
                A view if data copy is not required, and a copy otherwise.
1057
                If copying is required, it is done by copying from the original
1058
                allocation device to the host, followed by copying from host
1059
                to the target device.
1060
        """
1061
        cdef c_dpctl.DPCTLSyclQueueRef QRef = NULL
1✔
1062
        cdef c_dpmem._Memory arr_buf
1063
        d = Device.create_device(target_device)
1✔
1064

1065
        _validate_and_use_stream(stream, self.sycl_queue)
1✔
1066

1067
        if (d.sycl_context == self.sycl_context):
1✔
1068
            arr_buf = <c_dpmem._Memory> self.usm_data
1✔
1069
            QRef = (<c_dpctl.SyclQueue> d.sycl_queue).get_queue_ref()
1✔
1070
            view_buffer = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1071
                <DPCTLSyclUSMRef>arr_buf.get_data_ptr(),
1✔
1072
                arr_buf.nbytes,
1073
                QRef,
1074
                memory_owner=arr_buf
1075
            )
1076
            res = usm_ndarray(
1✔
1077
                self.shape,
1✔
1078
                self.dtype,
1✔
1079
                buffer=view_buffer,
1✔
1080
                strides=self.strides,
1✔
1081
                offset=self.get_offset()
1✔
1082
            )
1083
            res.flags_ = self.flags_
1✔
1084
            return res
1✔
1085
        else:
1086
            nbytes = self.usm_data.nbytes
×
1087
            copy_buffer = type(self.usm_data)(
×
1088
                nbytes, queue=d.sycl_queue
×
1089
            )
1090
            copy_buffer.copy_from_device(self.usm_data)
×
1091
            res = usm_ndarray(
×
1092
                self.shape,
×
1093
                self.dtype,
×
1094
                buffer=copy_buffer,
×
1095
                strides=self.strides,
×
1096
                offset=self.get_offset()
×
1097
            )
1098
            res.flags_ = self.flags_
×
1099
            return res
×
1100

1101
    def _set_namespace(self, mod):
1✔
1102
        """ Sets array namespace to given module `mod`. """
1103
        self.array_namespace_ = mod
1✔
1104

1105
    def __array_namespace__(self, api_version=None):
1✔
1106
        """
1107
        Returns array namespace, member functions of which
1108
        implement data API.
1109

1110
        Args:
1111
            api_version (str, optional)
1112
                Request namespace compliant with given version of
1113
                array API. If ``None``, namespace for the most
1114
                recent supported version is returned.
1115
                Default: ``None``.
1116
        """
1117
        if api_version is not None:
1✔
1118
            from ._array_api import __array_api_version__
1✔
1119
            if not isinstance(api_version, str):
1✔
1120
                raise TypeError(f"Expected type str, got {type(api_version)}")
×
1121
            if api_version != __array_api_version__:
1✔
1122
                raise ValueError(f"Only {__array_api_version__} is supported")
×
1123
        return (
1✔
1124
            self.array_namespace_
1✔
1125
            if self.array_namespace_ is not None
1✔
1126
            else dpctl.tensor
1✔
1127
        )
1128

1129
    def __bool__(self):
1130
        if self.size == 1:
1✔
1131
            view = _as_zero_dim_ndarray(self)
1✔
1132
            return view.__bool__()
1✔
1133

1134
        if self.size == 0:
1✔
1135
            raise ValueError(
1✔
1136
                "The truth value of an empty array is ambiguous"
1137
            )
1138

1139
        raise ValueError(
1✔
1140
            "The truth value of an array with more than one element is "
1141
            "ambiguous. Use dpctl.tensor.any() or dpctl.tensor.all()"
1142
        )
1143

1144
    def __float__(self):
1145
        if self.size == 1:
1✔
1146
            view = _as_zero_dim_ndarray(self)
1✔
1147
            return view.__float__()
1✔
1148

1149
        raise ValueError(
1✔
1150
            "only size-1 arrays can be converted to Python scalars"
1151
        )
1152

1153
    def __complex__(self):
1✔
1154
        if self.size == 1:
1✔
1155
            view = _as_zero_dim_ndarray(self)
1✔
1156
            return view.__complex__()
1✔
1157

1158
        raise ValueError(
1✔
1159
            "only size-1 arrays can be converted to Python scalars"
1160
        )
1161

1162
    def __int__(self):
1163
        if self.size == 1:
1✔
1164
            view = _as_zero_dim_ndarray(self)
1✔
1165
            return view.__int__()
1✔
1166

1167
        raise ValueError(
1✔
1168
            "only size-1 arrays can be converted to Python scalars"
1169
        )
1170

1171
    def __index__(self):
1172
        if np.issubdtype(self.dtype, np.integer):
1✔
1173
            return int(self)
1✔
1174

1175
        raise IndexError("only integer arrays are valid indices")
1✔
1176

1177
    def __abs__(self):
1178
        return dpctl.tensor.abs(self)
1✔
1179

1180
    def __add__(self, other):
1181
        """
1182
        Implementation for operator.add
1183
        """
1184
        return dpctl.tensor.add(self, other)
1✔
1185

1186
    def __and__(self, other):
1187
        "Implementation for operator.and"
1188
        return dpctl.tensor.bitwise_and(self, other)
1✔
1189

1190
    def __dlpack__(
1✔
1191
        self, *, stream=None, max_version=None, dl_device=None, copy=None
1✔
1192
    ):
1193
        """
1194
        Produces DLPack capsule.
1195

1196
        Args:
1197
            stream (:class:`dpctl.SyclQueue`, optional):
1198
                Execution queue to synchronize with.
1199
                If ``None``, synchronization is not performed.
1200
                Default: ``None``.
1201
            max_version (tuple[int, int], optional):
1202
                The maximum DLPack version the consumer (caller of
1203
                ``__dlpack__``) supports. As ``__dlpack__`` may not
1204
                always return a DLPack capsule with version
1205
                `max_version`, the consumer must verify the version
1206
                even if this argument is passed.
1207
                Default: ``None``.
1208
            dl_device (tuple[enum.Enum, int], optional):
1209
                The device the returned DLPack capsule will be
1210
                placed on.
1211
                The device must be a 2-tuple matching the format of
1212
                ``__dlpack_device__`` method, an integer enumerator
1213
                representing the device type followed by an integer
1214
                representing the index of the device.
1215
                Default: ``None``.
1216
            copy (bool, optional):
1217
                Boolean indicating whether or not to copy the input.
1218

1219
                * If ``copy`` is ``True``, the input will always be
1220
                  copied.
1221
                * If ``False``, a ``BufferError`` will be raised if a
1222
                  copy is deemed necessary.
1223
                * If ``None``, a copy will be made only if deemed
1224
                  necessary, otherwise, the existing memory buffer will
1225
                  be reused.
1226

1227
                Default: ``None``.
1228

1229
        Raises:
1230
            MemoryError:
1231
                when host memory can not be allocated.
1232
            DLPackCreationError:
1233
                when array is allocated on a partitioned
1234
                SYCL device, or with a non-default context.
1235
            BufferError:
1236
                when a copy is deemed necessary but ``copy``
1237
                is ``False`` or when the provided ``dl_device``
1238
                cannot be handled.
1239
        """
1240
        if max_version is None:
1✔
1241
            # legacy path for DLManagedTensor
1242
            # copy kwarg ignored because copy flag can't be set
1243
            _caps = c_dlpack.to_dlpack_capsule(self)
1✔
1244
            _validate_and_use_stream(stream, self.sycl_queue)
1✔
1245
            return _caps
1✔
1246
        else:
1247
            if not isinstance(max_version, tuple) or len(max_version) != 2:
1✔
1248
                raise TypeError(
1✔
1249
                    "`__dlpack__` expects `max_version` to be a "
1✔
1250
                    "2-tuple of integers `(major, minor)`, instead "
1251
                    f"got {max_version}"
1✔
1252
                )
1253
            dpctl_dlpack_version = get_build_dlpack_version()
1✔
1254
            if max_version[0] >= dpctl_dlpack_version[0]:
1✔
1255
                # DLManagedTensorVersioned path
1256
                if dl_device is not None:
1✔
1257
                    if not isinstance(dl_device, tuple) or len(dl_device) != 2:
1✔
1258
                        raise TypeError(
1✔
1259
                            "`__dlpack__` expects `dl_device` to be a 2-tuple "
1✔
1260
                            "of `(device_type, device_id)`, instead "
1261
                            f"got {dl_device}"
1✔
1262
                        )
1263
                    if dl_device != self.__dlpack_device__():
1✔
1264
                        if copy is False:
1✔
1265
                            raise BufferError(
×
1266
                                "array cannot be placed on the requested "
1267
                                "device without a copy"
1268
                            )
1269
                        if _is_host_cpu(dl_device):
1✔
1270
                            if stream is not None:
1✔
1271
                                raise ValueError(
×
1272
                                    "`stream` must be `None` when `dl_device` "
1273
                                    "is of type `kDLCPU`"
1274
                                )
1275
                            from ._copy_utils import _copy_to_numpy
1✔
1276
                            _arr = _copy_to_numpy(self)
1✔
1277
                            _arr.flags["W"] = self.flags["W"]
1✔
1278
                            return c_dlpack.numpy_to_dlpack_versioned_capsule(
1✔
1279
                                _arr, True
1✔
1280
                            )
1281
                        else:
1282
                            raise BufferError(
×
NEW
1283
                                f"targeting `dl_device` {dl_device} with "
×
1284
                                "`__dlpack__` is not yet implemented"
1285
                            )
1286
                if copy is None:
1✔
1287
                    copy = False
1✔
1288
                # TODO: strategy for handling stream on different device
1289
                # from dl_device
1290
                if copy:
1✔
1291
                    _validate_and_use_stream(stream, self.sycl_queue)
1✔
1292
                    nbytes = self.usm_data.nbytes
1✔
1293
                    copy_buffer = type(self.usm_data)(
1✔
1294
                        nbytes, queue=self.sycl_queue
1✔
1295
                    )
1296
                    copy_buffer.copy_from_device(self.usm_data)
1✔
1297
                    _copied_arr = usm_ndarray(
1✔
1298
                        self.shape,
1✔
1299
                        self.dtype,
1✔
1300
                        buffer=copy_buffer,
1✔
1301
                        strides=self.strides,
1✔
1302
                        offset=self.get_offset()
1✔
1303
                    )
1304
                    _copied_arr.flags_ = self.flags_
1✔
1305
                    _caps = c_dlpack.to_dlpack_versioned_capsule(
1✔
1306
                        _copied_arr, copy
1✔
1307
                    )
1308
                else:
1309
                    _caps = c_dlpack.to_dlpack_versioned_capsule(self, copy)
1✔
1310
                    _validate_and_use_stream(stream, self.sycl_queue)
1✔
1311
                return _caps
1✔
1312
            else:
1313
                # legacy path for DLManagedTensor
1314
                _caps = c_dlpack.to_dlpack_capsule(self)
1✔
1315
                _validate_and_use_stream(stream, self.sycl_queue)
1✔
1316
                return _caps
1✔
1317

1318
    def __dlpack_device__(self):
1✔
1319
        """
1320
        Gives a tuple (``device_type``, ``device_id``) corresponding to
1321
        ``DLDevice`` entry in ``DLTensor`` in DLPack protocol.
1322

1323
        The tuple describes the non-partitioned device where the array has been
1324
        allocated, or the non-partitioned parent device of the allocation
1325
        device.
1326

1327
        See ``DLDeviceType`` for a list of devices supported by the DLPack
1328
        protocol.
1329

1330
        Raises:
1331
            DLPackCreationError:
1332
                when the ``device_id`` could not be determined.
1333
        """
1334
        try:
1✔
1335
            dev_id = self.sycl_device.get_device_id()
1✔
1336
        except ValueError as e:
×
1337
            raise c_dlpack.DLPackCreationError(
×
1338
                "Could not determine id of the device where array was "
1339
                "allocated."
1340
            )
1341
        return (
1✔
1342
            DLDeviceType.kDLOneAPI,
1✔
1343
            dev_id,
1✔
1344
        )
1345

1346
    def __eq__(self, other):
1347
        return dpctl.tensor.equal(self, other)
1✔
1348

1349
    def __floordiv__(self, other):
1350
        return dpctl.tensor.floor_divide(self, other)
1✔
1351

1352
    def __ge__(self, other):
1353
        return dpctl.tensor.greater_equal(self, other)
1✔
1354

1355
    def __gt__(self, other):
1356
        return dpctl.tensor.greater(self, other)
1✔
1357

1358
    def __invert__(self):
1359
        return dpctl.tensor.bitwise_invert(self)
1✔
1360

1361
    def __le__(self, other):
1362
        return dpctl.tensor.less_equal(self, other)
1✔
1363

1364
    def __len__(self):
1365
        if (self.nd_):
1✔
1366
            return self.shape[0]
1✔
1367
        else:
1368
            raise TypeError("len() of unsized object")
1✔
1369

1370
    def __lshift__(self, other):
1371
        return dpctl.tensor.bitwise_left_shift(self, other)
1✔
1372

1373
    def __lt__(self, other):
1374
        return dpctl.tensor.less(self, other)
1✔
1375

1376
    def __matmul__(self, other):
1377
        return dpctl.tensor.matmul(self, other)
1✔
1378

1379
    def __mod__(self, other):
1380
        return dpctl.tensor.remainder(self, other)
1✔
1381

1382
    def __mul__(self, other):
1383
        return dpctl.tensor.multiply(self, other)
1✔
1384

1385
    def __ne__(self, other):
1386
        return dpctl.tensor.not_equal(self, other)
1✔
1387

1388
    def __neg__(self):
1389
        return dpctl.tensor.negative(self)
1✔
1390

1391
    def __or__(self, other):
1392
        return dpctl.tensor.bitwise_or(self, other)
1✔
1393

1394
    def __pos__(self):
1395
        return dpctl.tensor.positive(self)
1✔
1396

1397
    def __pow__(self, other):
1398
        return dpctl.tensor.pow(self, other)
1✔
1399

1400
    def __rshift__(self, other):
1401
        return dpctl.tensor.bitwise_right_shift(self, other)
1✔
1402

1403
    def __setitem__(self, key, rhs):
1404
        cdef tuple _meta
1405
        cdef usm_ndarray Xv
1406

1407
        if (self.flags_ & USM_ARRAY_WRITABLE) == 0:
1✔
1408
            raise ValueError("Can not modify read-only array.")
1✔
1409

1410
        _meta = _basic_slice_meta(
1✔
1411
            key, (<object>self).shape, (<object> self).strides,
1✔
1412
            self.get_offset()
1✔
1413
        )
1414

1415
        if len(_meta) < 5:
1✔
1416
            raise RuntimeError
×
1417

1418
        Xv = usm_ndarray.__new__(
1✔
1419
            usm_ndarray,
1420
            _meta[0],
1✔
1421
            dtype=_make_typestr(self.typenum_),
1✔
1422
            strides=_meta[1],
1✔
1423
            buffer=self.base_,
1✔
1424
            offset=_meta[2],
1✔
1425
        )
1426
        # set namespace
1427
        Xv.array_namespace_ = self.array_namespace_
1✔
1428

1429
        from ._copy_utils import (
1✔
1430
            _copy_from_numpy_into,
1✔
1431
            _copy_from_usm_ndarray_to_usm_ndarray,
1432
            _nonzero_impl,
1433
            _place_impl,
1434
            _put_multi_index,
1435
        )
1436

1437
        adv_ind = _meta[3]
1✔
1438
        adv_ind_start_p = _meta[4]
1✔
1439

1440
        if adv_ind_start_p < 0:
1✔
1441
            # basic slicing
1442
            if isinstance(rhs, usm_ndarray):
1✔
1443
                _copy_from_usm_ndarray_to_usm_ndarray(Xv, rhs)
1✔
1444
            else:
1445
                if hasattr(rhs, "__sycl_usm_array_interface__"):
1✔
1446
                    from dpctl.tensor import asarray
×
1447
                    try:
×
1448
                        rhs_ar = asarray(rhs)
×
1449
                        _copy_from_usm_ndarray_to_usm_ndarray(Xv, rhs_ar)
×
1450
                    except Exception:
×
1451
                        raise ValueError(
×
1452
                            f"Input of type {type(rhs)} could not be "
×
1453
                            "converted to usm_ndarray"
1454
                        )
1455
                else:
1456
                    rhs_np = np.asarray(rhs)
1✔
1457
                    if type_bytesize(rhs_np.dtype.num) < 0:
1✔
1458
                        raise ValueError(
×
1459
                            f"Input of type {type(rhs)} can not be "
×
1460
                            "assigned to usm_ndarray because of "
1461
                            f"unsupported data type '{rhs_np.dtype}'"
×
1462
                        )
1463
                    try:
1✔
1464
                        _copy_from_numpy_into(Xv, rhs_np)
1✔
1465
                    except Exception:
×
1466
                        raise ValueError(
×
1467
                            f"Input of type {type(rhs)} could not be "
×
1468
                            "copied into dpctl.tensor.usm_ndarray"
1469
                        )
1470
            return
1✔
1471

1472
        if len(adv_ind) == 1 and adv_ind[0].dtype == dpt_bool:
1✔
1473
            _place_impl(Xv, adv_ind[0], rhs, axis=adv_ind_start_p)
1✔
1474
            return
1✔
1475

1476
        if any(
1✔
1477
            (
1478
                isinstance(ind, usm_ndarray) and ind.dtype == dpt_bool
1✔
1479
            ) for ind in adv_ind
1✔
1480
        ):
1481
            adv_ind_int = list()
1✔
1482
            for ind in adv_ind:
1✔
1483
                if isinstance(ind, usm_ndarray) and ind.dtype == dpt_bool:
1✔
1484
                    adv_ind_int.extend(_nonzero_impl(ind))
1✔
1485
                else:
1486
                    adv_ind_int.append(ind)
1✔
1487
            _put_multi_index(Xv, tuple(adv_ind_int), adv_ind_start_p, rhs)
1✔
1488
            return
1✔
1489

1490
        _put_multi_index(Xv, adv_ind, adv_ind_start_p, rhs)
1✔
1491
        return
1✔
1492

1493
    def __sub__(self, other):
1494
        return dpctl.tensor.subtract(self, other)
1✔
1495

1496
    def __truediv__(self, other):
1497
        return dpctl.tensor.divide(self, other)
1✔
1498

1499
    def __xor__(self, other):
1500
        return dpctl.tensor.bitwise_xor(self, other)
1✔
1501

1502
    def __radd__(self, other):
1503
        return dpctl.tensor.add(other, self)
1✔
1504

1505
    def __rand__(self, other):
1506
        return dpctl.tensor.bitwise_and(other, self)
1✔
1507

1508
    def __rfloordiv__(self, other):
1509
        return dpctl.tensor.floor_divide(other, self)
1✔
1510

1511
    def __rlshift__(self, other):
1512
        return dpctl.tensor.bitwise_left_shift(other, self)
1✔
1513

1514
    def __rmatmul__(self, other):
1515
        return dpctl.tensor.matmul(other, self)
1✔
1516

1517
    def __rmod__(self, other):
1518
        return dpctl.tensor.remainder(other, self)
1✔
1519

1520
    def __rmul__(self, other):
1521
        return dpctl.tensor.multiply(other, self)
1✔
1522

1523
    def __ror__(self, other):
1524
        return dpctl.tensor.bitwise_or(other, self)
1✔
1525

1526
    def __rpow__(self, other):
1527
        return dpctl.tensor.pow(other, self)
1✔
1528

1529
    def __rrshift__(self, other):
1530
        return dpctl.tensor.bitwise_right_shift(other, self)
1✔
1531

1532
    def __rsub__(self, other):
1533
        return dpctl.tensor.subtract(other, self)
1✔
1534

1535
    def __rtruediv__(self, other):
1536
        return dpctl.tensor.divide(other, self)
1✔
1537

1538
    def __rxor__(self, other):
1539
        return dpctl.tensor.bitwise_xor(other, self)
1✔
1540

1541
    def __iadd__(self, other):
1542
        return dpctl.tensor.add._inplace_op(self, other)
1✔
1543

1544
    def __iand__(self, other):
1545
        return dpctl.tensor.bitwise_and._inplace_op(self, other)
1✔
1546

1547
    def __ifloordiv__(self, other):
1548
        return dpctl.tensor.floor_divide._inplace_op(self, other)
1✔
1549

1550
    def __ilshift__(self, other):
1551
        return dpctl.tensor.bitwise_left_shift._inplace_op(self, other)
1✔
1552

1553
    def __imatmul__(self, other):
1554
        return dpctl.tensor.matmul(self, other, out=self, dtype=self.dtype)
1✔
1555

1556
    def __imod__(self, other):
1557
        return dpctl.tensor.remainder._inplace_op(self, other)
1✔
1558

1559
    def __imul__(self, other):
1560
        return dpctl.tensor.multiply._inplace_op(self, other)
1✔
1561

1562
    def __ior__(self, other):
1563
        return dpctl.tensor.bitwise_or._inplace_op(self, other)
1✔
1564

1565
    def __ipow__(self, other):
1566
        return dpctl.tensor.pow._inplace_op(self, other)
1✔
1567

1568
    def __irshift__(self, other):
1569
        return dpctl.tensor.bitwise_right_shift._inplace_op(self, other)
1✔
1570

1571
    def __isub__(self, other):
1572
        return dpctl.tensor.subtract._inplace_op(self, other)
1✔
1573

1574
    def __itruediv__(self, other):
1575
        return dpctl.tensor.divide._inplace_op(self, other)
1✔
1576

1577
    def __ixor__(self, other):
1578
        return dpctl.tensor.bitwise_xor._inplace_op(self, other)
1✔
1579

1580
    def __str__(self):
1581
        return usm_ndarray_str(self)
1✔
1582

1583
    def __repr__(self):
1584
        return usm_ndarray_repr(self)
1✔
1585

1586
    def __array__(self, dtype=None, /, *, copy=None):
1✔
1587
        """NumPy's array protocol method to disallow implicit conversion.
1588

1589
        Without this definition, `numpy.asarray(usm_ar)` converts
1590
        usm_ndarray instance into NumPy array with data type `object`
1591
        and every element being 0d usm_ndarray.
1592

1593
        https://github.com/IntelPython/dpctl/pull/1384#issuecomment-1707212972
1594
        """
1595
        raise TypeError(
1✔
1596
            "Implicit conversion to a NumPy array is not allowed. "
1597
            "Use `dpctl.tensor.asnumpy` to copy data from this "
1598
            "`dpctl.tensor.usm_ndarray` instance to NumPy array"
1599
        )
1600

1601

1602
cdef usm_ndarray _real_view(usm_ndarray ary):
1✔
1603
    """
1604
    View into real parts of a complex type array
1605
    """
1606
    cdef int r_typenum_ = -1
1✔
1607
    cdef usm_ndarray r = None
1✔
1608
    cdef Py_ssize_t offset_elems = 0
1✔
1609

1610
    if (ary.typenum_ == UAR_CFLOAT):
1✔
1611
        r_typenum_ = UAR_FLOAT
1✔
1612
    elif (ary.typenum_ == UAR_CDOUBLE):
1✔
1613
        r_typenum_ = UAR_DOUBLE
1✔
1614
    else:
1615
        raise InternalUSMArrayError(
×
1616
            "_real_view call on array of non-complex type.")
1617

1618
    offset_elems = ary.get_offset() * 2
1✔
1619
    r = usm_ndarray.__new__(
1✔
1620
        usm_ndarray,
1621
        _make_int_tuple(ary.nd_, ary.shape_) if ary.nd_ > 0 else tuple(),
1✔
1622
        dtype=_make_typestr(r_typenum_),
1✔
1623
        strides=tuple(2 * si for si in ary.strides),
1✔
1624
        buffer=ary.base_,
1✔
1625
        offset=offset_elems,
1✔
1626
        order=("C" if (ary.flags_ & USM_ARRAY_C_CONTIGUOUS) else "F")
1✔
1627
    )
1628
    r.flags_ = _copy_writable(r.flags_, ary.flags_)
1✔
1629
    r.array_namespace_ = ary.array_namespace_
1✔
1630
    return r
1✔
1631

1632

1633
cdef usm_ndarray _imag_view(usm_ndarray ary):
1✔
1634
    """
1635
    View into imaginary parts of a complex type array
1636
    """
1637
    cdef int r_typenum_ = -1
1✔
1638
    cdef usm_ndarray r = None
1✔
1639
    cdef Py_ssize_t offset_elems = 0
1✔
1640

1641
    if (ary.typenum_ == UAR_CFLOAT):
1✔
1642
        r_typenum_ = UAR_FLOAT
1✔
1643
    elif (ary.typenum_ == UAR_CDOUBLE):
1✔
1644
        r_typenum_ = UAR_DOUBLE
1✔
1645
    else:
1646
        raise InternalUSMArrayError(
×
1647
            "_imag_view call on array of non-complex type.")
1648

1649
    # displace pointer to imaginary part
1650
    offset_elems = 2 * ary.get_offset() + 1
1✔
1651
    r = usm_ndarray.__new__(
1✔
1652
        usm_ndarray,
1653
        _make_int_tuple(ary.nd_, ary.shape_) if ary.nd_ > 0 else tuple(),
1✔
1654
        dtype=_make_typestr(r_typenum_),
1✔
1655
        strides=tuple(2 * si for si in ary.strides),
1✔
1656
        buffer=ary.base_,
1✔
1657
        offset=offset_elems,
1✔
1658
        order=("C" if (ary.flags_ & USM_ARRAY_C_CONTIGUOUS) else "F")
1✔
1659
    )
1660
    r.flags_ = _copy_writable(r.flags_, ary.flags_)
1✔
1661
    r.array_namespace_ = ary.array_namespace_
1✔
1662
    return r
1✔
1663

1664

1665
cdef usm_ndarray _transpose(usm_ndarray ary):
1✔
1666
    """
1667
    Construct transposed array without copying the data
1668
    """
1669
    cdef usm_ndarray r = usm_ndarray.__new__(
1✔
1670
        usm_ndarray,
1671
        _make_reversed_int_tuple(ary.nd_, ary.shape_),
1✔
1672
        dtype=_make_typestr(ary.typenum_),
1✔
1673
        strides=(
1674
            _make_reversed_int_tuple(ary.nd_, ary.strides_)
1✔
1675
            if (ary.strides_) else None),
1✔
1676
        buffer=ary.base_,
1✔
1677
        order=("F" if (ary.flags_ & USM_ARRAY_C_CONTIGUOUS) else "C"),
1✔
1678
        offset=ary.get_offset()
1✔
1679
    )
1680
    r.flags_ = _copy_writable(r.flags_, ary.flags_)
1✔
1681
    return r
1✔
1682

1683

1684
cdef usm_ndarray _m_transpose(usm_ndarray ary):
1✔
1685
    """
1686
    Construct matrix transposed array
1687
    """
1688
    cdef usm_ndarray r = usm_ndarray.__new__(
1✔
1689
        usm_ndarray,
1690
        _swap_last_two(_make_int_tuple(ary.nd_, ary.shape_)),
1✔
1691
        dtype=_make_typestr(ary.typenum_),
1✔
1692
        strides=_swap_last_two(ary.strides),
1✔
1693
        buffer=ary.base_,
1✔
1694
        order=("F" if (ary.flags_ & USM_ARRAY_C_CONTIGUOUS) else "C"),
1✔
1695
        offset=ary.get_offset()
1✔
1696
    )
1697
    r.flags_ = _copy_writable(r.flags_, ary.flags_)
1✔
1698
    return r
1✔
1699

1700

1701
cdef usm_ndarray _zero_like(usm_ndarray ary):
1✔
1702
    """
1703
    Make C-contiguous array of zero elements with same shape,
1704
    type, device, and sycl_queue as ary.
1705
    """
1706
    cdef dt = _make_typestr(ary.typenum_)
1✔
1707
    cdef usm_ndarray r = usm_ndarray(
1✔
1708
        _make_int_tuple(ary.nd_, ary.shape_) if ary.nd_ > 0 else tuple(),
1✔
1709
        dtype=dt,
1✔
1710
        buffer=ary.base_.get_usm_type(),
1✔
1711
        buffer_ctor_kwargs={"queue": ary.get_sycl_queue()},
1✔
1712
    )
1713
    r.base_.memset()
1✔
1714
    return r
1✔
1715

1716

1717
cdef api char* UsmNDArray_GetData(usm_ndarray arr):
1✔
1718
    """Get allocation pointer of zero index element of array """
1719
    return arr.get_data()
1✔
1720

1721

1722
cdef api int UsmNDArray_GetNDim(usm_ndarray arr):
1✔
1723
    """Get array rank: length of its shape"""
1724
    return arr.get_ndim()
1✔
1725

1726

1727
cdef api Py_ssize_t* UsmNDArray_GetShape(usm_ndarray arr):
1✔
1728
    """Get host pointer to shape vector"""
1729
    return arr.get_shape()
1✔
1730

1731

1732
cdef api Py_ssize_t* UsmNDArray_GetStrides(usm_ndarray arr):
1✔
1733
    """Get host pointer to strides vector"""
1734
    return arr.get_strides()
1✔
1735

1736

1737
cdef api int UsmNDArray_GetTypenum(usm_ndarray arr):
1✔
1738
    """Get type number for data type of array elements"""
1739
    return arr.get_typenum()
1✔
1740

1741

1742
cdef api int UsmNDArray_GetElementSize(usm_ndarray arr):
1✔
1743
    """Get array element size in bytes"""
1744
    return arr.get_itemsize()
1✔
1745

1746

1747
cdef api int UsmNDArray_GetFlags(usm_ndarray arr):
1✔
1748
    """Get flags of array"""
1749
    return arr.get_flags()
1✔
1750

1751

1752
cdef api c_dpctl.DPCTLSyclQueueRef UsmNDArray_GetQueueRef(usm_ndarray arr):
1✔
1753
    """Get DPCTLSyclQueueRef for queue associated with the array"""
1754
    return arr.get_queue_ref()
1✔
1755

1756

1757
cdef api Py_ssize_t UsmNDArray_GetOffset(usm_ndarray arr):
1✔
1758
    """Get offset of zero-index array element from the beginning of the USM
1759
    allocation"""
1760
    return arr.get_offset()
1✔
1761

1762

1763
cdef api object UsmNDArray_GetUSMData(usm_ndarray arr):
1✔
1764
    """Get USM data object underlying the array"""
1765
    return arr.get_base()
1✔
1766

1767

1768
cdef api void UsmNDArray_SetWritableFlag(usm_ndarray arr, int flag):
1✔
1769
    """Set/unset USM_ARRAY_WRITABLE in the given array `arr`."""
1770
    arr._set_writable_flag(flag)
1✔
1771

1772

1773
cdef api object UsmNDArray_MakeSimpleFromMemory(
1✔
1774
    int nd, const Py_ssize_t *shape, int typenum,
1775
    c_dpmem._Memory mobj, Py_ssize_t offset, char order
1776
):
1777
    """Create contiguous usm_ndarray.
1778

1779
    Args:
1780
        nd: number of dimensions (non-negative)
1781
        shape: array of nd non-negative array's sizes along each dimension
1782
        typenum: array elemental type number
1783
        ptr: pointer to the start of allocation
1784
        QRef: DPCTLSyclQueueRef associated with the allocation
1785
        offset: distance between element with zero multi-index and the
1786
                start of allocation
1787
        order: Memory layout of the array. Use 'C' for C-contiguous or
1788
               row-major layout; 'F' for F-contiguous or column-major layout
1789
    Returns:
1790
        Created usm_ndarray instance
1791
    """
1792
    cdef object shape_tuple = _make_int_tuple(nd, <Py_ssize_t *>shape)
1✔
1793
    cdef usm_ndarray arr = usm_ndarray(
1✔
1794
        shape_tuple,
1795
        dtype=_make_typestr(typenum),
1✔
1796
        buffer=mobj,
1✔
1797
        offset=offset,
1✔
1798
        order=<bytes>(order)
1✔
1799
    )
1800
    return arr
1✔
1801

1802

1803
cdef api object UsmNDArray_MakeSimpleFromPtr(
1✔
1804
    size_t nelems,
1805
    int typenum,
1806
    c_dpctl.DPCTLSyclUSMRef ptr,
1807
    c_dpctl.DPCTLSyclQueueRef QRef,
1808
    object owner
1809
):
1810
    """Create 1D contiguous usm_ndarray from pointer.
1811

1812
    Args:
1813
        nelems: number of elements in array
1814
        typenum: array elemental type number
1815
        ptr: pointer to the start of allocation
1816
        QRef: DPCTLSyclQueueRef associated with the allocation
1817
        owner: Python object managing lifetime of USM allocation.
1818
               Value None implies transfer of USM allocation ownership
1819
               to the created array object.
1820
    Returns:
1821
        Created usm_ndarray instance
1822
    """
1823
    cdef int itemsize = type_bytesize(typenum)
1✔
1824
    if (itemsize < 1):
1✔
1825
        raise ValueError(
1✔
1826
            "dtype with typenum=" + str(typenum) + " is not supported."
1✔
1827
        )
1828
    cdef size_t nbytes = (<size_t> itemsize) * nelems
1✔
1829
    cdef c_dpmem._Memory mobj
1830
    mobj = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1831
        ptr, nbytes, QRef, memory_owner=owner
1832
    )
1833
    cdef usm_ndarray arr = usm_ndarray(
1✔
1834
        (nelems,),
1✔
1835
        dtype=_make_typestr(typenum),
1✔
1836
        buffer=mobj
1✔
1837
    )
1838
    return arr
1✔
1839

1840
cdef api object UsmNDArray_MakeFromPtr(
1✔
1841
    int nd,
1842
    const Py_ssize_t *shape,
1843
    int typenum,
1844
    const Py_ssize_t *strides,
1845
    c_dpctl.DPCTLSyclUSMRef ptr,
1846
    c_dpctl.DPCTLSyclQueueRef QRef,
1847
    Py_ssize_t offset,
1848
    object owner
1849
):
1850
    """
1851
    General usm_ndarray constructor from externally made USM-allocation.
1852

1853
    Args:
1854
        nd: number of dimensions (non-negative)
1855
        shape: array of nd non-negative array's sizes along each dimension
1856
        typenum: array elemental type number
1857
        strides: array of nd strides along each dimension in elements
1858
        ptr: pointer to the start of allocation
1859
        QRef: DPCTLSyclQueueRef associated with the allocation
1860
        offset: distance between element with zero multi-index and the
1861
                start of allocation
1862
        owner: Python object managing lifetime of USM allocation.
1863
               Value None implies transfer of USM allocation ownership
1864
               to the created array object.
1865
    Returns:
1866
        Created usm_ndarray instance
1867
    """
1868
    cdef int itemsize = type_bytesize(typenum)
1✔
1869
    cdef size_t nelems = 1
1✔
1870
    cdef Py_ssize_t min_disp = 0
1✔
1871
    cdef Py_ssize_t max_disp = 0
1✔
1872
    cdef Py_ssize_t step_ = 0
1✔
1873
    cdef Py_ssize_t dim_ = 0
1✔
1874
    cdef it = 0
1✔
1875
    cdef c_dpmem._Memory mobj
1876
    cdef usm_ndarray arr
1877
    cdef object obj_shape
1878
    cdef object obj_strides
1879

1880
    if (itemsize < 1):
1✔
1881
        raise ValueError(
1✔
1882
            "dtype with typenum=" + str(typenum) + " is not supported."
1✔
1883
        )
1884
    if (nd < 0):
1✔
1885
        raise ValueError("Dimensionality must be non-negative")
×
1886
    if (ptr is NULL or QRef is NULL):
1✔
1887
        raise ValueError(
×
1888
            "Non-null USM allocation pointer and QRef are expected"
1889
        )
1890
    if (nd == 0):
1✔
1891
        # case of 0d scalars
1892
        mobj = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1893
            ptr, itemsize, QRef, memory_owner=owner
1894
        )
1895
        arr = usm_ndarray(
1✔
1896
            tuple(),
1✔
1897
            dtype=_make_typestr(typenum),
1✔
1898
            buffer=mobj
1✔
1899
        )
1900
        return arr
1✔
1901
    if (shape is NULL or strides is NULL):
1✔
1902
        raise ValueError("Both shape and stride vectors are required")
×
1903
    for it in range(nd):
1✔
1904
        dim_ = shape[it]
1✔
1905
        if dim_ < 0:
1✔
1906
            raise ValueError(
×
1907
                f"Dimension along axis {it} must be non-negative"
×
1908
            )
1909
        nelems *= dim_
1✔
1910
        if dim_ > 0:
1✔
1911
            step_ = strides[it]
1✔
1912
            if step_ > 0:
1✔
1913
                max_disp += step_ * (dim_ - 1)
1✔
1914
            else:
1915
                min_disp += step_ * (dim_ - 1)
×
1916

1917
    obj_shape = _make_int_tuple(nd, shape)
1✔
1918
    obj_strides = _make_int_tuple(nd, strides)
1✔
1919
    if nelems == 0:
1✔
1920
        mobj = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1921
            ptr, itemsize, QRef, memory_owner=owner
1922
        )
1923
        arr = usm_ndarray(
1✔
1924
            obj_shape,
1925
            dtype=_make_typestr(typenum),
1✔
1926
            strides=obj_strides,
1✔
1927
            buffer=mobj,
1✔
1928
            offset=0
1929
        )
1930
        return arr
1✔
1931
    if offset + min_disp < 0:
1✔
1932
        raise ValueError(
×
1933
            "Given shape, strides and offset reference out-of-bound memory"
1934
        )
1935
    nbytes = (<size_t> itemsize) * (offset + max_disp + 1)
1✔
1936
    mobj = c_dpmem._Memory.create_from_usm_pointer_size_qref(
1✔
1937
        ptr, nbytes, QRef, memory_owner=owner
1938
    )
1939
    arr = usm_ndarray(
1✔
1940
        obj_shape,
1941
        dtype=_make_typestr(typenum),
1✔
1942
        strides=obj_strides,
1✔
1943
        buffer=mobj,
1✔
1944
        offset=offset
1✔
1945
    )
1946
    return arr
1✔
1947

1948

1949
def _is_object_with_buffer_protocol(o):
1✔
1950
    "Returns True if object supports Python buffer protocol"
1951
    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