Repository: chainer/chainer

Issue:
Pow operator doesn't work with a negative value and an integer
This code doesn't work::

```
>>> x = numpy.array([-1.1], numpy.float32)
>>> v = chainer.Variable(chainer.cuda.to_gpu(x))
>>> vv = v ** 2
array([ 1.21000004], dtype=float32)
>>> vv.backward(chainer.cuda.to_gpu(numpy.array([0.5], numpy.float32)))
>>> v.grad
array([ nan], dtype=float32)
>>> vv.grad
array([ 1.], dtype=float32)
```

Maybe `__powf` function that is used `backward_gpu` method of `PowXxxx` doesn't work for a negative value. `powf` is safe only when another argument is an integer.



Discussion:

Resolving patch:
diff --git a/chainer/functions/basic_math.py b/chainer/functions/basic_math.py
--- a/chainer/functions/basic_math.py
+++ b/chainer/functions/basic_math.py
@@ -298,8 +298,8 @@ def backward_gpu(self, x, gy):
                float* gx0, float* gx1, const float* x0, const float* x1,
                const float* gy
             ''', '''
-               gx0[i] = x1[i] * __powf(x0[i], x1[i] - 1) * gy[i];
-               gx1[i] = __logf(x0[i]) * __powf(x0[i], x1[i]) * gy[i];
+               gx0[i] = x1[i] * powf(x0[i], x1[i] - 1) * gy[i];
+               gx1[i] = __logf(x0[i]) * powf(x0[i], x1[i]) * gy[i];
             ''', 'pow_var_var_bwd')(gx0, gx1, x[0], x[1], gy[0])
         return gx0, gx1
 
@@ -329,7 +329,7 @@ def backward_gpu(self, x, gy):
                    float* gx, const float* x, const float* gy,
                    const float value
                 ''',
-                'gx[i] = value * __powf(x[i], value - 1) * gy[i]',
+                'gx[i] = value * powf(x[i], value - 1) * gy[i]',
                 'pow_var_const_bwd')(gx, x[0], gy[0], self.value)
         else:
             cuda.elementwise(
@@ -337,7 +337,7 @@ def backward_gpu(self, x, gy):
                    float* gx, const float* x, const float* gy,
                    const float* value
                 ''',
-                'gx[i] = value[i] * __powf(x[i], value[i] - 1) * gy[i]',
+                'gx[i] = value[i] * powf(x[i], value[i] - 1) * gy[i]',
                 'pow_var_const_bwd')(gx, x[0], gy[0], self.value)
         return gx,
 
@@ -365,11 +365,11 @@ def forward_gpu(self, x):
         y = cuda.empty_like(x[0])
         if isinstance(self.value, Number):
             cuda.elementwise('float* y, const float* x, const float value',
-                             'y[i] = __powf(value, x[i])',
+                             'y[i] = powf(value, x[i])',
                              'pow_const_var_fwd')(y, x[0], self.value)
         else:
             cuda.elementwise('float* y, const float* x, const float *value',
-                             'y[i] = __powf(value[i], x[i])',
+                             'y[i] = powf(value[i], x[i])',
                              'pow_const_var_fwd')(y, x[0], self.value)
         return y,
 
@@ -386,7 +386,7 @@ def backward_gpu(self, x, gy):
                    float* gx, const float* x, const float* gy,
                    const float value, const float logv
                 ''',
-                'gx[i] = logv * __powf(value, x[i]) * gy[i]',
+                'gx[i] = logv * powf(value, x[i]) * gy[i]',
                 'pow_const_var_bwd')(gx, x[0], gy[0], self.value, logv)
         else:
             cuda.elementwise(
@@ -394,7 +394,7 @@ def backward_gpu(self, x, gy):
                    float* gx, const float* x, const float* gy,
                    const float* value
                 ''',
-                'gx[i] = __logf(value[i]) * __powf(value[i], x[i]) * gy[i]',
+                'gx[i] = __logf(value[i]) * powf(value[i], x[i]) * gy[i]',
                 'pow_const_var_bwd')(gx, x[0], gy[0], self.value)
         return gx,
 
diff --git a/chainer/functions/local_response_normalization.py b/chainer/functions/local_response_normalization.py
--- a/chainer/functions/local_response_normalization.py
+++ b/chainer/functions/local_response_normalization.py
@@ -72,7 +72,7 @@ def forward_gpu(self, x):
             '''float* y, float* scale, const float* x,
                float k, float alpha, float beta''',
             '''scale[i] = k + alpha * scale[i];
-               y[i] = x[i] * __powf(scale[i], -beta);''',
+               y[i] = x[i] * powf(scale[i], -beta);''',
             'lrn_fwd')(self.y, self.scale, x[0], self.k, self.alpha, self.beta)
         return self.y,
 
@@ -88,7 +88,7 @@ def backward_gpu(self, x, gy):
         cuda.elementwise(
             '''float* gx, const float* x, const float* gy, const float* scale,
                float beta, float coeff''',
-            'gx[i] = __powf(scale[i], -beta) * gy[i] - coeff * x[i] * gx[i]',
+            'gx[i] = powf(scale[i], -beta) * gy[i] - coeff * x[i] * gx[i]',
             'lrn_bwd')(gx, x[0], gy[0], self.scale, self.beta,
                        2 * self.alpha * self.beta)
         return gx,


Test patch:
diff --git a/tests/functions_tests/test_basic_math.py b/tests/functions_tests/test_basic_math.py
--- a/tests/functions_tests/test_basic_math.py
+++ b/tests/functions_tests/test_basic_math.py
@@ -872,4 +872,30 @@ def make_data(self):
         return x, gy
 
 
+class TestNegativePow(unittest.TestCase):
+
+    def setUp(self):
+        self.x = numpy.random.uniform(-1, 0, (3, 2)).astype(numpy.float32)
+        self.gy = numpy.random.uniform(-1, 1, (3, 2)).astype(numpy.float32)
+
+    def check_backward(self, x_data, y_grad):
+        x = chainer.Variable(x_data)
+        y = x ** 2
+        y.grad = y_grad
+        y.backward()
+
+        func = y.creator
+        f = lambda: func.forward((x.data,))
+        gx, = gradient_check.numerical_grad(f, (x.data,), (y.grad,))
+
+        gradient_check.assert_allclose(gx, x.grad, atol=1e-4, rtol=1e-4)
+
+    def test_cpu(self):
+        self.check_backward(self.x, self.gy)
+
+    @attr.gpu
+    def test_gpu(self):
+        self.check_backward(cuda.to_gpu(self.x), cuda.to_gpu(self.gy))
+
+
 testing.run_module(__name__, __file__)