Repository: chainer/chainer

Issue:
`chainer.grad` does not check `len(outputs) == len(grad_outputs)`
What is worse, default grad for each output is 1.

* Code to reproduce
```python
import chainer
import numpy as np
x1 = chainer.Variable(np.array(2, np.float32))
x2 = chainer.Variable(np.array(3, np.float32))
y1, y2 = chainer.functions.identity(x1, x2)
print(chainer.grad([y1, y2], [x1, x2], None))
print(chainer.grad([y1, y2], [x1, x2], [None]))
print(chainer.grad([y1, y2], [x1, x2], [None, None]))
```

* Error messages, stack traces, or logs
```
[variable(1.), variable(1.)]
[variable(1.), None]
[variable(1.), variable(1.)]
```


Discussion:

Resolving patch:
diff --git a/chainer/function_node.py b/chainer/function_node.py
--- a/chainer/function_node.py
+++ b/chainer/function_node.py
@@ -978,14 +978,26 @@ def grad(outputs, inputs, grad_outputs=None, grad_inputs=None, set_grad=False,
     if not isinstance(inputs, (tuple, list)):
         raise TypeError(
             'inputs must be a tuple or a list, not {}.'.format(type(inputs)))
-    if not (grad_outputs is None or isinstance(grad_outputs, (tuple, list))):
-        raise TypeError(
-            'grad_outputs must be a tuple or a list or None, not {}.'.format(
-                type(grad_outputs)))
-    if not (grad_inputs is None or isinstance(grad_inputs, (tuple, list))):
-        raise TypeError(
-            'grad_inputs must be a tuple or a list or None, not {}.'.format(
-                type(grad_inputs)))
+    if grad_outputs is not None:
+        if not isinstance(grad_outputs, (tuple, list)):
+            raise TypeError(
+                'grad_outputs must be a tuple or a list or None, not {}.'
+                .format(type(grad_outputs)))
+        if len(outputs) != len(grad_outputs):
+            raise ValueError(
+                'grad_outputs must be of the same length as outputs.\n'
+                'len(outputs) = {}, len(grad_outputs) = {}'
+                .format(len(outputs), len(grad_outputs)))
+    if grad_inputs is not None:
+        if not isinstance(grad_inputs, (tuple, list)):
+            raise TypeError(
+                'grad_inputs must be a tuple or a list or None, not {}.'
+                .format(type(grad_inputs)))
+        if len(inputs) != len(grad_inputs):
+            raise ValueError(
+                'grad_inputs must be of the same length as inputs.\n'
+                'len(inputs) = {}, len(grad_inputs) = {}'
+                .format(len(inputs), len(grad_inputs)))
 
     for v in outputs:
         # Raise error here if v is created by Function.backward.


Test patch:
diff --git a/tests/chainer_tests/test_function_node.py b/tests/chainer_tests/test_function_node.py
--- a/tests/chainer_tests/test_function_node.py
+++ b/tests/chainer_tests/test_function_node.py
@@ -853,6 +853,22 @@ def test_type_check(self):
             chainer.grad([y], [x], [gx], gy)
 
 
+class TestGradValueCheck(unittest.TestCase):
+
+    def test_length_check(self):
+        x = chainer.Variable(numpy.array(3, numpy.float32))
+        y = chainer.functions.identity(x)
+
+        with self.assertRaises(ValueError):
+            chainer.grad([y], [x], [], [None])
+        with self.assertRaises(ValueError):
+            chainer.grad([y], [x], [None, None], [None])
+        with self.assertRaises(ValueError):
+            chainer.grad([y], [x], [None], [])
+        with self.assertRaises(ValueError):
+            chainer.grad([y], [x], [None], [None, None])
+
+
 class GradTestBase(object):
 
     shape = 3,