internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: chainer/chainer

Issue:
`backward` of HuberLoss fails when dimension of inputs >= 3
`backward` of HuberLoss fails when dimension of inputs >= 3. Should [this reshape](https://github.com/chainer/chainer/blob/v5.0.0rc1/chainer/functions/loss/huber_loss.py#L57) be `gy = gy.reshape(gy.shape[:1] + (1,) + gy.shape[1:])` ?

* Conditions
  - Platform: Linux-4.4.0-137-generic-x86_64-with-debian-stretch-sid
  - Chainer: 5.0.0rc1
  - NumPy: 1.15.2
  - CuPy: Not Available

* Code to reproduce
```
from chainer import Parameter
from chainer import functions as F

shape = (2, 3, 4)
x = Parameter(np.ones(shape))
y = Parameter(np.zeros(shape))
z = F.mean(F.huber_loss(x, y, 1.0))
z.backward()
```

* Error messages, stack traces, or logs
```
Traceback (most recent call last):
  File "test.py", line 9, in <module>
    z.backward()
  File "/chainer/chainer/variable.py", line 963, in backward
    self._backward_main(retain_grad, loss_scale)
  File "/chainer/chainer/variable.py", line 1040, in _backward_main
    func, target_input_indexes, out_grad, in_grad)
  File "/chainer/chainer/_backprop_utils.py", line 106, in backprop_step
    target_input_indexes, grad_outputs)
  File "/chainer/chainer/functions/loss/huber_loss.py", line 58, in backward
    gx = chainer.functions.broadcast_to(gy, gx.shape) * gx
  File "/chainer/chainer/functions/array/broadcast.py", line 132, in broadcast_to
    y, = BroadcastTo(shape).apply((x,))
  File "/chainer/chainer/function_node.py", line 245, in apply
    self._check_data_type_forward(in_data)
  File "/chainer/chainer/function_node.py", line 330, in _check_data_type_forward
    self.check_type_forward(in_type)
  File "/chainer/chainer/functions/array/broadcast.py", line 74, in check_type_forward
    type_check.expect(in_types[0].ndim <= ndim)
  File "/chainer/chainer/utils/type_check.py", line 546, in expect
    expr.expect()
  File "/chainer/chainer/utils/type_check.py", line 483, in expect
    '{0} {1} {2}'.format(left, self.inv, right))
chainer.utils.type_check.InvalidType:
Invalid operation is performed in: BroadcastTo (Forward)
```



Discussion:
`F.huber_loss` should check `ndim==2` or it should be tested with Variable with other `ndim`.
It's documented that the shapes must be (N, K).
Nevertheless, any failure in `backward` is a bug.

BTW,
- `F.expand_dims` looks better.
- automatic broadcast could be used below.

Resolving patch:
diff --git a/chainer/functions/loss/huber_loss.py b/chainer/functions/loss/huber_loss.py
--- a/chainer/functions/loss/huber_loss.py
+++ b/chainer/functions/loss/huber_loss.py
@@ -54,7 +54,7 @@ def backward(self, indexes, grad_outputs):
         gx = chainer.functions.clip(diff, -delta, delta)
 
         if self.reduce == 'sum_along_second_axis':
-            gy = gy.reshape(gy.shape + (1,) * (diff.ndim - 1))
+            gy = chainer.functions.expand_dims(gy, 1)
         gx = chainer.functions.broadcast_to(gy, gx.shape) * gx
         return gx, -gx
 


Test patch:
Repository: chainer/chainer

Issue:
Docstring of `functions.forget` is incorrect as `+` doesn't retain inputs anymore
The docstring says that `(x + y) + x` retains the immediate variable holding `x + y`. 

```
       Let ``f`` be a function defined as:
       >>> def f(a, b):
       ...   return a + b + a
       and, ``x`` and ``y`` be :class:`~chainer.Variable`\\ s:
       >>> x = chainer.Variable(np.random.uniform(-1, 1, 5).astype(np.float32))
       >>> y = chainer.Variable(np.random.uniform(-1, 1, 5).astype(np.float32))
       When ``z`` is calculated as ``z = f(x, y)``, its intermediate result
       ``x + y`` is stored in memory. Instead, if you call ``f`` with
       ``F.forget``:
       >>> z = F.forget(f, x, y)
       intermediate ``x + y`` is forgotten.
```

But this isn't true for new-style function of `+`, because addition don't requires book-kept inputs for backpropagation.

I checked the behavior by the following script, which traverses retained variables.

```python
import chainer
import chainer.functions as F
import numpy as np 


def f(a, b):
    return (a + b) + a


def recur_check_vars(v, x, y):
    creator = v.creator_node
    if creator is None:
        return
    for pnode in creator.inputs:
        p = pnode.get_variable()
        assert p.data is None or p is x or p is y
        print(p)
        recur_check_vars(p, x, y) 


def main():
    x = chainer.Variable(np.random.uniform(-1, 1, 5).astype(np.float32))
    y = chainer.Variable(np.random.uniform(-1, 1, 5).astype(np.float32))
    print(x)
    print(y)
    print()
    z = f(x, y) 
    recur_check_vars(z, x, y) 


if __name__ == '__main__':
    main()
```

The script doesn't fail, and the output is as follows. We can see that`x + y` is discarded. Living variables `x` and `y` are retrieved, as each `VariableNode` instance has a weakref to the corresponding variable.

```
variable([-0.7699733  -0.50523347 -0.20869003 -0.7912116   0.92058474])
variable([ 0.58832335 -0.06183117  0.1939743   0.9021316  -0.19973369])

variable(None)
variable([-0.7699733  -0.50523347 -0.20869003 -0.7912116   0.92058474])
variable([ 0.58832335 -0.06183117  0.1939743   0.9021316  -0.19973369])
variable([-0.7699733  -0.50523347 -0.20869003 -0.7912116   0.92058474])
```


Discussion:
You are right.  `(a + b) * a` can be a new example.  Could you send a pull request for the issue?

Resolving patch:
diff --git a/chainer/functions/util/forget.py b/chainer/functions/util/forget.py
--- a/chainer/functions/util/forget.py
+++ b/chainer/functions/util/forget.py
@@ -89,7 +89,7 @@ def forget(func, *xs):
        Let ``f`` be a function defined as:
 
        >>> def f(a, b):
-       ...   return a + b + a
+       ...   return (a + b) * a
 
        and, ``x`` and ``y`` be :class:`~chainer.Variable`\\ s:
 


Test patch:
Repository: chainer/chainer

Issue:
Documentation of Variable-compatible `ndarray`s
Related to #3929.

If a "wrapper function" accepts `Variable` inputs, it should accept `ndarray`s, too.  This compatibility could be documented.

The documentation could be referred as `` `ndarray` `` in #4887 (https://github.com/chainer/chainer/pull/4887#issuecomment-426289442).


Discussion:

Resolving patch:
diff --git a/chainer/functions/activation/clipped_relu.py b/chainer/functions/activation/clipped_relu.py
--- a/chainer/functions/activation/clipped_relu.py
+++ b/chainer/functions/activation/clipped_relu.py
@@ -129,8 +129,7 @@ def clipped_relu(x, z=20.0):
     .. math:: \\text{ClippedReLU}(x, z) = \\min(\\max(0, x), z).
 
     Args:
-        x (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
-        :class:`cupy.ndarray`):
+        x (:class:`~chainer.Variable` or :ref:`ndarray`):
             Input variable. A :math:`(s_1, s_2, ..., s_n)`-shaped float array.
         z (float): Clipping value. (default = 20.0)
 


Test patch:
Repository: chainer/chainer

Issue:
`backprop_step` only partially documented
The `backprop_step` function in `chainer/_backprop_utils.py` is documented but misses an explanation for the `func` argument. https://github.com/chainer/chainer/blob/master/chainer/_backprop_utils.py#L73-L89


Discussion:

Resolving patch:
diff --git a/chainer/_backprop_utils.py b/chainer/_backprop_utils.py
--- a/chainer/_backprop_utils.py
+++ b/chainer/_backprop_utils.py
@@ -78,13 +78,16 @@ def backprop_step(
     :func:`chainer.grad`.
 
     Args:
-        target_input_indexes (tuple of int): Sorted indices of the input
-            variables w.r.t. which the gradients are required. It is
-            guaranteed that this tuple contains at least one element.
+        func (~chainer.FunctionNode): The function for which gradients are
+            accumulated.
+        target_input_indexes (tuple of int): Sorted indices of the inputs
+            that require gradients. It is guaranteed that this tuple contains
+            at least one element.
         grad_outputs (tuple of Variable): Gradients w.r.t. the output
             variables. If the gradient w.r.t. an output variable is not
             given, the corresponding element is ``None``.
-        grad_inputs (dict): References of radients w.r.t. the input variables.
+        grad_inputs (dict): References of the gradients w.r.t. the input
+            variables.
 
     """
     is_debug = chainer.is_debug()


Test patch:
Repository: chainer/chainer

Issue:
Wrong usage of downsample_fb in ResNet
The output of model `chainer.links.ResNet50Layers` seems to be wrong in Chainer 5.0.0, in contrast to 4.4.0.

```
import chainer
import scipy.misc
model = chainer.links.ResNet50Layers()
img = scipy.misc.imread("/some/image.jpg")
prep = img.copy()
y=(prep.shape[0]-224)//2
x=(prep.shape[1]-224)//2
prep = prep[y:y+224,x:x+224,::-1].astype(np.float32)
prep -= [103.063, 115.903, 123.152]
prep *= 1
prep = np.transpose(prep, (2, 0, 1))
chainer.config.use_cudnn='never'
chainer.config.train = False

rets=model(prep[np.newaxis, ...],model.available_layers)
for k, v in rets.items():
    print(k)
    print(v.data.flatten()[:100])
```

The output of above code differs between Chainer 5.0.0 and Chainer 4.4.0.
Also, validation accuracy for each version is 62% and 70% on the pretrained model.

In Chainer 5.0.0, I can get the same result as 4.4.0 by `chainer.links.ResNet50Layers(downsample_fb=True)`.

It seems that stride contradicts to the documentation.
https://github.com/chainer/chainer/blob/ee0b971547de243077f3afe98bbee45db630de6a/chainer/links/model/vision/resnet.py#L619

It should be
```
stride_1x1, stride_3x3 = (1, stride) if downsample_fb else (stride, 1)
```

```
Platform: Linux-4.4.0-64-generic-x86_64-with-debian-jessie-sid
Chainer: 5.0.0
NumPy: 1.12.1
CuPy:
  CuPy Version          : 5.0.0
  CUDA Root             : /usr/local/cuda
  CUDA Build Version    : 9000
  CUDA Driver Version   : 9010
  CUDA Runtime Version  : 9000
  cuDNN Build Version   : 7300
  cuDNN Version         : 7300
  NCCL Build Version    : None
iDeep: Not Available
```


Discussion:
Thanks for reporting. Maybe introduced in https://github.com/chainer/chainer/pull/4479/files#diff-b27899a8e6e2ef04c4b64bb26c60669aR590?
@milhidaka Could you make a pull-request if you have interest?

Resolving patch:
diff --git a/chainer/links/model/vision/resnet.py b/chainer/links/model/vision/resnet.py
--- a/chainer/links/model/vision/resnet.py
+++ b/chainer/links/model/vision/resnet.py
@@ -616,7 +616,7 @@ def __init__(self, in_channels, mid_channels, out_channels,
         # In the original MSRA ResNet, stride=2 is on 1x1 convolution.
         # In Facebook ResNet, stride=2 is on 3x3 convolution.
 
-        stride_1x1, stride_3x3 = (stride, 1) if downsample_fb else (1, stride)
+        stride_1x1, stride_3x3 = (1, stride) if downsample_fb else (stride, 1)
         with self.init_scope():
             self.conv1 = Convolution2D(
                 in_channels, mid_channels, 1, stride_1x1, 0, initialW=initialW,


Test patch:
Repository: chainer/chainer

Issue:
MultiprocessParallelUpdater modifies `devices` argument as side effect
The `MultiprocessParallelUpdater` init method has `devices` argument that takes as input a list, a tuple or a dictionnary.

In the latter case, the creator pops the `'main'` key from it, thus modifies the object given as argument.
https://github.com/chainer/chainer/blob/12340007efee499ddcb53120938cc23c8d96c5f9/chainer/training/updaters/multiprocess_parallel_updater.py#L173-L176

If the user wants to reuse the devices dictionary later (e.g. when specifying `Evaluator`'s device), the user has to create a copy of it. 
It is in a way inconsistent with the behaviour of `ParallelUpdater` (which does not modify the `devices` dictionary), and can confuse the user.

I think the `MultiprocessParallelUpdater` should copy the `devices` argument in case of dictionary to avoid this.
What are your insights about this?


Discussion:
> I think the MultiprocessParallelUpdater should copy the devices argument in case of dictionary to avoid this.

I think this makes sense. Could you make a PR if you have interest?

Resolving patch:
diff --git a/chainer/training/updaters/multiprocess_parallel_updater.py b/chainer/training/updaters/multiprocess_parallel_updater.py
--- a/chainer/training/updaters/multiprocess_parallel_updater.py
+++ b/chainer/training/updaters/multiprocess_parallel_updater.py
@@ -171,6 +171,7 @@ def __init__(self, iterators, optimizer, converter=convert.concat_examples,
         )
 
         if isinstance(devices, dict):
+            devices = devices.copy()
             main = devices.pop('main')
             devices = list(six.itervalues(devices))
             devices = [main] + devices


Test patch:
Repository: chainer/chainer

Issue:
`split_dataset` doesn't accept `split_at == n_examples`
https://github.com/chainer/chainer/blob/master/chainer/datasets/sub_dataset.py#L104-L105

`split_dataset` accepts `split_at=0` and the first return value will be a SubDataset with length zero.
However, it raises an exception when `split_at` is exactly the same as `n_examples`.

I found it inconvenient when I had to manually split my dataset for cross validation (I couldn't use `get_cross_validation_datasets` for some reason). I first tried to split it into three pieces and concatenate the first and third ones, but I got an error for the last fold.

Is there any reason it declines when `split_at` is equal to `n_examples`?

Code to reproduce:
```
from chainer.dataset import DatasetMixin
from chainer.datasets import split_dataset


class Dataset(DatasetMixin):

    def __len__(self):
        return 3

    def get_example(self, i):
        return 42


dataset = Dataset()

a, b = split_dataset(dataset, 0)
print('a: {}, b: {}'.format(len(a), len(b)))

a, b = split_dataset(dataset, 3)
print('a: {}, b: {}'.format(len(a), len(b)))
```


Discussion:

Resolving patch:
diff --git a/chainer/datasets/sub_dataset.py b/chainer/datasets/sub_dataset.py
--- a/chainer/datasets/sub_dataset.py
+++ b/chainer/datasets/sub_dataset.py
@@ -101,7 +101,7 @@ def split_dataset(dataset, split_at, order=None):
                         .format(type(split_at)))
     if split_at < 0:
         raise ValueError('split_at must be non-negative')
-    if split_at >= n_examples:
+    if split_at > n_examples:
         raise ValueError('split_at exceeds the dataset size')
     subset1 = SubDataset(dataset, 0, split_at, order)
     subset2 = SubDataset(dataset, split_at, n_examples, order)


Test patch:
diff --git a/tests/chainer_tests/datasets_tests/test_sub_dataset.py b/tests/chainer_tests/datasets_tests/test_sub_dataset.py
--- a/tests/chainer_tests/datasets_tests/test_sub_dataset.py
+++ b/tests/chainer_tests/datasets_tests/test_sub_dataset.py
@@ -47,12 +47,24 @@ def test_split_dataset(self):
         self.assertEqual(subset2[1], 4)
         self.assertEqual(subset2[2], 5)
 
+    def test_split_dataset_head(self):
+        original = [1, 2, 3, 4, 5]
+        subset1, subset2 = datasets.split_dataset(original, 0)
+        self.assertEqual(len(subset1), 0)
+        self.assertEqual(len(subset2), 5)
+
+    def test_split_dataset_tail(self):
+        original = [1, 2, 3, 4, 5]
+        subset1, subset2 = datasets.split_dataset(original, 5)
+        self.assertEqual(len(subset1), 5)
+        self.assertEqual(len(subset2), 0)
+
     def test_split_dataset_invalid_position(self):
         original = [1, 2, 3, 4, 5]
         with self.assertRaises(ValueError):
             datasets.split_dataset(original, -1)
         with self.assertRaises(ValueError):
-            datasets.split_dataset(original, 5)
+            datasets.split_dataset(original, 6)
 
     def test_split_dataset_invalid_type(self):
         original = [1, 2, 3, 4, 5]
Repository: chainer/chainer

Issue:
Chainer X MNIST example not compatible with python 3.5
https://github.com/chainer/chainer/blob/ee05a5eacc593d77e8f2dbbaffc6487605938510/chainerx_cc/examples/mnist/train_mnist.py#L170-L172

In Python 3.5 (I used 3.5.2), the Path in pathlib needs to be explicitly converted to string before using it for `open` call.
A workaround could simply be:

```
    x_path = str(path / '{}-images-idx3-ubyte.gz'.format(name))
    y_path = str(path / '{}-labels-idx1-ubyte.gz'.format(name))
```

Also, the flags settings needed to be commented out in order to run properly.
https://github.com/chainer/chainer/blob/ee05a5eacc593d77e8f2dbbaffc6487605938510/chainerx_cc/examples/mnist/train_mnist.py#L179
https://github.com/chainer/chainer/blob/ee05a5eacc593d77e8f2dbbaffc6487605938510/chainerx_cc/examples/mnist/train_mnist.py#L184



Discussion:

Resolving patch:
diff --git a/chainerx_cc/examples/mnist_py/train_mnist.py b/chainerx_cc/examples/mnist_py/train_mnist.py
--- a/chainerx_cc/examples/mnist_py/train_mnist.py
+++ b/chainerx_cc/examples/mnist_py/train_mnist.py
@@ -168,20 +168,18 @@ def main():
 
 def get_mnist(path, name):
     path = pathlib.Path(path)
-    x_path = path / '{}-images-idx3-ubyte.gz'.format(name)
-    y_path = path / '{}-labels-idx1-ubyte.gz'.format(name)
+    x_path = str(path / '{}-images-idx3-ubyte.gz'.format(name))
+    y_path = str(path / '{}-labels-idx1-ubyte.gz'.format(name))
 
     with gzip.open(x_path, 'rb') as fx:
         fx.read(16)  # skip header
         # read/frombuffer is used instead of fromfile because fromfile does not
         # handle gzip file correctly
         x = np.frombuffer(fx.read(), dtype=np.uint8).reshape(-1, 784)
-        x.flags.writeable = True  # TODO(beam2d): remove this workaround
 
     with gzip.open(y_path, 'rb') as fy:
         fy.read(8)  # skip header
         y = np.frombuffer(fy.read(), dtype=np.uint8)
-        y.flags.writeable = True  # TODO(beam2d): remove this workaround
 
     assert x.shape[0] == y.shape[0]
 


Test patch:
Repository: chainer/chainer

Issue:
Occasional test failure in `TestWalkerAlias`
Occasionally, the result of `xp.random.uniform(0, 1, shape).astype(thr_dtype)` becomes `1.0`, and `self.threshold[index]` raises an `IndexError`.

https://ci.appveyor.com/project/pfnet/chainer/builds/21769400/job/96weerl928ipapc6


Discussion:

Resolving patch:
diff --git a/chainer/utils/walker_alias.py b/chainer/utils/walker_alias.py
--- a/chainer/utils/walker_alias.py
+++ b/chainer/utils/walker_alias.py
@@ -104,12 +104,11 @@ def sample(self, shape):
 
     def sample_xp(self, xp, shape):
         thr_dtype = self.threshold.dtype
-        ps = xp.random.uniform(0, 1, shape).astype(thr_dtype)
-        pb = ps * len(self.threshold)
+        pb = xp.random.uniform(0, len(self.threshold), shape)
         index = pb.astype(numpy.int32)
         left_right = (
             self.threshold[index]
-            < (pb - index.astype(thr_dtype)))
+            < (pb.astype(thr_dtype) - index.astype(thr_dtype)))
         left_right = left_right.astype(numpy.int32)
         return self.values[index * 2 + left_right]
 


Test patch:
Repository: chainer/chainer

Issue:
Shouldn't `Iterator.__exit__` call `finalize`?
`Iterator.__exit__` does nothing by default. A user-defined iterator will not be finalized with `with` syntax.

Documentation encourages overriding `__enter__` and `__exit__` as well as `finalize`, but it looks to me that they are duplicate interfaces that do not work well with each other.



Discussion:

Resolving patch:
diff --git a/chainer/dataset/iterator.py b/chainer/dataset/iterator.py
--- a/chainer/dataset/iterator.py
+++ b/chainer/dataset/iterator.py
@@ -32,6 +32,9 @@ class Iterator(object):
 
     """
 
+    def __del__(self):
+        self.finalize()
+
     def __iter__(self):
         """Returns self."""
         return self
@@ -58,26 +61,16 @@ def finalize(self):
         This method does nothing by default. Implementation may override it to
         better handle the internal resources.
 
+        This method can be called multiple times.
+
         """
         pass
 
     def __enter__(self):
-        """With statement context manager method
-
-        This method does nothing by default. Implementation may override it to
-        better handle the internal resources by with statement.
-
-        """
         return self
 
     def __exit__(self, exc_type, exc_value, traceback):
-        """With statement context manager method
-
-        This method does nothing by default. Implementation may override it to
-        better handle the internal resources by with statement.
-
-        """
-        return None
+        self.finalize()
 
     def serialize(self, serializer):
         """Serializes the internal state of the iterator.
diff --git a/chainer/iterators/multiprocess_iterator.py b/chainer/iterators/multiprocess_iterator.py
--- a/chainer/iterators/multiprocess_iterator.py
+++ b/chainer/iterators/multiprocess_iterator.py
@@ -162,7 +162,7 @@ def __next__(self):
 
     next = __next__
 
-    def __del__(self):
+    def finalize(self):
         if self._finalized:
             return
 
@@ -176,14 +176,6 @@ def __del__(self):
         self._prefetch_loop = None
         self._finalized = True
 
-    finalize = __del__
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_value, traceback):
-        self.finalize()
-
     def __copy__(self):
         # This function is implemented for backward compatibility.
         # Please use `reset` normally.
diff --git a/chainer/iterators/multithread_iterator.py b/chainer/iterators/multithread_iterator.py
--- a/chainer/iterators/multithread_iterator.py
+++ b/chainer/iterators/multithread_iterator.py
@@ -79,12 +79,6 @@ def reset(self):
         # reset internal state
         self._next = None
 
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_value, traceback):
-        self.finalize()
-
     def finalize(self):
         pool = self._pool
 


Test patch:
Repository: chainer/chainer

Issue:
cuDNN required in v6?
Chainer code that used to work fine with 5.2 does not work any more with 6.0.0b2, presumably because I don't have cuDNN installed.

* Conditions
  - Chainer version: 6.0.0b2
  - CuPy version: 6.0.0b2
  - OS/Platform: Ubuntu 16.04
  - CUDA/cuDNN version: CUDA 8.0/no cuDNN
* Code to reproduce: `python3 -c "from chainer.backends import cuda; cuda.check_cuda_available()"`
* Error messages, stack traces, or logs:
  ```
  Traceback (most recent call last):
    File "<string>", line 1, in <module>
    File "/usr/local/lib/python3.5/dist-packages/chainer/backends/cuda.py", line 135, in 
  check_cuda_available
      raise RuntimeError(msg)
  RuntimeError: CUDA environment is not correctly set up
  (see https://github.com/chainer/chainer#installation).module 'cupy.cuda' has no attribute 'cudnn'
  ```

@hvy pointed out that this change https://github.com/chainer/chainer/commit/91878a45fe40fb2ea70ff9bbf3e9a9ef4ee6df56#diff-b74802247d24e96cf7d5d8742480530dR69 may be the cause.


Discussion:

Resolving patch:
diff --git a/chainer/backends/cuda.py b/chainer/backends/cuda.py
--- a/chainer/backends/cuda.py
+++ b/chainer/backends/cuda.py
@@ -65,7 +65,6 @@
     from cupy.cuda import Event  # type: ignore # NOQA
     from cupy.cuda import Stream  # type: ignore # NOQA
 
-    libcudnn = cuda.cudnn  # type: tp.Any # NOQA
     available = True
 except Exception as e:
     _resolution_error = e
@@ -108,19 +107,23 @@ class Event(object):  # type: ignore # for type testing
     class Stream(object):  # type: ignore # for type testing
         pass
 
-    # for `xp is cuda.cupy` and `cuda.libcudnn` to always work
+    # for `xp is chainer.backends.cuda.cupy` to always work
     cupy = object()
-    libcudnn = object()
+
 
 if available:
     _cudnn_disabled_by_user = int(os.environ.get('CHAINER_CUDNN', '1')) == 0
     try:
         import cupy.cudnn
         cudnn = cupy.cudnn  # type: tp.Optional[types.ModuleType]
+        libcudnn = cupy.cuda.cudnn  # type: tp.Any # NOQA
         cudnn_enabled = not _cudnn_disabled_by_user
     except Exception as e:
         _resolution_error = e
 
+        # for `chainer.backends.cuda.libcudnn` to always work
+        libcudnn = object()
+
 
 def check_cuda_available():
     """Checks if CUDA is available.


Test patch: