Skip to content

Commit 80eb738

Browse files
npolina4antonwolfy
andauthored
Implemented dpnp.hstack() and dpnp.atleast_1d() functions. (#1544)
* Implemented dpnp.hstack() and dpnp.atleast_1d() functions. * Added tests for hstack and atleast_1d functions * Fixed test for hstack. * Update tests/test_arraymanipulation.py * Update tests/test_arraymanipulation.py * Fixed test coverage for concatenate and hstack functions. * Fixed limitation description for concatenate function --------- Co-authored-by: Anton <[email protected]>
1 parent 7cb3949 commit 80eb738

File tree

6 files changed

+168
-49
lines changed

6 files changed

+168
-49
lines changed

dpnp/dpnp_iface_manipulation.py

Lines changed: 100 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,52 @@ def atleast_1d(*arys):
129129
130130
For full documentation refer to :obj:`numpy.atleast_1d`.
131131
132-
Limitations
133-
-----------
134-
Input arrays is supported as :obj:`dpnp.ndarray`.
132+
Parameters
133+
----------
134+
arys : {dpnp_array, usm_ndarray}
135+
One or more input arrays.
136+
137+
Returns
138+
-------
139+
out : dpnp.ndarray
140+
An array, or list of arrays, each with ``a.ndim >= 1``.
141+
Copies are made only if necessary.
142+
143+
See Also
144+
--------
145+
atleast_2d, atleast_3d
146+
147+
Examples
148+
--------
149+
>>> import dpnp as np
150+
>>> np.atleast_1d(1.0)
151+
array([1.])
152+
153+
>>> x = np.arange(9.0).reshape(3,3)
154+
>>> np.atleast_1d(x)
155+
array([[0., 1., 2.],
156+
[3., 4., 5.],
157+
[6., 7., 8.]])
158+
>>> np.atleast_1d(x) is x
159+
True
160+
161+
>>> np.atleast_1d(1, [3, 4])
162+
[array([1]), array([3, 4])]
135163
136164
"""
137165

138-
return call_origin(numpy.atleast_1d, *arys)
166+
res = []
167+
for ary in arys:
168+
ary = dpnp.asanyarray(ary)
169+
if ary.ndim == 0:
170+
result = ary.reshape(1)
171+
else:
172+
result = ary
173+
res.append(result)
174+
if len(res) == 1:
175+
return res[0]
176+
else:
177+
return res
139178

140179

141180
def atleast_2d(*arys):
@@ -254,7 +293,9 @@ def broadcast_to(array, /, shape, subok=False):
254293
return call_origin(numpy.broadcast_to, array, shape=shape, subok=subok)
255294

256295

257-
def concatenate(arrays, /, *, axis=0, out=None, dtype=None, **kwargs):
296+
def concatenate(
297+
arrays, /, *, axis=0, out=None, dtype=None, casting="same_kind"
298+
):
258299
"""
259300
Join a sequence of arrays along an existing axis.
260301
@@ -271,7 +312,6 @@ def concatenate(arrays, /, *, axis=0, out=None, dtype=None, **kwargs):
271312
or :class:`dpctl.tensor.usm_ndarray`. Otherwise ``TypeError`` exception
272313
will be raised.
273314
Parameters `out` and `dtype are supported with default value.
274-
Keyword argument ``kwargs`` is currently unsupported.
275315
Otherwise the function will be executed sequentially on CPU.
276316
277317
See Also
@@ -305,12 +345,12 @@ def concatenate(arrays, /, *, axis=0, out=None, dtype=None, **kwargs):
305345
306346
"""
307347

308-
if kwargs:
309-
pass
310-
elif out is not None:
348+
if out is not None:
311349
pass
312350
elif dtype is not None:
313351
pass
352+
elif casting != "same_kind":
353+
pass
314354
else:
315355
usm_arrays = [dpnp.get_usm_ndarray(x) for x in arrays]
316356
usm_res = dpt.concat(usm_arrays, axis=axis)
@@ -322,7 +362,7 @@ def concatenate(arrays, /, *, axis=0, out=None, dtype=None, **kwargs):
322362
axis=axis,
323363
out=out,
324364
dtype=dtype,
325-
**kwargs,
365+
casting=casting,
326366
)
327367

328368

@@ -671,23 +711,62 @@ def flipud(m):
671711
return m[::-1, ...]
672712

673713

674-
def hstack(tup):
714+
def hstack(tup, *, dtype=None, casting="same_kind"):
675715
"""
676716
Stack arrays in sequence horizontally (column wise).
677717
678718
For full documentation refer to :obj:`numpy.hstack`.
679719
680-
"""
720+
Returns
721+
-------
722+
out : dpnp.ndarray
723+
The stacked array which has one more dimension than the input arrays.
681724
682-
# TODO:
683-
# `call_origin` cannot convert sequence of array to sequence of
684-
# nparrays
685-
tup_new = []
686-
for tp in tup:
687-
tpx = dpnp.asnumpy(tp) if not isinstance(tp, numpy.ndarray) else tp
688-
tup_new.append(tpx)
725+
Limitations
726+
-----------
727+
Each array in `tup` is supported as either :class:`dpnp.ndarray`
728+
or :class:`dpctl.tensor.usm_ndarray`. Otherwise ``TypeError`` exception
729+
will be raised.
730+
Parameters `dtype` and `casting` are supported with default value.
731+
Otherwise the function will be executed sequentially on CPU.
732+
733+
See Also
734+
--------
735+
:obj:`dpnp.concatenate` : Join a sequence of arrays along an existing axis.
736+
:obj:`dpnp.stack` : Join a sequence of arrays along a new axis.
737+
:obj:`dpnp.vstack` : Stack arrays in sequence vertically (row wise).
738+
:obj:`dpnp.block` : Assemble an nd-array from nested lists of blocks.
739+
:obj:`dpnp.split` : Split array into a list of multiple sub-arrays of equal size.
740+
741+
Examples
742+
--------
743+
>>> import dpnp as np
744+
>>> a = np.array((1,2,3))
745+
>>> b = np.array((4,5,6))
746+
>>> np.hstack((a,b))
747+
array([1, 2, 3, 4, 5, 6])
748+
749+
>>> a = np.array([[1],[2],[3]])
750+
>>> b = np.array([[4],[5],[6]])
751+
>>> np.hstack((a,b))
752+
array([[1, 4],
753+
[2, 5],
754+
[3, 6]])
755+
756+
"""
689757

690-
return call_origin(numpy.hstack, tup_new)
758+
if not hasattr(tup, "__getitem__"):
759+
raise TypeError(
760+
"Arrays to stack must be passed as a sequence type such as list or tuple."
761+
)
762+
arrs = dpnp.atleast_1d(*tup)
763+
if not isinstance(arrs, list):
764+
arrs = [arrs]
765+
# As a special case, dimension 0 of 1-dimensional arrays is "horizontal"
766+
if arrs and arrs[0].ndim == 1:
767+
return dpnp.concatenate(arrs, axis=0, dtype=dtype, casting=casting)
768+
else:
769+
return dpnp.concatenate(arrs, axis=1, dtype=dtype, casting=casting)
691770

692771

693772
def moveaxis(a, source, destination):
@@ -1143,7 +1222,7 @@ def stack(arrays, /, *, axis=0, out=None, dtype=None, **kwargs):
11431222
Each array in `arrays` is supported as either :class:`dpnp.ndarray`
11441223
or :class:`dpctl.tensor.usm_ndarray`. Otherwise ``TypeError`` exception
11451224
will be raised.
1146-
Parameters `out` and `dtype are supported with default value.
1225+
Parameters `out` and `dtype` are supported with default value.
11471226
Keyword argument ``kwargs`` is currently unsupported.
11481227
Otherwise the function will be executed sequentially on CPU.
11491228

tests/skipped_tests.tbl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ tests/third_party/cupy/fft_tests/test_fft.py::TestFftn_param_23_{axes=None, norm
3434

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

37-
tests/test_arraymanipulation.py::TestHstack::test_generator
3837
tests/test_arraymanipulation.py::TestVstack::test_generator
3938

4039
tests/test_linalg.py::test_cond[-1-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]

tests/skipped_tests_gpu.tbl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,6 @@ tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMult
216216
tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMultivariateNormal_param_3_{d=4, shape=(3, 2)}::test_normal
217217

218218
tests/third_party/intel/test_zero_copy_test1.py::test_dpnp_interaction_with_dpctl_memory
219-
tests/test_arraymanipulation.py::TestHstack::test_generator
220219
tests/test_arraymanipulation.py::TestVstack::test_generator
221220

222221
tests/test_linalg.py::test_cond[-1-[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]

tests/test_arraymanipulation.py

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -329,32 +329,45 @@ def test_concatenate_out(self, dtype):
329329
assert_array_equal(dp_out.asnumpy(), np_out)
330330
assert_array_equal(dp_res.asnumpy(), np_res)
331331

332+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
333+
@pytest.mark.parametrize(
334+
"dtype", get_all_dtypes(no_bool=True, no_none=True)
335+
)
336+
@pytest.mark.parametrize(
337+
"casting", ["no", "equiv", "safe", "same_kind", "unsafe"]
338+
)
339+
def test_concatenate_casting(self, dtype, casting):
340+
np_a = numpy.arange(2 * 3 * 7, dtype=dtype).reshape((2, 3, 7))
341+
342+
dp_a = dpnp.arange(2 * 3 * 7, dtype=dtype).reshape((2, 3, 7))
343+
344+
np_res = numpy.concatenate((np_a, np_a), axis=2, casting=casting)
345+
dp_res = dpnp.concatenate((dp_a, dp_a), axis=2, casting=casting)
346+
347+
assert_array_equal(dp_res.asnumpy(), np_res)
348+
332349

333350
class TestHstack:
334351
def test_non_iterable(self):
335352
assert_raises(TypeError, dpnp.hstack, 1)
336353

337-
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
338354
def test_empty_input(self):
339-
assert_raises(ValueError, dpnp.hstack, ())
355+
assert_raises(TypeError, dpnp.hstack, ())
340356

341-
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
342357
def test_0D_array(self):
343358
b = dpnp.array(2)
344359
a = dpnp.array(1)
345360
res = dpnp.hstack([a, b])
346361
desired = dpnp.array([1, 2])
347362
assert_array_equal(res, desired)
348363

349-
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
350364
def test_1D_array(self):
351365
a = dpnp.array([1])
352366
b = dpnp.array([2])
353367
res = dpnp.hstack([a, b])
354368
desired = dpnp.array([1, 2])
355369
assert_array_equal(res, desired)
356370

357-
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
358371
def test_2D_array(self):
359372
a = dpnp.array([[1], [2]])
360373
b = dpnp.array([[1], [2]])
@@ -363,10 +376,15 @@ def test_2D_array(self):
363376
assert_array_equal(res, desired)
364377

365378
def test_generator(self):
366-
with assert_warns(FutureWarning):
367-
dpnp.hstack((numpy.arange(3) for _ in range(2)))
368-
with assert_warns(FutureWarning):
369-
dpnp.hstack(map(lambda x: x, numpy.ones((3, 2))))
379+
with pytest.raises(TypeError):
380+
dpnp.hstack((dpnp.arange(3) for _ in range(2)))
381+
with pytest.raises(TypeError):
382+
dpnp.hstack(map(lambda x: x, dpnp.ones((3, 2))))
383+
384+
def test_one_element(self):
385+
a = dpnp.array([1])
386+
res = dpnp.hstack(a)
387+
assert_array_equal(res, a)
370388

371389

372390
class TestStack:
@@ -606,6 +624,44 @@ def test_generator(self):
606624
dpnp.vstack((numpy.arange(3) for _ in range(2)))
607625

608626

627+
class TestAtleast1d:
628+
def test_0D_array(self):
629+
a = dpnp.array(1)
630+
b = dpnp.array(2)
631+
res = [dpnp.atleast_1d(a), dpnp.atleast_1d(b)]
632+
desired = [dpnp.array([1]), dpnp.array([2])]
633+
assert_array_equal(res, desired)
634+
635+
def test_1D_array(self):
636+
a = dpnp.array([1, 2])
637+
b = dpnp.array([2, 3])
638+
res = [dpnp.atleast_1d(a), dpnp.atleast_1d(b)]
639+
desired = [dpnp.array([1, 2]), dpnp.array([2, 3])]
640+
assert_array_equal(res, desired)
641+
642+
def test_2D_array(self):
643+
a = dpnp.array([[1, 2], [1, 2]])
644+
b = dpnp.array([[2, 3], [2, 3]])
645+
res = [dpnp.atleast_1d(a), dpnp.atleast_1d(b)]
646+
desired = [a, b]
647+
assert_array_equal(res, desired)
648+
649+
def test_3D_array(self):
650+
a = dpnp.array([[1, 2], [1, 2]])
651+
b = dpnp.array([[2, 3], [2, 3]])
652+
a = dpnp.array([a, a])
653+
b = dpnp.array([b, b])
654+
res = [dpnp.atleast_1d(a), dpnp.atleast_1d(b)]
655+
desired = [a, b]
656+
assert_array_equal(res, desired)
657+
658+
def test_r1array(self):
659+
assert dpnp.atleast_1d(3).shape == (1,)
660+
assert dpnp.atleast_1d(3j).shape == (1,)
661+
assert dpnp.atleast_1d(3.0).shape == (1,)
662+
assert dpnp.atleast_1d([[2, 3], [4, 5]]).shape == (2, 2)
663+
664+
609665
class TestRollaxis:
610666
data = [
611667
(0, 0),

tests/third_party/cupy/manipulation_tests/test_dims.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ def check_atleast(self, func, xp):
1818
f = numpy.float32(1)
1919
return func(a, b, c, d, e, f)
2020

21-
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
2221
@testing.numpy_cupy_array_equal()
2322
def test_atleast_1d1(self, xp):
2423
return self.check_atleast(xp.atleast_1d, xp)
2524

26-
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
2725
@testing.numpy_cupy_array_equal()
2826
def test_atleast_1d2(self, xp):
2927
a = testing.shaped_arange((1, 3, 2), xp)

tests/third_party/cupy/manipulation_tests/test_join.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -270,30 +270,27 @@ def test_dstack_single_element_3(self, xp):
270270
a = testing.shaped_arange((1,), xp)
271271
return xp.dstack((a,))
272272

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

280-
@pytest.mark.skip("dpnp.hstack() is not implemented yet")
281279
@testing.numpy_cupy_array_equal()
282280
def test_hstack_scalars(self, xp):
283281
a = testing.shaped_arange((), xp)
284282
b = testing.shaped_arange((), xp)
285283
c = testing.shaped_arange((), xp)
286284
return xp.hstack((a, b, c))
287285

288-
@pytest.mark.skip("dpnp.hstack() is not implemented yet")
289286
@testing.numpy_cupy_array_equal()
290287
def test_hstack(self, xp):
291288
a = testing.shaped_arange((2, 1), xp)
292289
b = testing.shaped_arange((2, 2), xp)
293290
c = testing.shaped_arange((2, 3), xp)
294291
return xp.hstack((a, b, c))
295292

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

305-
@pytest.mark.skip("dpnp.hstack() is not implemented yet")
302+
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
306303
@testing.with_requires("numpy>=1.24.0")
307-
@pytest.mark.parametrize(
308-
"casting",
309-
[
310-
"no",
311-
"equiv",
312-
"safe",
313-
"same_kind",
314-
"unsafe",
315-
],
316-
)
304+
@testing.for_castings()
317305
@testing.for_all_dtypes_combination(names=["dtype1", "dtype2"])
318306
@testing.numpy_cupy_array_equal(
319307
accept_error=(TypeError, numpy.ComplexWarning)

0 commit comments

Comments
 (0)