Repository: chainer/chainer

Issue:
<type 'numpy.ndarray'> != <type 'numpy.float32'> error
When I ran a script, the following error occurred.
```
Traceback (most recent call last):
  File "D:\python_codes\chainer1.17jidai\multilayer_SM\opd_best_approx.py", line 46, in <module>
    loss = eq.update()
  File "D:\python_codes\chainer1.17jidai\multilayer_SM\opd_best_approx.py", line 33, in update
    loss.backward()
  File "C:\Python27\lib\site-packages\chainer\variable.py", line 406, in backward
    _check_grad_type(func, x, gx)
  File "C:\Python27\lib\site-packages\chainer\variable.py", line 41, in _check_grad_type
    raise TypeError(make_message(msg))
TypeError: Function `Add` (_ + _) has a bug.

Please report this error to the issue tracker with the stack trace,
the information of your environment, and your script:
https://github.com/pfnet/chainer/issues/new.
Type of data and grad mismatch
<type 'numpy.ndarray'> != <type 'numpy.float32'>
```
The versions of Chainer, Python, and OS are 1.17.0, 2.7.10, Windows 7 (32bit), respectively.
```
import numpy as np
import matplotlib.pyplot as plt
import chainer
from chainer import functions as F
from chainer import links as L
from chainer import \
     cuda, gradient_check, optimizers, serializers, utils, \
     Chain, ChainList, Function, Link, Variable

def samul(sca, arr):
    return F.broadcast(sca, arr)[0] * arr

class Equ(Chain):
    def __init__(self, v): # v: np.array
        self.v = Variable(v.astype(np.float32))
        self.opt = optimizers.Adam()
        # Do I have to declare something optimized here?
        super(Equ,self).__init__(R1 = L.Linear(1,1,nobias=True, initialW=np.array([[0.5]]).astype(np.float32)),
                                R2 = L.Linear(1,1,nobias=True, initialW=np.array([[0.5]]).astype(np.float32)))
        self.opt.setup(self)
    def __call__(self, v, r1, r2): # args: Variable (O,M), scalar, scalar
        q = r1**2 + r2**2
        w = samul(F.arcsin(r1/(2*(1+q))**0.5)/F.arcsin(q/(1+q)),v[:,0:1]) + samul(F.arcsin(r2/(2*(1+q))**0.5)/F.arcsin(q/(1+q)),v[:,1:2])
        e1 = F.matmul(F.transpose(v[:,0:1]), w) - samul(r1 * ((2*(1+q)-r1**2)/(2*q+1))**0.5, F.matmul(F.transpose(w), w))
        e2 = F.matmul(F.transpose(v[:,1:2]), w) - samul(r2 * ((2*(1+q)-r2**2)/(2*q+1))**0.5, F.matmul(F.transpose(w), w))
        return (e1**2 + e2**2)[0,0] # (1,1)->scalar
    def update(self):
        r1 = self.R1(Variable(np.array([[1.0]]).astype(np.float32)))
        r2 = self.R2(Variable(np.array([[1.0]]).astype(np.float32)))
        loss = self(self.v, r1[0,0], r2[0,0])
        self.cleargrads()
        loss.grad = np.ones(loss.data.shape).astype(np.float32)
        loss.backward()
        self.opt.update()
        return loss.data
    def getR1(self):
        return self.R1.W.data[0,0]
    def getR2(self):
        return self.R2.W.data[0,0]


eq = Equ(np.array([[1., 0.5]])) # v: np.array
loss = []
for i in range(10000):
    print('R1=',eq.getR1(), 'R2=',eq.getR2(), 'loss=', loss)
    loss = eq.update()
```


Discussion:
Thank you for reporting. I will edit your comments for users who do not read Japanese.
I updated chainer to 1.19.0, but the error message still appears.
The code below also causes an error message, a bit different from the error message in my first post.
```
import numpy as np
import matplotlib.pyplot as plt
import chainer
from chainer import functions as F
from chainer import links as L
from chainer import \
     cuda, gradient_check, optimizers, serializers, utils, \
     Chain, ChainList, Function, Link, Variable

a = Variable(np.array(1).astype(np.float32))
b = a/(a+1.0)
b.grad = np.ones(b.data.shape).astype(np.float32)
b.backward()
```
The error message is:
```
Traceback (most recent call last):
  File "D:/python_codes/chainer1.17jidai/multilayer_SM/backwardtest.py", line 13, in <module>
    b.backward()
  File "C:\Python27\lib\site-packages\chainer\variable.py", line 419, in backward
    x.grad = x.grad + gx  # copy
  File "C:\Python27\lib\site-packages\chainer\variable.py", line 192, in grad
    _check_grad_type(None, self, g)
  File "C:\Python27\lib\site-packages\chainer\variable.py", line 41, in _check_grad_type
    raise TypeError(make_message(msg))
TypeError: Type of data and grad mismatch
<type 'numpy.ndarray'> != <type 'numpy.float32'>
```
Thank you for reporting a problem. I found that when a user uses zero-dimension array, it causes problem. I'll fix it.

Resolving patch:
diff --git a/chainer/variable.py b/chainer/variable.py
--- a/chainer/variable.py
+++ b/chainer/variable.py
@@ -9,6 +9,7 @@
 import chainer
 from chainer import cuda
 from chainer import flag
+from chainer import utils
 
 
 def _check_grad_type(func, x, gx):
@@ -416,7 +417,7 @@ def add_cand(cand):
                             x.grad = gx
                             need_copy.add(id_x)
                         elif id_x in need_copy:
-                            x.grad = x.grad + gx  # copy
+                            x.grad = utils.force_array(x.grad + gx)  # copy
                             need_copy.remove(id_x)
                         else:
                             x._grad += gx
@@ -427,7 +428,7 @@ def add_cand(cand):
                             seen_vars.add(id_x)
                             need_copy.add(id_x)
                         elif id_x in need_copy:  # 2nd visit
-                            x._grad = gx + x._grad  # copied
+                            x._grad = utils.force_array(gx + x._grad)  # copied
                             need_copy.remove(id_x)
                         else:  # 3rd or later visit
                             x._grad += gx


Test patch:
diff --git a/tests/chainer_tests/test_variable.py b/tests/chainer_tests/test_variable.py
--- a/tests/chainer_tests/test_variable.py
+++ b/tests/chainer_tests/test_variable.py
@@ -34,12 +34,17 @@ def constant(xs, value):
     return Constant(value)(*xs)
 
 
+@testing.parameterize(
+    {'x_shape': (10,), 'c_shape': (2, 5), 'label': '(2, 5), float32'},
+    {'x_shape': (), 'c_shape': (1,), 'label': '(1), float32'},
+)
 class TestVariable(unittest.TestCase):
 
     def setUp(self):
-        self.x = np.random.uniform(-1, 1, 10).astype(np.float32)
-        self.a = np.random.uniform(0.1, 10, 10).astype(np.float32)
-        self.c = np.arange(10).reshape(2, 5).astype(np.float32)
+        self.x = np.random.uniform(-1, 1, self.x_shape).astype(np.float32)
+        self.a = np.random.uniform(0.1, 10, self.x_shape).astype(np.float32)
+        self.size = int(np.prod(self.x_shape))
+        self.c = np.arange(self.size).reshape(self.c_shape).astype(np.float32)
 
     def test_repr(self):
         x = chainer.Variable(self.x, name='x')
@@ -71,7 +76,7 @@ def check_len(self, gpu):
         if gpu:
             x = cuda.to_gpu(x)
         x = chainer.Variable(x)
-        self.assertEqual(len(x), 10)
+        self.assertEqual(len(x), self.size)
 
     def test_len_cpu(self):
         self.check_len(False)
@@ -85,12 +90,13 @@ def check_get_item(self, gpu):
         if gpu:
             x_data = cuda.to_gpu(x_data)
         x = chainer.Variable(x_data)
-        slices = slice(2, 5)
-        np.testing.assert_equal(cuda.to_cpu(x[slices].data),
-                                cuda.to_cpu(x_data[slices]))
-        slices = slice(2, 5),
-        np.testing.assert_equal(cuda.to_cpu(x[slices].data),
-                                cuda.to_cpu(x_data[slices]))
+        if len(self.x_shape) > 0:
+            slices = slice(2, 5)
+            np.testing.assert_equal(cuda.to_cpu(x[slices].data),
+                                    cuda.to_cpu(x_data[slices]))
+            slices = slice(2, 5),
+            np.testing.assert_equal(cuda.to_cpu(x[slices].data),
+                                    cuda.to_cpu(x_data[slices]))
 
     def test_get_item_cpu(self):
         self.check_get_item(False)
@@ -107,11 +113,11 @@ def check_label(self, expected, gpu):
         self.assertEqual(c.label, expected)
 
     def test_label_cpu(self):
-        self.check_label('(2, 5), float32', False)
+        self.check_label(self.label, False)
 
     @attr.gpu
     def test_label_gpu(self):
-        self.check_label('(2, 5), float32', True)
+        self.check_label(self.label, True)
 
     def check_backward(self, inputs, intermediates, outputs, retain_grad):
         for o in outputs:
@@ -145,9 +151,28 @@ def test_backward_cpu(self):
 
     @attr.gpu
     def test_backward_gpu(self):
-        ret = self.create_linear_chain(2, True)
+        ret = self.create_linear_chain(2, False)
         self.check_backward((ret[0], ), (ret[1], ), (ret[2], ), False)
 
+    def check_backward_accumulate(self, gpu):
+        if gpu:
+            x = chainer.Variable(cuda.to_gpu(self.x))
+            xp = cuda.cupy
+        else:
+            x = chainer.Variable(self.x)
+            xp = np
+        y = constant((x, x, x), (self.a, ))
+        y.grad = xp.zeros_like(y.data)
+        y.backward()
+        self.assertEqual(x.grad.shape, self.x_shape)
+
+    def test_backward_accumulate_cpu(self):
+        self.check_backward_accumulate(False)
+
+    @attr.gpu
+    def test_backward_accumulate_gpu(self):
+        self.check_backward_accumulate(True)
+
     def test_backward_cpu_retain_grad(self):
         ret = self.create_linear_chain(2, False)
         self.check_backward((ret[0], ), (ret[1], ), (ret[2], ), True)