Repository: chainer/chainer

Issue:
`chainer.backend.copyto` cannot copy chainerx array to cupy
* Code to reproduce
```python
import chainer
import numpy

for dst_device in ['@numpy', '@cupy:0', '@intel64']:
    for src_device in ['native', 'cuda:0']:
        print((dst_device, src_device))
        dst = chainer.get_device(dst_device).send(
            numpy.array([1, 2], numpy.float32))
        src = chainer.get_device(src_device).send(
            numpy.array([3, 4], numpy.float32))
        try:
            chainer.backend.copyto(dst, src)
        except Exception as e:
            print(repr(e))
        else:
            print('ok')
```

* Error messages, stack traces, or logs
```
('@numpy', 'native')
ok
('@numpy', 'cuda:0')
ok
('@cupy:0', 'native')
TypeError('object array cannot be set to float32 array')
('@cupy:0', 'cuda:0')
TypeError('object array cannot be set to float32 array')
('@intel64', 'native')
ok
('@intel64', 'cuda:0')
ok
```



Discussion:

Resolving patch:
diff --git a/chainer/backend.py b/chainer/backend.py
--- a/chainer/backend.py
+++ b/chainer/backend.py
@@ -52,7 +52,12 @@ def copyto(dst, src):
     """
     if isinstance(dst, chainerx.ndarray):
         dst[...] = _chainerx._array_to_chainerx(src, dst.device)
-    elif isinstance(dst, numpy.ndarray):
+        return
+
+    if isinstance(src, chainerx.ndarray):
+        src = from_chx(src)
+
+    if isinstance(dst, numpy.ndarray):
         numpy.copyto(dst, _cpu._to_cpu(src))
     elif isinstance(dst, intel64.mdarray):
         intel64.ideep.basic_copyto(


Test patch:
diff --git a/tests/chainer_tests/test_backend.py b/tests/chainer_tests/test_backend.py
--- a/tests/chainer_tests/test_backend.py
+++ b/tests/chainer_tests/test_backend.py
@@ -24,18 +24,22 @@ class _TestCopyToBase(object):
     def _get_dst(self):
         raise NotImplementedError
 
+    @staticmethod
+    def _to_cpu(arr):
+        return backend.CpuDevice().send(arr)
+
     def test_from_cpu(self):
         src = self.src_data
         dst = self._get_dst()
         backend.copyto(dst, src)
-        numpy.testing.assert_array_equal(cuda.to_cpu(dst), self.src_data)
+        numpy.testing.assert_array_equal(self._to_cpu(dst), self.src_data)
 
     @attr.gpu
     def test_from_gpu(self):
         src = cuda.cupy.array(self.src_data)
         dst = self._get_dst()
         backend.copyto(dst, src)
-        numpy.testing.assert_array_equal(cuda.to_cpu(dst), self.src_data)
+        numpy.testing.assert_array_equal(self._to_cpu(dst), self.src_data)
 
     @attr.ideep
     def test_from_ideep(self):
@@ -43,7 +47,22 @@ def test_from_ideep(self):
         dst = self._get_dst()
         assert isinstance(src, intel64.mdarray)
         backend.copyto(dst, src)
-        numpy.testing.assert_array_equal(cuda.to_cpu(dst), self.src_data)
+        numpy.testing.assert_array_equal(self._to_cpu(dst), self.src_data)
+
+    @attr.chainerx
+    def test_from_chx_native(self):
+        src = chainerx.array(self.src_data, device='native')
+        dst = self._get_dst()
+        backend.copyto(dst, src)
+        numpy.testing.assert_array_equal(self._to_cpu(dst), self.src_data)
+
+    @attr.chainerx
+    @attr.gpu
+    def test_from_chx_cuda(self):
+        src = chainerx.array(self.src_data, device='cuda:0')
+        dst = self._get_dst()
+        backend.copyto(dst, src)
+        numpy.testing.assert_array_equal(self._to_cpu(dst), self.src_data)
 
 
 class TestCopyToCPU(_TestCopyToBase, unittest.TestCase):
@@ -73,6 +92,19 @@ def _get_dst(self):
         return dst
 
 
+@attr.chainerx
+class TestCopyToChxNative(_TestCopyToBase, unittest.TestCase):
+    def _get_dst(self):
+        return chainerx.array(self.dst_data, device='native')
+
+
+@attr.chainerx
+@attr.gpu
+class TestCopyToChxCuda(_TestCopyToBase, unittest.TestCase):
+    def _get_dst(self):
+        return chainerx.array(self.dst_data, device='cuda:0')
+
+
 class TestCopyToError(unittest.TestCase):
     def test_fail_on_invalid_src(self):
         src = None
diff --git a/tests/chainer_tests/test_variable.py b/tests/chainer_tests/test_variable.py
--- a/tests/chainer_tests/test_variable.py
+++ b/tests/chainer_tests/test_variable.py
@@ -672,8 +672,8 @@ def test_pickle_gpu(self):
         cp.testing.assert_array_equal(x.grad, d.grad)
 
 
-@testing.backend.inject_backend_tests(None, _chainerx_backend_params)
-@testing.backend.inject_backend_tests(None, _chainerx_backend_params)
+@testing.backend.inject_backend_tests(None, _backend_params)
+@testing.backend.inject_backend_tests(None, _backend_params)
 @testing.parameterize(*testing.product({'shape': [(10,), (0,), ()]}))
 class TestVariableCopydata(unittest.TestCase):