Repository: chainer/chainer

Issue:
Bug: Reshape() with -1 fails
When calling the chainer function Reshape() with a -1 value (e.g. Reshape((-1, 10, 10))) I get an error: 

```
InvalidType: Expect: prod(in_types[0].shape) == prod((-1, 10, 10))
Actual: 2800 != -100
```

The cupy function reshape does support -1 arguments, so the error seems to be just the check in chainer's Reshape. The problematic check is:
   _type_check_prod(in_types[0].shape) == _type_check_prod(self.shape)

If a -1 argument is present, it should instead check if the size of in_types[0] is divisible by the product of the axis-lengths that are not -1. 

I would send a PR but I'm not exactly clear on the correct usage of type_check.expect, and had some issues with it in the past. Should be simple enough to fix though.



Discussion:
Thank you for reporting a problem. As you say, `Reshape` can easily support -1 value.
I'll fix its type check method.


Resolving patch:
diff --git a/chainer/functions/array/reshape.py b/chainer/functions/array/reshape.py
--- a/chainer/functions/array/reshape.py
+++ b/chainer/functions/array/reshape.py
@@ -1,10 +1,12 @@
-import numpy
-
 from chainer import function
 from chainer.utils import type_check
 
 
-_type_check_prod = type_check.Variable(numpy.prod, 'prod')
+def _count_unknown_dims(shape):
+    cnt = 0
+    for dim in shape:
+        cnt += dim < 0
+    return cnt
 
 
 class Reshape(function.Function):
@@ -12,15 +14,32 @@ class Reshape(function.Function):
     """Reshapes an input array without copy."""
 
     def __init__(self, shape):
+        cnt = _count_unknown_dims(shape)
+        assert cnt == 0 or cnt == 1
+
         self.shape = shape
 
     def check_type_forward(self, in_types):
         type_check.expect(
             in_types.size() == 1,
-            _type_check_prod(in_types[0].shape) ==
-            _type_check_prod(self.shape)
         )
 
+        x_type, = in_types
+
+        cnt = _count_unknown_dims(self.shape)
+        if cnt == 0:
+            type_check.expect(
+                type_check.prod(x_type.shape) == type_check.prod(self.shape))
+        else:
+            known_size = 1
+            for s in self.shape:
+                if s > 0:
+                    known_size *= s
+            size_var = type_check.Variable(known_size,
+                                           'known_size(=%d)' % known_size)
+            type_check.expect(
+                type_check.prod(x_type.shape) % size_var == 0)
+
     def forward(self, x):
         return x[0].reshape(self.shape),
 


Test patch:
diff --git a/tests/chainer_tests/functions_tests/array_tests/test_reshape.py b/tests/chainer_tests/functions_tests/array_tests/test_reshape.py
--- a/tests/chainer_tests/functions_tests/array_tests/test_reshape.py
+++ b/tests/chainer_tests/functions_tests/array_tests/test_reshape.py
@@ -10,13 +10,14 @@
 
 
 class TestReshape(unittest.TestCase):
+    out_shape = (2, 2, 6)
 
     def setUp(self):
         self.x = numpy.random.uniform(-1, 1, (4, 3, 2)).astype(numpy.float32)
         self.gy = numpy.random.uniform(-1, 1, (2, 2, 6)).astype(numpy.float32)
 
     def check_forward(self, x_data):
-        shape = self.gy.shape
+        shape = self.out_shape
         x = chainer.Variable(x_data)
         y = functions.reshape(x, shape)
         self.assertEqual(y.data.dtype, numpy.float32)
@@ -39,4 +40,8 @@ def check_backward(self, x_data, y_grad):
         self.assertTrue((self.gy.reshape(shape) == cuda.to_cpu(x.grad)).all())
 
 
+class TestReshapeUnknownDimension(TestReshape):
+    out_shape = (2, -1, 6)
+
+
 testing.run_module(__name__, __file__)