Repository: chainer/chainer

Issue:
average_pooling2d, and other pooling do not call ascontiguousarray before calling cudnn's functions
convolution_2d calls ascontiguousarray before calling cudnn's convolution function but average_pooling2d does not. This causes unexpected error when we use pooling against non-contiguous array.

I found that other poolings have similar problem.

We should call ascontiguousarray in pooling functions.


Discussion:

Resolving patch:
diff --git a/chainer/functions/pooling/pooling_2d.py b/chainer/functions/pooling/pooling_2d.py
--- a/chainer/functions/pooling/pooling_2d.py
+++ b/chainer/functions/pooling/pooling_2d.py
@@ -49,7 +49,7 @@ def check_type_forward(self, in_types):
 
     def forward_gpu(self, x):
         # Implementation using cudnn
-        x = x[0]
+        x = cuda.cupy.ascontiguousarray(x[0])
         n, c, h, w = x.shape
         y_h = conv.get_conv_outsize(
             h, self.kh, self.sy, self.ph, self.cover_all)
@@ -74,11 +74,10 @@ def forward_gpu(self, x):
 
     def backward_gpu(self, x, gy):
         # Implementation using cudnn
-        x = x[0]
+        x = cuda.cupy.ascontiguousarray(x[0])
         handle = cudnn.get_handle()
         pool_desc = self.create_pool_desc()
 
-        # Pooling of cuDNNv2 does not seem to support non-contiguous gradients
         gy = cuda.cupy.ascontiguousarray(gy[0])
 
         x_desc = cudnn.create_tensor_descriptor(x)
diff --git a/chainer/functions/pooling/pooling_nd.py b/chainer/functions/pooling/pooling_nd.py
--- a/chainer/functions/pooling/pooling_nd.py
+++ b/chainer/functions/pooling/pooling_nd.py
@@ -45,7 +45,7 @@ def check_type_forward(self, in_types):
 
     def forward_gpu(self, x):
         # Implementation using cuDNN.
-        x = x[0]
+        x = cuda.cupy.ascontiguousarray(x[0])
         n, c = x.shape[:2]
         dims = x.shape[2:]
         ys = tuple(conv.get_conv_outsize(d, k, s, p, self.cover_all)
@@ -71,11 +71,10 @@ def forward_gpu(self, x):
 
     def backward_gpu(self, x, gy):
         # Implementation using cudnn
-        x = x[0]
+        x = cuda.cupy.ascontiguousarray(x[0])
         handle = cudnn.get_handle()
         pool_desc = self.create_pool_desc()
 
-        # Pooling of cuDNNv2 does not seem to support non-contiguous gradients
         gy = cuda.cupy.ascontiguousarray(gy[0])
 
         x_desc = cudnn.create_tensor_descriptor(x)


Test patch:
diff --git a/tests/chainer_tests/functions_tests/pooling_tests/test_average_pooling_2d.py b/tests/chainer_tests/functions_tests/pooling_tests/test_average_pooling_2d.py
--- a/tests/chainer_tests/functions_tests/pooling_tests/test_average_pooling_2d.py
+++ b/tests/chainer_tests/functions_tests/pooling_tests/test_average_pooling_2d.py
@@ -56,6 +56,11 @@ 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):
@@ -75,6 +80,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(3)
+    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(3)
     def test_backward_gpu_no_cudnn(self):
diff --git a/tests/chainer_tests/functions_tests/pooling_tests/test_average_pooling_nd.py b/tests/chainer_tests/functions_tests/pooling_tests/test_average_pooling_nd.py
--- a/tests/chainer_tests/functions_tests/pooling_tests/test_average_pooling_nd.py
+++ b/tests/chainer_tests/functions_tests/pooling_tests/test_average_pooling_nd.py
@@ -77,6 +77,11 @@ def test_forward_cpu(self):
     def test_forward_gpu(self):
         self.check_forward(cuda.to_gpu(self.x))
 
+    @attr.cudnn
+    @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):
@@ -128,6 +133,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.cudnn
+    @condition.retry(3)
+    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(3)
     def test_backward_gpu_no_cudnn(self):
diff --git a/tests/chainer_tests/functions_tests/pooling_tests/test_max_pooling_2d.py b/tests/chainer_tests/functions_tests/pooling_tests/test_max_pooling_2d.py
--- a/tests/chainer_tests/functions_tests/pooling_tests/test_max_pooling_2d.py
+++ b/tests/chainer_tests/functions_tests/pooling_tests/test_max_pooling_2d.py
@@ -70,6 +70,11 @@ def test_forward_cpu_wide(self):  # see #120
     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):
@@ -91,6 +96,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(3)
+    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(3)
     def test_backward_gpu_no_cudnn(self):
diff --git a/tests/chainer_tests/functions_tests/pooling_tests/test_max_pooling_nd.py b/tests/chainer_tests/functions_tests/pooling_tests/test_max_pooling_nd.py
--- a/tests/chainer_tests/functions_tests/pooling_tests/test_max_pooling_nd.py
+++ b/tests/chainer_tests/functions_tests/pooling_tests/test_max_pooling_nd.py
@@ -88,6 +88,11 @@ def test_forward_cpu_wide(self):  # see #120
     def test_forward_gpu(self):
         self.check_forward(cuda.to_gpu(self.x))
 
+    @attr.cudnn
+    @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):
@@ -141,6 +146,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.cudnn
+    @condition.retry(3)
+    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(3)
     def test_backward_gpu_no_cudnn(self):