Repository: cupy/cupy

Issue:
cupy.random.gamma returns the same values.
After the 2nd call of the `cupy.random.gamma` function, it always returns the same value as follows.
Does anyone know the reason why this happens?

* Code to reproduce
   ```
   import cupy as cp

   print(cp.random.gamma(1, 1)) 
   print(cp.random.gamma(1, 1)) 
   print(cp.random.gamma(1, 1)) 
   print(cp.random.gamma(1, 1)) 
   ```
   ```
   0.7588770516300537
   2.2603186238781365
   2.2603186238781365
   2.2603186238781365
   ```
   (Edited: I pasted the wrong results)
* Conditions:
  - CuPy version: 5.0.0
  - OS/Platform: Ubuntu 16.04, Python 3.7.0
  - CUDA version: 9.2


Discussion:
**This is not the answer.**

I checked this phenomenon also happened in my environment.
But I found this could be avoided by setting random seed.

```bash
Python 3.6.6 (default, Dec 20 2018, 14:31:26)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import cupy; print(cupy.__version__)
6.0.0b2

In [2]: cupy.random.gamma(1, 1)  # Reproduce the phenomenon you found.
Out[2]: array(2.18394268)

In [3]: cupy.random.gamma(1, 1)
Out[3]: array(0.35274883)

In [4]: cupy.random.gamma(1, 1)
Out[4]: array(0.35274883)

In [5]: cupy.random.gamma(1, 1)
Out[5]: array(0.35274883)

In [6]: cupy.random.gamma(1, 1)
Out[6]: array(0.35274883)

In [7]: cupy.random.seed(42)  # set random seed here

In [8]: cupy.random.gamma(1, 1)
Out[8]: array(0.05074151)

In [9]: cupy.random.gamma(1, 1)
Out[9]: array(0.51184208)

In [10]: cupy.random.gamma(1, 1)
Out[10]: array(0.12552692)

In [11]: cupy.random.gamma(1, 1)
Out[11]: array(3.36554147)

In [12]: cupy.random.gamma(1, 1)
Out[12]: array(3.20557046)
```
This is very simple bug.

`cupy.random.generator.RandomState` internally has a seed (key) for a hash function as `.rk_seed`.

However, the initial value is set very huge `float64`, and `rk_seed` is refreshed by adding `1` (or the number of sample drawn), the `rk_seed` is not refreshed actually.

https://github.com/cupy/cupy/blob/4c14b75cb9c755572112c7b85c65f2e36b1afffe/cupy/random/generator.py#L185

You can see that if you draw very large number of random values, subsequently the returned value changes.

```
>>> rs.gamma(1, 1, None, float)
array(0.45322576)
>>> rs.gamma(1, 1, None, float)
array(0.45322576)
>>> rs.gamma(1, 1, 100000, float)
array([0.45322576, 3.13516939, 1.47352404, ..., 0.10493381, 0.60155031,
       0.34489441])
>>> rs.gamma(1, 1, None, float)
array(0.29594084)
```

Resolving patch:
diff --git a/cupy/random/_kernels.py b/cupy/random/_kernels.py
--- a/cupy/random/_kernels.py
+++ b/cupy/random/_kernels.py
@@ -840,7 +840,7 @@
      rk_standard_exponential_definition, rk_standard_gamma_definition,
      rk_standard_t_definition]
 standard_t_kernel = core.ElementwiseKernel(
-    'S df, uint32 seed', 'Y y',
+    'S df, uint64 seed', 'Y y',
     '''
     rk_seed(seed + i, &internal_state);
     y = rk_standard_t(&internal_state, df);
@@ -855,7 +855,7 @@
      rk_standard_exponential_definition, rk_standard_gamma_definition,
      rk_chisquare_definition]
 chisquare_kernel = core.ElementwiseKernel(
-    'T df, uint32 seed', 'Y y',
+    'T df, uint64 seed', 'Y y',
     '''
     rk_seed(seed + i, &internal_state);
     y = rk_chisquare(&internal_state, df);
@@ -870,7 +870,7 @@
      rk_standard_exponential_definition, rk_standard_gamma_definition,
      rk_chisquare_definition, rk_f_definition]
 f_kernel = core.ElementwiseKernel(
-    'S dfnum, T dfden, uint32 seed', 'Y y',
+    'S dfnum, T dfden, uint64 seed', 'Y y',
     '''
     rk_seed(seed + i, &internal_state);
     y = rk_f(&internal_state, dfnum, dfden);
@@ -883,7 +883,7 @@
 definitions = \
     [rk_basic_definition, rk_geometric_definition]
 geometric_kernel = core.ElementwiseKernel(
-    'T p, uint32 seed', 'Y y',
+    'T p, uint64 seed', 'Y y',
     '''
     rk_seed(seed + i, &internal_state);
     y = rk_geometric(&internal_state, p);
@@ -897,7 +897,7 @@
     [rk_basic_definition, loggam_definition, long_min_max_definition,
      rk_hypergeometric_definition]
 hypergeometric_kernel = core.ElementwiseKernel(
-    'S good, T bad, U sample, uint32 seed', 'Y y',
+    'S good, T bad, U sample, uint64 seed', 'Y y',
     '''
     rk_seed(seed + i, &internal_state);
     y = rk_hypergeometric(&internal_state, good, bad, sample);
@@ -910,7 +910,7 @@
 definitions = \
     [rk_basic_definition, rk_logseries_definition]
 logseries_kernel = core.ElementwiseKernel(
-    'T p, uint32 seed', 'Y y',
+    'T p, uint64 seed', 'Y y',
     '''
     rk_seed(seed + i, &internal_state);
     y = rk_logseries(&internal_state, p);
@@ -959,7 +959,7 @@
      rk_poisson_mult_definition, rk_poisson_ptrs_definition,
      rk_poisson_definition]
 poisson_kernel = core.ElementwiseKernel(
-    'T lam, uint32 seed', 'Y y',
+    'T lam, uint64 seed', 'Y y',
     '''
     rk_seed(seed + i, &internal_state);
     y = rk_poisson(&internal_state, lam);
diff --git a/cupy/random/generator.py b/cupy/random/generator.py
--- a/cupy/random/generator.py
+++ b/cupy/random/generator.py
@@ -87,7 +87,7 @@ def beta(self, a, b, size=None, dtype=float):
             size = cupy.broadcast(a, b).shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.beta_kernel(a, b, self.rk_seed, y)
-        self.rk_seed += y.size
+        self.rk_seed += numpy.prod(y.size, dtype=self.rk_seed.dtype)
         return y
 
     def binomial(self, n, p, size=None, dtype=int):
@@ -102,7 +102,7 @@ def binomial(self, n, p, size=None, dtype=int):
             size = cupy.broadcast(n, p).shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.binomial_kernel(n, p, self.rk_seed, y)
-        self.rk_seed += y.size
+        self.rk_seed += numpy.prod(y.size, dtype=self.rk_seed.dtype)
         return y
 
     def chisquare(self, df, size=None, dtype=float):
@@ -117,7 +117,7 @@ def chisquare(self, df, size=None, dtype=float):
             size = df.shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.chisquare_kernel(df, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def dirichlet(self, alpha, size=None, dtype=float):
@@ -135,7 +135,7 @@ def dirichlet(self, alpha, size=None, dtype=float):
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.standard_gamma_kernel(alpha, self.rk_seed, y)
         y /= y.sum(axis=-1, keepdims=True)
-        self.rk_seed += cupy.core.internal.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def exponential(self, scale=1.0, size=None, dtype=float):
@@ -166,7 +166,7 @@ def f(self, dfnum, dfden, size=None, dtype=float):
             size = cupy.broadcast(dfnum, dfden).shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.f_kernel(dfnum, dfden, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def gamma(self, shape, scale=1.0, size=None, dtype=float):
@@ -182,7 +182,7 @@ def gamma(self, shape, scale=1.0, size=None, dtype=float):
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.standard_gamma_kernel(shape, self.rk_seed, y)
         y *= scale
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def geometric(self, p, size=None, dtype=int):
@@ -197,7 +197,7 @@ def geometric(self, p, size=None, dtype=int):
             size = p.shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.geometric_kernel(p, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def hypergeometric(self, ngood, nbad, nsample, size=None, dtype=int):
@@ -213,7 +213,7 @@ def hypergeometric(self, ngood, nbad, nsample, size=None, dtype=int):
             size = cupy.broadcast(ngood, nbad, nsample).shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.hypergeometric_kernel(ngood, nbad, nsample, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     _laplace_kernel = core.ElementwiseKernel(
@@ -248,7 +248,7 @@ def logistic(self, loc=0.0, scale=1.0, size=None, dtype=float):
             size = cupy.broadcast(loc, scale).shape
         x = cupy.empty(shape=size, dtype=dtype)
         _kernels.open_uniform_kernel(self.rk_seed, x)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         x = (1.0 - x) / x
         cupy.log(x, out=x)
         cupy.multiply(x, scale, out=x)
@@ -287,7 +287,7 @@ def logseries(self, p, size=None, dtype=int):
             size = p.shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.logseries_kernel(p, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def multivariate_normal(self, mean, cov, size=None, check_valid='ignore',
@@ -405,7 +405,7 @@ def noncentral_chisquare(self, df, nonc, size=None, dtype=float):
             size = cupy.broadcast(df, nonc).shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.noncentral_chisquare_kernel(df, nonc, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def noncentral_f(self, dfnum, dfden, nonc, size=None, dtype=float):
@@ -427,7 +427,7 @@ def noncentral_f(self, dfnum, dfden, nonc, size=None, dtype=float):
             size = cupy.broadcast(dfnum, dfden, nonc).shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.noncentral_f_kernel(dfnum, dfden, nonc, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def poisson(self, lam=1.0, size=None, dtype=int):
@@ -442,7 +442,7 @@ def poisson(self, lam=1.0, size=None, dtype=int):
             size = lam.shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.poisson_kernel(lam, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def power(self, a, size=None, dtype=float):
@@ -664,7 +664,7 @@ def standard_gamma(self, shape, size=None, dtype=float):
             size = shape.shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.standard_gamma_kernel(shape, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def standard_normal(self, size=None, dtype=float):
@@ -689,7 +689,7 @@ def standard_t(self, df, size=None, dtype=float):
             size = df.shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.standard_t_kernel(df, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def tomaxint(self, size=None):
@@ -792,7 +792,7 @@ def vonmises(self, mu, kappa, size=None, dtype=float):
             size = cupy.broadcast(mu, kappa).shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.vonmises_kernel(mu, kappa, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     _wald_kernel = core.ElementwiseKernel(
@@ -853,7 +853,7 @@ def zipf(self, a, size=None, dtype=int):
             size = a.shape
         y = cupy.empty(shape=size, dtype=dtype)
         _kernels.zipf_kernel(a, self.rk_seed, y)
-        self.rk_seed += numpy.prod(size)
+        self.rk_seed += numpy.prod(size, dtype=self.rk_seed.dtype)
         return y
 
     def choice(self, a, size=None, replace=True, p=None):


Test patch: