From f292bb63f59c7ed1f815d1c0e99b080c21f6e2c8 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 15 Apr 2025 11:43:15 +0200 Subject: [PATCH 01/23] Add a blank line prior Default --- dpnp/dpnp_array.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index a70a4e99e668..9f627ca82dfc 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -126,7 +126,7 @@ def mT(self): Raises ------ ValueError - If the array is of dimension less than 2. + If the array is of dimension less than ``2``. Examples -------- @@ -217,6 +217,7 @@ def __array_namespace__(self, /, *, api_version=None): Request namespace compliant with given version of array API. If ``None``, namespace for the most recent supported version is returned. + Default: ``None``. Returns @@ -273,12 +274,14 @@ def __dlpack__( stream : {:class:`dpctl.SyclQueue`, None}, optional Execution queue to synchronize with. If ``None``, synchronization is not performed. + Default: ``None``. max_version {tuple of ints, None}, optional The maximum DLPack version the consumer (caller of ``__dlpack__``) supports. As ``__dlpack__`` may not always return a DLPack capsule with version `max_version`, the consumer must verify the version even if this argument is passed. + Default: ``None``. dl_device {tuple, None}, optional: The device the returned DLPack capsule will be placed on. The @@ -286,6 +289,7 @@ def __dlpack__( ``__dlpack_device__`` method, an integer enumerator representing the device type followed by an integer representing the index of the device. + Default: ``None``. copy {bool, None}, optional: Boolean indicating whether or not to copy the input. @@ -655,7 +659,7 @@ def _create_from_usm_ndarray(usm_ary: dpt.usm_ndarray): def all(self, axis=None, out=None, keepdims=False, *, where=True): """ - Returns True if all elements evaluate to True. + Returns ``True`` if all elements evaluate to ``True.`` Refer to :obj:`dpnp.all` for full documentation. @@ -671,7 +675,7 @@ def all(self, axis=None, out=None, keepdims=False, *, where=True): def any(self, axis=None, out=None, keepdims=False, *, where=True): """ - Returns True if any of the elements of `a` evaluate to True. + Returns ``True`` if any of the elements of `a` evaluate to ``True``. Refer to :obj:`dpnp.any` for full documentation. @@ -721,22 +725,26 @@ def argsort( Axis along which to sort. If ``None``, the array is flattened before sorting. The default is ``-1``, which sorts along the last axis. + Default: ``-1``. kind : {None, "stable", "mergesort", "radixsort"}, optional Sorting algorithm. The default is ``None``, which uses parallel merge-sort or parallel radix-sort algorithms depending on the array data type. + Default: ``None``. descending : bool, optional Sort order. If ``True``, the array must be sorted in descending order (by value). If ``False``, the array must be sorted in ascending order (by value). + Default: ``False``. stable : {None, bool}, optional Sort stability. If ``True``, the returned array will maintain the relative order of `a` values which compare as equal. The same behavior applies when set to ``False`` or ``None``. Internally, this option selects ``kind="stable"``. + Default: ``None``. See Also @@ -935,6 +943,7 @@ def copy(self, order="C", device=None, usm_type=None, sycl_queue=None): ---------- order : {None, "C", "F", "A", "K"}, optional Memory layout of the newly output array. + Default: ``"C"``. device : {None, string, SyclDevice, SyclQueue, Device}, optional An array API concept of device where the output array is created. @@ -947,12 +956,14 @@ def copy(self, order="C", device=None, usm_type=None, sycl_queue=None): Default: ``None``. usm_type : {None, "device", "shared", "host"}, optional The type of SYCL USM allocation for the output array. + Default: ``None``. sycl_queue : {None, SyclQueue}, optional A SYCL queue to use for output array allocation and copying. The `sycl_queue` can be passed as ``None`` (the default), which means to get the SYCL queue from `device` keyword if present or to use a default queue. + Default: ``None``. Returns @@ -1208,6 +1219,7 @@ def imag(self, value): array([1.+9.j, 3.+9.j, 5.+9.j]) """ + if dpnp.issubdtype(self.dtype, dpnp.complexfloating): dpnp.copyto(self._array_obj.imag, value) else: @@ -1673,22 +1685,26 @@ def sort( axis : int, optional Axis along which to sort. The default is ``-1``, which sorts along the last axis. + Default: ``-1``. kind : {None, "stable", "mergesort", "radixsort"}, optional Sorting algorithm. The default is ``None``, which uses parallel merge-sort or parallel radix-sort algorithms depending on the array data type. + Default: ``None``. descending : bool, optional Sort order. If ``True``, the array must be sorted in descending order (by value). If ``False``, the array must be sorted in ascending order (by value). + Default: ``False``. stable : {None, bool}, optional Sort stability. If ``True``, the returned array will maintain the relative order of `a` values which compare as equal. The same behavior applies when set to ``False`` or ``None``. Internally, this option selects ``kind="stable"``. + Default: ``None``. See Also @@ -1851,6 +1867,7 @@ def to_device(self, device, /, *, stream=None): stream : {SyclQueue, None}, optional Execution queue to synchronize with. If ``None``, synchronization is not performed. + Default: ``None``. Returns From fafb86fd3796eabc6de52bde9caddd4de1a7f62a Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 15 Apr 2025 11:44:24 +0200 Subject: [PATCH 02/23] Enable pylink hook --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e2a26056f12..033de2bb2aff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -119,7 +119,7 @@ repos: "--disable=redefined-builtin", "--disable=unused-wildcard-import" ] - files: '^dpnp/(dpnp_iface.*|fft|linalg)' + files: '^dpnp/(dpnp_iface.*|fft|linalg|dpnp_array)' - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks rev: v2.14.0 hooks: From 88c9a2c230fdf1a8b1a0e8bb3e989995b4f54888 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 15 Apr 2025 14:25:28 +0200 Subject: [PATCH 03/23] Add missing docstrings to methods and attributes --- .flake8 | 2 + dpnp/dpnp_array.py | 325 ++++++++++++++++++++++++++++++--------------- 2 files changed, 223 insertions(+), 104 deletions(-) diff --git a/.flake8 b/.flake8 index 68a21b79ec4a..3203f7e4f698 100644 --- a/.flake8 +++ b/.flake8 @@ -24,6 +24,8 @@ extend-ignore = D105, # missing docstring in __init__: D107, + # TODO: remove: + D200, D403, # no blank lines allowed after function docstring: D202, # 1 blank line required between summary line and description: diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 9f627ca82dfc..1f98ec3e8f63 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -24,6 +24,14 @@ # THE POSSIBILITY OF SUCH DAMAGE. # ***************************************************************************** +""" +TODO: add mdoule docstring + +""" + +# pylint: disable=invalid-name +# pylint: disable=protected-access + import dpctl.tensor as dpt import dpctl.tensor._type_utils as dtu from dpctl.tensor._numpy_helper import AxisError @@ -36,8 +44,8 @@ def _get_unwrapped_index_key(key): """ Get an unwrapped index key. - Return a key where each nested instance of DPNP array is unwrapped into USM ndarray - for further processing in DPCTL advanced indexing functions. + Return a key where each nested instance of DPNP array is unwrapped into + USM ndarray for further processing in DPCTL advanced indexing functions. """ @@ -52,15 +60,18 @@ def _get_unwrapped_index_key(key): return key +# pylint: disable=too-many-public-methods class dpnp_array: """ - Multi-dimensional array object. + An array object represents a multidimensional tensor of numeric elements + stored in a USM allocation on a SYCL device. - This is a wrapper around dpctl.tensor.usm_ndarray that provides + This is a wrapper around :class:`dpctl.tensor.usm_ndarray` that provides methods to be compliant with original NumPy. """ + # pylint: disable=too-many-positional-arguments def __init__( self, shape, @@ -104,15 +115,46 @@ def __init__( @property def __sycl_usm_array_interface__(self): + """ + Give ``__sycl_usm_array_interface__`` dictionary describing the array. + + """ return self._array_obj.__sycl_usm_array_interface__ def get_array(self): - """Get usm_ndarray object.""" + """Get :class:`dpctl.tensor.usm_ndarray` object.""" return self._array_obj @property def T(self): - """View of the transposed array.""" + """ + View of the transposed array. + + Same as ``self.transpose()``. + + See Also + -------- + :obj:`dpnp.transpose` : Equivalent function. + + Examples + -------- + >>> import dpnp as np + >>> a = np.array([[1, 2], [3, 4]]) + >>> a + array([[1, 2], + [3, 4]]) + >>> a.T + array([[1, 3], + [2, 4]]) + + >>> a = np.array([1, 2, 3, 4]) + >>> a + array([1, 2, 3, 4]) + >>> a.T + array([1, 2, 3, 4]) + + """ + return self.transpose() @property @@ -159,38 +201,93 @@ def mT(self): return dpnp_array._create_from_usm_ndarray(self._array_obj.mT) @property - def sycl_queue(self): - return self._array_obj.sycl_queue + def device(self): + """ + Return :class:`dpctl.tensor.Device` object representing residence of + the array data. - @property - def sycl_device(self): - return self._array_obj.sycl_device + The ``Device`` object represents Array API notion of the device, and + contains :class:`dpctl.SyclQueue` associated with this array. Hence, + ``.device`` property provides information distinct from ``.sycl_device`` + property. + + Examples + -------- + >>> import dpnp as np + >>> x = np.ones(10) + >>> x.device + Device(level_zero:gpu:0) + + """ + + return self._array_obj.device @property def sycl_context(self): + """ + Return :class:`dpctl.SyclContext` object to which USM data is bound. + + """ return self._array_obj.sycl_context @property - def device(self): - return self._array_obj.device + def sycl_device(self): + """ + Return :class:`dpctl.SyclDevice` object on which USM data was + allocated. + + """ + return self._array_obj.sycl_device + + @property + def sycl_queue(self): + """ + Return :class:`dpctl.SyclQueue` object associated with USM data. + + """ + return self._array_obj.sycl_queue @property def usm_type(self): + """ + USM type of underlying memory. Possible values are: + + * ``"device"`` + USM-device allocation in device memory, only accessible to kernels + executed on the device + * ``"shared"`` + USM-shared allocation in device memory, accessible both from the + device and from the host + * ``"host"`` + USM-host allocation in host memory, accessible both from the device + and from the host + + """ + return self._array_obj.usm_type def __abs__(self): - r"""Return ``\|self\|``.""" + """Return :math:`|self|`.""" return dpnp.abs(self) def __add__(self, other): - """Return ``self+value``.""" + """Return :math:`self+value`.""" return dpnp.add(self, other) def __and__(self, other): - """Return ``self&value``.""" + """Return :math:`self&value`.""" return dpnp.bitwise_and(self, other) def __array__(self, dtype=None, /, *, copy=None): + """ + NumPy's array protocol method to disallow implicit conversion. + + Without this definition, ``numpy.asarray(dpnp_arr)`` converts + :class:`dpnp.ndarray` instance into NumPy array with data type `object` + and every element being zero-dimensional :class:`dpnp.ndarray`. + + """ + raise TypeError( "Implicit conversion to a NumPy array is not allowed. " "Please use `.asnumpy()` to construct a NumPy array explicitly." @@ -199,13 +296,6 @@ def __array__(self, dtype=None, /, *, copy=None): # '__array_finalize__', # '__array_function__', # '__array_interface__', - # '__array_prepare__', - # '__array_priority__', - # '__array_struct__', - - __array_ufunc__ = None - - # '__array_wrap__', def __array_namespace__(self, /, *, api_version=None): """ @@ -233,6 +323,14 @@ def __array_namespace__(self, /, *, api_version=None): return self._array_obj.__array_namespace__(api_version=api_version) + # '__array_prepare__', + # '__array_priority__', + # '__array_struct__', + + __array_ufunc__ = None + + # '__array_wrap__', + def __bool__(self): """``True`` if self else ``False``.""" return self._array_obj.__bool__() @@ -241,6 +339,7 @@ def __bool__(self): # `__class_getitem__`, def __complex__(self): + """Convert a zero-dimensional array to a Python complex object.""" return self._array_obj.__complex__() def __contains__(self, value, /): @@ -249,7 +348,8 @@ def __contains__(self, value, /): def __copy__(self): """ - Used if :func:`copy.copy` is called on an array. Returns a copy of the array. + Used if :func:`copy.copy` is called on an array. Returns a copy of the + array. Equivalent to ``a.copy(order="K")``. @@ -261,7 +361,6 @@ def __copy__(self): # '__delitem__', # '__dir__', # '__divmod__', - # '__doc__', def __dlpack__( self, *, stream=None, max_version=None, dl_device=None, copy=None @@ -340,27 +439,30 @@ def __dlpack_device__(self): return self._array_obj.__dlpack_device__() + # '__doc__', + def __eq__(self, other): - """Return ``self==value``.""" + """Return :math:`self==value`.""" return dpnp.equal(self, other) def __float__(self): + """Convert a zero-dimensional array to a Python float object.""" return self._array_obj.__float__() def __floordiv__(self, other): - """Return ``self//value``.""" + """Return :math:`self//value`.""" return dpnp.floor_divide(self, other) # '__format__', def __ge__(self, other): - """Return ``self>=value``.""" + """Return :math:`self>=value`.""" return dpnp.greater_equal(self, other) # '__getattribute__', def __getitem__(self, key): - """Return ``self[key]``.""" + """Return :math:`self[key]`.""" key = _get_unwrapped_index_key(key) item = self._array_obj.__getitem__(key) @@ -369,41 +471,39 @@ def __getitem__(self, key): # '__getstate__', def __gt__(self, other): - """Return ``self>value``.""" + """Return :math:`self>value`.""" return dpnp.greater(self, other) # '__hash__', def __iadd__(self, other): - """Return ``self+=value``.""" + """Return :math:`self+=value`:math:.""" dpnp.add(self, other, out=self) return self def __iand__(self, other): - """Return ``self&=value``.""" + """Return :math:`self&=value`:math:.""" dpnp.bitwise_and(self, other, out=self) return self def __ifloordiv__(self, other): - """Return ``self//=value``.""" + """Return :math:`self//=value`.""" dpnp.floor_divide(self, other, out=self) return self def __ilshift__(self, other): - """Return ``self<<=value``.""" + """Return :math:`self<<=value`.""" dpnp.left_shift(self, other, out=self) return self def __imatmul__(self, other): - """Return ``self@=value``.""" + """Return :math:`self@=value`.""" - """ - Unlike `matmul(a, b, out=a)` we ensure that the result is not broadcast - if the result without `out` would have less dimensions than `a`. - Since the signature of matmul is '(n?,k),(k,m?)->(n?,m?)' this is the - case exactly when the second operand has both core dimensions. - We have to enforce this check by passing the correct `axes=`. - """ + # Unlike `matmul(a, b, out=a)` we ensure that the result isn't broadcast + # if the result without `out` would have less dimensions than `a`. + # Since the signature of matmul is '(n?,k),(k,m?)->(n?,m?)' this is the + # case exactly when the second operand has both core dimensions. + # We have to enforce this check by passing the correct `axes=`. if self.ndim == 1: axes = [(-1,), (-2, -1), (-1,)] else: @@ -411,130 +511,132 @@ def __imatmul__(self, other): try: dpnp.matmul(self, other, out=self, dtype=self.dtype, axes=axes) - except AxisError: + except AxisError as e: # AxisError should indicate that the axes argument didn't work out # which should mean the second operand not being 2 dimensional. raise ValueError( "inplace matrix multiplication requires the first operand to " "have at least one and the second at least two dimensions." - ) + ) from e return self def __imod__(self, other): - """Return ``self%=value``.""" + """Return :math:`self%=value`.""" dpnp.remainder(self, other, out=self) return self def __imul__(self, other): - """Return ``self*=value``.""" + """Return :math:`self*=value`.""" dpnp.multiply(self, other, out=self) return self def __index__(self): + """Convert a zero-dimensional array to a Python int object.""" return self._array_obj.__index__() # '__init__', # '__init_subclass__', def __int__(self): + """Convert a zero-dimensional array to a Python int object.""" return self._array_obj.__int__() def __invert__(self): - """Return ``~self``.""" + """Return :math:`~self`.""" return dpnp.invert(self) def __ior__(self, other): - """Return ``self|=value``.""" + """Return :math:`self|=value`.""" dpnp.bitwise_or(self, other, out=self) return self def __ipow__(self, other): - """Return ``self**=value``.""" + """Return :math:`self**=value`.""" dpnp.power(self, other, out=self) return self def __irshift__(self, other): - """Return ``self>>=value``.""" + """Return :math:`self>>=value`.""" dpnp.right_shift(self, other, out=self) return self def __isub__(self, other): - """Return ``self-=value``.""" + """Return :math:`self-=value`.""" dpnp.subtract(self, other, out=self) return self def __iter__(self): - """Return ``iter(self)``.""" + """Return :math:`iter(self)`.""" if self.ndim == 0: raise TypeError("iteration over a 0-d array") return (self[i] for i in range(self.shape[0])) def __itruediv__(self, other): - """Return ``self/=value``.""" + """Return :math:`self/=value`.""" dpnp.true_divide(self, other, out=self) return self def __ixor__(self, other): - """Return ``self^=value``.""" + """Return :math:`self^=value`.""" dpnp.bitwise_xor(self, other, out=self) return self def __le__(self, other): - """Return ``self<=value``.""" + """Return :math:`self<=value`.""" return dpnp.less_equal(self, other) def __len__(self): - """Return ``len(self)``.""" + """Return :math:`len(self)`.""" return self._array_obj.__len__() def __lshift__(self, other): - """Return ``self<>self``.""" + """Return :math:`value>>self`.""" return dpnp.right_shift(other, self) def __rshift__(self, other): - """Return ``self>>value``.""" + """Return :math:`self>>value`.""" return dpnp.right_shift(self, other) def __rsub__(self, other): - """Return ``value-self``.""" + """Return :math:`value-self`.""" return dpnp.subtract(other, self) def __rtruediv__(self, other): - """Return ``value/self``.""" + """Return :math:`value/self`.""" return dpnp.true_divide(other, self) def __rxor__(self, other): - """Return ``value^self``.""" + """Return :math:`value^self`.""" return dpnp.bitwise_xor(other, self) # '__setattr__', @@ -610,17 +712,17 @@ def __setitem__(self, key, val): __slots__ = ("_array_obj",) def __str__(self): - """Return ``str(self)``.""" + """Return :math:`str(self)`.""" return self._array_obj.__str__() def __sub__(self, other): - """Return ``self-value``.""" + """Return :math:`self-value`.""" return dpnp.subtract(self, other) # '__subclasshook__', def __truediv__(self, other): - """Return ``self/value``.""" + """Return :math:`self/value`.""" return dpnp.true_divide(self, other) @property @@ -632,9 +734,9 @@ def __usm_ndarray__(self): corresponding to the content of the object. This property is intended to speed-up conversion from - :class:`dpnp.ndarray` to :class:`dpctl.tensor.usm_ndarray` passed - into `dpctl.tensor.asarray` function. The input object that implements - `__usm_ndarray__` protocol is recognized as owner of USM allocation + :class:`dpnp.ndarray` to :class:`dpctl.tensor.usm_ndarray` passed into + :func:`dpctl.tensor.asarray` function. The input object that implements + ``__usm_ndarray__`` protocol is recognized as owner of USM allocation that is managed by a smart pointer, and asynchronous deallocation will not involve GIL. @@ -643,11 +745,17 @@ def __usm_ndarray__(self): return self._array_obj def __xor__(self, other): - """Return ``self^value``.""" + """Return :math:`self^value`.""" return dpnp.bitwise_xor(self, other) @staticmethod def _create_from_usm_ndarray(usm_ary: dpt.usm_ndarray): + """ + Return :class:`dpnp.ndarray` instance from USM allocation providing + by an instance of :class:`dpctl.tensor.usm_ndarray`. + + """ + if not isinstance(usm_ary, dpt.usm_ndarray): raise TypeError( f"Expected dpctl.tensor.usm_ndarray, got {type(usm_ary)}" @@ -715,7 +823,8 @@ def argsort( self, axis=-1, kind=None, order=None, *, descending=False, stable=None ): """ - Return an ndarray of indices that sort the array along the specified axis. + Return an ndarray of indices that sort the array along the specified + axis. Refer to :obj:`dpnp.argsort` for full documentation. @@ -829,8 +938,8 @@ def astype( Specifies whether to copy an array when the specified dtype matches the data type of that array. If ``True``, a newly allocated array must always be returned. If ``False`` and the specified dtype - matches the data type of that array, the self array must be returned; - otherwise, a newly allocated array must be returned. + matches the data type of that array, the self array must be + returned; otherwise, a newly allocated array must be returned. Default: ``True``. device : {None, string, SyclDevice, SyclQueue, Device}, optional @@ -880,7 +989,8 @@ def astype( def choose(self, /, choices, out=None, mode="wrap"): """ - Use an array as index array to construct a new array from a set of choices. + Use an array as index array to construct a new array from a set of + choices. Refer to :obj:`dpnp.choose` for full documentation. @@ -915,10 +1025,7 @@ def conj(self): """ - if not dpnp.issubdtype(self.dtype, dpnp.complexfloating): - return self - else: - return dpnp.conjugate(self) + return self.conjugate() def conjugate(self): """ @@ -930,8 +1037,7 @@ def conjugate(self): if not dpnp.issubdtype(self.dtype, dpnp.complexfloating): return self - else: - return dpnp.conjugate(self) + return dpnp.conjugate(self) def copy(self, order="C", device=None, usm_type=None, sycl_queue=None): """ @@ -1089,7 +1195,10 @@ def dot(self, b, out=None): @property def dtype(self): - """Returns NumPy's dtype corresponding to the type of the array elements.""" + """ + Returns NumPy's dtype corresponding to the type of the array elements. + + """ return self._array_obj.dtype @@ -1122,6 +1231,7 @@ def fill(self, value): """ # lazy import avoids circular imports + # pylint: disable=import-outside-toplevel from .dpnp_algo.dpnp_fill import dpnp_fill dpnp_fill(self, value) @@ -1134,7 +1244,10 @@ def flags(self): @property def flat(self): - """Return a flat iterator, or set a flattened version of self to value.""" + """ + Return a flat iterator, or set a flattened version of self to value. + + """ return dpnp.flatiter(self) @@ -1245,7 +1358,8 @@ def item(self, *args): Returns ------- out : Standard Python scalar object - A copy of the specified element of the array as a suitable Python scalar. + A copy of the specified element of the array as a suitable Python + scalar. Examples -------- @@ -1537,7 +1651,8 @@ def reshape(self, *shape, order="C", copy=None): Notes ----- Unlike the free function `dpnp.reshape`, this method on `ndarray` allows - the elements of the shape parameter to be passed in as separate arguments. + the elements of the shape parameter to be passed in as separate + arguments. For example, ``a.reshape(10, 11)`` is equivalent to ``a.reshape((10, 11))``. @@ -1936,8 +2051,10 @@ def transpose(self, *axes): See Also -------- :obj:`dpnp.transpose` : Equivalent function. - :obj:`dpnp.ndarray.ndarray.T` : Array property returning the array transposed. - :obj:`dpnp.ndarray.reshape` : Give a new shape to an array without changing its data. + :obj:`dpnp.ndarray.ndarray.T` : Array property returning the array + transposed. + :obj:`dpnp.ndarray.reshape` : Give a new shape to an array without + changing its data. Examples -------- From e0b08686e6882a003aaf1c59b291ceca61d89332 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 15 Apr 2025 14:26:41 +0200 Subject: [PATCH 04/23] Add links to missing attriburtes and methods on reference page --- doc/reference/ndarray.rst | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/doc/reference/ndarray.rst b/doc/reference/ndarray.rst index 4f9aef8a9160..0e388cf4eab7 100644 --- a/doc/reference/ndarray.rst +++ b/doc/reference/ndarray.rst @@ -66,7 +66,11 @@ of the array: dpnp.ndarray.size dpnp.ndarray.itemsize dpnp.ndarray.nbytes - dpnp.ndarray.base + dpnp.ndarray.device + dpnp.ndarray.sycl_context + dpnp.ndarray.sycl_device + dpnp.ndarray.sycl_queue + dpnp.ndarray.usm_type Data type @@ -98,6 +102,17 @@ Other attributes dpnp.ndarray.flat +Special attributes +------------------ + +.. autosummary:: + :toctree: generated/ + :nosignatures: + + dpnp.ndarray.__sycl_usm_array_interface__ + dpnp.ndarray.__usm_ndarray__ + + Array methods ------------- @@ -145,6 +160,7 @@ Array conversion dpnp.ndarray.getfield dpnp.ndarray.setflags dpnp.ndarray.fill + dpnp.ndarray.get_array Shape manipulation @@ -371,7 +387,10 @@ Basic customization: dpnp.ndarray.__new__ dpnp.ndarray.__array__ + dpnp.ndarray.__array_namespace__ dpnp.ndarray.__array_wrap__ + dpnp.ndarray.__dlpack__ + dpnp.ndarray.__dlpack_device__ Container customization: (see :ref:`Indexing `) @@ -380,12 +399,13 @@ Container customization: (see :ref:`Indexing `) :nosignatures: dpnp.ndarray.__len__ + dpnp.ndarray.__iter__ dpnp.ndarray.__getitem__ dpnp.ndarray.__setitem__ dpnp.ndarray.__contains__ -Conversion; the operations :class:`int() `, -:class:`float() ` and :class:`complex() `. +Conversion; the operations :class:`int() `, :class:`float() `, +:class:`complex() ` and :func:`operator.index() `. They work only on arrays that have one element in them and return the appropriate scalar. @@ -393,6 +413,7 @@ and return the appropriate scalar. :toctree: generated/ :nosignatures: + dpnp.ndarray.__index__ dpnp.ndarray.__int__ dpnp.ndarray.__float__ dpnp.ndarray.__complex__ From bf1489b484f060b6ebbc5e4eb9e5da51ef93c5b0 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 8 May 2025 12:20:11 +0200 Subject: [PATCH 05/23] Reorder methods by lexicographical order --- dpnp/dpnp_array.py | 306 ++++++++++++++++++++++----------------------- 1 file changed, 153 insertions(+), 153 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 1f98ec3e8f63..901ef569655b 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -113,159 +113,6 @@ def __init__( array_namespace=dpnp, ) - @property - def __sycl_usm_array_interface__(self): - """ - Give ``__sycl_usm_array_interface__`` dictionary describing the array. - - """ - return self._array_obj.__sycl_usm_array_interface__ - - def get_array(self): - """Get :class:`dpctl.tensor.usm_ndarray` object.""" - return self._array_obj - - @property - def T(self): - """ - View of the transposed array. - - Same as ``self.transpose()``. - - See Also - -------- - :obj:`dpnp.transpose` : Equivalent function. - - Examples - -------- - >>> import dpnp as np - >>> a = np.array([[1, 2], [3, 4]]) - >>> a - array([[1, 2], - [3, 4]]) - >>> a.T - array([[1, 3], - [2, 4]]) - - >>> a = np.array([1, 2, 3, 4]) - >>> a - array([1, 2, 3, 4]) - >>> a.T - array([1, 2, 3, 4]) - - """ - - return self.transpose() - - @property - def mT(self): - """ - View of the matrix transposed array. - - The matrix transpose is the transpose of the last two dimensions, even - if the array is of higher dimension. - - Raises - ------ - ValueError - If the array is of dimension less than ``2``. - - Examples - -------- - >>> import dpnp as np - >>> a = np.array([[1, 2], [3, 4]]) - >>> a - array([[1, 2], - [3, 4]]) - >>> a.mT - array([[1, 3], - [2, 4]]) - - >>> a = np.arange(8).reshape((2, 2, 2)) - >>> a - array([[[0, 1], - [2, 3]], - [[4, 5], - [6, 7]]]) - >>> a.mT - array([[[0, 2], - [1, 3]], - [[4, 6], - [5, 7]]]) - - """ - - if self.ndim < 2: - raise ValueError("matrix transpose with ndim < 2 is undefined") - - return dpnp_array._create_from_usm_ndarray(self._array_obj.mT) - - @property - def device(self): - """ - Return :class:`dpctl.tensor.Device` object representing residence of - the array data. - - The ``Device`` object represents Array API notion of the device, and - contains :class:`dpctl.SyclQueue` associated with this array. Hence, - ``.device`` property provides information distinct from ``.sycl_device`` - property. - - Examples - -------- - >>> import dpnp as np - >>> x = np.ones(10) - >>> x.device - Device(level_zero:gpu:0) - - """ - - return self._array_obj.device - - @property - def sycl_context(self): - """ - Return :class:`dpctl.SyclContext` object to which USM data is bound. - - """ - return self._array_obj.sycl_context - - @property - def sycl_device(self): - """ - Return :class:`dpctl.SyclDevice` object on which USM data was - allocated. - - """ - return self._array_obj.sycl_device - - @property - def sycl_queue(self): - """ - Return :class:`dpctl.SyclQueue` object associated with USM data. - - """ - return self._array_obj.sycl_queue - - @property - def usm_type(self): - """ - USM type of underlying memory. Possible values are: - - * ``"device"`` - USM-device allocation in device memory, only accessible to kernels - executed on the device - * ``"shared"`` - USM-shared allocation in device memory, accessible both from the - device and from the host - * ``"host"`` - USM-host allocation in host memory, accessible both from the device - and from the host - - """ - - return self._array_obj.usm_type - def __abs__(self): """Return :math:`|self|`.""" return dpnp.abs(self) @@ -721,6 +568,14 @@ def __sub__(self, other): # '__subclasshook__', + @property + def __sycl_usm_array_interface__(self): + """ + Give ``__sycl_usm_array_interface__`` dictionary describing the array. + + """ + return self._array_obj.__sycl_usm_array_interface__ + def __truediv__(self, other): """Return :math:`self/value`.""" return dpnp.true_divide(self, other) @@ -1148,6 +1003,28 @@ def data(self): return dpm.create_data(self._array_obj) + @property + def device(self): + """ + Return :class:`dpctl.tensor.Device` object representing residence of + the array data. + + The ``Device`` object represents Array API notion of the device, and + contains :class:`dpctl.SyclQueue` associated with this array. Hence, + ``.device`` property provides information distinct from ``.sycl_device`` + property. + + Examples + -------- + >>> import dpnp as np + >>> x = np.ones(10) + >>> x.device + Device(level_zero:gpu:0) + + """ + + return self._array_obj.device + def diagonal(self, offset=0, axis1=0, axis2=1): """ Return specified diagonals. @@ -1295,6 +1172,10 @@ def flatten(self, order="C"): return self.reshape(-1, order=order, copy=True) + def get_array(self): + """Get :class:`dpctl.tensor.usm_ndarray` object.""" + return self._array_obj + # 'getfield', @property @@ -1455,6 +1336,49 @@ def min( where=where, ) + @property + def mT(self): + """ + View of the matrix transposed array. + + The matrix transpose is the transpose of the last two dimensions, even + if the array is of higher dimension. + + Raises + ------ + ValueError + If the array is of dimension less than ``2``. + + Examples + -------- + >>> import dpnp as np + >>> a = np.array([[1, 2], [3, 4]]) + >>> a + array([[1, 2], + [3, 4]]) + >>> a.mT + array([[1, 3], + [2, 4]]) + + >>> a = np.arange(8).reshape((2, 2, 2)) + >>> a + array([[[0, 1], + [2, 3]], + [[4, 5], + [6, 7]]]) + >>> a.mT + array([[[0, 2], + [1, 3]], + [[4, 6], + [5, 7]]]) + + """ + + if self.ndim < 2: + raise ValueError("matrix transpose with ndim < 2 is undefined") + + return dpnp_array._create_from_usm_ndarray(self._array_obj.mT) + @property def nbytes(self): """Total bytes consumed by the elements of the array.""" @@ -1956,6 +1880,63 @@ def swapaxes(self, axis1, axis2): return dpnp.swapaxes(self, axis1=axis1, axis2=axis2) + @property + def sycl_context(self): + """ + Return :class:`dpctl.SyclContext` object to which USM data is bound. + + """ + return self._array_obj.sycl_context + + @property + def sycl_device(self): + """ + Return :class:`dpctl.SyclDevice` object on which USM data was + allocated. + + """ + return self._array_obj.sycl_device + + @property + def sycl_queue(self): + """ + Return :class:`dpctl.SyclQueue` object associated with USM data. + + """ + return self._array_obj.sycl_queue + + @property + def T(self): + """ + View of the transposed array. + + Same as ``self.transpose()``. + + See Also + -------- + :obj:`dpnp.transpose` : Equivalent function. + + Examples + -------- + >>> import dpnp as np + >>> a = np.array([[1, 2], [3, 4]]) + >>> a + array([[1, 2], + [3, 4]]) + >>> a.T + array([[1, 3], + [2, 4]]) + + >>> a = np.array([1, 2, 3, 4]) + >>> a + array([1, 2, 3, 4]) + >>> a.T + array([1, 2, 3, 4]) + + """ + + return self.transpose() + def take(self, indices, axis=None, out=None, mode="wrap"): """ Take elements from an array along an axis. @@ -2251,3 +2232,22 @@ def view(self, dtype=None, *, type=None): buffer=self, strides=new_strides, ) + + @property + def usm_type(self): + """ + USM type of underlying memory. Possible values are: + + * ``"device"`` + USM-device allocation in device memory, only accessible to kernels + executed on the device + * ``"shared"`` + USM-shared allocation in device memory, accessible both from the + device and from the host + * ``"host"`` + USM-host allocation in host memory, accessible both from the device + and from the host + + """ + + return self._array_obj.usm_type From 3ac22b8ec5118bb271b56174f09d2ca5991e717c Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 8 May 2025 12:20:46 +0200 Subject: [PATCH 06/23] Add docstring to a file --- dpnp/dpnp_array.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 901ef569655b..bd527269754e 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -25,7 +25,8 @@ # ***************************************************************************** """ -TODO: add mdoule docstring +Interface of an ndarray representing a multidimensional tensor of numeric +elements stored in a USM allocation on a SYCL device. """ From 3eb1ff90bffc738dc921c75a9ec12e43a8dbcfc4 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 8 May 2025 12:28:59 +0200 Subject: [PATCH 07/23] Add inplace ignoring of flake8 warnings --- .flake8 | 2 -- dpnp/dpnp_array.py | 12 ++++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.flake8 b/.flake8 index 3203f7e4f698..68a21b79ec4a 100644 --- a/.flake8 +++ b/.flake8 @@ -24,8 +24,6 @@ extend-ignore = D105, # missing docstring in __init__: D107, - # TODO: remove: - D200, D403, # no blank lines allowed after function docstring: D202, # 1 blank line required between summary line and description: diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index bd527269754e..378da9e7b3c4 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -134,7 +134,7 @@ def __array__(self, dtype=None, /, *, copy=None): :class:`dpnp.ndarray` instance into NumPy array with data type `object` and every element being zero-dimensional :class:`dpnp.ndarray`. - """ + """ # noqa: D403 raise TypeError( "Implicit conversion to a NumPy array is not allowed. " @@ -574,7 +574,7 @@ def __sycl_usm_array_interface__(self): """ Give ``__sycl_usm_array_interface__`` dictionary describing the array. - """ + """ # noqa: D200 return self._array_obj.__sycl_usm_array_interface__ def __truediv__(self, other): @@ -1076,7 +1076,7 @@ def dtype(self): """ Returns NumPy's dtype corresponding to the type of the array elements. - """ + """ # noqa: D200 return self._array_obj.dtype @@ -1125,7 +1125,7 @@ def flat(self): """ Return a flat iterator, or set a flattened version of self to value. - """ + """ # noqa: D200 return dpnp.flatiter(self) @@ -1886,7 +1886,7 @@ def sycl_context(self): """ Return :class:`dpctl.SyclContext` object to which USM data is bound. - """ + """ # noqa: D200 return self._array_obj.sycl_context @property @@ -1903,7 +1903,7 @@ def sycl_queue(self): """ Return :class:`dpctl.SyclQueue` object associated with USM data. - """ + """ # noqa: D200 return self._array_obj.sycl_queue @property From ad469758473a2d544d16796b8ca836080d54e8b0 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Mon, 21 Jul 2025 16:01:45 +0200 Subject: [PATCH 08/23] Fix typos in the docstrings --- dpnp/dpnp_array.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 378da9e7b3c4..d80fc45d17ef 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -151,7 +151,7 @@ def __array_namespace__(self, /, *, api_version=None): Parameters ---------- - api_version : str, optional + api_version : {None, str}, optional Request namespace compliant with given version of array API. If ``None``, namespace for the most recent supported version is returned. @@ -223,14 +223,14 @@ def __dlpack__( is not performed. Default: ``None``. - max_version {tuple of ints, None}, optional + max_version : {tuple of ints, None}, optional The maximum DLPack version the consumer (caller of ``__dlpack__``) supports. As ``__dlpack__`` may not always return a DLPack capsule with version `max_version`, the consumer must verify the version even if this argument is passed. Default: ``None``. - dl_device {tuple, None}, optional: + dl_device : {tuple, None}, optional: The device the returned DLPack capsule will be placed on. The device must be a 2-tuple matching the format of ``__dlpack_device__`` method, an integer enumerator representing @@ -238,7 +238,7 @@ def __dlpack__( the device. Default: ``None``. - copy {bool, None}, optional: + copy : {bool, None}, optional: Boolean indicating whether or not to copy the input. * If `copy` is ``True``, the input will always be copied. @@ -251,12 +251,12 @@ def __dlpack__( Raises ------ - MemoryError: + MemoryError when host memory can not be allocated. - DLPackCreationError: + DLPackCreationError when array is allocated on a partitioned SYCL device, or with a non-default context. - BufferError: + BufferError when a copy is deemed necessary but `copy` is ``False`` or when the provided `dl_device` cannot be handled. @@ -325,12 +325,12 @@ def __gt__(self, other): # '__hash__', def __iadd__(self, other): - """Return :math:`self+=value`:math:.""" + """Return :math:`self+=value`.""" dpnp.add(self, other, out=self) return self def __iand__(self, other): - """Return :math:`self&=value`:math:.""" + """Return :math:`self&=value`.""" dpnp.bitwise_and(self, other, out=self) return self @@ -446,7 +446,7 @@ def __lt__(self, other): return dpnp.less(self, other) def __matmul__(self, other): - """Return `:math:self@value`.""" + """Return :math:`self@value`.""" return dpnp.matmul(self, other) def __mod__(self, other): @@ -546,7 +546,7 @@ def __rxor__(self, other): # '__setattr__', def __setitem__(self, key, val): - """Set ``self[key]`` to value.""" + """Set :math:`self[key]` to a value.""" key = _get_unwrapped_index_key(key) if isinstance(val, dpnp_array): @@ -584,7 +584,7 @@ def __truediv__(self, other): @property def __usm_ndarray__(self): """ - Property to support `__usm_ndarray__` protocol. + Property to support ``__usm_ndarray__`` protocol. It assumes to return :class:`dpctl.tensor.usm_ndarray` instance corresponding to the content of the object. From ec8d3894c0474861a2be3e43da43393ea3c59784 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Mon, 21 Jul 2025 16:03:30 +0200 Subject: [PATCH 09/23] Use \text command in math --- dpnp/dpnp_array.py | 112 ++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index d80fc45d17ef..695d21ed9c0a 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -115,15 +115,15 @@ def __init__( ) def __abs__(self): - """Return :math:`|self|`.""" + r"""Return :math:`|\text{self}|`.""" return dpnp.abs(self) def __add__(self, other): - """Return :math:`self+value`.""" + r"""Return :math:`\text{self + value}`.""" return dpnp.add(self, other) def __and__(self, other): - """Return :math:`self&value`.""" + r"""Return :math:`\text{self & value}`.""" return dpnp.bitwise_and(self, other) def __array__(self, dtype=None, /, *, copy=None): @@ -180,7 +180,7 @@ def __array_namespace__(self, /, *, api_version=None): # '__array_wrap__', def __bool__(self): - """``True`` if self else ``False``.""" + """``True`` if `self` else ``False``.""" return self._array_obj.__bool__() # '__class__', @@ -290,7 +290,7 @@ def __dlpack_device__(self): # '__doc__', def __eq__(self, other): - """Return :math:`self==value`.""" + r"""Return :math:`\text{self == value}`.""" return dpnp.equal(self, other) def __float__(self): @@ -298,19 +298,19 @@ def __float__(self): return self._array_obj.__float__() def __floordiv__(self, other): - """Return :math:`self//value`.""" + r"""Return :math:`\text{self // value}`.""" return dpnp.floor_divide(self, other) # '__format__', def __ge__(self, other): - """Return :math:`self>=value`.""" + r"""Return :math:`\text{self >= value}`.""" return dpnp.greater_equal(self, other) # '__getattribute__', def __getitem__(self, key): - """Return :math:`self[key]`.""" + r"""Return :math:`\text{self[key]}`.""" key = _get_unwrapped_index_key(key) item = self._array_obj.__getitem__(key) @@ -319,33 +319,33 @@ def __getitem__(self, key): # '__getstate__', def __gt__(self, other): - """Return :math:`self>value`.""" + r"""Return :math:`\text{self > value}`.""" return dpnp.greater(self, other) # '__hash__', def __iadd__(self, other): - """Return :math:`self+=value`.""" + r"""Return :math:`\text{self += value}`.""" dpnp.add(self, other, out=self) return self def __iand__(self, other): - """Return :math:`self&=value`.""" + r"""Return :math:`\text{self &= value}`.""" dpnp.bitwise_and(self, other, out=self) return self def __ifloordiv__(self, other): - """Return :math:`self//=value`.""" + r"""Return :math:`\text{self //= value}`.""" dpnp.floor_divide(self, other, out=self) return self def __ilshift__(self, other): - """Return :math:`self<<=value`.""" + r"""Return :math:`\text{self <<= value}`.""" dpnp.left_shift(self, other, out=self) return self def __imatmul__(self, other): - """Return :math:`self@=value`.""" + r"""Return :math:`\text{self @= value}`.""" # Unlike `matmul(a, b, out=a)` we ensure that the result isn't broadcast # if the result without `out` would have less dimensions than `a`. @@ -369,12 +369,12 @@ def __imatmul__(self, other): return self def __imod__(self, other): - """Return :math:`self%=value`.""" + r"""Return :math:`\text{self %= value}`.""" dpnp.remainder(self, other, out=self) return self def __imul__(self, other): - """Return :math:`self*=value`.""" + r"""Return :math:`\text{self *= value}`.""" dpnp.multiply(self, other, out=self) return self @@ -390,101 +390,101 @@ def __int__(self): return self._array_obj.__int__() def __invert__(self): - """Return :math:`~self`.""" + r"""Return :math:`\text{~self}`.""" return dpnp.invert(self) def __ior__(self, other): - """Return :math:`self|=value`.""" + r"""Return :math:`\text{self |= value}`.""" dpnp.bitwise_or(self, other, out=self) return self def __ipow__(self, other): - """Return :math:`self**=value`.""" + r"""Return :math:`\text{self **= value}`.""" dpnp.power(self, other, out=self) return self def __irshift__(self, other): - """Return :math:`self>>=value`.""" + r"""Return :math:`\text{self >>= value}`.""" dpnp.right_shift(self, other, out=self) return self def __isub__(self, other): - """Return :math:`self-=value`.""" + r"""Return :math:`\text{self -= value}`.""" dpnp.subtract(self, other, out=self) return self def __iter__(self): - """Return :math:`iter(self)`.""" + r"""Return :math:`\text{iter(self)}`.""" if self.ndim == 0: raise TypeError("iteration over a 0-d array") return (self[i] for i in range(self.shape[0])) def __itruediv__(self, other): - """Return :math:`self/=value`.""" + r"""Return :math:`\text{self /= value}`.""" dpnp.true_divide(self, other, out=self) return self def __ixor__(self, other): - """Return :math:`self^=value`.""" + r"""Return :math:`\text{self ^= value}`.""" dpnp.bitwise_xor(self, other, out=self) return self def __le__(self, other): - """Return :math:`self<=value`.""" + r"""Return :math:`\text{self <= value}`.""" return dpnp.less_equal(self, other) def __len__(self): - """Return :math:`len(self)`.""" + r"""Return :math:`\text{len(self)}`.""" return self._array_obj.__len__() def __lshift__(self, other): - """Return :math:`self<>self`.""" + r"""Return :math:`\text{value >> self}`.""" return dpnp.right_shift(other, self) def __rshift__(self, other): - """Return :math:`self>>value`.""" + r"""Return :math:`\text{self >> value}`.""" return dpnp.right_shift(self, other) def __rsub__(self, other): - """Return :math:`value-self`.""" + r"""Return :math:`\text{value - self}`.""" return dpnp.subtract(other, self) def __rtruediv__(self, other): - """Return :math:`value/self`.""" + r"""Return :math:`\text{value / self}`.""" return dpnp.true_divide(other, self) def __rxor__(self, other): - """Return :math:`value^self`.""" + r"""Return :math:`\text{value ^ self}`.""" return dpnp.bitwise_xor(other, self) # '__setattr__', def __setitem__(self, key, val): - """Set :math:`self[key]` to a value.""" + r"""Set :math:`\text{self[key]}` to a value.""" key = _get_unwrapped_index_key(key) if isinstance(val, dpnp_array): @@ -560,11 +560,11 @@ def __setitem__(self, key, val): __slots__ = ("_array_obj",) def __str__(self): - """Return :math:`str(self)`.""" + r"""Return :math:`\text{str(self)}`.""" return self._array_obj.__str__() def __sub__(self, other): - """Return :math:`self-value`.""" + r"""Return :math:`\text{self - value}`.""" return dpnp.subtract(self, other) # '__subclasshook__', @@ -578,7 +578,7 @@ def __sycl_usm_array_interface__(self): return self._array_obj.__sycl_usm_array_interface__ def __truediv__(self, other): - """Return :math:`self/value`.""" + r"""Return :math:`\text{self / value}`.""" return dpnp.true_divide(self, other) @property @@ -601,7 +601,7 @@ def __usm_ndarray__(self): return self._array_obj def __xor__(self, other): - """Return :math:`self^value`.""" + r"""Return :math:`\text{self ^ value}`.""" return dpnp.bitwise_xor(self, other) @staticmethod From 53b1a361b41d82ca63bc4724d6a9cf5beffca38d Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Mon, 21 Jul 2025 21:34:49 +0200 Subject: [PATCH 10/23] Correct link towards operator.index() in docs --- doc/reference/ndarray.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/reference/ndarray.rst b/doc/reference/ndarray.rst index 0e388cf4eab7..232c4d066012 100644 --- a/doc/reference/ndarray.rst +++ b/doc/reference/ndarray.rst @@ -405,7 +405,7 @@ Container customization: (see :ref:`Indexing `) dpnp.ndarray.__contains__ Conversion; the operations :class:`int() `, :class:`float() `, -:class:`complex() ` and :func:`operator.index() `. +:class:`complex() ` and :func:`operator.index() `. They work only on arrays that have one element in them and return the appropriate scalar. From 873b6b5e214f3780eb8ef32c6688d794a2689152 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 22 Jul 2025 11:38:34 +0200 Subject: [PATCH 11/23] Add description to axis, dtype and out keyword --- doc/reference/ndarray.rst | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/doc/reference/ndarray.rst b/doc/reference/ndarray.rst index 232c4d066012..96ee9fbc1bc8 100644 --- a/doc/reference/ndarray.rst +++ b/doc/reference/ndarray.rst @@ -211,6 +211,25 @@ the operation should proceed. Calculation ----------- +Many of these methods take an argument named *axis*. In such cases, + +- If *axis* is *None* (the default), the array is treated as a 1-D array and + the operation is performed over the entire array. This behavior is also the + default if *self* is a 0-dimensional array. + +- If *axis* is an integer, then the operation is done over the given axis (for + each 1-D subarray that can be created along the given axis). + +The parameter *dtype* specifies the data type over which a reduction operation +(like summing) should take place. The default reduce data type is the same as +the data type of *self*. To avoid overflow, it can be useful to perform the +reduction using a larger data type. + +For several methods, an optional *out* argument can also be provided and the +result will be placed into the output array given. The *out* argument must be +an :class:`dpnp.ndarray` and have the same number of elements. It can have a +different data type in which case casting will be performed. + .. autosummary:: :toctree: generated/ :nosignatures: @@ -242,12 +261,11 @@ Arithmetic and comparison operations on :class:`dpnp.ndarrays ` are defined as element-wise operations, and generally yield :class:`dpnp.ndarray` objects as results. -Each of the arithmetic operations (``+``, ``-``, ``*``, ``/``, ``//``, -``%``, ``divmod()``, ``**`` or ``pow()``, ``<<``, ``>>``, ``&``, -``^``, ``|``, ``~``) and the comparisons (``==``, ``<``, ``>``, -``<=``, ``>=``, ``!=``) is equivalent to the corresponding -universal function (or :term:`ufunc` for short) in DPNP. For -more information, see the section on :ref:`Universal Functions +Each of the arithmetic operations (``+``, ``-``, ``*``, ``/``, ``//``, ``%``, +``divmod()``, ``**`` or ``pow()``, ``<<``, ``>>``, ``&``, ``^``, ``|``, ``~``) +and the comparisons (``==``, ``<``, ``>``, ``<=``, ``>=``, ``!=``) is +equivalent to the corresponding universal function (or :term:`ufunc` for short) +in DPNP. For more information, see the section on :ref:`Universal Functions `. From 8345a05ed32afdffafad5a6cfcfe52ac75fe3fa7 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 22 Jul 2025 11:40:25 +0200 Subject: [PATCH 12/23] Add missing :nosignatures: --- doc/reference/ndarray.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/reference/ndarray.rst b/doc/reference/ndarray.rst index 96ee9fbc1bc8..7c7a2b581971 100644 --- a/doc/reference/ndarray.rst +++ b/doc/reference/ndarray.rst @@ -286,6 +286,7 @@ Truth value of an array (:class:`bool() `): .. autosummary:: :toctree: generated/ + :nosignatures: dpnp.ndarray.__bool__ @@ -377,6 +378,7 @@ Matrix Multiplication: .. autosummary:: :toctree: generated/ + :nosignatures: dpnp.ndarray.__matmul__ dpnp.ndarray.__rmatmul__ From 3ae8efff926b3167e4ddc4b1b93b6d8ad303aac9 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 22 Jul 2025 12:44:00 +0200 Subject: [PATCH 13/23] Align signature of methods with numpy.ndarray --- dpnp/dpnp_array.py | 186 ++++++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 88 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 695d21ed9c0a..3e0d49d03efd 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -114,15 +114,15 @@ def __init__( array_namespace=dpnp, ) - def __abs__(self): + def __abs__(self, /): r"""Return :math:`|\text{self}|`.""" return dpnp.abs(self) - def __add__(self, other): + def __add__(self, other, /): r"""Return :math:`\text{self + value}`.""" return dpnp.add(self, other) - def __and__(self, other): + def __and__(self, other, /): r"""Return :math:`\text{self & value}`.""" return dpnp.bitwise_and(self, other) @@ -179,14 +179,14 @@ def __array_namespace__(self, /, *, api_version=None): # '__array_wrap__', - def __bool__(self): + def __bool__(self, /): """``True`` if `self` else ``False``.""" return self._array_obj.__bool__() # '__class__', # `__class_getitem__`, - def __complex__(self): + def __complex__(self, /): """Convert a zero-dimensional array to a Python complex object.""" return self._array_obj.__complex__() @@ -211,7 +211,7 @@ def __copy__(self): # '__divmod__', def __dlpack__( - self, *, stream=None, max_version=None, dl_device=None, copy=None + self, /, *, stream=None, max_version=None, dl_device=None, copy=None ): """ Produces DLPack capsule. @@ -269,7 +269,7 @@ def __dlpack__( copy=copy, ) - def __dlpack_device__(self): + def __dlpack_device__(self, /): """ Gives a tuple (``device_type``, ``device_id``) corresponding to ``DLDevice`` entry in ``DLTensor`` in DLPack protocol. @@ -289,27 +289,27 @@ def __dlpack_device__(self): # '__doc__', - def __eq__(self, other): + def __eq__(self, other, /): r"""Return :math:`\text{self == value}`.""" return dpnp.equal(self, other) - def __float__(self): + def __float__(self, /): """Convert a zero-dimensional array to a Python float object.""" return self._array_obj.__float__() - def __floordiv__(self, other): + def __floordiv__(self, other, /): r"""Return :math:`\text{self // value}`.""" return dpnp.floor_divide(self, other) # '__format__', - def __ge__(self, other): + def __ge__(self, other, /): r"""Return :math:`\text{self >= value}`.""" return dpnp.greater_equal(self, other) # '__getattribute__', - def __getitem__(self, key): + def __getitem__(self, key, /): r"""Return :math:`\text{self[key]}`.""" key = _get_unwrapped_index_key(key) @@ -318,33 +318,33 @@ def __getitem__(self, key): # '__getstate__', - def __gt__(self, other): + def __gt__(self, other, /): r"""Return :math:`\text{self > value}`.""" return dpnp.greater(self, other) # '__hash__', - def __iadd__(self, other): + def __iadd__(self, other, /): r"""Return :math:`\text{self += value}`.""" dpnp.add(self, other, out=self) return self - def __iand__(self, other): + def __iand__(self, other, /): r"""Return :math:`\text{self &= value}`.""" dpnp.bitwise_and(self, other, out=self) return self - def __ifloordiv__(self, other): + def __ifloordiv__(self, other, /): r"""Return :math:`\text{self //= value}`.""" dpnp.floor_divide(self, other, out=self) return self - def __ilshift__(self, other): + def __ilshift__(self, other, /): r"""Return :math:`\text{self <<= value}`.""" dpnp.left_shift(self, other, out=self) return self - def __imatmul__(self, other): + def __imatmul__(self, other, /): r"""Return :math:`\text{self @= value}`.""" # Unlike `matmul(a, b, out=a)` we ensure that the result isn't broadcast @@ -368,68 +368,68 @@ def __imatmul__(self, other): ) from e return self - def __imod__(self, other): + def __imod__(self, other, /): r"""Return :math:`\text{self %= value}`.""" dpnp.remainder(self, other, out=self) return self - def __imul__(self, other): + def __imul__(self, other, /): r"""Return :math:`\text{self *= value}`.""" dpnp.multiply(self, other, out=self) return self - def __index__(self): + def __index__(self, /): """Convert a zero-dimensional array to a Python int object.""" return self._array_obj.__index__() # '__init__', # '__init_subclass__', - def __int__(self): + def __int__(self, /): """Convert a zero-dimensional array to a Python int object.""" return self._array_obj.__int__() - def __invert__(self): + def __invert__(self, /): r"""Return :math:`\text{~self}`.""" return dpnp.invert(self) - def __ior__(self, other): + def __ior__(self, other, /): r"""Return :math:`\text{self |= value}`.""" dpnp.bitwise_or(self, other, out=self) return self - def __ipow__(self, other): + def __ipow__(self, other, /): r"""Return :math:`\text{self **= value}`.""" dpnp.power(self, other, out=self) return self - def __irshift__(self, other): + def __irshift__(self, other, /): r"""Return :math:`\text{self >>= value}`.""" dpnp.right_shift(self, other, out=self) return self - def __isub__(self, other): + def __isub__(self, other, /): r"""Return :math:`\text{self -= value}`.""" dpnp.subtract(self, other, out=self) return self - def __iter__(self): + def __iter__(self, /): r"""Return :math:`\text{iter(self)}`.""" if self.ndim == 0: raise TypeError("iteration over a 0-d array") return (self[i] for i in range(self.shape[0])) - def __itruediv__(self, other): + def __itruediv__(self, other, /): r"""Return :math:`\text{self /= value}`.""" dpnp.true_divide(self, other, out=self) return self - def __ixor__(self, other): + def __ixor__(self, other, /): r"""Return :math:`\text{self ^= value}`.""" dpnp.bitwise_xor(self, other, out=self) return self - def __le__(self, other): + def __le__(self, other, /): r"""Return :math:`\text{self <= value}`.""" return dpnp.less_equal(self, other) @@ -437,53 +437,53 @@ def __len__(self): r"""Return :math:`\text{len(self)}`.""" return self._array_obj.__len__() - def __lshift__(self, other): + def __lshift__(self, other, /): r"""Return :math:`\text{self << value}`.""" return dpnp.left_shift(self, other) - def __lt__(self, other): + def __lt__(self, other, /): r"""Return :math:`\text{self < value}`.""" return dpnp.less(self, other) - def __matmul__(self, other): + def __matmul__(self, other, /): r"""Return :math:`\text{self @ value}`.""" return dpnp.matmul(self, other) - def __mod__(self, other): + def __mod__(self, other, /): r"""Return :math:`\text{self % value}`.""" return dpnp.remainder(self, other) - def __mul__(self, other): + def __mul__(self, other, /): r"""Return :math:`\text{self * value}`.""" return dpnp.multiply(self, other) - def __ne__(self, other): + def __ne__(self, other, /): r"""Return :math:`\text{self != value}`.""" return dpnp.not_equal(self, other) - def __neg__(self): + def __neg__(self, /): r"""Return :math:`\text{-self}`.""" return dpnp.negative(self) # '__new__', - def __or__(self, other): + def __or__(self, other, /): r"""Return :math:`\text{self | value}`.""" return dpnp.bitwise_or(self, other) - def __pos__(self): + def __pos__(self, /): r"""Return :math:`\text{+self}`.""" return dpnp.positive(self) - def __pow__(self, other): + def __pow__(self, other, mod=None, /): r"""Return :math:`\text{self ** value}`.""" return dpnp.power(self, other) - def __radd__(self, other): + def __radd__(self, other, /): r"""Return :math:`\text{value + self}`.""" return dpnp.add(other, self) - def __rand__(self, other): + def __rand__(self, other, /): r"""Return :math:`\text{value & self}`.""" return dpnp.bitwise_and(other, self) @@ -495,64 +495,64 @@ def __repr__(self): r"""Return :math:`\text{repr(self)}`.""" return dpt.usm_ndarray_repr(self._array_obj, prefix="array") - def __rfloordiv__(self, other): + def __rfloordiv__(self, other, /): r"""Return :math:`\text{value // self}`.""" return dpnp.floor_divide(self, other) - def __rlshift__(self, other): + def __rlshift__(self, other, /): r"""Return :math:`\text{value << self}`.""" return dpnp.left_shift(other, self) - def __rmatmul__(self, other): + def __rmatmul__(self, other, /): r"""Return :math:`\text{value @ self}`.""" return dpnp.matmul(other, self) - def __rmod__(self, other): + def __rmod__(self, other, /): r"""Return :math:`\text{value % self}`.""" return dpnp.remainder(other, self) - def __rmul__(self, other): + def __rmul__(self, other, /): r"""Return :math:`\text{value * self}`.""" return dpnp.multiply(other, self) - def __ror__(self, other): + def __ror__(self, other, /): r"""Return :math:`\text{value | self}`.""" return dpnp.bitwise_or(other, self) - def __rpow__(self, other): + def __rpow__(self, other, mod=None, /): r"""Return :math:`\text{value ** self}`.""" return dpnp.power(other, self) - def __rrshift__(self, other): + def __rrshift__(self, other, /): r"""Return :math:`\text{value >> self}`.""" return dpnp.right_shift(other, self) - def __rshift__(self, other): + def __rshift__(self, other, /): r"""Return :math:`\text{self >> value}`.""" return dpnp.right_shift(self, other) - def __rsub__(self, other): + def __rsub__(self, other, /): r"""Return :math:`\text{value - self}`.""" return dpnp.subtract(other, self) - def __rtruediv__(self, other): + def __rtruediv__(self, other, /): r"""Return :math:`\text{value / self}`.""" return dpnp.true_divide(other, self) - def __rxor__(self, other): + def __rxor__(self, other, /): r"""Return :math:`\text{value ^ self}`.""" return dpnp.bitwise_xor(other, self) # '__setattr__', - def __setitem__(self, key, val): + def __setitem__(self, key, value, /): r"""Set :math:`\text{self[key]}` to a value.""" key = _get_unwrapped_index_key(key) - if isinstance(val, dpnp_array): - val = val.get_array() + if isinstance(value, dpnp_array): + value = value.get_array() - self._array_obj.__setitem__(key, val) + self._array_obj.__setitem__(key, value) # '__setstate__', # '__sizeof__', @@ -563,7 +563,7 @@ def __str__(self): r"""Return :math:`\text{str(self)}`.""" return self._array_obj.__str__() - def __sub__(self, other): + def __sub__(self, other, /): r"""Return :math:`\text{self - value}`.""" return dpnp.subtract(self, other) @@ -577,7 +577,7 @@ def __sycl_usm_array_interface__(self): """ # noqa: D200 return self._array_obj.__sycl_usm_array_interface__ - def __truediv__(self, other): + def __truediv__(self, other, /): r"""Return :math:`\text{self / value}`.""" return dpnp.true_divide(self, other) @@ -600,7 +600,7 @@ def __usm_ndarray__(self): return self._array_obj - def __xor__(self, other): + def __xor__(self, other, /): r"""Return :math:`\text{self ^ value}`.""" return dpnp.bitwise_xor(self, other) @@ -621,7 +621,7 @@ def _create_from_usm_ndarray(usm_ary: dpt.usm_ndarray): res._array_obj._set_namespace(dpnp) return res - def all(self, axis=None, out=None, keepdims=False, *, where=True): + def all(self, axis=None, *, out=None, keepdims=False, where=True): """ Returns ``True`` if all elements evaluate to ``True.`` @@ -637,7 +637,7 @@ def all(self, axis=None, out=None, keepdims=False, *, where=True): self, axis=axis, out=out, keepdims=keepdims, where=where ) - def any(self, axis=None, out=None, keepdims=False, *, where=True): + def any(self, axis=None, *, out=None, keepdims=False, where=True): """ Returns ``True`` if any of the elements of `a` evaluate to ``True``. @@ -653,7 +653,7 @@ def any(self, axis=None, out=None, keepdims=False, *, where=True): self, axis=axis, out=out, keepdims=keepdims, where=where ) - def argmax(self, axis=None, out=None, *, keepdims=False): + def argmax(self, /, axis=None, out=None, *, keepdims=False): """ Returns array of indices of the maximum values along the given axis. @@ -663,7 +663,7 @@ def argmax(self, axis=None, out=None, *, keepdims=False): return dpnp.argmax(self, axis=axis, out=out, keepdims=keepdims) - def argmin(self, axis=None, out=None, *, keepdims=False): + def argmin(self, /, axis=None, out=None, *, keepdims=False): """ Return array of indices to the minimum values along the given axis. @@ -854,7 +854,7 @@ def choose(self, /, choices, out=None, mode="wrap"): return dpnp.choose(self, choices, out, mode) - def clip(self, min=None, max=None, out=None, **kwargs): + def clip(self, /, min=None, max=None, out=None, **kwargs): """ Clip (limit) the values in an array. @@ -864,7 +864,7 @@ def clip(self, min=None, max=None, out=None, **kwargs): return dpnp.clip(self, min, max, out=out, **kwargs) - def compress(self, condition, axis=None, out=None): + def compress(self, /, condition, axis=None, *, out=None): """ Select slices of an array along a given axis. @@ -895,7 +895,9 @@ def conjugate(self): return self return dpnp.conjugate(self) - def copy(self, order="C", device=None, usm_type=None, sycl_queue=None): + def copy( + self, /, order="C", *, device=None, usm_type=None, sycl_queue=None + ): """ Return a copy of the array. @@ -974,7 +976,7 @@ def copy(self, order="C", device=None, usm_type=None, sycl_queue=None): # 'ctypes', - def cumprod(self, axis=None, dtype=None, out=None): + def cumprod(self, /, axis=None, dtype=None, *, out=None): """ Return the cumulative product of the elements along the given axis. @@ -984,7 +986,7 @@ def cumprod(self, axis=None, dtype=None, out=None): return dpnp.cumprod(self, axis=axis, dtype=dtype, out=out) - def cumsum(self, axis=None, dtype=None, out=None): + def cumsum(self, /, axis=None, dtype=None, *, out=None): """ Return the cumulative sum of the elements along the given axis. @@ -1129,7 +1131,7 @@ def flat(self): return dpnp.flatiter(self) - def flatten(self, order="C"): + def flatten(self, /, order="C"): """ Return a copy of the array collapsed into one dimension. @@ -1180,7 +1182,7 @@ def get_array(self): # 'getfield', @property - def imag(self): + def imag(self, /): """ The imaginary part of the array. @@ -1199,7 +1201,7 @@ def imag(self): ) @imag.setter - def imag(self, value): + def imag(self, value, /): """ Set the imaginary part of the array. @@ -1220,7 +1222,7 @@ def imag(self, value): else: raise TypeError("array does not have imaginary part to set") - def item(self, *args): + def item(self, /, *args): """ Copy an element of an array to a standard Python scalar and return it. @@ -1279,7 +1281,9 @@ def itemsize(self): def max( self, + /, axis=None, + *, out=None, keepdims=False, initial=None, @@ -1302,7 +1306,7 @@ def max( ) def mean( - self, axis=None, dtype=None, out=None, keepdims=False, *, where=True + self, /, axis=None, dtype=None, *, out=None, keepdims=False, where=True ): """ Returns the average of the array elements. @@ -1315,7 +1319,9 @@ def mean( def min( self, + /, axis=None, + *, out=None, keepdims=False, initial=None, @@ -1428,7 +1434,7 @@ def nonzero(self): return dpnp.nonzero(self) - def partition(self, kth, axis=-1, kind="introselect", order=None): + def partition(self, /, kth, axis=-1, kind="introselect", order=None): """ Return a partitioned copy of an array. @@ -1462,8 +1468,10 @@ def partition(self, kth, axis=-1, kind="introselect", order=None): def prod( self, + /, axis=None, dtype=None, + *, out=None, keepdims=False, initial=None, @@ -1486,7 +1494,7 @@ def prod( where=where, ) - def put(self, indices, vals, /, *, axis=None, mode="wrap"): + def put(self, /, indices, vals, axis=None, mode="wrap"): """ Puts values of an array into another array along a given axis. @@ -1496,7 +1504,7 @@ def put(self, indices, vals, /, *, axis=None, mode="wrap"): return dpnp.put(self, indices, vals, axis=axis, mode=mode) - def ravel(self, order="C"): + def ravel(self, /, order="C"): """ Return a contiguous flattened array. @@ -1507,7 +1515,7 @@ def ravel(self, order="C"): return dpnp.ravel(self, order=order) @property - def real(self): + def real(self, /): """ The real part of the array. @@ -1529,7 +1537,7 @@ def real(self): return self @real.setter - def real(self, value): + def real(self, value, /): """ Set the real part of the array. @@ -1557,7 +1565,7 @@ def repeat(self, repeats, axis=None): return dpnp.repeat(self, repeats, axis=axis) - def reshape(self, *shape, order="C", copy=None): + def reshape(self, /, *shape, order="C", copy=None): """ Returns an array containing the same data with a new shape. @@ -1589,7 +1597,7 @@ def reshape(self, *shape, order="C", copy=None): # 'resize', - def round(self, decimals=0, out=None): + def round(self, /, decimals=0, *, out=None): """ Return array with each element rounded to the given number of decimals. @@ -1804,10 +1812,10 @@ def std( self, axis=None, dtype=None, + *, out=None, ddof=0, keepdims=False, - *, where=True, mean=None, correction=None, @@ -1847,8 +1855,10 @@ def strides(self): def sum( self, + /, axis=None, dtype=None, + *, out=None, keepdims=False, initial=None, @@ -1938,7 +1948,7 @@ def T(self): return self.transpose() - def take(self, indices, axis=None, out=None, mode="wrap"): + def take(self, indices, axis=None, *, out=None, mode="wrap"): """ Take elements from an array along an axis. @@ -1996,7 +2006,7 @@ def to_device(self, device, /, *, stream=None): # 'tofile', # 'tolist', - def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): + def trace(self, offset=0, axis1=0, axis2=1, dtype=None, *, out=None): """ Return the sum along diagonals of the array. @@ -2083,10 +2093,10 @@ def var( self, axis=None, dtype=None, + *, out=None, ddof=0, keepdims=False, - *, where=True, mean=None, correction=None, @@ -2110,7 +2120,7 @@ def var( correction=correction, ) - def view(self, dtype=None, *, type=None): + def view(self, /, dtype=None, *, type=None): """ New view of array with the same data. From 7b59ad68ee544653c8d9df71996dbae001fffc8a Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 22 Jul 2025 14:28:21 +0200 Subject: [PATCH 14/23] Update test to pass out as a keyword only argument --- dpnp/tests/third_party/cupy/core_tests/test_ndarray_math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_math.py b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_math.py index 11a6826c99a7..4de290741375 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_math.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_math.py @@ -39,7 +39,7 @@ def test_round_out(self, xp): self.shape, xp, scale=100, dtype=cupy.default_float_type() ) out = xp.empty_like(a) - a.round(self.decimals, out) + a.round(self.decimals, out=out) return out From 199c4f97a24bc82aaded476273c16d75a541054b Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 22 Jul 2025 14:34:10 +0200 Subject: [PATCH 15/23] Replace "Returns" word with "Return" in the docstring --- dpnp/dpnp_array.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 3e0d49d03efd..c2bea0b4746d 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -147,7 +147,7 @@ def __array__(self, dtype=None, /, *, copy=None): def __array_namespace__(self, /, *, api_version=None): """ - Returns array namespace, member functions of which implement data API. + Return array namespace, member functions of which implement data API. Parameters ---------- @@ -196,7 +196,7 @@ def __contains__(self, value, /): def __copy__(self): """ - Used if :func:`copy.copy` is called on an array. Returns a copy of the + Used if :func:`copy.copy` is called on an array. Return a copy of the array. Equivalent to ``a.copy(order="K")``. @@ -214,7 +214,7 @@ def __dlpack__( self, /, *, stream=None, max_version=None, dl_device=None, copy=None ): """ - Produces DLPack capsule. + Produce DLPack capsule. Parameters ---------- @@ -271,7 +271,7 @@ def __dlpack__( def __dlpack_device__(self, /): """ - Gives a tuple (``device_type``, ``device_id``) corresponding to + Give a tuple (``device_type``, ``device_id``) corresponding to ``DLDevice`` entry in ``DLTensor`` in DLPack protocol. The tuple describes the non-partitioned device where the array has been @@ -623,7 +623,7 @@ def _create_from_usm_ndarray(usm_ary: dpt.usm_ndarray): def all(self, axis=None, *, out=None, keepdims=False, where=True): """ - Returns ``True`` if all elements evaluate to ``True.`` + Return ``True`` if all elements evaluate to ``True.`` Refer to :obj:`dpnp.all` for full documentation. @@ -639,7 +639,7 @@ def all(self, axis=None, *, out=None, keepdims=False, where=True): def any(self, axis=None, *, out=None, keepdims=False, where=True): """ - Returns ``True`` if any of the elements of `a` evaluate to ``True``. + Return ``True`` if any of the elements of `a` evaluate to ``True``. Refer to :obj:`dpnp.any` for full documentation. @@ -655,7 +655,7 @@ def any(self, axis=None, *, out=None, keepdims=False, where=True): def argmax(self, /, axis=None, out=None, *, keepdims=False): """ - Returns array of indices of the maximum values along the given axis. + Return array of indices of the maximum values along the given axis. Refer to :obj:`dpnp.argmax` for full documentation. @@ -1076,7 +1076,7 @@ def dot(self, b, out=None): @property def dtype(self): """ - Returns NumPy's dtype corresponding to the type of the array elements. + Return NumPy's dtype corresponding to the type of the array elements. """ # noqa: D200 @@ -1309,7 +1309,7 @@ def mean( self, /, axis=None, dtype=None, *, out=None, keepdims=False, where=True ): """ - Returns the average of the array elements. + Return the average of the array elements. Refer to :obj:`dpnp.mean` for full documentation. @@ -1478,7 +1478,7 @@ def prod( where=True, ): """ - Returns the prod along a given axis. + Return the prod along a given axis. Refer to :obj:`dpnp.prod` for full documentation. @@ -1496,7 +1496,7 @@ def prod( def put(self, /, indices, vals, axis=None, mode="wrap"): """ - Puts values of an array into another array along a given axis. + Put values of an array into another array along a given axis. Refer to :obj:`dpnp.put` for full documentation. @@ -1567,7 +1567,7 @@ def repeat(self, repeats, axis=None): def reshape(self, /, *shape, order="C", copy=None): """ - Returns an array containing the same data with a new shape. + Return an array containing the same data with a new shape. Refer to :obj:`dpnp.reshape` for full documentation. @@ -1821,7 +1821,7 @@ def std( correction=None, ): """ - Returns the standard deviation of the array elements, along given axis. + Return the standard deviation of the array elements, along given axis. Refer to :obj:`dpnp.std` for full documentation. @@ -1842,7 +1842,7 @@ def std( @property def strides(self): """ - Returns memory displacement in array elements, upon unit + Return memory displacement in array elements, upon unit change of respective index. For example, for strides ``(s1, s2, s3)`` and multi-index @@ -1865,7 +1865,7 @@ def sum( where=True, ): """ - Returns the sum along a given axis. + Return the sum along a given axis. Refer to :obj:`dpnp.sum` for full documentation. @@ -1960,7 +1960,7 @@ def take(self, indices, axis=None, *, out=None, mode="wrap"): def to_device(self, device, /, *, stream=None): """ - Transfers this array to specified target device. + Transfer this array to specified target device. Parameters ---------- @@ -2020,7 +2020,7 @@ def trace(self, offset=0, axis1=0, axis2=1, dtype=None, *, out=None): def transpose(self, *axes): """ - Returns a view of the array with axes transposed. + Return a view of the array with axes transposed. For full documentation refer to :obj:`numpy.ndarray.transpose`. @@ -2102,7 +2102,7 @@ def var( correction=None, ): """ - Returns the variance of the array elements, along given axis. + Return the variance of the array elements, along given axis. Refer to :obj:`dpnp.var` for full documentation. From 06e9984ff75e7b298125062bc60f083c3aeea1cb Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 23 Jul 2025 15:33:34 +0200 Subject: [PATCH 16/23] Add a page with constants documented and set a proper reference on dpnp.DLDeviceType --- doc/reference/constants.rst | 162 +++++++++++++++++++++++++++++++ doc/reference/routines.rst | 1 + dpnp/dpnp_array.py | 11 ++- dpnp/dpnp_iface_arraycreation.py | 13 +-- 4 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 doc/reference/constants.rst diff --git a/doc/reference/constants.rst b/doc/reference/constants.rst new file mode 100644 index 000000000000..39c79c443163 --- /dev/null +++ b/doc/reference/constants.rst @@ -0,0 +1,162 @@ +Constants +================================= + +DPNP includes several constants: + +.. currentmodule:: dpnp + +.. autodata:: DLDeviceType + +.. data:: e + + Euler's constant, base of natural logarithms, Napier's constant. + + ``e = 2.71828182845904523536028747135266249775724709369995...`` + + .. rubric:: See Also + + :func:`exp` : Exponential function + + :func:`log` : Natural logarithm + + .. rubric:: References + + https://en.wikipedia.org/wiki/E_%28mathematical_constant%29 + + +.. data:: euler_gamma + + ``γ = 0.5772156649015328606065120900824024310421...`` + + .. rubric:: References + + https://en.wikipedia.org/wiki/Euler%27s_constant + + +.. data:: inf + + IEEE 754 floating point representation of (positive) infinity. + + .. rubric:: Returns + + y : float + A floating point representation of positive infinity. + + .. rubric:: See Also + + :func:`isinf` : Shows which elements are positive or negative infinity + + :func:`isposinf` : Shows which elements are positive infinity + + :func:`isneginf` : Shows which elements are negative infinity + + :func:`isnan` : Shows which elements are Not a Number + + :func:`isfinite` : Shows which elements are finite (not one of Not a Number, + positive infinity and negative infinity) + + .. rubric:: Notes + + DPNP uses the IEEE Standard for Binary Floating-Point for Arithmetic + (IEEE 754). This means that Not a Number is not equivalent to infinity. + Also that positive infinity is not equivalent to negative infinity. But + infinity is equivalent to positive infinity. + + .. rubric:: Examples + + .. code-block:: python + + >>> import dpnp as np + >>> np.inf + inf + >>> np.array([1]) / 0.0 + array([inf]) + + +.. data:: nan + + IEEE 754 floating point representation of Not a Number (NaN). + + .. rubric:: Returns + + y : A floating point representation of Not a Number. + + .. rubric:: See Also + + :func:`isnan` : Shows which elements are Not a Number + + :func:`isfinite` : Shows which elements are finite (not one of Not a Number, + positive infinity and negative infinity) + + .. rubric:: Notes + + DPNP uses the IEEE Standard for Binary Floating-Point for Arithmetic + (IEEE 754). This means that Not a Number is not equivalent to infinity. + + .. rubric:: Examples + + .. code-block:: python + + >>> import dpnp as np + >>> np.nan + nan + >>> np.log(np.array(-1)) + array(nan) + >>> np.log(np.array([-1, 1, 2])) + array([ nan, 0. , 0.69314718]) + + +.. data:: newaxis + + A convenient alias for *None*, useful for indexing arrays. + + .. rubric:: Examples + + .. code-block:: python + + >>> import dpnp as np + >>> np.newaxis is None + True + >>> x = np.arange(3) + >>> x + array([0, 1, 2]) + >>> x[:, np.newaxis] + array([[0], + [1], + [2]]) + >>> x[:, np.newaxis, np.newaxis] + array([[[0]], + [[1]], + [[2]]]) + >>> x[:, np.newaxis] * x + array([[0, 0, 0], + [0, 1, 2], + [0, 2, 4]]) + + Outer product, same as ``outer(x, y)``: + + >>> y = np.arange(3, 6) + >>> x[:, np.newaxis] * y + array([[ 0, 0, 0], + [ 3, 4, 5], + [ 6, 8, 10]]) + + ``x[np.newaxis, :]`` is equivalent to ``x[np.newaxis]`` and ``x[None]``: + + >>> x[np.newaxis, :].shape + (1, 3) + >>> x[np.newaxis].shape + (1, 3) + >>> x[None].shape + (1, 3) + >>> x[:, np.newaxis].shape + (3, 1) + + +.. data:: pi + + ``pi = 3.1415926535897932384626433...`` + + .. rubric:: References + + https://en.wikipedia.org/wiki/Pi diff --git a/doc/reference/routines.rst b/doc/reference/routines.rst index 1dd4a205b0cf..5cc64b74246a 100644 --- a/doc/reference/routines.rst +++ b/doc/reference/routines.rst @@ -11,6 +11,7 @@ These functions cover a subset of .. toctree:: :maxdepth: 2 + constants array-creation array-manipulation bitwise diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index c2bea0b4746d..25704e8d0548 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -233,9 +233,9 @@ def __dlpack__( dl_device : {tuple, None}, optional: The device the returned DLPack capsule will be placed on. The device must be a 2-tuple matching the format of - ``__dlpack_device__`` method, an integer enumerator representing - the device type followed by an integer representing the index of - the device. + :meth:`dpnp.ndarray.__dlpack_device__`, an integer enumerator + representing the device type followed by an integer representing + the index of the device. Default: ``None``. copy : {bool, None}, optional: @@ -278,9 +278,12 @@ def __dlpack_device__(self, /): allocated, or the non-partitioned parent device of the allocation device. + See :class:`dpnp.DLDeviceType` for a list of devices supported by the + DLPack protocol. + Raises ------ - DLPackCreationError: + DLPackCreationError when the ``device_id`` could not be determined. """ diff --git a/dpnp/dpnp_iface_arraycreation.py b/dpnp/dpnp_iface_arraycreation.py index 1d93274513d0..0c256d8d58de 100644 --- a/dpnp/dpnp_iface_arraycreation.py +++ b/dpnp/dpnp_iface_arraycreation.py @@ -2180,8 +2180,9 @@ def from_dlpack(x, /, *, device=None, copy=None): Parameters ---------- x : object - A Python object representing an array that implements the ``__dlpack__`` - and ``__dlpack_device__`` methods. + A Python object representing an array that implements the + :meth:`dpnp.ndarray.__dlpack__` and + :meth:`dpnp.ndarray.__dlpack_device__`. device : {None, string, tuple, device}, optional Device where the output array is to be placed. `device` keyword values can be: @@ -2197,10 +2198,10 @@ def from_dlpack(x, /, *, device=None, copy=None): ``device.sycl_queue``. The `device` object is obtained via :attr:`dpctl.tensor.usm_ndarray.device`. * ``(device_type, device_id)`` : 2-tuple matching the format of the - output of the ``__dlpack_device__`` method: an integer enumerator - representing the device type followed by an integer representing - the index of the device. The only supported :class:`dpnp.DLDeviceType` - device types are ``"kDLCPU"`` and ``"kDLOneAPI"``. + output of the :meth:`dpnp.ndarray.__dlpack_device__`: an integer + enumerator representing the device type followed by an integer + representing the index of the device. The only supported + :class:`dpnp.DLDeviceType` is ``"kDLCPU"`` or ``"kDLOneAPI"``. Default: ``None``. copy : {bool, None}, optional From 6be05db4b57feb2e5a33e260107cf5dacb23b483 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 23 Jul 2025 17:26:58 +0200 Subject: [PATCH 17/23] Add PR to the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab8d1fd619e8..6a72cd584c90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Removed the use of class template argument deduction for alias template to conform to the C++17 standard [#2517](https://github.com/IntelPython/dpnp/pull/2517) * Changed th order of individual FFTs over `axes` for `dpnp.fft.irfftn` to be in forward order [#2524](https://github.com/IntelPython/dpnp/pull/2524) * Replaced the use of `numpy.testing.suppress_warnings` with appropriate calls from the warnings module [#2529](https://github.com/IntelPython/dpnp/pull/2529) +* Improved documentations of `dpnp.ndarray` class and added page with description of supported constants [#2422](https://github.com/IntelPython/dpnp/pull/2422) ### Deprecated From a571c9afd683aafdce8ab36e16694c32c6a3e8b2 Mon Sep 17 00:00:00 2001 From: Anton <100830759+antonwolfy@users.noreply.github.com> Date: Wed, 23 Jul 2025 20:29:42 +0200 Subject: [PATCH 18/23] Apply suggestions from code review Co-authored-by: Vahid Tavanashad <120411540+vtavana@users.noreply.github.com> --- doc/reference/constants.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/reference/constants.rst b/doc/reference/constants.rst index 39c79c443163..1678229c060e 100644 --- a/doc/reference/constants.rst +++ b/doc/reference/constants.rst @@ -1,5 +1,5 @@ Constants -================================= +========= DPNP includes several constants: From 694b969fa3643e126920661eca84dc08ccc62f48 Mon Sep 17 00:00:00 2001 From: Anton <100830759+antonwolfy@users.noreply.github.com> Date: Wed, 23 Jul 2025 20:30:39 +0200 Subject: [PATCH 19/23] Update CHANGELOG.md Co-authored-by: Vahid Tavanashad <120411540+vtavana@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a72cd584c90..8263db169c22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Removed the use of class template argument deduction for alias template to conform to the C++17 standard [#2517](https://github.com/IntelPython/dpnp/pull/2517) * Changed th order of individual FFTs over `axes` for `dpnp.fft.irfftn` to be in forward order [#2524](https://github.com/IntelPython/dpnp/pull/2524) * Replaced the use of `numpy.testing.suppress_warnings` with appropriate calls from the warnings module [#2529](https://github.com/IntelPython/dpnp/pull/2529) -* Improved documentations of `dpnp.ndarray` class and added page with description of supported constants [#2422](https://github.com/IntelPython/dpnp/pull/2422) +* Improved documentations of `dpnp.ndarray` class and added a page with description of supported constants [#2422](https://github.com/IntelPython/dpnp/pull/2422) ### Deprecated From e8127d40b716c6a5d07735b1ad242f28d736ade7 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 23 Jul 2025 20:32:50 +0200 Subject: [PATCH 20/23] Aligned examples per review comment --- doc/reference/constants.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/reference/constants.rst b/doc/reference/constants.rst index 1678229c060e..256a2bdb12dd 100644 --- a/doc/reference/constants.rst +++ b/doc/reference/constants.rst @@ -122,16 +122,16 @@ DPNP includes several constants: array([0, 1, 2]) >>> x[:, np.newaxis] array([[0], - [1], - [2]]) + [1], + [2]]) >>> x[:, np.newaxis, np.newaxis] array([[[0]], - [[1]], - [[2]]]) + [[1]], + [[2]]]) >>> x[:, np.newaxis] * x array([[0, 0, 0], - [0, 1, 2], - [0, 2, 4]]) + [0, 1, 2], + [0, 2, 4]]) Outer product, same as ``outer(x, y)``: From aae481a05866d727e55d334f4a05e5a9d0fb5b51 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 23 Jul 2025 20:37:23 +0200 Subject: [PATCH 21/23] Clarify common description of out keyword --- doc/reference/ndarray.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/reference/ndarray.rst b/doc/reference/ndarray.rst index 7c7a2b581971..9a03c680a012 100644 --- a/doc/reference/ndarray.rst +++ b/doc/reference/ndarray.rst @@ -227,8 +227,9 @@ reduction using a larger data type. For several methods, an optional *out* argument can also be provided and the result will be placed into the output array given. The *out* argument must be -an :class:`dpnp.ndarray` and have the same number of elements. It can have a -different data type in which case casting will be performed. +an :class:`dpnp.ndarray` and have the same number of elements as the result +array. It can have a different data type in which case casting will be +performed. .. autosummary:: :toctree: generated/ From 649f01f61d8eb54d76d45442ed2573b963e44b6f Mon Sep 17 00:00:00 2001 From: Anton <100830759+antonwolfy@users.noreply.github.com> Date: Wed, 23 Jul 2025 20:38:54 +0200 Subject: [PATCH 22/23] Update dpnp/dpnp_array.py Co-authored-by: Vahid Tavanashad <120411540+vtavana@users.noreply.github.com> --- dpnp/dpnp_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 25704e8d0548..b73fdad36960 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -626,7 +626,7 @@ def _create_from_usm_ndarray(usm_ary: dpt.usm_ndarray): def all(self, axis=None, *, out=None, keepdims=False, where=True): """ - Return ``True`` if all elements evaluate to ``True.`` + Return ``True`` if all elements evaluate to ``True``. Refer to :obj:`dpnp.all` for full documentation. From 77b8529242b58d2cd6253ff9138c604607bb20ab Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 23 Jul 2025 21:17:38 +0200 Subject: [PATCH 23/23] Return NotImplemented when non-default mod is passed to __pow__ and __rpow__ --- dpnp/dpnp_array.py | 4 ++++ dpnp/tests/test_ndarray.py | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index b73fdad36960..6d9aaab20518 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -480,6 +480,8 @@ def __pos__(self, /): def __pow__(self, other, mod=None, /): r"""Return :math:`\text{self ** value}`.""" + if mod is not None: + return NotImplemented return dpnp.power(self, other) def __radd__(self, other, /): @@ -524,6 +526,8 @@ def __ror__(self, other, /): def __rpow__(self, other, mod=None, /): r"""Return :math:`\text{value ** self}`.""" + if mod is not None: + return NotImplemented return dpnp.power(other, self) def __rrshift__(self, other, /): diff --git a/dpnp/tests/test_ndarray.py b/dpnp/tests/test_ndarray.py index 90b4045f62f9..ce857d73ea25 100644 --- a/dpnp/tests/test_ndarray.py +++ b/dpnp/tests/test_ndarray.py @@ -550,3 +550,15 @@ def test_rmatmul_numpy_array(): with pytest.raises(TypeError): b @ a + + +@pytest.mark.parametrize("xp", [dpnp, numpy]) +def test_pow_modulo(xp): + a = xp.array([2, 3, 4]) + b = xp.array([5, 2, 3]) + + assert a.__pow__(b, 10) == NotImplemented + assert a.__rpow__(b, 10) == NotImplemented + + assert (a.__pow__(b, None) == a**b).all() + assert (a.__rpow__(b, None) == b**a).all()