Repository: chainer/chainer

Issue:
F.broadcast forbids broadcasting to size 0 arrays.
* Conditions
```
>>> chainer.print_runtime_info()
Chainer: 4.0.0
NumPy: 1.14.2
CuPy: Not Available
```
* Code to reproduce
`chainer.functions.broadcast(chainer.Variable(np.ones(0, 'f')), chainer.Variable(np.ones(1, 'f')))`
* Error messages, stack traces, or logs
```
Python 3.6.5 (default, Mar 30 2018, 06:41:53)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import chainer; import numpy as np
>>> chainer.functions.broadcast(chainer.Variable(np.ones(2, 'f')), chainer.Variable(np.ones(1, 'f')))
(variable([1., 1.]), variable([1., 1.]))
>>> chainer.functions.broadcast(chainer.Variable(np.ones(0, 'f')), chainer.Variable(np.ones(1, 'f')))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/tos/py3/lib/python3.6/site-packages/chainer/functions/array/broadcast.py", line 79, in broadcast
    return Broadcast().apply(args)
  File "/Users/tos/py3/lib/python3.6/site-packages/chainer/function_node.py", line 243, in apply
    self._check_data_type_forward(in_data)
  File "/Users/tos/py3/lib/python3.6/site-packages/chainer/function_node.py", line 328, in _check_data_type_forward
    self.check_type_forward(in_type)
  File "/Users/tos/py3/lib/python3.6/site-packages/chainer/functions/array/broadcast.py", line 37, in check_type_forward
    raise type_check.InvalidType(expect, actual)
chainer.utils.type_check.InvalidType:
Invalid operation is performed in: Broadcast (Forward)

Expect: each dimension has the same size or is 1
Actual: shapes: (0,), (1,)
>>>
```



Discussion:

Resolving patch:
diff --git a/chainer/functions/array/broadcast.py b/chainer/functions/array/broadcast.py
--- a/chainer/functions/array/broadcast.py
+++ b/chainer/functions/array/broadcast.py
@@ -13,15 +13,8 @@ class Broadcast(function_node.FunctionNode):
     def check_type_forward(self, in_types):
         type_check.expect(in_types.size() > 0)
 
-        shapes = [type_check.eval(t).shape for t in in_types]
-        r_shapes = [s[::-1] for s in shapes]
-        r_filled = six.moves.zip_longest(*r_shapes, fillvalue=1)
-        for ss in r_filled:
-            d = max(ss)
-            if not all(s == d or s == 1 for s in ss):
-                expect = 'each dimension has the same size or is 1'
-                actual = 'shapes: ' + ', '.join(map(str, shapes))
-                raise type_check.InvalidType(expect, actual)
+        shapes = [t.shape for t in in_types]
+        type_check.expect_broadcast_shapes(*shapes)
 
     def forward(self, inputs):
         self._xp = cuda.get_array_module(*inputs)


Test patch:
diff --git a/tests/chainer_tests/functions_tests/array_tests/test_broadcast.py b/tests/chainer_tests/functions_tests/array_tests/test_broadcast.py
--- a/tests/chainer_tests/functions_tests/array_tests/test_broadcast.py
+++ b/tests/chainer_tests/functions_tests/array_tests/test_broadcast.py
@@ -23,6 +23,7 @@
         {'in_shapes': [(3, 2, 4)], 'out_shape': (3, 2, 4)},
         {'in_shapes': [(3, 1, 4), (1, 2, 4), (3, 2, 1)],
          'out_shape': (3, 2, 4)},
+        {'in_shapes': [(1, 0, 1), (2,)], 'out_shape': (1, 0, 2)},
     ],
     [
         {'dtype': numpy.float16},