Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement dpnp.rot90 and dpnp.resize #2030

Merged
merged 6 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 204 additions & 12 deletions dpnp/dpnp_iface_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@


import math
import operator

import dpctl.tensor as dpt
import numpy
Expand Down Expand Up @@ -77,9 +78,11 @@
"ravel",
"repeat",
"reshape",
"resize",
"result_type",
"roll",
"rollaxis",
"rot90",
"row_stack",
"shape",
"size",
Expand Down Expand Up @@ -1522,6 +1525,7 @@ def fliplr(m):
--------
:obj:`dpnp.flipud` : Flip an array vertically (axis=0).
:obj:`dpnp.flip` : Flip array in one or more dimensions.
:obj:`dpnp.rot90` : Rotate array counterclockwise.

Examples
--------
Expand Down Expand Up @@ -1572,6 +1576,7 @@ def flipud(m):
--------
:obj:`dpnp.fliplr` : Flip array in the left/right direction.
:obj:`dpnp.flip` : Flip array in one or more dimensions.
:obj:`dpnp.rot90` : Rotate array counterclockwise.

Examples
--------
Expand Down Expand Up @@ -1985,7 +1990,8 @@ def reshape(a, /, newshape, order="C", copy=None):
If ``False``, the result array can never be a copy
and a ValueError exception will be raised in case the copy is necessary.
If ``None``, the result array will reuse existing memory buffer of `a`
if possible and copy otherwise. Default: None.
if possible and copy otherwise.
Default: ``None``.

Returns
-------
Expand All @@ -2004,14 +2010,14 @@ def reshape(a, /, newshape, order="C", copy=None):

Examples
--------
>>> import dpnp as dp
>>> a = dp.array([[1, 2, 3], [4, 5, 6]])
>>> dp.reshape(a, 6)
>>> import dpnp as np
>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> np.reshape(a, 6)
array([1, 2, 3, 4, 5, 6])
>>> dp.reshape(a, 6, order='F')
>>> np.reshape(a, 6, order='F')
array([1, 4, 2, 5, 3, 6])

>>> dp.reshape(a, (3, -1)) # the unspecified value is inferred to be 2
>>> np.reshape(a, (3, -1)) # the unspecified value is inferred to be 2
array([[1, 2],
[3, 4],
[5, 6]])
Expand All @@ -2031,6 +2037,91 @@ def reshape(a, /, newshape, order="C", copy=None):
return dpnp_array._create_from_usm_ndarray(usm_res)


def resize(a, new_shape):
"""
Return a new array with the specified shape.

If the new array is larger than the original array, then the new array is
filled with repeated copies of `a`. Note that this behavior is different
from ``a.resize(new_shape)`` which fills with zeros instead of repeated
copies of `a`.

For full documentation refer to :obj:`numpy.resize`.

Parameters
----------
a : {dpnp.ndarray, usm_ndarray}
Array to be resized.
new_shape : {int, tuple or list of ints}
Shape of resized array.

Returns
-------
out : dpnp.ndarray
The new array is formed from the data in the old array, repeated
if necessary to fill out the required number of elements. The
data are repeated iterating over the array in C-order.

See Also
--------
:obj:`dpnp.ndarray.resize` : Resize an array in-place.
:obj:`dpnp.reshape` : Reshape an array without changing the total size.
:obj:`dpnp.pad` : Enlarge and pad an array.
:obj:`dpnp.repeat` : Repeat elements of an array.

Notes
-----
When the total size of the array does not change :obj:`dpnp.reshape` should
be used. In most other cases either indexing (to reduce the size) or
padding (to increase the size) may be a more appropriate solution.

Warning: This functionality does **not** consider axes separately,
i.e. it does not apply interpolation/extrapolation.
It fills the return array with the required number of elements, iterating
over `a` in C-order, disregarding axes (and cycling back from the start if
the new shape is larger). This functionality is therefore not suitable to
resize images, or data where each axis represents a separate and distinct
entity.

Examples
--------
>>> import dpnp as np
>>> a = np.array([[0, 1], [2, 3]])
>>> np.resize(a, (2, 3))
array([[0, 1, 2],
[3, 0, 1]])
>>> np.resize(a, (1, 4))
array([[0, 1, 2, 3]])
>>> np.resize(a, (2, 4))
array([[0, 1, 2, 3],
[0, 1, 2, 3]])

"""

dpnp.check_supported_arrays_type(a)
if a.ndim == 0:
return dpnp.full_like(a, a, shape=new_shape)

if isinstance(new_shape, (int, numpy.integer)):
new_shape = (new_shape,)

new_size = 1
for dim_length in new_shape:
if dim_length < 0:
raise ValueError("all elements of `new_shape` must be non-negative")
new_size *= dim_length

a_size = a.size
if a_size == 0 or new_size == 0:
# First case must zero fill. The second would have repeats == 0.
return dpnp.zeros_like(a, shape=new_shape)

repeats = -(-new_size // a_size) # ceil division
a = dpnp.concatenate((dpnp.ravel(a),) * repeats)[:new_size]

return a.reshape(new_shape)


def result_type(*arrays_and_dtypes):
"""
result_type(*arrays_and_dtypes)
Expand All @@ -2052,16 +2143,16 @@ def result_type(*arrays_and_dtypes):

Examples
--------
>>> import dpnp as dp
>>> a = dp.arange(3, dtype=dp.int64)
>>> b = dp.arange(7, dtype=dp.int32)
>>> dp.result_type(a, b)
>>> import dpnp as np
>>> a = np.arange(3, dtype=np.int64)
>>> b = np.arange(7, dtype=np.int32)
>>> np.result_type(a, b)
dtype('int64')

>>> dp.result_type(dp.int64, dp.complex128)
>>> np.result_type(np.int64, np.complex128)
dtype('complex128')

>>> dp.result_type(dp.ones(10, dtype=dp.float32), dp.float64)
>>> np.result_type(np.ones(10, dtype=np.float32), np.float64)
dtype('float64')

"""
Expand Down Expand Up @@ -2200,6 +2291,107 @@ def rollaxis(x, axis, start=0):
return dpnp.moveaxis(usm_array, source=axis, destination=start)


def rot90(m, k=1, axes=(0, 1)):
"""
Rotate an array by 90 degrees in the plane specified by axes.

Rotation direction is from the first towards the second axis.
This means for a 2D array with the default `k` and `axes`, the
rotation will be counterclockwise.

For full documentation refer to :obj:`numpy.rot90`.

Parameters
----------
m : {dpnp.ndarray, usm_ndarray}
Array of two or more dimensions.
k : integer, optional
Number of times the array is rotated by 90 degrees.
Default: ``1``.
axes : (2,) array_like of ints, optional
The array is rotated in the plane defined by the axes.
Axes must be different.
Default: ``(0, 1)``.

Returns
-------
out : dpnp.ndarray
A rotated view of `m`.

See Also
--------
:obj:`dpnp.flip` : Reverse the order of elements in an array along
the given axis.
:obj:`dpnp.fliplr` : Flip an array horizontally.
vtavana marked this conversation as resolved.
Show resolved Hide resolved
:obj:`dpnp.flipud` : Flip an array vertically.
vtavana marked this conversation as resolved.
Show resolved Hide resolved

Notes
-----
``rot90(m, k=1, axes=(1,0))`` is the reverse of
``rot90(m, k=1, axes=(0,1))``.

``rot90(m, k=1, axes=(1,0))`` is equivalent to
``rot90(m, k=-1, axes=(0,1))``.

Examples
--------
>>> import dpnp as np
>>> m = np.array([[1, 2], [3, 4]])
>>> m
array([[1, 2],
[3, 4]])
>>> np.rot90(m)
array([[2, 4],
[1, 3]])
>>> np.rot90(m, 2)
array([[4, 3],
[2, 1]])
>>> m = np.arange(8).reshape((2, 2, 2))
>>> np.rot90(m, 1, (1, 2))
array([[[1, 3],
[0, 2]],
[[5, 7],
[4, 6]]])

"""

dpnp.check_supported_arrays_type(m)
k = operator.index(k)

m_ndim = m.ndim
if m_ndim < 2:
raise ValueError("Input must be at least 2-d.")

if len(axes) != 2:
raise ValueError("len(axes) must be 2.")

if axes[0] == axes[1] or abs(axes[0] - axes[1]) == m_ndim:
raise ValueError("Axes must be different.")

if not (-m_ndim <= axes[0] < m_ndim and -m_ndim <= axes[1] < m_ndim):
raise ValueError(
f"Axes={axes} out of range for array of ndim={m_ndim}."
)

k %= 4
if k == 0:
return m[:]
if k == 2:
return dpnp.flip(dpnp.flip(m, axes[0]), axes[1])

axes_list = list(range(0, m_ndim))
(axes_list[axes[0]], axes_list[axes[1]]) = (
axes_list[axes[1]],
axes_list[axes[0]],
)

if k == 1:
return dpnp.transpose(dpnp.flip(m, axes[1]), axes_list)

# k == 3
return dpnp.flip(dpnp.transpose(m, axes_list), axes[1])


def shape(a):
"""
Return the shape of an array.
Expand Down
Loading
Loading