Skip to content

Commit 0548a72

Browse files
authored
add copyto (#79)
1 parent 358e39a commit 0548a72

File tree

4 files changed

+311
-0
lines changed

4 files changed

+311
-0
lines changed

dpnp/backend_manipulation.pyx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,60 @@ from dpnp.dpnp_utils cimport *
3737

3838

3939
__all__ += [
40+
"dpnp_copyto",
4041
"dpnp_repeat",
4142
"dpnp_transpose"
4243
]
4344

4445

46+
cpdef dparray dpnp_copyto(dparray dst, dparray src, where=True):
47+
cdef dparray_shape_type shape_src = src.shape
48+
cdef long size_src = src.size
49+
output_shape = dparray(len(shape_src), dtype=numpy.int64)
50+
for id, shape_ in enumerate(shape_src):
51+
output_shape[id] = shape_
52+
cdef long prod = 1
53+
for i in range(len(output_shape)):
54+
if output_shape[i] != 0:
55+
prod *= output_shape[i]
56+
result_array = [None] * prod
57+
src_shape_offsets = [None] * len(shape_src)
58+
acc = 1
59+
for i in range(len(shape_src)):
60+
ind = len(shape_src) - 1 - i
61+
src_shape_offsets[ind] = acc
62+
acc *= shape_src[ind]
63+
output_shape_offsets = [None] * len(shape_src)
64+
acc = 1
65+
for i in range(len(output_shape)):
66+
ind = len(output_shape) - 1 - i
67+
output_shape_offsets[ind] = acc
68+
acc *= output_shape[ind]
69+
result_offsets = src_shape_offsets[:] # need copy. not a reference
70+
71+
for source_idx in range(size_src):
72+
73+
# reconstruct x,y,z from linear source_idx
74+
xyz = []
75+
remainder = source_idx
76+
for i in src_shape_offsets:
77+
quotient, remainder = divmod(remainder, i)
78+
xyz.append(quotient)
79+
80+
result_indexes = []
81+
for idx, offset in enumerate(xyz):
82+
result_indexes.append(offset)
83+
84+
result_offset = 0
85+
for i, result_indexes_val in enumerate(result_indexes):
86+
result_offset += (output_shape_offsets[i] * result_indexes_val)
87+
88+
src_elem = src.item(source_idx)
89+
dst[source_idx] = src_elem
90+
91+
return dst
92+
93+
4594
cpdef dparray dpnp_repeat(dparray array1, repeats, axes=None):
4695
cdef long new_size = array1.size * repeats
4796
cdef dparray result = dparray((new_size, ), dtype=array1.dtype)

dpnp/dpnp_iface_manipulation.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050

5151

5252
__all__ = [
53+
"copyto",
5354
"moveaxis",
5455
"ravel",
5556
"repeat",
@@ -59,6 +60,67 @@
5960
]
6061

6162

63+
def copyto(dst, src, casting='same_kind', where=True):
64+
"""
65+
Copies values from one array to another, broadcasting as necessary.
66+
Raises a TypeError if the casting rule is violated, and if where is provided,
67+
it selects which elements to copy.
68+
69+
:param dst: ndarray
70+
The array into which values are copied.
71+
:param src: array_like
72+
The array from which values are copied.
73+
:param casting: {‘no’, ‘equiv’, ‘safe’, ‘same_kind’, ‘unsafe’}, optional
74+
Controls what kind of data casting may occur when copying.
75+
76+
‘no’ means the data types should not be cast at all.
77+
78+
‘equiv’ means only byte-order changes are allowed.
79+
80+
‘safe’ means only casts which can preserve values are allowed.
81+
82+
‘same_kind’ means only safe casts or casts within a kind, like float64 to float32, are allowed.
83+
84+
‘unsafe’ means any data conversions may be done.
85+
:param where: array_like of bool, optional
86+
A boolean array which is broadcasted to match the dimensions of dst,
87+
and selects elements to copy from src to dst wherever it contains the value True.
88+
"""
89+
90+
is_input_dparray1 = isinstance(dst, dparray)
91+
is_input_dparray2 = isinstance(src, dparray)
92+
93+
if not use_origin_backend(dst) and is_input_dparray1 and is_input_dparray2:
94+
if casting != 'same_kind':
95+
checker_throw_value_error("copyto", "casting", type(casting), 'same_kind')
96+
97+
if not isinstance(where, bool) and not isinstance(where, dparray):
98+
checker_throw_type_error('copyto', type(where))
99+
100+
if where is not True:
101+
checker_throw_value_error("copyto", "where", where, True)
102+
103+
if dst.shape != src.shape:
104+
raise NotImplemented
105+
106+
result = dpnp_copyto(dst, src, where=where)
107+
108+
return result
109+
110+
input1 = dpnp.asnumpy(dst) if is_input_dparray1 else dst
111+
input2 = dpnp.asnumpy(src) if is_input_dparray2 else src
112+
113+
# TODO need to put dparray memory into NumPy call
114+
result_numpy = numpy.copyto(dst, src, where=where)
115+
result = result_numpy
116+
if isinstance(result, numpy.ndarray):
117+
result = dparray(result_numpy.shape, dtype=result_numpy.dtype)
118+
for i in range(result.size):
119+
result._setitem_scalar(i, result_numpy.item(i))
120+
121+
return result
122+
123+
62124
def moveaxis(x1, source, destination):
63125
"""
64126
Move axes of an array to new positions. Other axes remain in their original order.

tests/skipped_tests.tbl

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,79 @@ tests/third_party/cupy/logic_tests/test_comparison.py::TestComparisonOperator::t
205205
tests/third_party/cupy/logic_tests/test_comparison.py::TestComparisonOperator::test_binary_array_pyscalar
206206
tests/third_party/cupy/logic_tests/test_comparison.py::TestComparisonOperator::test_binary_npscalar_array
207207
tests/third_party/cupy/logic_tests/test_comparison.py::TestComparisonOperator::test_binary_pyscalar_array
208+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestBasic::test_copyto_dtype
209+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestBasic::test_copyto_broadcast
210+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestBasic::test_copyto_where
211+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_0_{dst_shape=(), src=3.2}::test_copyto
212+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_0_{dst_shape=(), src=3.2}::test_copyto_where
213+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_1_{dst_shape=(), src=0}::test_copyto
214+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_1_{dst_shape=(), src=0}::test_copyto_where
215+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_2_{dst_shape=(), src=4}::test_copyto
216+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_2_{dst_shape=(), src=4}::test_copyto_where
217+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_3_{dst_shape=(), src=-4}::test_copyto
218+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_3_{dst_shape=(), src=-4}::test_copyto_where
219+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_4_{dst_shape=(), src=True}::test_copyto
220+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_4_{dst_shape=(), src=True}::test_copyto_where
221+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_5_{dst_shape=(), src=False}::test_copyto
222+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_5_{dst_shape=(), src=False}::test_copyto_where
223+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_6_{dst_shape=(), src=(1+1j)}::test_copyto
224+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_6_{dst_shape=(), src=(1+1j)}::test_copyto_where
225+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_7_{dst_shape=(0,), src=3.2}::test_copyto
226+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_7_{dst_shape=(0,), src=3.2}::test_copyto_where
227+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_8_{dst_shape=(0,), src=0}::test_copyto
228+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_8_{dst_shape=(0,), src=0}::test_copyto_where
229+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_9_{dst_shape=(0,), src=4}::test_copyto
230+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_9_{dst_shape=(0,), src=4}::test_copyto_where
231+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_10_{dst_shape=(0,), src=-4}::test_copyto
232+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_10_{dst_shape=(0,), src=-4}::test_copyto_where
233+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_11_{dst_shape=(0,), src=True}::test_copyto
234+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_11_{dst_shape=(0,), src=True}::test_copyto_where
235+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_12_{dst_shape=(0,), src=False}::test_copyto
236+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_12_{dst_shape=(0,), src=False}::test_copyto_where
237+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_13_{dst_shape=(0,), src=(1+1j)}::test_copyto
238+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_13_{dst_shape=(0,), src=(1+1j)}::test_copyto_where
239+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_14_{dst_shape=(1,), src=3.2}::test_copyto
240+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_14_{dst_shape=(1,), src=3.2}::test_copyto_where
241+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_15_{dst_shape=(1,), src=0}::test_copyto
242+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_15_{dst_shape=(1,), src=0}::test_copyto_where
243+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_16_{dst_shape=(1,), src=4}::test_copyto
244+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_16_{dst_shape=(1,), src=4}::test_copyto_where
245+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_17_{dst_shape=(1,), src=-4}::test_copyto
246+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_17_{dst_shape=(1,), src=-4}::test_copyto_where
247+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_18_{dst_shape=(1,), src=True}::test_copyto
248+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_18_{dst_shape=(1,), src=True}::test_copyto_where
249+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_19_{dst_shape=(1,), src=False}::test_copyto
250+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_19_{dst_shape=(1,), src=False}::test_copyto_where
251+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_20_{dst_shape=(1,), src=(1+1j)}::test_copyto
252+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_20_{dst_shape=(1,), src=(1+1j)}::test_copyto_where
253+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_21_{dst_shape=(1, 1), src=3.2}::test_copyto
254+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_21_{dst_shape=(1, 1), src=3.2}::test_copyto_where
255+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_22_{dst_shape=(1, 1), src=0}::test_copyto
256+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_22_{dst_shape=(1, 1), src=0}::test_copyto_where
257+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_23_{dst_shape=(1, 1), src=4}::test_copyto
258+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_23_{dst_shape=(1, 1), src=4}::test_copyto_where
259+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_24_{dst_shape=(1, 1), src=-4}::test_copyto
260+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_24_{dst_shape=(1, 1), src=-4}::test_copyto_where
261+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_25_{dst_shape=(1, 1), src=True}::test_copyto
262+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_25_{dst_shape=(1, 1), src=True}::test_copyto_where
263+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_26_{dst_shape=(1, 1), src=False}::test_copyto
264+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_26_{dst_shape=(1, 1), src=False}::test_copyto_where
265+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_27_{dst_shape=(1, 1), src=(1+1j)}::test_copyto
266+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_27_{dst_shape=(1, 1), src=(1+1j)}::test_copyto_where
267+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_28_{dst_shape=(2, 2), src=3.2}::test_copyto
268+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_28_{dst_shape=(2, 2), src=3.2}::test_copyto_where
269+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_29_{dst_shape=(2, 2), src=0}::test_copyto
270+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_29_{dst_shape=(2, 2), src=0}::test_copyto_where
271+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_30_{dst_shape=(2, 2), src=4}::test_copyto
272+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_30_{dst_shape=(2, 2), src=4}::test_copyto_where
273+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_31_{dst_shape=(2, 2), src=-4}::test_copyto
274+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_31_{dst_shape=(2, 2), src=-4}::test_copyto_where
275+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_32_{dst_shape=(2, 2), src=True}::test_copyto
276+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_32_{dst_shape=(2, 2), src=True}::test_copyto_where
277+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_33_{dst_shape=(2, 2), src=False}::test_copyto
278+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_33_{dst_shape=(2, 2), src=False}::test_copyto_where
279+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_34_{dst_shape=(2, 2), src=(1+1j)}::test_copyto
280+
tests/third_party/cupy/manipulation_tests/test_basic.py::TestCopytoFromScalar_param_34_{dst_shape=(2, 2), src=(1+1j)}::test_copyto_where
208281
tests/third_party/cupy/manipulation_tests/test_shape.py::TestRavel::test_ravel3
209282
tests/third_party/cupy/manipulation_tests/test_shape.py::TestReshapeOrder_param_0_{order_init='C', order_reshape='C', shape_in_out=((2, 3), (1, 6, 1))}::test_reshape_contiguity
210283
tests/third_party/cupy/manipulation_tests/test_shape.py::TestReshapeOrder_param_10_{order_init='C', order_reshape='c', shape_in_out=((6,), (2, 3))}::test_reshape_contiguity
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import itertools
2+
import unittest
3+
4+
import numpy
5+
6+
import dpnp as cupy
7+
# from cupy import cuda
8+
from tests.third_party.cupy import testing
9+
10+
11+
@testing.gpu
12+
class TestBasic(unittest.TestCase):
13+
14+
@testing.for_all_dtypes()
15+
@testing.numpy_cupy_array_equal()
16+
def test_copyto(self, xp, dtype):
17+
a = testing.shaped_arange((2, 3, 4), xp, dtype)
18+
b = xp.empty((2, 3, 4), dtype=dtype)
19+
xp.copyto(b, a)
20+
return b
21+
22+
@testing.for_all_dtypes()
23+
@testing.numpy_cupy_array_equal()
24+
def test_copyto_dtype(self, xp, dtype):
25+
a = testing.shaped_arange((2, 3, 4), xp, dtype='?')
26+
b = xp.empty((2, 3, 4), dtype=dtype)
27+
xp.copyto(b, a)
28+
return b
29+
30+
@testing.for_all_dtypes()
31+
@testing.numpy_cupy_array_equal()
32+
def test_copyto_broadcast(self, xp, dtype):
33+
a = testing.shaped_arange((3, 1), xp, dtype)
34+
b = xp.empty((2, 3, 4), dtype=dtype)
35+
xp.copyto(b, a)
36+
return b
37+
38+
@testing.for_all_dtypes()
39+
@testing.numpy_cupy_array_equal()
40+
def test_copyto_where(self, xp, dtype):
41+
a = testing.shaped_arange((2, 3, 4), xp, dtype)
42+
b = testing.shaped_reverse_arange((2, 3, 4), xp, dtype)
43+
c = testing.shaped_arange((2, 3, 4), xp, '?')
44+
xp.copyto(a, b, where=c)
45+
return a
46+
47+
# def _check_copyto_where_multigpu_raises(self, dtype, ngpus):
48+
# def get_numpy():
49+
# a = testing.shaped_arange((2, 3, 4), numpy, dtype)
50+
# b = testing.shaped_reverse_arange((2, 3, 4), numpy, dtype)
51+
# c = testing.shaped_arange((2, 3, 4), numpy, '?')
52+
# numpy.copyto(a, b, where=c)
53+
# return a
54+
#
55+
# for dev1, dev2, dev3, dev4 in itertools.product(*[range(ngpus)] * 4):
56+
# if dev1 == dev2 == dev3 == dev4:
57+
# continue
58+
# if not dev1 <= dev2 <= dev3 <= dev4:
59+
# continue
60+
#
61+
# with cuda.Device(dev1):
62+
# a = testing.shaped_arange((2, 3, 4), cupy, dtype)
63+
# with cuda.Device(dev2):
64+
# b = testing.shaped_reverse_arange((2, 3, 4), cupy, dtype)
65+
# with cuda.Device(dev3):
66+
# c = testing.shaped_arange((2, 3, 4), cupy, '?')
67+
# with cuda.Device(dev4):
68+
# with self.assertRaisesRegex(
69+
# ValueError,
70+
# '^Array device must be same as the current device'):
71+
# cupy.copyto(a, b, where=c)
72+
#
73+
# @testing.multi_gpu(2)
74+
# @testing.for_all_dtypes()
75+
# def test_copyto_where_multigpu_raises(self, dtype):
76+
# self._check_copyto_where_multigpu_raises(dtype, 2)
77+
#
78+
# @testing.multi_gpu(2)
79+
# @testing.for_all_dtypes()
80+
# @testing.numpy_cupy_array_equal()
81+
# def test_copyto_multigpu(self, xp, dtype):
82+
# with cuda.Device(0):
83+
# a = testing.shaped_arange((2, 3, 4), xp, dtype)
84+
# with cuda.Device(1):
85+
# b = xp.empty((2, 3, 4), dtype=dtype)
86+
# xp.copyto(b, a)
87+
# return b
88+
#
89+
# @testing.multi_gpu(2)
90+
# @testing.for_all_dtypes()
91+
# def test_copyto_multigpu_noncontinguous(self, dtype):
92+
# with cuda.Device(0):
93+
# src = testing.shaped_arange((2, 3, 4), cupy, dtype)
94+
# src = src.swapaxes(0, 1)
95+
# with cuda.Device(1):
96+
# dst = cupy.empty_like(src)
97+
# cupy.copyto(dst, src)
98+
#
99+
# expected = testing.shaped_arange((2, 3, 4), numpy, dtype)
100+
# expected = expected.swapaxes(0, 1)
101+
#
102+
# testing.assert_array_equal(expected, src.get())
103+
# testing.assert_array_equal(expected, dst.get())
104+
105+
106+
@testing.parameterize(
107+
*testing.product(
108+
{'src': [float(3.2), int(0), int(4), int(-4), True, False, 1 + 1j],
109+
'dst_shape': [(), (0,), (1,), (1, 1), (2, 2)]}))
110+
@testing.gpu
111+
class TestCopytoFromScalar(unittest.TestCase):
112+
113+
@testing.for_all_dtypes()
114+
@testing.numpy_cupy_allclose(accept_error=TypeError)
115+
def test_copyto(self, xp, dtype):
116+
dst = xp.ones(self.dst_shape, dtype=dtype)
117+
xp.copyto(dst, self.src)
118+
return dst
119+
120+
@testing.for_all_dtypes()
121+
@testing.numpy_cupy_allclose(accept_error=TypeError)
122+
def test_copyto_where(self, xp, dtype):
123+
dst = xp.ones(self.dst_shape, dtype=dtype)
124+
mask = (testing.shaped_arange(
125+
self.dst_shape, xp, dtype) % 2).astype(xp.bool_)
126+
xp.copyto(dst, self.src, where=mask)
127+
return dst

0 commit comments

Comments
 (0)