Repository: chainer/chainer

Issue:
CUDNN_STATUS_BAD_PARAM raised when dilate=0 in L.Convolution2D 
The sample code I tried:
```
import numpy as np
import chainer
import chainer.links as L

# cpu
link = L.Convolution2D(in_channels=1, out_channels=64, ksize=3, stride=(1, 1), pad=(0, 0), nobias=False, dilate=(0, 0), groups=1)
data = np.random.random((1, 1, 3, 80)).astype(np.float32)
o = link(data)

print('cpu:', o.shape)

print('===================')
chainer.print_runtime_info()
print('===================')
# gpu
gpu_id = 0
to_device = chainer.dataset.convert.to_device
chainer.cuda.get_device_from_id(gpu_id).use()
link.to_gpu()
data = to_device(gpu_id, data)
o = link(data)
print('gpu:', o.shape)
```

result:
```
cpu: (1, 64, 3, 80)
===================
Platform: Linux-4.4.0-142-generic-x86_64-with-debian-stretch-sid
Chainer: 6.0.0
NumPy: 1.16.3
CuPy:
  CuPy Version          : 6.0.0
  CUDA Root             : /usr/local/cuda-9.0
  CUDA Build Version    : 9000
  CUDA Driver Version   : 10010
  CUDA Runtime Version  : 9000
  cuDNN Build Version   : 7402
  cuDNN Version         : 7402
  NCCL Build Version    : 2307
  NCCL Runtime Version  : 2307
iDeep: Not Available
===================
Traceback (most recent call last):
  File "test_vgg.py", line 21, in <module>
    o = link(data)
  File "/env/.pyenv/versions/speech_3.6/lib/python3.6/site-packages/chainer/link.py", line 294, in __call__
    out = forward(*args, **kwargs)
  File "/env/.pyenv/versions/speech_3.6/lib/python3.6/site-packages/chainer/links/connection/convolution_2d.py", line 184, in forward
    groups=self.groups)
  File "/env/.pyenv/versions/speech_3.6/lib/python3.6/site-packages/chainer/functions/connection/convolution_2d.py", line 582, in convolution_2d
    y, = fnode.apply(args)
  File "/env/.pyenv/versions/speech_3.6/lib/python3.6/site-packages/chainer/function_node.py", line 321, in apply
    outputs = self.forward(in_data)
  File "/env/.pyenv/versions/speech_3.6/lib/python3.6/site-packages/chainer/function_node.py", line 512, in forward
    return self.forward_gpu(inputs)
  File "/env/.pyenv/versions/speech_3.6/lib/python3.6/site-packages/chainer/functions/connection/convolution_2d.py", line 185, in forward_gpu
    return self._forward_cudnn(x, W, b, y)
  File "/env/.pyenv/versions/speech_3.6/lib/python3.6/site-packages/chainer/functions/connection/convolution_2d.py", line 246, in _forward_cudnn
    auto_tune=auto_tune, tensor_core=tensor_core)
  File "cupy/cudnn.pyx", line 1545, in cupy.cudnn.convolution_forward
  File "cupy/cudnn.pyx", line 216, in cupy.cudnn._create_convolution_descriptor
  File "cupy/cuda/cudnn.pyx", line 1078, in cupy.cuda.cudnn.setConvolution2dDescriptor_v5
  File "cupy/cuda/cudnn.pyx", line 712, in cupy.cuda.cudnn.check_status
cupy.cuda.cudnn.CuDNNError: CUDNN_STATUS_BAD_PARAM
```


Discussion:
The default `dilate = 1`, we cannot use `dilate  = 0` ?

→ I think we need to mention that `diltate >= 1` ?

Resolving patch:
diff --git a/chainer/functions/connection/convolution_2d.py b/chainer/functions/connection/convolution_2d.py
--- a/chainer/functions/connection/convolution_2d.py
+++ b/chainer/functions/connection/convolution_2d.py
@@ -53,6 +53,10 @@ def __init__(self, stride=1, pad=0, cover_all=False, **kwargs):
         self.dy, self.dx = _pair(dilate)
         self.groups = groups
 
+        if self.dx < 1 or self.dy < 1:
+            raise ValueError('Dilate should be positive, but {} is '
+                             'supplied.'.format(dilate))
+
     def check_type_forward(self, in_types):
         n_in = in_types.size()
         type_check.expect(2 <= n_in, n_in <= 3)


Test patch:
diff --git a/tests/chainer_tests/functions_tests/connection_tests/test_convolution_2d.py b/tests/chainer_tests/functions_tests/connection_tests/test_convolution_2d.py
--- a/tests/chainer_tests/functions_tests/connection_tests/test_convolution_2d.py
+++ b/tests/chainer_tests/functions_tests/connection_tests/test_convolution_2d.py
@@ -300,4 +300,50 @@ def test_1(self):
         z.backward()
 
 
+class TestConvolution2DInvalidDilation(unittest.TestCase):
+
+    n_batches = 2
+    in_channels = 3
+    out_channels = 2
+    dilate = 0
+    x_shape = (n_batches, in_channels, 10, 10)
+    w_shape = (out_channels, in_channels, 3, 3)
+
+    def check_invalid_dilation(self, x_data, w_data):
+        x = chainer.Variable(x_data)
+        w = chainer.Variable(w_data)
+        F.convolution_2d(x, w, dilate=self.dilate)
+
+    def test_invalid_dilation_cpu(self):
+        x = numpy.ones(self.x_shape, numpy.float32)
+        w = numpy.ones(self.w_shape, numpy.float32)
+        with self.assertRaises(ValueError):
+            with chainer.using_config('use_ideep', 'never'):
+                self.check_invalid_dilation(x, w)
+
+    @attr.ideep
+    def test_invalid_dilation_cpu_ideep(self):
+        x = numpy.ones(self.x_shape, numpy.float32)
+        w = numpy.ones(self.w_shape, numpy.float32)
+        with self.assertRaises(ValueError):
+            with chainer.using_config('use_ideep', 'always'):
+                self.check_invalid_dilation(x, w)
+
+    @attr.gpu
+    def test_invalid_dilation_gpu(self):
+        x = cuda.cupy.ones(self.x_shape, numpy.float32)
+        w = cuda.cupy.ones(self.w_shape, numpy.float32)
+        with self.assertRaises(ValueError):
+            with chainer.using_config('use_cudnn', 'never'):
+                self.check_invalid_dilation(x, w)
+
+    @attr.cudnn
+    def test_invalid_dilation_gpu_cudnn(self):
+        x = cuda.cupy.ones(self.x_shape, numpy.float32)
+        w = cuda.cupy.ones(self.w_shape, numpy.float32)
+        with self.assertRaises(ValueError):
+            with chainer.using_config('use_cudnn', 'always'):
+                self.check_invalid_dilation(x, w)
+
+
 testing.run_module(__name__, __file__)