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: diff --git a/CHANGELOG.md b/CHANGELOG.md index ab8d1fd619e8..8263db169c22 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 a page with description of supported constants [#2422](https://github.com/IntelPython/dpnp/pull/2422) ### Deprecated diff --git a/doc/reference/constants.rst b/doc/reference/constants.rst new file mode 100644 index 000000000000..256a2bdb12dd --- /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/ndarray.rst b/doc/reference/ndarray.rst index 4f9aef8a9160..9a03c680a012 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 @@ -195,6 +211,26 @@ 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 as the result +array. It can have a different data type in which case casting will be +performed. + .. autosummary:: :toctree: generated/ :nosignatures: @@ -226,12 +262,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 `. @@ -252,6 +287,7 @@ Truth value of an array (:class:`bool() `): .. autosummary:: :toctree: generated/ + :nosignatures: dpnp.ndarray.__bool__ @@ -343,6 +379,7 @@ Matrix Multiplication: .. autosummary:: :toctree: generated/ + :nosignatures: dpnp.ndarray.__matmul__ dpnp.ndarray.__rmatmul__ @@ -371,7 +408,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 +420,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 +434,7 @@ and return the appropriate scalar. :toctree: generated/ :nosignatures: + dpnp.ndarray.__index__ dpnp.ndarray.__int__ dpnp.ndarray.__float__ dpnp.ndarray.__complex__ 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 a70a4e99e668..6d9aaab20518 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -24,6 +24,15 @@ # THE POSSIBILITY OF SUCH DAMAGE. # ***************************************************************************** +""" +Interface of an ndarray representing a multidimensional tensor of numeric +elements stored in a USM allocation on a SYCL device. + +""" + +# 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 +45,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 +61,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, @@ -102,95 +114,28 @@ def __init__( array_namespace=dpnp, ) - @property - def __sycl_usm_array_interface__(self): - return self._array_obj.__sycl_usm_array_interface__ - - def get_array(self): - """Get usm_ndarray object.""" - return self._array_obj - - @property - def T(self): - """View of the transposed array.""" - 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 sycl_queue(self): - return self._array_obj.sycl_queue - - @property - def sycl_device(self): - return self._array_obj.sycl_device - - @property - def sycl_context(self): - return self._array_obj.sycl_context - - @property - def device(self): - return self._array_obj.device - - @property - def usm_type(self): - return self._array_obj.usm_type - - def __abs__(self): - r"""Return ``\|self\|``.""" + def __abs__(self, /): + r"""Return :math:`|\text{self}|`.""" return dpnp.abs(self) - def __add__(self, other): - """Return ``self+value``.""" + def __add__(self, other, /): + r"""Return :math:`\text{self + value}`.""" return dpnp.add(self, other) - def __and__(self, other): - """Return ``self&value``.""" + def __and__(self, other, /): + r"""Return :math:`\text{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`. + + """ # noqa: D403 + raise TypeError( "Implicit conversion to a NumPy array is not allowed. " "Please use `.asnumpy()` to construct a NumPy array explicitly." @@ -199,24 +144,18 @@ 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): """ - Returns array namespace, member functions of which implement data API. + Return array namespace, member functions of which implement data API. 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. + Default: ``None``. Returns @@ -232,14 +171,23 @@ def __array_namespace__(self, /, *, api_version=None): return self._array_obj.__array_namespace__(api_version=api_version) - def __bool__(self): - """``True`` if self else ``False``.""" + # '__array_prepare__', + # '__array_priority__', + # '__array_struct__', + + __array_ufunc__ = None + + # '__array_wrap__', + + 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__() def __contains__(self, value, /): @@ -248,7 +196,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. Return a copy of the + array. Equivalent to ``a.copy(order="K")``. @@ -260,34 +209,36 @@ def __copy__(self): # '__delitem__', # '__dir__', # '__divmod__', - # '__doc__', 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. + Produce DLPack capsule. Parameters ---------- 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 + 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 - 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: + copy : {bool, None}, optional: Boolean indicating whether or not to copy the input. * If `copy` is ``True``, the input will always be copied. @@ -300,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. @@ -318,45 +269,51 @@ def __dlpack__( copy=copy, ) - def __dlpack_device__(self): + 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 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. """ return self._array_obj.__dlpack_device__() - def __eq__(self, other): - """Return ``self==value``.""" + # '__doc__', + + 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): - """Return ``self//value``.""" + def __floordiv__(self, other, /): + r"""Return :math:`\text{self // value}`.""" return dpnp.floor_divide(self, other) # '__format__', - def __ge__(self, other): - """Return ``self>=value``.""" + def __ge__(self, other, /): + r"""Return :math:`\text{self >= value}`.""" return dpnp.greater_equal(self, other) # '__getattribute__', - def __getitem__(self, key): - """Return ``self[key]``.""" + def __getitem__(self, key, /): + r"""Return :math:`\text{self[key]}`.""" key = _get_unwrapped_index_key(key) item = self._array_obj.__getitem__(key) @@ -364,42 +321,40 @@ def __getitem__(self, key): # '__getstate__', - def __gt__(self, other): - """Return ``self>value``.""" + def __gt__(self, other, /): + r"""Return :math:`\text{self > value}`.""" return dpnp.greater(self, other) # '__hash__', - def __iadd__(self, other): - """Return ``self+=value``.""" + def __iadd__(self, other, /): + r"""Return :math:`\text{self += value}`.""" dpnp.add(self, other, out=self) return self - def __iand__(self, other): - """Return ``self&=value``.""" + def __iand__(self, other, /): + r"""Return :math:`\text{self &= value}`.""" dpnp.bitwise_and(self, other, out=self) return self - def __ifloordiv__(self, other): - """Return ``self//=value``.""" + def __ifloordiv__(self, other, /): + r"""Return :math:`\text{self //= value}`.""" dpnp.floor_divide(self, other, out=self) return self - def __ilshift__(self, other): - """Return ``self<<=value``.""" + def __ilshift__(self, other, /): + r"""Return :math:`\text{self <<= value}`.""" dpnp.left_shift(self, other, out=self) return self - def __imatmul__(self, other): - """Return ``self@=value``.""" + def __imatmul__(self, other, /): + r"""Return :math:`\text{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: @@ -407,130 +362,134 @@ 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``.""" + def __imod__(self, other, /): + r"""Return :math:`\text{self %= value}`.""" dpnp.remainder(self, other, out=self) return self - def __imul__(self, other): - """Return ``self*=value``.""" + 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): - """Return ``~self``.""" + def __invert__(self, /): + r"""Return :math:`\text{~self}`.""" return dpnp.invert(self) - def __ior__(self, other): - """Return ``self|=value``.""" + def __ior__(self, other, /): + r"""Return :math:`\text{self |= value}`.""" dpnp.bitwise_or(self, other, out=self) return self - def __ipow__(self, other): - """Return ``self**=value``.""" + def __ipow__(self, other, /): + r"""Return :math:`\text{self **= value}`.""" dpnp.power(self, other, out=self) return self - def __irshift__(self, other): - """Return ``self>>=value``.""" + def __irshift__(self, other, /): + r"""Return :math:`\text{self >>= value}`.""" dpnp.right_shift(self, other, out=self) return self - def __isub__(self, other): - """Return ``self-=value``.""" + def __isub__(self, other, /): + r"""Return :math:`\text{self -= value}`.""" dpnp.subtract(self, other, out=self) return self - def __iter__(self): - """Return ``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): - """Return ``self/=value``.""" + def __itruediv__(self, other, /): + r"""Return :math:`\text{self /= value}`.""" dpnp.true_divide(self, other, out=self) return self - def __ixor__(self, other): - """Return ``self^=value``.""" + def __ixor__(self, other, /): + r"""Return :math:`\text{self ^= value}`.""" dpnp.bitwise_xor(self, other, out=self) return self - def __le__(self, other): - """Return ``self<=value``.""" + def __le__(self, other, /): + r"""Return :math:`\text{self <= value}`.""" return dpnp.less_equal(self, other) def __len__(self): - """Return ``len(self)``.""" + r"""Return :math:`\text{len(self)}`.""" return self._array_obj.__len__() - def __lshift__(self, other): - """Return ``self<>self``.""" + def __rrshift__(self, other, /): + r"""Return :math:`\text{value >> self}`.""" return dpnp.right_shift(other, self) - def __rshift__(self, other): - """Return ``self>>value``.""" + def __rshift__(self, other, /): + r"""Return :math:`\text{self >> value}`.""" return dpnp.right_shift(self, other) - def __rsub__(self, other): - """Return ``value-self``.""" + def __rsub__(self, other, /): + r"""Return :math:`\text{value - self}`.""" return dpnp.subtract(other, self) - def __rtruediv__(self, other): - """Return ``value/self``.""" + def __rtruediv__(self, other, /): + r"""Return :math:`\text{value / self}`.""" return dpnp.true_divide(other, self) - def __rxor__(self, other): - """Return ``value^self``.""" + def __rxor__(self, other, /): + r"""Return :math:`\text{value ^ self}`.""" return dpnp.bitwise_xor(other, self) # '__setattr__', - def __setitem__(self, key, val): - """Set ``self[key]`` to value.""" + 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__', @@ -606,31 +567,39 @@ def __setitem__(self, key, val): __slots__ = ("_array_obj",) def __str__(self): - """Return ``str(self)``.""" + r"""Return :math:`\text{str(self)}`.""" return self._array_obj.__str__() - def __sub__(self, other): - """Return ``self-value``.""" + def __sub__(self, other, /): + r"""Return :math:`\text{self - value}`.""" return dpnp.subtract(self, other) # '__subclasshook__', - def __truediv__(self, other): - """Return ``self/value``.""" + @property + 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, /): + r"""Return :math:`\text{self / value}`.""" return dpnp.true_divide(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. 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. @@ -638,12 +607,18 @@ def __usm_ndarray__(self): return self._array_obj - def __xor__(self, other): - """Return ``self^value``.""" + def __xor__(self, other, /): + r"""Return :math:`\text{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)}" @@ -653,9 +628,9 @@ 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. + Return ``True`` if all elements evaluate to ``True``. Refer to :obj:`dpnp.all` for full documentation. @@ -669,9 +644,9 @@ 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. + Return ``True`` if any of the elements of `a` evaluate to ``True``. Refer to :obj:`dpnp.any` for full documentation. @@ -685,9 +660,9 @@ 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. + Return array of indices of the maximum values along the given axis. Refer to :obj:`dpnp.argmax` for full documentation. @@ -695,7 +670,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. @@ -711,7 +686,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. @@ -721,22 +697,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 @@ -821,8 +801,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 @@ -872,7 +852,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. @@ -880,7 +861,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. @@ -890,7 +871,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. @@ -907,10 +888,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): """ @@ -922,10 +900,11 @@ 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): + def copy( + self, /, order="C", *, device=None, usm_type=None, sycl_queue=None + ): """ Return a copy of the array. @@ -935,6 +914,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 +927,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 @@ -1001,7 +983,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. @@ -1011,7 +993,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. @@ -1031,6 +1013,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. @@ -1078,7 +1082,10 @@ 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 return self._array_obj.dtype @@ -1111,6 +1118,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) @@ -1123,11 +1131,14 @@ 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. + + """ # noqa: D200 return dpnp.flatiter(self) - def flatten(self, order="C"): + def flatten(self, /, order="C"): """ Return a copy of the array collapsed into one dimension. @@ -1171,10 +1182,14 @@ 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 - def imag(self): + def imag(self, /): """ The imaginary part of the array. @@ -1193,7 +1208,7 @@ def imag(self): ) @imag.setter - def imag(self, value): + def imag(self, value, /): """ Set the imaginary part of the array. @@ -1208,12 +1223,13 @@ 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: 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. @@ -1233,7 +1249,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 -------- @@ -1271,7 +1288,9 @@ def itemsize(self): def max( self, + /, axis=None, + *, out=None, keepdims=False, initial=None, @@ -1294,10 +1313,10 @@ 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. + Return the average of the array elements. Refer to :obj:`dpnp.mean` for full documentation. @@ -1307,7 +1326,9 @@ def mean( def min( self, + /, axis=None, + *, out=None, keepdims=False, initial=None, @@ -1329,6 +1350,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.""" @@ -1377,7 +1441,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. @@ -1411,15 +1475,17 @@ def partition(self, kth, axis=-1, kind="introselect", order=None): def prod( self, + /, axis=None, dtype=None, + *, out=None, keepdims=False, initial=None, where=True, ): """ - Returns the prod along a given axis. + Return the prod along a given axis. Refer to :obj:`dpnp.prod` for full documentation. @@ -1435,9 +1501,9 @@ 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. + Put values of an array into another array along a given axis. Refer to :obj:`dpnp.put` for full documentation. @@ -1445,7 +1511,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. @@ -1456,7 +1522,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. @@ -1478,7 +1544,7 @@ def real(self): return self @real.setter - def real(self, value): + def real(self, value, /): """ Set the real part of the array. @@ -1506,9 +1572,9 @@ 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. + Return an array containing the same data with a new shape. Refer to :obj:`dpnp.reshape` for full documentation. @@ -1525,7 +1591,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))``. @@ -1537,7 +1604,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. @@ -1673,22 +1740,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 @@ -1748,16 +1819,16 @@ def std( self, axis=None, dtype=None, + *, out=None, ddof=0, keepdims=False, - *, where=True, mean=None, 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. @@ -1778,7 +1849,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 @@ -1791,15 +1862,17 @@ def strides(self): def sum( self, + /, axis=None, dtype=None, + *, out=None, keepdims=False, initial=None, where=True, ): """ - Returns the sum along a given axis. + Return the sum along a given axis. Refer to :obj:`dpnp.sum` for full documentation. @@ -1825,7 +1898,64 @@ def swapaxes(self, axis1, axis2): return dpnp.swapaxes(self, axis1=axis1, axis2=axis2) - def take(self, indices, axis=None, out=None, mode="wrap"): + @property + def sycl_context(self): + """ + Return :class:`dpctl.SyclContext` object to which USM data is bound. + + """ # noqa: D200 + 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. + + """ # noqa: D200 + 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. @@ -1837,7 +1967,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 ---------- @@ -1851,6 +1981,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 @@ -1882,7 +2013,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. @@ -1896,7 +2027,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`. @@ -1919,8 +2050,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 -------- @@ -1967,16 +2100,16 @@ def var( self, axis=None, dtype=None, + *, out=None, ddof=0, keepdims=False, - *, where=True, mean=None, 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. @@ -1994,7 +2127,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. @@ -2117,3 +2250,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 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 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() 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