Repository: chainer/chainer

Issue:
Forbid chainerx/cupy array mix in FunctionNode and _fallback_workarounds impls
Don't allow numpy cupy arrays mixture in function_node


Discussion:

Resolving patch:
diff --git a/chainer/__init__.py b/chainer/__init__.py
--- a/chainer/__init__.py
+++ b/chainer/__init__.py
@@ -181,13 +181,10 @@ def is_arrays_compatible(arrays):
         return True
 
     # If there's at least one chainerx.ndarray, all other arrays
-    # will be converted to memory-shared chainerx.ndarrays.
-    # TODO(niboshi): intel64.mdarray is not supported yet.
-    # TODO(niboshi): Delegate array compatibility check to chainerx.
-    if (chainerx.is_available()
-            and any([isinstance(arr, chainerx.ndarray) for arr in arrays])):
-        return not any([
-            isinstance(arr, backends.intel64.mdarray) for arr in arrays])
+    # must be chainerx as well
+    are_chainerx = [isinstance(arr, chainerx.ndarray) for arr in arrays]
+    if chainerx.is_available() and any(are_chainerx):
+        return all(are_chainerx)
 
     if isinstance(arrays[0], backends.cuda.ndarray):
         types = backends.cuda.ndarray
diff --git a/chainer/function_node.py b/chainer/function_node.py
--- a/chainer/function_node.py
+++ b/chainer/function_node.py
@@ -263,6 +263,8 @@ def apply(self, inputs):
         chainerx_device = None
         is_chainerx, in_data = _extract_apply_in_data(inputs)
 
+        utils._check_arrays_forward_compatible(in_data, self.label)
+
         if is_chainerx:
             # Try ChainerX C++ implementation.
             # If it's supported, the output arrays are wrapped with Variables
@@ -290,8 +292,6 @@ def apply(self, inputs):
             self._is_chainerx_fallback_mode = True
             self.chainerx_device = chainerx_device
 
-        utils._check_arrays_forward_compatible(in_data, self.label)
-
         is_debug = chainer.is_debug()
         if is_debug:
             # Keep stack trace for debug
@@ -1194,8 +1194,8 @@ def _extract_apply_in_data(inputs):
     # `Variable._data[0]` (which is backproppable in contrast to
     # `Variable.array`) is returned.
     #
-    # If at least one of the arrays is a ChainerX array, all other NumPy/CuPy
-    # arrays are converted to ChainerX arrays without copy.
+    # If at least one of the arrays is a ChainerX array, all other
+    # arrays need to be ChainerX arrays.
     if not inputs:
         return False, ()
 
@@ -1216,12 +1216,7 @@ def _extract_apply_in_data(inputs):
                 if not has_chainerx_array:
                     if isinstance(x, chainerx.ndarray):
                         has_chainerx_array = True
-
-        if has_chainerx_array:
-            return True, tuple(backend.to_chx(arrays))
-        else:
-            return False, tuple(arrays)
-
+        return has_chainerx_array, tuple(arrays)
     else:
         return False, tuple([
             x.array if isinstance(x, variable.Variable) else x


Test patch:
diff --git a/tests/chainer_tests/test_function_node.py b/tests/chainer_tests/test_function_node.py
--- a/tests/chainer_tests/test_function_node.py
+++ b/tests/chainer_tests/test_function_node.py
@@ -362,11 +362,9 @@ def check_mix_xp(self, xp):
         xp_x1 = xp.random.randn(2, 3).astype(numpy.float32)
         xp_x2 = xp.random.randn(2, 3).astype(numpy.float32)
         x2 = backend.to_chx(xp_x2)
-        y, = self.SimpleFunctionNode(xp).apply((xp_x1, x2))
-
-        assert isinstance(y.array, chainerx.ndarray)
-        chainerx.testing.assert_array_equal(
-            backend.CpuDevice().send(xp_x1 * xp_x2), y.array)
+        fnode = self.SimpleFunctionNode(xp)
+        with self.assertRaises(TypeError):
+            fnode.apply((xp_x1, x2))
 
     @attr.chainerx
     def test_mix_numpy(self):