Repository: chainer/chainer

Issue:
`ConcatWithAsyncTransfer` does not work with the latest CuPy
`ConcatWithAsyncTransfer` fails due to a bug in [this line](https://github.com/chainer/chainer/blob/v6.0.0rc1/chainer/dataset/convert.py#L462); in the latest dev version of CuPy, `cupy.empty_like` does not accept numpy ndarray. The fix should be simple, but the root problem is the lack of tests for `ConcatWithAsyncTransfer`. We need them.

Details:

* Conditions
Chainer: 6.0.0rc1
NumPy: 1.16.2
CuPy: 6.0.0rc1

* Code to reproduce
```py3
import numpy
import chainer

converter = chainer.dataset.ConcatWithAsyncTransfer()
batch = [(numpy.zeros(1),)]
converter(batch, device=0)
```

* Error messages, stack traces, or logs
```
Traceback (most recent call last):
  File "foo.py", line 6, in <module>
    converter(batch, device=0)
  File "/home/beam2d/miniconda3/envs/py37/lib/python3.7/site-packages/chainer/dataset/convert.py", line 363, in __call__
    [example[i] for example in batch], padding[i]))
  File "/home/beam2d/miniconda3/envs/py37/lib/python3.7/site-packages/chainer/dataset/convert.py", line 462, in put
    cp_array = cuda.cupy.empty_like(array)
  File "/home/beam2d/miniconda3/envs/py37/lib/python3.7/site-packages/cupy/creation/basic.py", line 69, in empty_like
    order, strides, memptr = _new_like_order_and_strides(a, dtype, order)
  File "/home/beam2d/miniconda3/envs/py37/lib/python3.7/site-packages/cupy/creation/basic.py", line 35, in _new_like_order_and_strides
    order = chr(_update_order_char(a, ord(order)))
TypeError: Argument 'x' has incorrect type (expected cupy.core.core.ndarray, got numpy.ndarray)
```



Discussion:

Resolving patch:
diff --git a/chainer/dataset/convert.py b/chainer/dataset/convert.py
--- a/chainer/dataset/convert.py
+++ b/chainer/dataset/convert.py
@@ -459,7 +459,7 @@ def put(self, array):
                                              array.dtype,
                                              array.size
                                              ).reshape(array.shape)
-                cp_array = cuda.cupy.empty_like(array)
+                cp_array = cuda.cupy.empty(array.shape, array.dtype)
 
             pin_array[...] = array  # copy(CPU): paged -> pinned
             cp_array.set(pin_array, self._stream)  # copy: CPU to GPU


Test patch:
diff --git a/tests/chainer_tests/dataset_tests/test_convert.py b/tests/chainer_tests/dataset_tests/test_convert.py
--- a/tests/chainer_tests/dataset_tests/test_convert.py
+++ b/tests/chainer_tests/dataset_tests/test_convert.py
@@ -12,7 +12,7 @@
 import chainerx
 
 
-@testing.backend.inject_backend_tests(
+_inject_backend_tests = testing.backend.inject_backend_tests(
     None,
     [
         # NumPy
@@ -25,7 +25,9 @@
         {'use_chainerx': True, 'chainerx_device': 'cuda:0'},
         {'use_chainerx': True, 'chainerx_device': 'cuda:1'},
     ])
-class TestConcatExamples(unittest.TestCase):
+
+
+class ConverterTestBase(object):
 
     def get_arrays_to_concat(self, backend_config):
         return [
@@ -33,7 +35,7 @@ def get_arrays_to_concat(self, backend_config):
             for _ in range(5)]
 
     def check_concat_arrays(self, arrays, device, expected_device):
-        array = dataset.concat_examples(arrays, device)
+        array = self.converter(arrays, device)
         self.assertEqual(array.shape, (len(arrays),) + arrays[0].shape)
 
         assert backend.get_device_from_array(array) == expected_device
@@ -66,7 +68,7 @@ def get_tuple_arrays_to_concat(self, backend_config):
             for _ in range(5)]
 
     def check_concat_tuples(self, tuples, device, expected_device):
-        arrays = dataset.concat_examples(tuples, device)
+        arrays = self.converter(tuples, device)
         self.assertEqual(len(arrays), len(tuples[0]))
         for i in range(len(arrays)):
             shape = (len(tuples),) + tuples[0][i].shape
@@ -103,7 +105,7 @@ def get_dict_arrays_to_concat(self, backend_config):
             for _ in range(5)]
 
     def check_concat_dicts(self, dicts, device, expected_device):
-        arrays = dataset.concat_examples(dicts, device)
+        arrays = self.converter(dicts, device)
         self.assertEqual(frozenset(arrays.keys()), frozenset(dicts[0].keys()))
         for key in arrays:
             shape = (len(dicts),) + dicts[0][key].shape
@@ -134,19 +136,55 @@ def test_concat_dicts_to_chainerx(self, backend_config):
             arrays, device, backend.ChainerxDevice(device))
 
 
+@_inject_backend_tests
+class TestConcatExamples(ConverterTestBase, unittest.TestCase):
+
+    def setUp(self):
+        self.converter = dataset.concat_examples
+
+
+class _XFailConcatWithAsyncTransfer(object):
+
+    @attr.chainerx
+    @unittest.expectedFailure
+    def test_concat_arrays_to_chainerx(self, *args, **kwargs):
+        (
+            super(_XFailConcatWithAsyncTransfer, self)
+            .test_concat_arrays_to_chainerx(*args, **kwargs)
+        )
+
+    @attr.chainerx
+    @unittest.expectedFailure
+    def test_concat_tuples_to_chainerx(self, *args, **kwargs):
+        (
+            super(_XFailConcatWithAsyncTransfer, self)
+            .test_concat_tuples_to_chainerx(*args, **kwargs)
+        )
+
+    @attr.chainerx
+    @unittest.expectedFailure
+    def test_concat_dicts_to_chainerx(self, *args, **kwargs):
+        (
+            super(_XFailConcatWithAsyncTransfer, self)
+            .test_concat_dicts_to_chainerx(*args, **kwargs)
+        )
+
+
 @testing.backend.inject_backend_tests(
     None,
     [
         # NumPy
         {},
-        # CuPy
-        {'use_cuda': True, 'cuda_device': 0},
-        {'use_cuda': True, 'cuda_device': 1},
-        # ChainerX
-        {'use_chainerx': True, 'chainerx_device': 'native:0'},
-        {'use_chainerx': True, 'chainerx_device': 'cuda:0'},
-        {'use_chainerx': True, 'chainerx_device': 'cuda:1'},
     ])
+class TestConcatWithAsyncTransfer(
+        _XFailConcatWithAsyncTransfer,
+        ConverterTestBase, unittest.TestCase):
+
+    def setUp(self):
+        self.converter = chainer.dataset.ConcatWithAsyncTransfer()
+
+
+@_inject_backend_tests
 class TestConcatExamplesWithPadding(unittest.TestCase):
 
     def test_concat_arrays_padding(self, backend_config):