Repository: chainer/chainer

Issue:
Concat reverse indexing not working correctly
The following works correctly:

```
a_data = np.zeros((10, 5))
b_data = np.zeros((10, 3))
a = Variable(a_data)
b = Variable(b_data)
F.concat((a, b), axis=1)
```

However, if I change the last line into:

```
F.concat((a, b), axis=-1)
```

it gives the error:

```
Invalid operation is performed in: Concat (Forward)

Expect: in_types[0].shape[1] == in_types[1].shape[1]
Actual: 5 != 3
```



Discussion:

Resolving patch:
diff --git a/chainer/functions/array/concat.py b/chainer/functions/array/concat.py
--- a/chainer/functions/array/concat.py
+++ b/chainer/functions/array/concat.py
@@ -18,14 +18,19 @@ def check_type_forward(self, in_types):
         type_check.expect(in_types[0].ndim >
                           type_check.Variable(self.axis, 'axis'))
 
+        type_check.expect(
+            -in_types[0].ndim <= self.axis,
+            self.axis < in_types[0].ndim
+        )
         ndim = in_types[0].ndim.eval()
+        axis = self.axis % ndim
         for i in range(1, in_types.size().eval()):
             type_check.expect(
                 in_types[0].dtype == in_types[i].dtype,
                 in_types[0].ndim == in_types[i].ndim,
             )
             for d in range(0, ndim):
-                if d == self.axis:
+                if d == axis:
                     continue
                 type_check.expect(in_types[0].shape[d] == in_types[i].shape[d])
 
diff --git a/cupy/manipulation/split.py b/cupy/manipulation/split.py
--- a/cupy/manipulation/split.py
+++ b/cupy/manipulation/split.py
@@ -12,8 +12,10 @@ def array_split(ary, indices_or_sections, axis=0):
     .. seealso:: :func:`cupy.split` for more detail, :func:`numpy.array_split`
 
     """
-    if ary.ndim <= axis:
+    ndim = ary.ndim
+    if -ndim > axis or ndim <= axis:
         raise IndexError('Axis exceeds ndim')
+    axis %= ndim
     size = ary.shape[axis]
 
     if numpy.isscalar(indices_or_sections):
@@ -33,7 +35,6 @@ def array_split(ary, indices_or_sections, axis=0):
         ret.append(ary[skip + (slice(i, index),)])
         i = index
     ret.append(ary[skip + (slice(i, size),)])
-
     return ret
 
 


Test patch:
diff --git a/tests/chainer_tests/functions_tests/array_tests/test_concat.py b/tests/chainer_tests/functions_tests/array_tests/test_concat.py
--- a/tests/chainer_tests/functions_tests/array_tests/test_concat.py
+++ b/tests/chainer_tests/functions_tests/array_tests/test_concat.py
@@ -25,6 +25,9 @@
         {'shape': (2, 7, 3), 'axis': 1,
          'slices': [[slice(None), slice(None, 2)], [slice(None), slice(2, 5)],
                     [slice(None), slice(5, None)]]},
+        {'shape': (2, 7, 3), 'axis': -2,
+         'slices': [[slice(None), slice(None, 2)], [slice(None), slice(2, 5)],
+                    [slice(None), slice(5, None)]]},
     ],
     [
         {'dtype': numpy.float16},
diff --git a/tests/cupy_tests/manipulation_tests/test_split.py b/tests/cupy_tests/manipulation_tests/test_split.py
--- a/tests/cupy_tests/manipulation_tests/test_split.py
+++ b/tests/cupy_tests/manipulation_tests/test_split.py
@@ -9,15 +9,15 @@ class TestSplit(unittest.TestCase):
     _multiprocess_can_split_ = True
 
     @testing.numpy_cupy_array_equal()
-    def test_array_spliti1(self, xp):
+    def test_array_split1(self, xp):
         a = testing.shaped_arange((3, 11), xp)
         split = xp.array_split(a, 4, 1)
         return xp.concatenate(split, 1)
 
     @testing.numpy_cupy_array_equal()
-    def test_array_spliti2(self, xp):
+    def test_array_split2(self, xp):
         a = testing.shaped_arange((3, 11), xp)
-        split = xp.array_split(a, 4, 1)
+        split = xp.array_split(a, 4, -1)
         return xp.concatenate(split, -1)
 
     @testing.numpy_cupy_array_equal()
@@ -53,7 +53,7 @@ def test_split_by_sections1(self, xp):
     @testing.numpy_cupy_array_equal()
     def test_split_by_sections2(self, xp):
         a = testing.shaped_arange((3, 11), xp)
-        split = xp.split(a, (2, 4, 9), 1)
+        split = xp.split(a, (2, 4, 9), -1)
         return xp.concatenate(split, -1)
 
     @testing.numpy_cupy_array_equal()