Skip to content

Commit a73d959

Browse files
authored
Merge pull request #1585 from IntelPython/update_atleast_func
Updated atleast_2d and atleast_3d functions.
2 parents e59c1f4 + 8249846 commit a73d959

File tree

5 files changed

+184
-125
lines changed

5 files changed

+184
-125
lines changed

dpnp/dpnp_algo/dpnp_algo_manipulation.pxi

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ and the rest of the library
3636
# NO IMPORTs here. All imports must be placed into main "dpnp_algo.pyx" file
3737

3838
__all__ += [
39-
"dpnp_atleast_2d",
40-
"dpnp_atleast_3d",
4139
"dpnp_repeat",
4240
]
4341

@@ -48,60 +46,6 @@ ctypedef c_dpctl.DPCTLSyclEventRef(*fptr_dpnp_repeat_t)(c_dpctl.DPCTLSyclQueueRe
4846
const c_dpctl.DPCTLEventVectorRef)
4947

5048

51-
cpdef utils.dpnp_descriptor dpnp_atleast_2d(utils.dpnp_descriptor arr):
52-
# it looks like it should be dpnp.copy + dpnp.reshape
53-
cdef utils.dpnp_descriptor result
54-
cdef size_t arr_ndim = arr.ndim
55-
cdef long arr_size = arr.size
56-
if arr_ndim == 1:
57-
arr_obj = arr.get_array()
58-
result = utils_py.create_output_descriptor_py((1, arr_size),
59-
arr.dtype,
60-
None,
61-
device=arr_obj.sycl_device,
62-
usm_type=arr_obj.usm_type,
63-
sycl_queue=arr_obj.sycl_queue)
64-
for i in range(arr_size):
65-
result.get_pyobj()[0, i] = arr.get_pyobj()[i]
66-
return result
67-
else:
68-
return arr
69-
70-
71-
cpdef utils.dpnp_descriptor dpnp_atleast_3d(utils.dpnp_descriptor arr):
72-
# it looks like it should be dpnp.copy + dpnp.reshape
73-
cdef utils.dpnp_descriptor result
74-
cdef size_t arr_ndim = arr.ndim
75-
cdef shape_type_c arr_shape = arr.shape
76-
cdef long arr_size = arr.size
77-
78-
arr_obj = arr.get_array()
79-
80-
if arr_ndim == 1:
81-
result = utils_py.create_output_descriptor_py((1, 1, arr_size),
82-
arr.dtype,
83-
None,
84-
device=arr_obj.sycl_device,
85-
usm_type=arr_obj.usm_type,
86-
sycl_queue=arr_obj.sycl_queue)
87-
for i in range(arr_size):
88-
result.get_pyobj()[0, 0, i] = arr.get_pyobj()[i]
89-
return result
90-
elif arr_ndim == 2:
91-
result = utils_py.create_output_descriptor_py((1, arr_shape[0], arr_shape[1]),
92-
arr.dtype,
93-
None,
94-
device=arr_obj.sycl_device,
95-
usm_type=arr_obj.usm_type,
96-
sycl_queue=arr_obj.sycl_queue)
97-
for i in range(arr_shape[0]):
98-
for j in range(arr_shape[1]):
99-
result.get_pyobj()[0, i, j] = arr.get_pyobj()[i, j]
100-
return result
101-
else:
102-
return arr
103-
104-
10549
cpdef utils.dpnp_descriptor dpnp_repeat(utils.dpnp_descriptor array1, repeats, axes=None):
10650
cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(array1.dtype)
10751

dpnp/dpnp_iface_manipulation.py

Lines changed: 116 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,13 @@ def atleast_1d(*arys):
124124
"""
125125
Convert inputs to arrays with at least one dimension.
126126
127-
Scalar inputs are converted to 1-dimensional arrays, whilst
128-
higher-dimensional inputs are preserved.
129-
130127
For full documentation refer to :obj:`numpy.atleast_1d`.
131128
132129
Parameters
133130
----------
134-
arys : {dpnp_array, usm_ndarray}
135-
One or more input arrays.
131+
arys : {dpnp.ndarray, usm_ndarray}
132+
One or more array-like sequences. Arrays that already have one or more
133+
dimensions are preserved.
136134
137135
Returns
138136
-------
@@ -142,14 +140,20 @@ def atleast_1d(*arys):
142140
143141
See Also
144142
--------
145-
atleast_2d, atleast_3d
143+
:obj:`dpnp.atleast_2d` : View inputs as arrays with at least two dimensions.
144+
:obj:`dpnp.atleast_3d` : View inputs as arrays with at least three dimensions.
146145
147146
Examples
148147
--------
149148
>>> import dpnp as np
150-
>>> np.atleast_1d(1.0)
149+
>>> x = np.array(1.0)
150+
>>> np.atleast_1d(x)
151151
array([1.])
152152
153+
>>> y = np.array([3, 4])
154+
>>> np.atleast_1d(x, y)
155+
[array([1.]), array([3, 4])]
156+
153157
>>> x = np.arange(9.0).reshape(3,3)
154158
>>> np.atleast_1d(x)
155159
array([[0., 1., 2.],
@@ -158,18 +162,21 @@ def atleast_1d(*arys):
158162
>>> np.atleast_1d(x) is x
159163
True
160164
161-
>>> np.atleast_1d(1, [3, 4])
162-
[array([1]), array([3, 4])]
163-
164165
"""
165166

166167
res = []
167168
for ary in arys:
168-
ary = dpnp.asanyarray(ary)
169+
if not dpnp.is_supported_array_type(ary):
170+
raise TypeError(
171+
"Each input array must be any of supported type, "
172+
f"but got {type(ary)}"
173+
)
169174
if ary.ndim == 0:
170175
result = ary.reshape(1)
171176
else:
172177
result = ary
178+
if isinstance(result, dpt.usm_ndarray):
179+
result = dpnp_array._create_from_usm_ndarray(result)
173180
res.append(result)
174181
if len(res) == 1:
175182
return res[0]
@@ -183,36 +190,57 @@ def atleast_2d(*arys):
183190
184191
For full documentation refer to :obj:`numpy.atleast_2d`.
185192
186-
Limitations
187-
-----------
188-
Input arrays is supported as :obj:`dpnp.ndarray`.
193+
Parameters
194+
----------
195+
arys : {dpnp.ndarray, usm_ndarray}
196+
One or more array-like sequences. Arrays that already have two or more
197+
dimensions are preserved.
198+
199+
Returns
200+
-------
201+
out : dpnp.ndarray
202+
An array, or list of arrays, each with ``a.ndim >= 2``.
203+
Copies are avoided where possible, and views with two or more
204+
dimensions are returned.
205+
206+
See Also
207+
--------
208+
:obj:`dpnp.atleast_1d` : Convert inputs to arrays with at least one dimension.
209+
:obj:`dpnp.atleast_3d` : View inputs as arrays with at least three dimensions.
210+
211+
Examples
212+
--------
213+
>>> import dpnp as np
214+
>>> x = np.array(3.0)
215+
>>> np.atleast_2d(x)
216+
array([[3.]])
217+
218+
>>> x = np.arange(3.0)
219+
>>> np.atleast_2d(x)
220+
array([[0., 1., 2.]])
221+
189222
"""
190223

191-
all_is_array = True
192-
arys_desc = []
224+
res = []
193225
for ary in arys:
194-
if not dpnp.isscalar(ary):
195-
ary_desc = dpnp.get_dpnp_descriptor(
196-
ary, copy_when_nondefault_queue=False
226+
if not dpnp.is_supported_array_type(ary):
227+
raise TypeError(
228+
"Each input array must be any of supported type, "
229+
f"but got {type(ary)}"
197230
)
198-
if ary_desc:
199-
arys_desc.append(ary_desc)
200-
continue
201-
all_is_array = False
202-
break
203-
204-
if not use_origin_backend(arys[0]) and all_is_array:
205-
result = []
206-
for ary_desc in arys_desc:
207-
res = dpnp_atleast_2d(ary_desc).get_pyobj()
208-
result.append(res)
209-
210-
if len(result) == 1:
211-
return result[0]
231+
if ary.ndim == 0:
232+
result = ary.reshape(1, 1)
233+
elif ary.ndim == 1:
234+
result = ary[dpnp.newaxis, :]
212235
else:
213-
return result
214-
215-
return call_origin(numpy.atleast_2d, *arys)
236+
result = ary
237+
if isinstance(result, dpt.usm_ndarray):
238+
result = dpnp_array._create_from_usm_ndarray(result)
239+
res.append(result)
240+
if len(res) == 1:
241+
return res[0]
242+
else:
243+
return res
216244

217245

218246
def atleast_3d(*arys):
@@ -221,36 +249,63 @@ def atleast_3d(*arys):
221249
222250
For full documentation refer to :obj:`numpy.atleast_3d`.
223251
224-
Limitations
225-
-----------
226-
Input arrays is supported as :obj:`dpnp.ndarray`.
252+
Parameters
253+
----------
254+
arys : {dpnp.ndarray, usm_ndarray}
255+
One or more array-like sequences. Arrays that already have three or more
256+
dimensions are preserved.
257+
258+
Returns
259+
-------
260+
out : dpnp.ndarray
261+
An array, or list of arrays, each with ``a.ndim >= 3``. Copies are
262+
avoided where possible, and views with three or more dimensions are
263+
returned.
264+
265+
See Also
266+
--------
267+
:obj:`dpnp.atleast_1d` : Convert inputs to arrays with at least one dimension.
268+
:obj:`dpnp.atleast_2d` : View inputs as arrays with at least three dimensions.
269+
270+
Examples
271+
--------
272+
>>> import dpnp as np
273+
>>> x = np.array(3.0)
274+
>>> np.atleast_3d(x)
275+
array([[[3.]]])
276+
277+
>>> x = np.arange(3.0)
278+
>>> np.atleast_3d(x).shape
279+
(1, 3, 1)
280+
281+
>>> x = np.arange(12.0).reshape(4, 3)
282+
>>> np.atleast_3d(x).shape
283+
(4, 3, 1)
284+
227285
"""
228286

229-
all_is_array = True
230-
arys_desc = []
287+
res = []
231288
for ary in arys:
232-
if not dpnp.isscalar(ary):
233-
ary_desc = dpnp.get_dpnp_descriptor(
234-
ary, copy_when_nondefault_queue=False
289+
if not dpnp.is_supported_array_type(ary):
290+
raise TypeError(
291+
"Each input array must be any of supported type, "
292+
f"but got {type(ary)}"
235293
)
236-
if ary_desc:
237-
arys_desc.append(ary_desc)
238-
continue
239-
all_is_array = False
240-
break
241-
242-
if not use_origin_backend(arys[0]) and all_is_array:
243-
result = []
244-
for ary_desc in arys_desc:
245-
res = dpnp_atleast_3d(ary_desc).get_pyobj()
246-
result.append(res)
247-
248-
if len(result) == 1:
249-
return result[0]
294+
if ary.ndim == 0:
295+
result = ary.reshape(1, 1, 1)
296+
elif ary.ndim == 1:
297+
result = ary[dpnp.newaxis, :, dpnp.newaxis]
298+
elif ary.ndim == 2:
299+
result = ary[:, :, dpnp.newaxis]
250300
else:
251-
return result
252-
253-
return call_origin(numpy.atleast_3d, *arys)
301+
result = ary
302+
if isinstance(result, dpt.usm_ndarray):
303+
result = dpnp_array._create_from_usm_ndarray(result)
304+
res.append(result)
305+
if len(res) == 1:
306+
return res[0]
307+
else:
308+
return res
254309

255310

256311
def broadcast_to(array, /, shape, subok=False):

tests/skipped_tests.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ tests/third_party/cupy/logic_tests/test_comparison.py::TestArrayEqual::test_arra
427427
tests/third_party/cupy/logic_tests/test_comparison.py::TestArrayEqual::test_array_equal_diff_length
428428
tests/third_party/cupy/logic_tests/test_comparison.py::TestArrayEqual::test_array_equal_is_equal
429429
tests/third_party/cupy/logic_tests/test_comparison.py::TestArrayEqual::test_array_equal_not_equal
430+
430431
tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_0_{shapes=[(), ()]}::test_broadcast
431432
tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_0_{shapes=[(), ()]}::test_broadcast_arrays
432433
tests/third_party/cupy/manipulation_tests/test_dims.py::TestBroadcast_param_10_{shapes=[(0, 1, 1, 0, 3), (5, 2, 0, 1, 0, 0, 3), (2, 1, 0, 0, 0, 3)]}::test_broadcast

tests/test_arraymanipulation.py

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -655,12 +655,6 @@ def test_3D_array(self):
655655
desired = [a, b]
656656
assert_array_equal(res, desired)
657657

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-
664658

665659
class TestRollaxis:
666660
data = [
@@ -725,3 +719,67 @@ def test_results(self):
725719
jp = j + 1 if j < 4 else j
726720
res = dpnp.rollaxis(dp_a, axis=-ip, start=-jp)
727721
exp = numpy.rollaxis(np_a, axis=-ip, start=-jp)
722+
723+
724+
class TestAtleast2d:
725+
def test_0D_array(self):
726+
a = dpnp.array(1)
727+
b = dpnp.array(2)
728+
res = [dpnp.atleast_2d(a), dpnp.atleast_2d(b)]
729+
desired = [dpnp.array([[1]]), dpnp.array([[2]])]
730+
assert_array_equal(res, desired)
731+
732+
def test_1D_array(self):
733+
a = dpnp.array([1, 2])
734+
b = dpnp.array([2, 3])
735+
res = [dpnp.atleast_2d(a), dpnp.atleast_2d(b)]
736+
desired = [dpnp.array([[1, 2]]), dpnp.array([[2, 3]])]
737+
assert_array_equal(res, desired)
738+
739+
def test_2D_array(self):
740+
a = dpnp.array([[1, 2], [1, 2]])
741+
b = dpnp.array([[2, 3], [2, 3]])
742+
res = [dpnp.atleast_2d(a), dpnp.atleast_2d(b)]
743+
desired = [a, b]
744+
assert_array_equal(res, desired)
745+
746+
def test_3D_array(self):
747+
a = dpnp.array([[1, 2], [1, 2]])
748+
b = dpnp.array([[2, 3], [2, 3]])
749+
a = dpnp.array([a, a])
750+
b = dpnp.array([b, b])
751+
res = [dpnp.atleast_2d(a), dpnp.atleast_2d(b)]
752+
desired = [a, b]
753+
assert_array_equal(res, desired)
754+
755+
756+
class TestAtleast3d:
757+
def test_0D_array(self):
758+
a = dpnp.array(1)
759+
b = dpnp.array(2)
760+
res = [dpnp.atleast_3d(a), dpnp.atleast_3d(b)]
761+
desired = [dpnp.array([[[1]]]), dpnp.array([[[2]]])]
762+
assert_array_equal(res, desired)
763+
764+
def test_1D_array(self):
765+
a = dpnp.array([1, 2])
766+
b = dpnp.array([2, 3])
767+
res = [dpnp.atleast_3d(a), dpnp.atleast_3d(b)]
768+
desired = [dpnp.array([[[1], [2]]]), dpnp.array([[[2], [3]]])]
769+
assert_array_equal(res, desired)
770+
771+
def test_2D_array(self):
772+
a = dpnp.array([[1, 2], [1, 2]])
773+
b = dpnp.array([[2, 3], [2, 3]])
774+
res = [dpnp.atleast_3d(a), dpnp.atleast_3d(b)]
775+
desired = [a[:, :, dpnp.newaxis], b[:, :, dpnp.newaxis]]
776+
assert_array_equal(res, desired)
777+
778+
def test_3D_array(self):
779+
a = dpnp.array([[1, 2], [1, 2]])
780+
b = dpnp.array([[2, 3], [2, 3]])
781+
a = dpnp.array([a, a])
782+
b = dpnp.array([b, b])
783+
res = [dpnp.atleast_3d(a), dpnp.atleast_3d(b)]
784+
desired = [a, b]
785+
assert_array_equal(res, desired)

0 commit comments

Comments
 (0)