Skip to content

Commit e526833

Browse files
committed
Implemented column_stack, dstack and row_stack
1 parent 74609d6 commit e526833

File tree

2 files changed

+125
-22
lines changed

2 files changed

+125
-22
lines changed

dpnp/dpnp_iface_manipulation.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@
5757
"broadcast_arrays",
5858
"broadcast_to",
5959
"can_cast",
60+
"column_stack",
6061
"concatenate",
6162
"copyto",
63+
"dstack",
6264
"expand_dims",
6365
"flip",
6466
"fliplr",
@@ -71,6 +73,7 @@
7173
"result_type",
7274
"roll",
7375
"rollaxis",
76+
"row_stack",
7477
"shape",
7578
"squeeze",
7679
"stack",
@@ -444,6 +447,61 @@ def can_cast(from_, to, casting="safe"):
444447
return dpt.can_cast(dtype_from, to, casting)
445448

446449

450+
def column_stack(tup):
451+
"""
452+
Stacks 1-D and 2-D arrays as columns into a 2-D array.
453+
454+
Take a sequence of 1-D arrays and stack them as columns to make a single
455+
2-D array. 2-D arrays are stacked as-is, just like with :obj:`dpnp.hstack`.
456+
1-D arrays are turned into 2-D columns first.
457+
458+
For full documentation refer to :obj:`numpy.column_stack`.
459+
460+
Parameters
461+
----------
462+
tup : {dpnp.ndarray, usm_ndarray}
463+
A sequence of 1-D or 2-D arrays to stack. All of them must have
464+
the same first dimension.
465+
466+
Returns
467+
-------
468+
out : dpnp.ndarray
469+
The array formed by stacking the given arrays.
470+
471+
See Also
472+
--------
473+
:obj:`dpnp.stack` : Stack a sequence of arrays along a new axis.
474+
:obj:`dpnp.hstack` : Stack arrays in sequence horizontally (column wise).
475+
:obj:`dpnp.vstack` : Stack arrays in sequence vertically (row wise).
476+
:obj:`dpnp.concatenate` : Join a sequence of arrays along an existing axis.
477+
478+
Examples
479+
--------
480+
>>> import dpnp as np
481+
>>> a = np.array((1, 2, 3))
482+
>>> b = np.array((2, 3, 4))
483+
>>> np.column_stack((a, b))
484+
array([[1, 2],
485+
[2, 3],
486+
[3, 4]])
487+
488+
"""
489+
490+
arrays = []
491+
for v in tup:
492+
dpnp.check_supported_arrays_type(v)
493+
494+
if v.ndim == 1:
495+
v = v[:, dpnp.newaxis]
496+
elif v.ndim != 2:
497+
raise ValueError(
498+
"Only 1 or 2 dimensional arrays can be column stacked"
499+
)
500+
501+
arrays.append(v)
502+
return dpnp.concatenate(arrays, 1)
503+
504+
447505
def concatenate(
448506
arrays, /, *, axis=0, out=None, dtype=None, casting="same_kind"
449507
):
@@ -601,6 +659,63 @@ def copyto(dst, src, casting="same_kind", where=True):
601659
dst_usm[mask_usm] = src_usm[mask_usm]
602660

603661

662+
def dstack(tup):
663+
"""
664+
Stack arrays in sequence depth wise (along third axis).
665+
666+
This is equivalent to concatenation along the third axis after 2-D arrays
667+
of shape `(M, N)` have been reshaped to `(M, N, 1)` and 1-D arrays of shape
668+
`(N,)` have been reshaped to `(1, N, 1)`. Rebuilds arrays divided by
669+
`dsplit`.
670+
671+
For full documentation refer to :obj:`numpy.dstack`.
672+
673+
Parameters
674+
----------
675+
tup : {dpnp.ndarray, usm_ndarray}
676+
One or more array-like sequences. The arrays must have the same shape
677+
along all but the third axis. 1-D or 2-D arrays must have the same shape.
678+
679+
Returns
680+
-------
681+
out : dpnp.ndarray
682+
The array formed by stacking the given arrays, will be at least 3-D.
683+
684+
See Also
685+
--------
686+
:obj:`dpnp.concatenate` : Join a sequence of arrays along an existing axis.
687+
:obj:`dpnp.stack` : Join a sequence of arrays along a new axis.
688+
:obj:`dpnp.block` : Assemble an nd-array from nested lists of blocks.
689+
:obj:`dpnp.vstack` : Stack arrays in sequence vertically (row wise).
690+
:obj:`dpnp.hstack` : Stack arrays in sequence horizontally (column wise).
691+
:obj:`dpnp.column_stack` : Stack 1-D arrays as columns into a 2-D array.
692+
:obj:`dpnp.dsplit` : Split array along third axis.
693+
694+
Examples
695+
--------
696+
>>> import dpnp as np
697+
>>> a = np.array((1, 2, 3))
698+
>>> b = np.array((2, 3, 4))
699+
>>> np.dstack((a, b))
700+
array([[[1, 2],
701+
[2, 3],
702+
[3, 4]]])
703+
704+
>>> a = np.array([[1], [2], [3]])
705+
>>> b = np.array([[2], [3], [4]])
706+
>>> np.dstack((a, b))
707+
array([[[1, 2]],
708+
[[2, 3]],
709+
[[3, 4]]])
710+
711+
"""
712+
713+
arrs = atleast_3d(*tup)
714+
if not isinstance(arrs, list):
715+
arrs = [arrs]
716+
return dpnp.concatenate(arrs, axis=2)
717+
718+
604719
def expand_dims(a, axis):
605720
"""
606721
Expand the shape of an array.
@@ -1738,3 +1853,6 @@ def vstack(tup, *, dtype=None, casting="same_kind"):
17381853
if not isinstance(arrs, list):
17391854
arrs = [arrs]
17401855
return dpnp.concatenate(arrs, axis=0, dtype=dtype, casting=casting)
1856+
1857+
1858+
row_stack = vstack

tests/third_party/cupy/manipulation_tests/test_join.py

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
import unittest
2-
31
import numpy
42
import pytest
53

64
import dpnp as cupy
5+
from tests.helper import has_support_aspect64
76
from tests.third_party.cupy import testing
87

98

10-
class TestJoin(unittest.TestCase):
11-
@pytest.mark.skip("dpnp.column_stack() is not implemented yet")
9+
class TestJoin:
1210
@testing.for_all_dtypes(name="dtype1")
1311
@testing.for_all_dtypes(name="dtype2")
1412
@testing.numpy_cupy_array_equal()
@@ -18,21 +16,18 @@ def test_column_stack(self, xp, dtype1, dtype2):
1816
c = testing.shaped_arange((4, 2), xp, dtype1)
1917
return xp.column_stack((a, b, c))
2018

21-
@pytest.mark.skip("dpnp.column_stack() is not implemented yet")
2219
def test_column_stack_wrong_ndim1(self):
2320
a = cupy.zeros(())
2421
b = cupy.zeros((3,))
2522
with pytest.raises(ValueError):
2623
cupy.column_stack((a, b))
2724

28-
@pytest.mark.skip("dpnp.column_stack() is not implemented yet")
2925
def test_column_stack_wrong_ndim2(self):
3026
a = cupy.zeros((3, 2, 3))
3127
b = cupy.zeros((3, 2))
3228
with pytest.raises(ValueError):
3329
cupy.column_stack((a, b))
3430

35-
@pytest.mark.skip("dpnp.column_stack() is not implemented yet")
3631
def test_column_stack_wrong_shape(self):
3732
a = cupy.zeros((3, 2))
3833
b = cupy.zeros((4, 3))
@@ -87,9 +82,8 @@ def test_concatenate_large_4(self, xp, dtype):
8782
b = testing.shaped_reverse_arange((2, 3, 4), xp, dtype)
8883
return xp.concatenate((a, b) * 10, axis=-1)
8984

90-
@pytest.mark.skip("TODO: remove once dpctl #1325 is resolved")
9185
@testing.for_all_dtypes(name="dtype")
92-
@testing.numpy_cupy_array_equal()
86+
@testing.numpy_cupy_array_equal(type_check=has_support_aspect64())
9387
def test_concatenate_large_5(self, xp, dtype):
9488
a = testing.shaped_arange((2, 3, 4), xp, dtype)
9589
b = testing.shaped_reverse_arange((2, 3, 4), xp, "i")
@@ -113,8 +107,7 @@ def test_concatenate_large_f_contiguous(self, xp, dtype):
113107
e = testing.shaped_arange((2, 3, 2), xp, dtype)
114108
return xp.concatenate((a, b, c, d, e) * 2, axis=-1)
115109

116-
@pytest.mark.skip("TODO: remove once dpctl #1325 is resolved")
117-
@testing.numpy_cupy_array_equal()
110+
@testing.numpy_cupy_array_equal(type_check=has_support_aspect64())
118111
def test_concatenate_many_multi_dtype(self, xp):
119112
a = testing.shaped_arange((2, 1), xp, "i")
120113
b = testing.shaped_arange((2, 1), xp, "f")
@@ -191,9 +184,8 @@ def test_concatenate_out_invalid_dtype(self):
191184
with pytest.raises(TypeError):
192185
xp.concatenate((a, b, c), axis=1, out=out)
193186

194-
@pytest.mark.skip("TODO: remove once dpctl #1325 is resolved")
195187
@testing.for_all_dtypes_combination(names=["dtype1", "dtype2"])
196-
@testing.numpy_cupy_array_equal()
188+
@testing.numpy_cupy_array_equal(type_check=has_support_aspect64())
197189
def test_concatenate_different_dtype(self, xp, dtype1, dtype2):
198190
a = testing.shaped_arange((3, 4), xp, dtype1)
199191
b = testing.shaped_arange((3, 4), xp, dtype2)
@@ -235,27 +227,23 @@ def test_concatenate_casting(self, xp, dtype1, dtype2, casting):
235227
b = testing.shaped_arange((3, 4), xp, dtype1)
236228
return xp.concatenate((a, b), dtype=dtype2, casting=casting)
237229

238-
@pytest.mark.skip("dpnp.dstack() is not implemented yet")
239230
@testing.numpy_cupy_array_equal()
240231
def test_dstack(self, xp):
241232
a = testing.shaped_arange((1, 3, 2), xp)
242233
b = testing.shaped_arange((3,), xp)
243234
c = testing.shaped_arange((1, 3), xp)
244235
return xp.dstack((a, b, c))
245236

246-
@pytest.mark.skip("dpnp.dstack() is not implemented yet")
247237
@testing.numpy_cupy_array_equal()
248238
def test_dstack_single_element(self, xp):
249239
a = testing.shaped_arange((1, 2, 3), xp)
250240
return xp.dstack((a,))
251241

252-
@pytest.mark.skip("dpnp.dstack() is not implemented yet")
253242
@testing.numpy_cupy_array_equal()
254243
def test_dstack_single_element_2(self, xp):
255244
a = testing.shaped_arange((1, 2), xp)
256245
return xp.dstack((a,))
257246

258-
@pytest.mark.skip("dpnp.dstack() is not implemented yet")
259247
@testing.numpy_cupy_array_equal()
260248
def test_dstack_single_element_3(self, xp):
261249
a = testing.shaped_arange((1,), xp)
@@ -473,33 +461,30 @@ def test_stack_dtype(self, xp, dtype1, dtype2):
473461
def test_stack_casting(self, xp, dtype1, dtype2, casting):
474462
a = testing.shaped_arange((3, 4), xp, dtype1)
475463
b = testing.shaped_arange((3, 4), xp, dtype1)
464+
# may raise TypeError or ComplexWarning
476465
return xp.stack((a, b), dtype=dtype2, casting=casting)
477466

478-
@pytest.mark.skip("dpnp.row_stack() is not implemented yet")
479467
@testing.for_all_dtypes(name="dtype1")
480468
@testing.for_all_dtypes(name="dtype2")
481-
@testing.numpy_cupy_array_equal()
469+
@testing.numpy_cupy_array_equal(type_check=has_support_aspect64())
482470
def test_row_stack(self, xp, dtype1, dtype2):
483471
a = testing.shaped_arange((4, 3), xp, dtype1)
484472
b = testing.shaped_arange((3,), xp, dtype2)
485473
c = testing.shaped_arange((2, 3), xp, dtype1)
486474
return xp.row_stack((a, b, c))
487475

488-
@pytest.mark.skip("dpnp.row_stack() is not implemented yet")
489476
def test_row_stack_wrong_ndim1(self):
490477
a = cupy.zeros(())
491478
b = cupy.zeros((3,))
492479
with pytest.raises(ValueError):
493480
cupy.row_stack((a, b))
494481

495-
@pytest.mark.skip("dpnp.row_stack() is not implemented yet")
496482
def test_row_stack_wrong_ndim2(self):
497483
a = cupy.zeros((3, 2, 3))
498484
b = cupy.zeros((3, 2))
499485
with pytest.raises(ValueError):
500486
cupy.row_stack((a, b))
501487

502-
@pytest.mark.skip("dpnp.row_stack() is not implemented yet")
503488
def test_row_stack_wrong_shape(self):
504489
a = cupy.zeros((3, 2))
505490
b = cupy.zeros((4, 3))

0 commit comments

Comments
 (0)