Repository: chainer/chainer

Issue:
generateNormal and generateNormalDouble raise errors when size is odd
When `curand.generateNormal` is called with argument `size` odd (as long as I investigated).
For example, the following code fails.

```
In [84]: generator = curand.createGenerator(curand.CURAND_RNG_PSEUDO_DEFAULT)
In [85]: curand.generateNormal(generator, out.data.ptr, 1, 0.0, 1.0)
```

The error message is as follows:

```
CURANDError                               Traceback (most recent call last)
<ipython-input-85-e3c65b1a19eb> in <module>()
----> 1 curand.generateNormal(generator, out.data.ptr, 1, 0.0, 1.0)

/home/delta/dev/chainer/cupy/cuda/curand.py in generateNormal(generator, outputPtr, n, mean, stddev)
    185     status = _curand.curandGenerateNormal(generator, outputPtr, n, mean,
    186                                           stddev)
--> 187     check_status(status)
    188 
    189 

/home/delta/dev/chainer/cupy/cuda/curand.py in check_status(status)
     77 def check_status(status):
     78     if status != 0:
---> 79         raise CURANDError(status)
     80 
     81 ###############################################################################

CURANDError: CURAND_STATUS_LENGTH_NOT_MULTIPLE
```

But when `size` is even, no error is raised.

```
In [86]: curand.generateNormal(generator, out.data.ptr, 2, 0.0, 1.0)
```

The situation is true for `curand.generateNormalDouble`.



Discussion:

Resolving patch:
diff --git a/cupy/cuda/curand.py b/cupy/cuda/curand.py
--- a/cupy/cuda/curand.py
+++ b/cupy/cuda/curand.py
@@ -182,6 +182,10 @@ def generateUniformDouble(generator, outputPtr, num):
 
 
 def generateNormal(generator, outputPtr, n, mean, stddev):
+    if n % 2 == 1:
+        msg = 'curandGenerateNormal can only generate even number of '\
+              'random variables simultaneously. See issue #390 for detail.'
+        raise ValueError(msg)
     status = _curand.curandGenerateNormal(generator, outputPtr, n, mean,
                                           stddev)
     check_status(status)
@@ -191,6 +195,10 @@ def generateNormal(generator, outputPtr, n, mean, stddev):
 
 
 def generateNormalDouble(generator, outputPtr, n, mean, stddev):
+    if n % 2 == 1:
+        msg = 'curandGenerateNormalDouble can only generate even number of '\
+              'random variables simultaneously. See issue #390 for detail.'
+        raise ValueError(msg)
     status = _curand.curandGenerateNormalDouble(generator, outputPtr, n, mean,
                                                 stddev)
     check_status(status)


Test patch:
diff --git a/tests/cupy_tests/cuda_tests/test_curand.py b/tests/cupy_tests/cuda_tests/test_curand.py
new file mode 100644
--- /dev/null
+++ b/tests/cupy_tests/cuda_tests/test_curand.py
@@ -0,0 +1,25 @@
+import unittest
+
+import numpy
+
+import cupy
+from cupy.cuda import curand
+
+
+class TestGenerateNormal(unittest.TestCase):
+
+    def setUp(self):
+        self.generator = curand.createGenerator(
+            curand.CURAND_RNG_PSEUDO_DEFAULT)
+
+    def test_invalid_argument_float(self):
+        out = cupy.empty((1,), dtype=numpy.float32)
+        with self.assertRaises(ValueError):
+            curand.generateNormal(
+                self.generator, out.data.ptr, 1, 0.0, 1.0)
+
+    def test_invalid_argument_double(self):
+        out = cupy.empty((1,), dtype=numpy.float64)
+        with self.assertRaises(ValueError):
+            curand.generateNormalDouble(
+                self.generator, out.data.ptr, 1, 0.0, 1.0)