Repository: chainer/chainer

Issue:
CUDNN_STATUS_NOT_SUPPORTED when dilate != 1 and deterministic
### Code
```
import chainer
from chainer import functions as F
import numpy, cupy

xp = cupy
dtype = numpy.float32

n = 2
c_in = 3
c_out = 2
size_in = (4, 3)
ksize = (3, 3)
x_shape = (n, c_in) + size_in
W_shape = (c_out, c_in) + ksize
b_shape = (c_out,)
dilate = 2
pad = ((ksize[0] // 2) * dilate, (ksize[1] // 2) * dilate)

x = chainer.Variable(xp.random.randn(*x_shape).astype(dtype))
W = chainer.Variable(xp.random.randn(*W_shape).astype(dtype))

y = F.convolution_2d(x, W, dilate=dilate, pad=pad)
y.grad = xp.ones(y.shape, dtype=y.dtype)

with chainer.using_config('use_cudnn', 'always'), \
     chainer.using_config('cudnn_deterministic', True):

    y.backward()
```

### Output
```
Traceback (most recent call last):
  File "test-cudnn.py", line 28, in <module>
    y.backward()
  File "/data/work/w/repos/chainer/chainer/variable.py", line 878, in backward
    self._backward_main(retain_grad)
  File "/data/work/w/repos/chainer/chainer/variable.py", line 979, in _backward_main
    target_input_indexes, out_grad, in_grad)
  File "/data/work/w/repos/chainer/chainer/function_node.py", line 514, in backward_accumulate
    gxs = self.backward(target_input_indexes, grad_outputs)
  File "/data/work/w/repos/chainer/chainer/functions/connection/convolution_2d.py", line 227, in backward
    outsize=(xh, xw), dilate=(self.dy, self.dx))
  File "/data/work/w/repos/chainer/chainer/functions/connection/deconvolution_2d.py", line 399, in deconvolution_2d
    y, = func.apply(args)
  File "/data/work/w/repos/chainer/chainer/function_node.py", line 245, in apply
    outputs = self.forward(in_data)
  File "/data/work/w/repos/chainer/chainer/function_node.py", line 337, in forward
    return self.forward_gpu(inputs)
  File "/data/work/w/repos/chainer/chainer/functions/connection/deconvolution_2d.py", line 228, in forward_gpu
    zero.data, y_desc.value, y.data.ptr)
  File "cupy/cuda/cudnn.pyx", line 894, in cupy.cuda.cudnn.convolutionBackwardData_v3
  File "cupy/cuda/cudnn.pyx", line 908, in cupy.cuda.cudnn.convolutionBackwardData_v3
  File "cupy/cuda/cudnn.pyx", line 449, in cupy.cuda.cudnn.check_status
cupy.cuda.cudnn.CuDNNError: CUDNN_STATUS_NOT_SUPPORTED: b'CUDNN_STATUS_NOT_SUPPORTED'

```
### Environment
Chainer: current master (ea4699eb9db3fbcc178b2c57d5cf97c37a3a6539)
CuPy: current master (https://github.com/cupy/cupy/commit/d6a22f23ec333f964423953907a0c92575b8ec2c)
```
>>> cupy.cuda.cudnn.getVersion()                    
7003                                                
```
```
$ cat /usr/local/cuda/version.txt 
CUDA Version 9.0.176
```


Discussion:

Resolving patch:
diff --git a/chainer/functions/connection/deconvolution_2d.py b/chainer/functions/connection/deconvolution_2d.py
--- a/chainer/functions/connection/deconvolution_2d.py
+++ b/chainer/functions/connection/deconvolution_2d.py
@@ -155,9 +155,12 @@ def forward_gpu(self, inputs):
         self._calc_out_size(x, W)
         self._set_cover_all(x, W)
 
-        if (not self.cover_all and chainer.should_use_cudnn('>=auto') and
-                x.dtype == W.dtype and
-                ((self.dy == 1 and self.dx == 1) or _cudnn_version_ >= 6000)):
+        if (not self.cover_all
+                and chainer.should_use_cudnn('>=auto')
+                and x.dtype == W.dtype
+                and ((self.dy == 1 and self.dx == 1)
+                     or (_cudnn_version_ >= 6000
+                         and not configuration.config.cudnn_deterministic))):
 
             # cuDNN implementation
             return self._forward_cudnn(x, W, b)


Test patch:
diff --git a/tests/chainer_tests/functions_tests/connection_tests/test_deconvolution_2d.py b/tests/chainer_tests/functions_tests/connection_tests/test_deconvolution_2d.py
--- a/tests/chainer_tests/functions_tests/connection_tests/test_deconvolution_2d.py
+++ b/tests/chainer_tests/functions_tests/connection_tests/test_deconvolution_2d.py
@@ -27,6 +27,7 @@ def _pair(x):
         'test_outsize': [True, False],
         'nobias': [True],
         'stride': [1, 2],
+        'dilate': [1],
         'x_dtype': [numpy.float32],
         'W_dtype': [numpy.float32],
     })
@@ -35,6 +36,7 @@ def _pair(x):
         'test_outsize': [True],
         'nobias': [False],
         'stride': [1, 2],
+        'dilate': [1, 2],
         'x_dtype': [numpy.float16, numpy.float32, numpy.float64],
         'W_dtype': [numpy.float16, numpy.float32, numpy.float64],
     }),
@@ -80,8 +82,8 @@ def setUp(self):
 
         N = 2
         inh, inw = 4, 3
-        outh = conv.get_deconv_outsize(inh, kh, sh, ph)
-        outw = conv.get_deconv_outsize(inw, kw, sw, pw)
+        outh = conv.get_deconv_outsize(inh, kh, sh, ph, d=self.dilate)
+        outw = conv.get_deconv_outsize(inw, kw, sw, pw, d=self.dilate)
         self.outsize = (outh, outw) if self.test_outsize else None
         x = numpy.random.uniform(
             -1, 1, (N, self.in_channels, inh, inw)).astype(self.x_dtype)
@@ -117,7 +119,7 @@ def forward_cpu(self, inputs):
         b_cpu = None if b is None else chainer.Variable(b)
         y_cpu = F.deconvolution_2d(
             x_cpu, W_cpu, b_cpu, stride=self.stride, pad=self.pad,
-            outsize=self.outsize)
+            outsize=self.outsize, dilate=self.dilate)
         return y_cpu,
 
     def check_forward(self, inputs, backend_config):
@@ -134,7 +136,7 @@ def check_forward(self, inputs, backend_config):
         with backend_config:
             y_actual = F.deconvolution_2d(
                 x, W, b, stride=self.stride, pad=self.pad,
-                outsize=self.outsize)
+                outsize=self.outsize, dilate=self.dilate)
 
         assert y_expected.data.dtype == self.x_dtype
         assert y_actual.data.dtype == self.x_dtype
@@ -178,7 +180,8 @@ def check_backward(self, inputs, grad_outputs, backend_config):
 
         def f(*args):
             return F.deconvolution_2d(
-                *args, stride=self.stride, pad=self.pad, outsize=self.outsize)
+                *args, stride=self.stride, pad=self.pad, outsize=self.outsize,
+                dilate=self.dilate)
 
         with backend_config:
             gradient_check.check_backward(
@@ -231,7 +234,8 @@ def check_double_backward(
 
         def f(*args):
             y = F.deconvolution_2d(
-                *args, stride=self.stride, pad=self.pad, outsize=self.outsize)
+                *args, stride=self.stride, pad=self.pad, outsize=self.outsize,
+                dilate=self.dilate)
             return y * y  # make the function nonlinear
 
         with backend_config: