Repository: chainer/chainer

Issue:
Error in F.softmax when using non-contiguous array
F.softmax will produces error when using both F.split_axis and F.softmax, enabling cuDNN.  
I think this is related to #907 and #2034 . I confirmed this occurs both in Chainer v1 and v2.

## Conditions
  - Chainer 2.0.1 (same error in 1.24.0)
  - CuPy 1.0.1
  - Ubuntu 16.04
  - CUDA 8.0
  - cuDNN 5.1
## Code to reproduce

```python
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import chainer.functions as F
from chainer import Variable, cuda

data = np.ones((2, 2), dtype=np.float32)
data = cuda.to_gpu(data, 0)
x = Variable(data)
ys = F.split_axis(x, 2, axis=1)
y = F.softmax(ys[0], axis=0)  # Error
```

## Error messages
Traceback (most recent call last):
  File "bug_softmax.py", line 12, in <module>
    y = F.softmax(ys[0], axis=0)  # Error
  File "/home/yagi/pyenv/versions/lab/lib/python3.4/site-packages/chainer/functions/activation/softmax.py", line 106, in softmax
    return Softmax(axis=axis)(x)
  File "/home/yagi/pyenv/versions/lab/lib/python3.4/site-packages/chainer/function.py", line 200, in __call__
    outputs = self.forward(in_data)
  File "/home/yagi/pyenv/versions/lab/lib/python3.4/site-packages/chainer/functions/activation/softmax.py", line 42, in forward
    desc = cudnn.create_tensor_descriptor(x_tensor4d)
  File "/home/yagi/pyenv/versions/lab/lib/python3.4/site-packages/cupy/cudnn.py", line 75, in create_tensor_descriptor
    raise ValueError('cupy.cudnn supports c-contiguous arrays only')
ValueError: cupy.cudnn supports c-contiguous arrays only


Discussion:

Resolving patch:
diff --git a/chainer/functions/activation/softmax.py b/chainer/functions/activation/softmax.py
--- a/chainer/functions/activation/softmax.py
+++ b/chainer/functions/activation/softmax.py
@@ -36,7 +36,8 @@ def forward(self, x):
             one = numpy.array(1, dtype=oz_dtype).ctypes
             zero = numpy.array(0, dtype=oz_dtype).ctypes
             handle = cudnn.get_handle()
-            x_tensor4d = x[0].reshape(self._get_tensor4d_shape(x[0].shape))
+            x_tensor4d = cuda.cupy.ascontiguousarray(
+                x[0].reshape(self._get_tensor4d_shape(x[0].shape)))
             desc = cudnn.create_tensor_descriptor(x_tensor4d)
             y = xp.empty_like(x[0])
             libcudnn.softmaxForward(
@@ -62,11 +63,13 @@ def backward(self, x, gy):
             zero = numpy.array(0, dtype=oz_dtype).ctypes
             handle = cudnn.get_handle()
             gx = xp.empty_like(y)
-            gx_tensor4d = gx.reshape(self._get_tensor4d_shape(gx.shape))
+            gx_tensor4d = cuda.cupy.ascontiguousarray(
+                gx.reshape(self._get_tensor4d_shape(gx.shape)))
+            gy = cuda.cupy.ascontiguousarray(gy[0])
             desc = cudnn.create_tensor_descriptor(gx_tensor4d)
             libcudnn.softmaxBackward(
                 handle, _algorithm, _mode, one.data, desc.value,
-                y.data.ptr, desc.value, gy[0].data.ptr, zero.data,
+                y.data.ptr, desc.value, gy.data.ptr, zero.data,
                 desc.value, gx.data.ptr)
         else:
             gx = y * gy[0]


Test patch:
diff --git a/tests/chainer_tests/functions_tests/activation_tests/test_softmax.py b/tests/chainer_tests/functions_tests/activation_tests/test_softmax.py
--- a/tests/chainer_tests/functions_tests/activation_tests/test_softmax.py
+++ b/tests/chainer_tests/functions_tests/activation_tests/test_softmax.py
@@ -63,6 +63,12 @@ def test_forward_cpu(self):
     def test_forward_gpu(self):
         self.check_forward(cuda.to_gpu(self.x))
 
+    @attr.gpu
+    @condition.retry(3)
+    def test_forward_gpu_non_contiguous(self):
+        self.check_forward(
+            cuda.cupy.asfortranarray(cuda.to_gpu(self.x)))
+
     @attr.gpu
     @condition.retry(3)
     def test_forward_gpu_no_cudnn(self):
@@ -83,6 +89,13 @@ def test_backward_cpu(self):
     def test_backward_gpu(self):
         self.check_backward(cuda.to_gpu(self.x), cuda.to_gpu(self.gy))
 
+    @attr.gpu
+    @condition.retry(10)
+    def test_backward_gpu_non_contiguous(self):
+        self.check_backward(
+            cuda.cupy.asfortranarray(cuda.to_gpu(self.x)),
+            cuda.cupy.asfortranarray(cuda.to_gpu(self.gy)))
+
     @attr.gpu
     @condition.retry(10)
     def test_backward_gpu_no_cudnn(self):