Repository: chainer/chainer

Issue:
Function may raise CUDNN_STATUS_BAD_PARAM when CPU/GPU mismatch
When cuDNN is available, `spatial_pyramid_pooling_2d` raises following error when function is on CPU and passed `Variable` is on GPU. Can this error to be more understandable?

```
# GPU
arr = np.random.randn(5, 3, 5, 5)
v = Variable(arr)
v.to_gpu()

F.spatial_pyramid_pooling_2d(v, 3, F.MaxPooling2D)
# cupy/cuda/cudnn.pyx in cupy.cuda.cudnn.poolingForward (cupy/cuda/cudnn.cpp:6127)()
# cupy/cuda/cudnn.pyx in cupy.cuda.cudnn.poolingForward (cupy/cuda/cudnn.cpp:5978)()
# cupy/cuda/cudnn.pyx in cupy.cuda.cudnn.check_status (cupy/cuda/cudnn.cpp:1308)()
# CuDNNError: CUDNN_STATUS_BAD_PARAM: CUDNN_STATUS_BAD_PARAM
```



Discussion:
I think this error is not caused by inconsistency between CPU and GPU because `spatial_pyramid_pooling_2d` does call cuDNN (and it fails).

When I change the dtype of `arr` to float32 by `arr = np.random.randn(5, 3, 5, 5).astype(np.float32)`, error does not occur. So we should check the dtype of inputs passed to cuDNN. Or change the function called according to the dtype of inputs.


Resolving patch:
diff --git a/chainer/functions/pooling/spatial_pyramid_pooling_2d.py b/chainer/functions/pooling/spatial_pyramid_pooling_2d.py
--- a/chainer/functions/pooling/spatial_pyramid_pooling_2d.py
+++ b/chainer/functions/pooling/spatial_pyramid_pooling_2d.py
@@ -2,12 +2,12 @@
 import six
 
 from chainer import cuda
-from chainer import function
 from chainer.functions.array import concat
 from chainer.functions.pooling import max_pooling_2d
+from chainer.functions.pooling import pooling_2d
 
 
-class SpatialPyramidPooling2D(function.Function):
+class SpatialPyramidPooling2D(pooling_2d.Pooling2D):
 
     """Spatial pyramid pooling over a set of 2d planes."""
 


Test patch:
diff --git a/tests/chainer_tests/functions_tests/pooling_tests/test_spatial_pyramid_pooling_2d.py b/tests/chainer_tests/functions_tests/pooling_tests/test_spatial_pyramid_pooling_2d.py
--- a/tests/chainer_tests/functions_tests/pooling_tests/test_spatial_pyramid_pooling_2d.py
+++ b/tests/chainer_tests/functions_tests/pooling_tests/test_spatial_pyramid_pooling_2d.py
@@ -9,6 +9,7 @@
 from chainer import testing
 from chainer.testing import attr
 from chainer.testing import condition
+from chainer.utils import type_check
 
 
 class TestSpatialPyramidPooling2D(unittest.TestCase):
@@ -101,4 +102,44 @@ def test_backward_gpu_no_cudnn(self):
         self.check_backward(cuda.to_gpu(self.x), cuda.to_gpu(self.gy), False)
 
 
+class TestValidDtype(unittest.TestCase):
+
+    def setUp(self):
+        self.x = numpy.random.randn(5, 3, 5, 5)
+        self.v = chainer.Variable(self.x.astype(numpy.float32))
+
+    def check_valid_dtype(self):
+        functions.spatial_pyramid_pooling_2d(
+            self.v, 3, functions.MaxPooling2D)
+
+    def test_valid_dtype_cpu(self):
+        self.check_valid_dtype()
+
+    @attr.gpu
+    def test_valid_dtype_gpu(self):
+        self.v.to_gpu()
+        self.check_valid_dtype()
+
+
+class TestInvalidDtype(unittest.TestCase):
+
+    def setUp(self):
+        self.x = numpy.random.randn(5, 3, 5, 5)
+        self.v = chainer.Variable(self.x.astype(numpy.int32))
+
+    def check_invalid_dtype(self):
+        functions.spatial_pyramid_pooling_2d(
+            self.v, 3, functions.MaxPooling2D)
+
+    def test_invalid_dtype_cpu(self):
+        with self.assertRaises(type_check.InvalidType):
+            self.check_invalid_dtype()
+
+    @attr.gpu
+    def test_invalid_dtype_gpu(self):
+        self.v.to_gpu()
+        with self.assertRaises(type_check.InvalidType):
+            self.check_invalid_dtype()
+
+
 testing.run_module(__name__, __file__)