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

Implemented dpnp.hstack() and dpnp.atleast_1d() functions. #1544

Merged
merged 10 commits into from
Sep 6, 2023
115 changes: 99 additions & 16 deletions dpnp/dpnp_iface_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,52 @@ def atleast_1d(*arys):

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

Limitations
-----------
Input arrays is supported as :obj:`dpnp.ndarray`.
Parameters
----------
arys : {dpnp_array, usm_ndarray}
One or more input arrays.

Returns
-------
out : dpnp.ndarray
An array, or list of arrays, each with ``a.ndim >= 1``.
Copies are made only if necessary.

See Also
--------
atleast_2d, atleast_3d

Examples
--------
>>> import dpnp as np
>>> np.atleast_1d(1.0)
npolina4 marked this conversation as resolved.
Show resolved Hide resolved
array([1.])

>>> x = np.arange(9.0).reshape(3,3)
>>> np.atleast_1d(x)
array([[0., 1., 2.],
[3., 4., 5.],
[6., 7., 8.]])
>>> np.atleast_1d(x) is x
True

>>> np.atleast_1d(1, [3, 4])
[array([1]), array([3, 4])]

"""

return call_origin(numpy.atleast_1d, *arys)
res = []
for ary in arys:
ary = dpnp.asanyarray(ary)
if ary.ndim == 0:
result = ary.reshape(1)
else:
result = ary
res.append(result)
if len(res) == 1:
return res[0]
else:
return res


def atleast_2d(*arys):
Expand Down Expand Up @@ -242,7 +281,9 @@ def broadcast_to(array, /, shape, subok=False):
return call_origin(numpy.broadcast_to, array, shape=shape, subok=subok)


def concatenate(arrays, /, *, axis=0, out=None, dtype=None, **kwargs):
def concatenate(
arrays, /, *, axis=0, out=None, dtype=None, casting="same_kind", **kwargs
npolina4 marked this conversation as resolved.
Show resolved Hide resolved
):
"""
Join a sequence of arrays along an existing axis.

Expand Down Expand Up @@ -299,6 +340,8 @@ def concatenate(arrays, /, *, axis=0, out=None, dtype=None, **kwargs):
pass
elif dtype is not None:
pass
elif casting != "same_kind":
pass
npolina4 marked this conversation as resolved.
Show resolved Hide resolved
else:
usm_arrays = [dpnp.get_usm_ndarray(x) for x in arrays]
usm_res = dpt.concat(usm_arrays, axis=axis)
Expand All @@ -310,6 +353,7 @@ def concatenate(arrays, /, *, axis=0, out=None, dtype=None, **kwargs):
axis=axis,
out=out,
dtype=dtype,
casting=casting,
**kwargs,
)

Expand Down Expand Up @@ -659,23 +703,62 @@ def flipud(m):
return m[::-1, ...]


def hstack(tup):
def hstack(tup, *, dtype=None, casting="same_kind"):
"""
Stack arrays in sequence horizontally (column wise).

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

"""
Returns
-------
out : dpnp.ndarray
The stacked array which has one more dimension than the input arrays.

# TODO:
# `call_origin` cannot convert sequence of array to sequence of
# nparrays
tup_new = []
for tp in tup:
tpx = dpnp.asnumpy(tp) if not isinstance(tp, numpy.ndarray) else tp
tup_new.append(tpx)
Limitations
-----------
Each array in `tup` is supported as either :class:`dpnp.ndarray`
or :class:`dpctl.tensor.usm_ndarray`. Otherwise ``TypeError`` exception
will be raised.
Parameters `dtype` and `casting` are supported with default value.
Otherwise the function will be executed sequentially on CPU.

See Also
--------
:obj:`dpnp.concatenate` : Join a sequence of arrays along an existing axis.
:obj:`dpnp.stack` : Join a sequence of arrays along a new axis.
:obj:`dpnp.vstack` : Stack arrays in sequence vertically (row wise).
:obj:`dpnp.block` : Assemble an nd-array from nested lists of blocks.
:obj:`dpnp.split` : Split array into a list of multiple sub-arrays of equal size.

antonwolfy marked this conversation as resolved.
Show resolved Hide resolved
return call_origin(numpy.hstack, tup_new)
Examples
--------
>>> import dpnp as np
>>> a = np.array((1,2,3))
>>> b = np.array((4,5,6))
>>> np.hstack((a,b))
array([1, 2, 3, 4, 5, 6])

>>> a = np.array([[1],[2],[3]])
>>> b = np.array([[4],[5],[6]])
>>> np.hstack((a,b))
array([[1, 4],
[2, 5],
[3, 6]])

"""

if not hasattr(tup, "__getitem__"):
raise TypeError(
"Arrays to stack must be passed as a sequence type such as list or tuple."
)
arrs = dpnp.atleast_1d(*tup)
if not isinstance(arrs, list):
arrs = [arrs]
npolina4 marked this conversation as resolved.
Show resolved Hide resolved
# As a special case, dimension 0 of 1-dimensional arrays is "horizontal"
if arrs and arrs[0].ndim == 1:
return dpnp.concatenate(arrs, axis=0, dtype=dtype, casting=casting)
else:
return dpnp.concatenate(arrs, axis=1, dtype=dtype, casting=casting)


def moveaxis(a, source, destination):
Expand Down Expand Up @@ -1131,7 +1214,7 @@ def stack(arrays, /, *, axis=0, out=None, dtype=None, **kwargs):
Each array in `arrays` is supported as either :class:`dpnp.ndarray`
or :class:`dpctl.tensor.usm_ndarray`. Otherwise ``TypeError`` exception
will be raised.
Parameters `out` and `dtype are supported with default value.
Parameters `out` and `dtype` are supported with default value.
Keyword argument ``kwargs`` is currently unsupported.
Otherwise the function will be executed sequentially on CPU.

Expand Down
1 change: 0 additions & 1 deletion tests/skipped_tests.tbl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ tests/third_party/cupy/fft_tests/test_fft.py::TestFftn_param_23_{axes=None, norm

tests/third_party/intel/test_zero_copy_test1.py::test_dpnp_interaction_with_dpctl_memory

tests/test_arraymanipulation.py::TestHstack::test_generator
tests/test_arraymanipulation.py::TestVstack::test_generator

tests/test_linalg.py::test_cond[-1-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
Expand Down
1 change: 0 additions & 1 deletion tests/skipped_tests_gpu.tbl
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMult
tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMultivariateNormal_param_3_{d=4, shape=(3, 2)}::test_normal

tests/third_party/intel/test_zero_copy_test1.py::test_dpnp_interaction_with_dpctl_memory
tests/test_arraymanipulation.py::TestHstack::test_generator
tests/test_arraymanipulation.py::TestVstack::test_generator

tests/test_linalg.py::test_cond[-1-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
Expand Down
48 changes: 41 additions & 7 deletions tests/test_arraymanipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,27 +335,23 @@ class TestHstack:
def test_non_iterable(self):
assert_raises(TypeError, dpnp.hstack, 1)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_empty_input(self):
assert_raises(ValueError, dpnp.hstack, ())
assert_raises(TypeError, 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])
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])
assert_array_equal(res, desired)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
def test_2D_array(self):
npolina4 marked this conversation as resolved.
Show resolved Hide resolved
a = dpnp.array([[1], [2]])
b = dpnp.array([[1], [2]])
Expand All @@ -364,9 +360,9 @@ def test_2D_array(self):
assert_array_equal(res, desired)

def test_generator(self):
with assert_warns(FutureWarning):
with pytest.raises(TypeError):
dpnp.hstack((numpy.arange(3) for _ in range(2)))
antonwolfy marked this conversation as resolved.
Show resolved Hide resolved
with assert_warns(FutureWarning):
with pytest.raises(TypeError):
dpnp.hstack(map(lambda x: x, numpy.ones((3, 2))))
antonwolfy marked this conversation as resolved.
Show resolved Hide resolved


Expand Down Expand Up @@ -607,6 +603,44 @@ def test_generator(self):
dpnp.vstack((numpy.arange(3) for _ in range(2)))


npolina4 marked this conversation as resolved.
Show resolved Hide resolved
class TestAtleast1d:
def test_0D_array(self):
a = dpnp.array(1)
b = dpnp.array(2)
res = [dpnp.atleast_1d(a), dpnp.atleast_1d(b)]
desired = [dpnp.array([1]), dpnp.array([2])]
assert_array_equal(res, desired)

def test_1D_array(self):
a = dpnp.array([1, 2])
b = dpnp.array([2, 3])
res = [dpnp.atleast_1d(a), dpnp.atleast_1d(b)]
desired = [dpnp.array([1, 2]), dpnp.array([2, 3])]
assert_array_equal(res, desired)

def test_2D_array(self):
a = dpnp.array([[1, 2], [1, 2]])
b = dpnp.array([[2, 3], [2, 3]])
res = [dpnp.atleast_1d(a), dpnp.atleast_1d(b)]
desired = [a, b]
assert_array_equal(res, desired)

def test_3D_array(self):
a = dpnp.array([[1, 2], [1, 2]])
b = dpnp.array([[2, 3], [2, 3]])
a = dpnp.array([a, a])
b = dpnp.array([b, b])
res = [dpnp.atleast_1d(a), dpnp.atleast_1d(b)]
desired = [a, b]
assert_array_equal(res, desired)

def test_r1array(self):
assert dpnp.atleast_1d(3).shape == (1,)
assert dpnp.atleast_1d(3j).shape == (1,)
assert dpnp.atleast_1d(3.0).shape == (1,)
assert dpnp.atleast_1d([[2, 3], [4, 5]]).shape == (2, 2)


class TestRollaxis:
data = [
(0, 0),
Expand Down
2 changes: 0 additions & 2 deletions tests/third_party/cupy/manipulation_tests/test_dims.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ def check_atleast(self, func, xp):
f = numpy.float32(1)
return func(a, b, c, d, e, f)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
@testing.numpy_cupy_array_equal()
def test_atleast_1d1(self, xp):
return self.check_atleast(xp.atleast_1d, xp)

@pytest.mark.usefixtures("allow_fall_back_on_numpy")
@testing.numpy_cupy_array_equal()
def test_atleast_1d2(self, xp):
a = testing.shaped_arange((1, 3, 2), xp)
Expand Down
18 changes: 3 additions & 15 deletions tests/third_party/cupy/manipulation_tests/test_join.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,30 +270,27 @@ def test_dstack_single_element_3(self, xp):
a = testing.shaped_arange((1,), xp)
return xp.dstack((a,))

@pytest.mark.skip("dpnp.hstack() is not implemented yet")
@testing.numpy_cupy_array_equal()
def test_hstack_vectors(self, xp):
a = xp.arange(3)
b = xp.arange(2, -1, -1)
return xp.hstack((a, b))

@pytest.mark.skip("dpnp.hstack() is not implemented yet")
@testing.numpy_cupy_array_equal()
def test_hstack_scalars(self, xp):
a = testing.shaped_arange((), xp)
b = testing.shaped_arange((), xp)
c = testing.shaped_arange((), xp)
return xp.hstack((a, b, c))

@pytest.mark.skip("dpnp.hstack() is not implemented yet")
@testing.numpy_cupy_array_equal()
def test_hstack(self, xp):
a = testing.shaped_arange((2, 1), xp)
b = testing.shaped_arange((2, 2), xp)
c = testing.shaped_arange((2, 3), xp)
return xp.hstack((a, b, c))

@pytest.mark.skip("dpnp.hstack() is not implemented yet")
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
@testing.with_requires("numpy>=1.24.0")
@testing.for_all_dtypes_combination(names=["dtype1", "dtype2"])
@testing.numpy_cupy_array_equal(accept_error=TypeError)
Expand All @@ -302,18 +299,9 @@ def test_hstack_dtype(self, xp, dtype1, dtype2):
b = testing.shaped_arange((3, 4), xp, dtype1)
return xp.hstack((a, b), dtype=dtype2)

@pytest.mark.skip("dpnp.hstack() is not implemented yet")
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
@testing.with_requires("numpy>=1.24.0")
@pytest.mark.parametrize(
"casting",
[
"no",
"equiv",
"safe",
"same_kind",
"unsafe",
],
)
@testing.for_castings()
@testing.for_all_dtypes_combination(names=["dtype1", "dtype2"])
@testing.numpy_cupy_array_equal(
accept_error=(TypeError, numpy.ComplexWarning)
Expand Down
Loading