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

Added flipping functions #1543

Merged
merged 3 commits into from
Aug 28, 2023
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
185 changes: 183 additions & 2 deletions dpnp/dpnp_iface_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
"concatenate",
"copyto",
"expand_dims",
"flip",
"fliplr",
"flipud",
"hstack",
"moveaxis",
"ravel",
Expand Down Expand Up @@ -399,7 +402,7 @@ def expand_dims(a, axis):

Returns
-------
dpnp.ndarray
out : dpnp.ndarray
An array with the number of dimensions increased.
A view is returned whenever possible.

Expand Down Expand Up @@ -476,6 +479,184 @@ def expand_dims(a, axis):
)


def flip(m, axis=None):
"""
Reverse the order of elements in an array along the given axis.

The shape of the array is preserved, but the elements are reordered.

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

Returns
-------
out : dpnp.ndarray
A view of `m` with the entries of axis reversed.

Limitations
-----------
Parameters `m` is supported either as :class:`dpnp.ndarray`
or :class:`dpctl.tensor.usm_ndarray`.
Input array data types are limited by supported DPNP :ref:`Data types`.
Otherwise ``TypeError`` exception will be raised.

See Also
--------
:obj:`dpnp.flipud` : Flip an array vertically (axis=0).
:obj:`dpnp.fliplr` : Flip an array horizontally (axis=1).

Examples
--------
>>> import dpnp as np
>>> A = np.arange(8).reshape((2, 2, 2))
>>> A
array([[[0, 1],
antonwolfy marked this conversation as resolved.
Show resolved Hide resolved
[2, 3]],
[[4, 5],
[6, 7]]])
>>> np.flip(A, 0)
array([[[4, 5],
[6, 7]],
[[0, 1],
[2, 3]]])
>>> np.flip(A, 1)
array([[[2, 3],
[0, 1]],
[[6, 7],
[4, 5]]])
>>> np.flip(A)
array([[[7, 6],
[5, 4]],
[[3, 2],
[1, 0]]])
>>> np.flip(A, (0, 2))
array([[[5, 4],
[7, 6]],
[[1, 0],
[3, 2]]])
>>> A = np.random.randn(3, 4, 5)
>>> np.all(np.flip(A, 2) == A[:, :, ::-1, ...])
array(True)

"""

m_usm = dpnp.get_usm_ndarray(m)
return dpnp_array._create_from_usm_ndarray(dpt.flip(m_usm, axis=axis))


def fliplr(m):
"""
Reverse the order of elements along axis 1 (left/right).

For a 2-D array, this flips the entries in each row in the left/right
direction. Columns are preserved, but appear in a different order than
before.

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

Returns
-------
out : dpnp.ndarray
A view of `m` with the columns reversed.

Limitations
-----------
Parameters `m` is supported either as :class:`dpnp.ndarray`
or :class:`dpctl.tensor.usm_ndarray`.
Input array data types are limited by supported DPNP :ref:`Data types`.
Otherwise ``TypeError`` exception will be raised.

See Also
--------
:obj:`dpnp.flipud` : Flip an array vertically (axis=0).
:obj:`dpnp.flip` : Flip array in one or more dimensions.

Examples
--------
>>> import dpnp as np
>>> A = np.diag(np.array([1., 2., 3.]))
>>> A
array([[1., 0., 0.],
[0., 2., 0.],
[0., 0., 3.]])
>>> np.fliplr(A)
array([[0., 0., 1.],
[0., 2., 0.],
[3., 0., 0.]])

>>> A = np.random.randn(2, 3, 5)
>>> np.all(np.fliplr(A) == A[:, ::-1, ...])
array(True)

"""

if not dpnp.is_supported_array_type(m):
raise TypeError(
"An array must be any of supported type, but got {}".format(type(m))
)

if m.ndim < 2:
raise ValueError(f"Input must be >= 2-d, but got {m.ndim}")
return m[:, ::-1]


def flipud(m):
"""
Reverse the order of elements along axis 0 (up/down).

For a 2-D array, this flips the entries in each column in the up/down
direction. Rows are preserved, but appear in a different order than before.

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

Returns
-------
out : dpnp.ndarray
A view of `m` with the rows reversed.

Limitations
-----------
Parameters `m` is supported either as :class:`dpnp.ndarray`
or :class:`dpctl.tensor.usm_ndarray`.
Input array data types are limited by supported DPNP :ref:`Data types`.
Otherwise ``TypeError`` exception will be raised.

See Also
--------
:obj:`dpnp.fliplr` : Flip array in the left/right direction.
:obj:`dpnp.flip` : Flip array in one or more dimensions.

Examples
--------
>>> import dpnp as np
>>> A = np.diag(np.array([1., 2., 3.]))
>>> A
array([[1., 0., 0.],
[0., 2., 0.],
[0., 0., 3.]])
>>> np.flipud(A)
array([[0., 0., 3.],
[0., 2., 0.],
[1., 0., 0.]])

>>> A = np.random.randn(2, 3, 5)
>>> np.all(np.flipud(A) == A[::-1, ...])
array(True)

>>> np.flipud(np.array([1, 2]))
array([2, 1])

"""

if not dpnp.is_supported_array_type(m):
raise TypeError(
"An array must be any of supported type, but got {}".format(type(m))
)

if m.ndim < 1:
raise ValueError(f"Input must be >= 1-d, but got {m.ndim}")
return m[::-1, ...]


def hstack(tup):
"""
Stack arrays in sequence horizontally (column wise).
Expand Down Expand Up @@ -949,7 +1130,7 @@ def swapaxes(a, axis1, axis2):

Returns
-------
dpnp.ndarray
out : dpnp.ndarray
An array with with swapped axes.
A view is returned whenever possible.

Expand Down
169 changes: 169 additions & 0 deletions tests/test_flipping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
from math import prod

import numpy
import pytest
from numpy.testing import (
assert_equal,
)

import dpnp

from .helper import (
get_all_dtypes,
)


class TestFlip:
@pytest.mark.parametrize("dtype", get_all_dtypes())
def test_arange_2d_default_axis(self, dtype):
sh = (2, 3) if dtype != dpnp.bool else (1, 1)
dp_a = dpnp.arange(prod(sh), dtype=dtype).reshape(sh)
np_a = numpy.arange(prod(sh), dtype=dtype).reshape(sh)

assert_equal(dpnp.flip(dp_a), numpy.flip(np_a))

@pytest.mark.parametrize("axis", list(range(3)))
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_bool=True, no_none=True)
)
def test_arange_3d(self, axis, dtype):
sh = (2, 2, 2)
dp_a = dpnp.arange(prod(sh), dtype=dtype).reshape(sh)
np_a = numpy.arange(prod(sh), dtype=dtype).reshape(sh)

assert_equal(dpnp.flip(dp_a, axis=axis), numpy.flip(np_a, axis=axis))

@pytest.mark.parametrize("axis", [(), (0, 2), (1, 2)])
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_bool=True, no_none=True)
)
def test_arange_3d_multiple_axes(self, axis, dtype):
sh = (2, 2, 2)
dp_a = dpnp.arange(prod(sh), dtype=dtype).reshape(sh)
np_a = numpy.arange(prod(sh), dtype=dtype).reshape(sh)

assert_equal(dpnp.flip(dp_a, axis=axis), numpy.flip(np_a, axis=axis))

@pytest.mark.parametrize("axis", list(range(4)))
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_bool=True, no_none=True)
)
def test_arange_4d(self, axis, dtype):
sh = (2, 3, 4, 5)
dp_a = dpnp.arange(prod(sh), dtype=dtype).reshape(sh)
np_a = numpy.arange(prod(sh), dtype=dtype).reshape(sh)

assert_equal(dpnp.flip(dp_a, axis=axis), numpy.flip(np_a, axis=axis))

@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_bool=True, no_none=True)
)
def test_lr_equivalent(self, dtype):
dp_a = dpnp.arange(4, dtype=dtype)
dp_a = dp_a[:, dpnp.newaxis] + dp_a[dpnp.newaxis, :]
assert_equal(dpnp.flip(dp_a, 1), dpnp.fliplr(dp_a))

np_a = numpy.arange(4, dtype=dtype)
np_a = numpy.add.outer(np_a, np_a)
assert_equal(dpnp.flip(dp_a, 1), numpy.flip(np_a, 1))

@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_bool=True, no_none=True)
)
def test_ud_equivalent(self, dtype):
dp_a = dpnp.arange(4, dtype=dtype)
dp_a = dp_a[:, dpnp.newaxis] + dp_a[dpnp.newaxis, :]
assert_equal(dpnp.flip(dp_a, 0), dpnp.flipud(dp_a))

np_a = numpy.arange(4, dtype=dtype)
np_a = numpy.add.outer(np_a, np_a)
assert_equal(dpnp.flip(dp_a, 0), numpy.flip(np_a, 0))

@pytest.mark.parametrize(
"x, axis",
[
pytest.param(dpnp.ones(4), 1, id="1-d, axis=1"),
pytest.param(dpnp.ones((4, 4)), 2, id="2-d, axis=2"),
pytest.param(dpnp.ones((4, 4)), -3, id="2-d, axis=-3"),
pytest.param(dpnp.ones((4, 4)), (0, 3), id="2-d, axis=(0, 3)"),
],
)
def test_axes(self, x, axis):
with pytest.raises(numpy.AxisError):
dpnp.flip(x, axis=axis)


class TestFliplr:
@pytest.mark.parametrize("dtype", get_all_dtypes())
def test_arange(self, dtype):
sh = (2, 3) if dtype != dpnp.bool else (1, 1)
dp_a = dpnp.arange(prod(sh), dtype=dtype).reshape(sh)
np_a = numpy.arange(prod(sh), dtype=dtype).reshape(sh)

assert_equal(dpnp.fliplr(dp_a), numpy.fliplr(np_a))

@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_bool=True, no_none=True)
)
def test_equivalent(self, dtype):
dp_a = dpnp.arange(4, dtype=dtype)
dp_a = dp_a[:, dpnp.newaxis] + dp_a[dpnp.newaxis, :]
assert_equal(dpnp.fliplr(dp_a), dp_a[:, ::-1])

np_a = numpy.arange(4, dtype=dtype)
np_a = numpy.add.outer(np_a, np_a)
assert_equal(dpnp.fliplr(dp_a), numpy.fliplr(np_a))

@pytest.mark.parametrize(
"val",
[-1.2, numpy.arange(7), [2, 7, 3.6], (-3, 4), range(4)],
ids=["scalar", "numpy.array", "list", "tuple", "range"],
)
def test_raises_array_type(self, val):
with pytest.raises(
TypeError, match="An array must be any of supported type, but got"
):
dpnp.fliplr(val)

def test_raises_1d(self):
a = dpnp.ones(4)
with pytest.raises(ValueError, match="Input must be >= 2-d, but got"):
dpnp.fliplr(a)


class TestFlipud:
@pytest.mark.parametrize("dtype", get_all_dtypes())
def test_arange(self, dtype):
sh = (2, 3) if dtype != dpnp.bool else (1, 1)
dp_a = dpnp.arange(prod(sh), dtype=dtype).reshape(sh)
np_a = numpy.arange(prod(sh), dtype=dtype).reshape(sh)

assert_equal(dpnp.flipud(dp_a), numpy.flipud(np_a))

@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_bool=True, no_none=True)
)
def test_equivalent(self, dtype):
dp_a = dpnp.arange(4, dtype=dtype)
dp_a = dp_a[:, dpnp.newaxis] + dp_a[dpnp.newaxis, :]
assert_equal(dpnp.flipud(dp_a), dp_a[::-1, :])

np_a = numpy.arange(4, dtype=dtype)
np_a = numpy.add.outer(np_a, np_a)
assert_equal(dpnp.flipud(dp_a), numpy.flipud(np_a))

@pytest.mark.parametrize(
"val",
[3.4, numpy.arange(6), [2, -1.7, 6], (-2, 4), range(5)],
ids=["scalar", "numpy.array", "list", "tuple", "range"],
)
def test_raises_array_type(self, val):
with pytest.raises(
TypeError, match="An array must be any of supported type, but got"
):
dpnp.flipud(val)

def test_raises_0d(self):
a = dpnp.array(3)
with pytest.raises(ValueError, match="Input must be >= 1-d, but got"):
dpnp.flipud(a)
Loading