Skip to content

Add dpnp.broadcast_to() function #1333

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

Merged
merged 1 commit into from
Mar 7, 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
44 changes: 44 additions & 0 deletions dpnp/dpnp_iface_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,18 @@
from dpnp.dpnp_iface_arraycreation import array

import dpnp
from dpnp.dpnp_array import dpnp_array

import numpy
import dpctl.tensor as dpt


__all__ = [
"asfarray",
"atleast_1d",
"atleast_2d",
"atleast_3d",
"broadcast_to",
"concatenate",
"copyto",
"expand_dims",
Expand Down Expand Up @@ -190,6 +194,46 @@ def atleast_3d(*arys):
return call_origin(numpy.atleast_3d, *arys)


def broadcast_to(x, /, shape, subok=False):
"""
Broadcast an array to a new shape.

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

Returns
-------
y : dpnp.ndarray
An array having a specified shape. Must have the same data type as `x`.

Limitations
-----------
Parameter `x` is supported as either :class:`dpnp.ndarray`
or :class:`dpctl.tensor.usm_ndarray`.
Parameter `subok` is supported with default value.
Otherwise the function will be executed sequentially on CPU.
Input array data types of `x` is limited by supported DPNP :ref:`Data types`.

Examples
--------
>>> import dpnp as dp
>>> x = dp.array([1, 2, 3])
>>> dp.broadcast_to(x, (3, 3))
array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3]])

"""

if subok is not False:
pass
elif isinstance(x, dpnp_array) or isinstance(x, dpt.usm_ndarray):
dpt_array = x.get_array() if isinstance(x, dpnp_array) else x
new_array = dpt.broadcast_to(dpt_array, shape)
return dpnp_array._create_from_usm_ndarray(new_array)

return call_origin(numpy.broadcast_to, x, shape=shape, subok=subok)


def concatenate(arrs, axis=0, out=None, dtype=None, casting="same_kind"):
"""
Join a sequence of arrays along an existing axis.
Expand Down
7 changes: 1 addition & 6 deletions tests/skipped_tests.tbl
Original file line number Diff line number Diff line change
Expand Up @@ -648,12 +648,7 @@ tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_8_{s
tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_8_{shapes=[(2, 0, 1, 1, 3), (2, 1, 0, 0, 3)]}::test_broadcast_arrays
tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_9_{shapes=[(0, 1, 1, 3), (2, 1, 0, 0, 3)]}::test_broadcast
tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_9_{shapes=[(0, 1, 1, 3), (2, 1, 0, 0, 3)]}::test_broadcast_arrays
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_fail
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_fail_numpy19
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_numpy19
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_short_shape
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_short_shape_numpy19

tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_squeeze_int_axis_failure1
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_squeeze_int_axis_failure2
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_squeeze_scalar_failure1
Expand Down
7 changes: 1 addition & 6 deletions tests/skipped_tests_gpu.tbl
Original file line number Diff line number Diff line change
Expand Up @@ -853,12 +853,7 @@ tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_8_{s
tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_8_{shapes=[(2, 0, 1, 1, 3), (2, 1, 0, 0, 3)]}::test_broadcast_arrays
tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_9_{shapes=[(0, 1, 1, 3), (2, 1, 0, 0, 3)]}::test_broadcast
tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_9_{shapes=[(0, 1, 1, 3), (2, 1, 0, 0, 3)]}::test_broadcast_arrays
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_fail
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_fail_numpy19
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_numpy19
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_short_shape
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_broadcast_to_short_shape_numpy19

tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_squeeze_int_axis_failure1
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_squeeze_int_axis_failure2
tests/third_party/cupy/manipulation_tests/test_dims.py::TestDims::test_squeeze_scalar_failure1
Expand Down
173 changes: 137 additions & 36 deletions tests/test_arraymanipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@
from .helper import get_all_dtypes

import dpnp

import numpy
from numpy.testing import (
assert_,
assert_allclose,
assert_array_equal,
assert_equal,
assert_raises,
assert_warns
)


@pytest.mark.usefixtures("allow_fall_back_on_numpy")
Expand All @@ -14,7 +23,7 @@ def test_asfarray(dtype, data):
expected = numpy.asfarray(data, dtype)
result = dpnp.asfarray(data, dtype)

numpy.testing.assert_array_equal(result, expected)
assert_array_equal(result, expected)


@pytest.mark.parametrize("dtype", get_all_dtypes())
Expand All @@ -24,7 +33,99 @@ def test_asfarray2(dtype, data, data_dtype):
expected = numpy.asfarray(numpy.array(data, dtype=data_dtype), dtype)
result = dpnp.asfarray(dpnp.array(data, dtype=data_dtype), dtype)

numpy.testing.assert_array_equal(result, expected)
assert_array_equal(result, expected)


class TestDims:
@pytest.mark.parametrize("dt", get_all_dtypes())
@pytest.mark.parametrize("sh",
[(0,), (1,), (3,)],
ids=['(0,)', '(1,)', '(3,)'])
def test_broadcast_array(self, sh, dt):
np_a = numpy.array(0, dtype=dt)
dp_a = dpnp.array(0, dtype=dt)
func = lambda xp, a: xp.broadcast_to(a, sh)

assert_allclose(func(numpy, np_a), func(dpnp, dp_a))

@pytest.mark.parametrize("dt", get_all_dtypes())
@pytest.mark.parametrize("sh",
[(1,), (2,), (1, 2, 3)],
ids=['(1,)', '(2,)', '(1, 2, 3)'])
def test_broadcast_ones(self, sh, dt):
np_a = numpy.ones(1, dtype=dt)
dp_a = dpnp.ones(1, dtype=dt)
func = lambda xp, a: xp.broadcast_to(a, sh)

assert_allclose(func(numpy, np_a), func(dpnp, dp_a))

@pytest.mark.parametrize("dt", get_all_dtypes(no_bool=True))
@pytest.mark.parametrize("sh",
[(3,), (1, 3), (2, 3)],
ids=['(3,)', '(1, 3)', '(2, 3)'])
def test_broadcast_arange(self, sh, dt):
np_a = numpy.arange(3, dtype=dt)
dp_a = dpnp.arange(3, dtype=dt)
func = lambda xp, a: xp.broadcast_to(a, sh)

assert_allclose(func(numpy, np_a), func(dpnp, dp_a))

@pytest.mark.parametrize("dt", get_all_dtypes())
@pytest.mark.parametrize(
"sh1, sh2",
[
pytest.param([0], [0], id="(0)"),
pytest.param([1], [1], id="(1)"),
pytest.param([1], [2], id="(2)"),
],
)
def test_broadcast_not_tuple(self, sh1, sh2, dt):
np_a = numpy.ones(sh1, dtype=dt)
dp_a = dpnp.ones(sh1, dtype=dt)
func = lambda xp, a: xp.broadcast_to(a, sh2)

assert_allclose(func(numpy, np_a), func(dpnp, dp_a))

@pytest.mark.parametrize("dt", get_all_dtypes())
@pytest.mark.parametrize(
"sh1, sh2",
[
pytest.param([1], (0,), id="(0,)"),
pytest.param((1, 2), (0, 2), id="(0, 2)"),
pytest.param((2, 1), (2, 0), id="(2, 0)"),
],
)
def test_broadcast_zero_shape(self, sh1, sh2, dt):
np_a = numpy.ones(sh1, dtype=dt)
dp_a = dpnp.ones(sh1, dtype=dt)
func = lambda xp, a: xp.broadcast_to(a, sh2)

assert_allclose(func(numpy, np_a), func(dpnp, dp_a))

@pytest.mark.parametrize(
"sh1, sh2",
[
pytest.param((0,), (), id="(0,)-()"),
pytest.param((1,), (), id="(1,)-()"),
pytest.param((3,), (), id="(3,)-()"),
pytest.param((3,), (1,), id="(3,)-(1,)"),
pytest.param((3,), (2,), id="(3,)-(2,)"),
pytest.param((3,), (4,), id="(3,)-(4,)"),
pytest.param((1, 2), (2, 1), id="(1, 2)-(2, 1)"),
pytest.param((1, 2), (1,), id="(1, 2)-(1,)"),
pytest.param((1,), -1, id="(1,)--1"),
pytest.param((1,), (-1,), id="(1,)-(-1,)"),
pytest.param((1, 2), (-1, 2), id="(1, 2)-(-1, 2)"),
],
)
def test_broadcast_raise(self, sh1, sh2):
np_a = numpy.zeros(sh1)
dp_a = dpnp.zeros(sh1)
func = lambda xp, a: xp.broadcast_to(a, sh2)

with pytest.raises(ValueError):
func(numpy, np_a)
func(dpnp, dp_a)


@pytest.mark.usefixtures("allow_fall_back_on_numpy")
Expand All @@ -38,134 +139,134 @@ def test_returns_copy(self):
def test_large_concatenate_axis_None(self):
x = dpnp.arange(1, 100)
r = dpnp.concatenate(x, None)
numpy.testing.assert_array_equal(x, r)
assert_array_equal(x, r)
r = dpnp.concatenate(x, 100)
numpy.testing.assert_array_equal(x, r)
assert_array_equal(x, r)

def test_concatenate(self):
# Test concatenate function
# One sequence returns unmodified (but as array)
r4 = list(range(4))
numpy.testing.assert_array_equal(dpnp.concatenate((r4,)), r4)
assert_array_equal(dpnp.concatenate((r4,)), r4)
# Any sequence
numpy.testing.assert_array_equal(dpnp.concatenate((tuple(r4),)), r4)
numpy.testing.assert_array_equal(dpnp.concatenate((dpnp.array(r4),)), r4)
assert_array_equal(dpnp.concatenate((tuple(r4),)), r4)
assert_array_equal(dpnp.concatenate((dpnp.array(r4),)), r4)
# 1D default concatenation
r3 = list(range(3))
numpy.testing.assert_array_equal(dpnp.concatenate((r4, r3)), r4 + r3)
assert_array_equal(dpnp.concatenate((r4, r3)), r4 + r3)
# Mixed sequence types
numpy.testing.assert_array_equal(dpnp.concatenate((tuple(r4), r3)), r4 + r3)
numpy.testing.assert_array_equal(
assert_array_equal(dpnp.concatenate((tuple(r4), r3)), r4 + r3)
assert_array_equal(
dpnp.concatenate((dpnp.array(r4), r3)), r4 + r3
)
# Explicit axis specification
numpy.testing.assert_array_equal(dpnp.concatenate((r4, r3), 0), r4 + r3)
assert_array_equal(dpnp.concatenate((r4, r3), 0), r4 + r3)
# Including negative
numpy.testing.assert_array_equal(dpnp.concatenate((r4, r3), -1), r4 + r3)
assert_array_equal(dpnp.concatenate((r4, r3), -1), r4 + r3)
# 2D
a23 = dpnp.array([[10, 11, 12], [13, 14, 15]])
a13 = dpnp.array([[0, 1, 2]])
res = dpnp.array([[10, 11, 12], [13, 14, 15], [0, 1, 2]])
numpy.testing.assert_array_equal(dpnp.concatenate((a23, a13)), res)
numpy.testing.assert_array_equal(dpnp.concatenate((a23, a13), 0), res)
numpy.testing.assert_array_equal(dpnp.concatenate((a23.T, a13.T), 1), res.T)
numpy.testing.assert_array_equal(dpnp.concatenate((a23.T, a13.T), -1), res.T)
assert_array_equal(dpnp.concatenate((a23, a13)), res)
assert_array_equal(dpnp.concatenate((a23, a13), 0), res)
assert_array_equal(dpnp.concatenate((a23.T, a13.T), 1), res.T)
assert_array_equal(dpnp.concatenate((a23.T, a13.T), -1), res.T)
# Arrays much match shape
numpy.testing.assert_raises(ValueError, dpnp.concatenate, (a23.T, a13.T), 0)
assert_raises(ValueError, dpnp.concatenate, (a23.T, a13.T), 0)
# 3D
res = dpnp.reshape(dpnp.arange(2 * 3 * 7), (2, 3, 7))
a0 = res[..., :4]
a1 = res[..., 4:6]
a2 = res[..., 6:]
numpy.testing.assert_array_equal(dpnp.concatenate((a0, a1, a2), 2), res)
numpy.testing.assert_array_equal(dpnp.concatenate((a0, a1, a2), -1), res)
numpy.testing.assert_array_equal(dpnp.concatenate((a0.T, a1.T, a2.T), 0), res.T)
assert_array_equal(dpnp.concatenate((a0, a1, a2), 2), res)
assert_array_equal(dpnp.concatenate((a0, a1, a2), -1), res)
assert_array_equal(dpnp.concatenate((a0.T, a1.T, a2.T), 0), res.T)

out = dpnp.copy(res)
rout = dpnp.concatenate((a0, a1, a2), 2, out=out)
numpy.testing.assert_(out is rout)
numpy.testing.assert_equal(res, rout)
assert_(out is rout)
assert_equal(res, rout)


class TestHstack:
def test_non_iterable(self):
numpy.testing.assert_raises(TypeError, dpnp.hstack, 1)
assert_raises(TypeError, dpnp.hstack, 1)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_empty_input(self):
numpy.testing.assert_raises(ValueError, dpnp.hstack, ())
assert_raises(ValueError, dpnp.hstack, ())

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_0D_array(self):
b = dpnp.array(2)
a = dpnp.array(1)
res = dpnp.hstack([a, b])
desired = dpnp.array([1, 2])
numpy.testing.assert_array_equal(res, desired)
assert_array_equal(res, desired)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_1D_array(self):
a = dpnp.array([1])
b = dpnp.array([2])
res = dpnp.hstack([a, b])
desired = dpnp.array([1, 2])
numpy.testing.assert_array_equal(res, desired)
assert_array_equal(res, desired)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_2D_array(self):
a = dpnp.array([[1], [2]])
b = dpnp.array([[1], [2]])
res = dpnp.hstack([a, b])
desired = dpnp.array([[1, 1], [2, 2]])
numpy.testing.assert_array_equal(res, desired)
assert_array_equal(res, desired)

def test_generator(self):
with numpy.testing.assert_warns(FutureWarning):
with assert_warns(FutureWarning):
dpnp.hstack((numpy.arange(3) for _ in range(2)))
with numpy.testing.assert_warns(FutureWarning):
with assert_warns(FutureWarning):
dpnp.hstack(map(lambda x: x, numpy.ones((3, 2))))


class TestVstack:
def test_non_iterable(self):
numpy.testing.assert_raises(TypeError, dpnp.vstack, 1)
assert_raises(TypeError, dpnp.vstack, 1)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_empty_input(self):
numpy.testing.assert_raises(ValueError, dpnp.vstack, ())
assert_raises(ValueError, dpnp.vstack, ())

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_0D_array(self):
a = dpnp.array(1)
b = dpnp.array(2)
res = dpnp.vstack([a, b])
desired = dpnp.array([[1], [2]])
numpy.testing.assert_array_equal(res, desired)
assert_array_equal(res, desired)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_1D_array(self):
a = dpnp.array([1])
b = dpnp.array([2])
res = dpnp.vstack([a, b])
desired = dpnp.array([[1], [2]])
numpy.testing.assert_array_equal(res, desired)
assert_array_equal(res, desired)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_2D_array(self):
a = dpnp.array([[1], [2]])
b = dpnp.array([[1], [2]])
res = dpnp.vstack([a, b])
desired = dpnp.array([[1], [2], [1], [2]])
numpy.testing.assert_array_equal(res, desired)
assert_array_equal(res, desired)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_2D_array2(self):
a = dpnp.array([1, 2])
b = dpnp.array([1, 2])
res = dpnp.vstack([a, b])
desired = dpnp.array([[1, 2], [1, 2]])
numpy.testing.assert_array_equal(res, desired)
assert_array_equal(res, desired)

def test_generator(self):
with numpy.testing.assert_warns(FutureWarning):
with assert_warns(FutureWarning):
dpnp.vstack((numpy.arange(3) for _ in range(2)))
Loading