diff options
author | Julian Taylor <jtaylor.debian@googlemail.com> | 2014-05-05 19:13:28 +0200 |
---|---|---|
committer | Julian Taylor <jtaylor.debian@googlemail.com> | 2014-05-15 02:13:34 +0200 |
commit | 6b1a1205eac6fe5d162f16155d500765e8bca53c (patch) | |
tree | 60944a074113cf726a9d114542cdbea31054c8cb /numpy/random | |
parent | cd0d0579614481076fef473f1fc0c4b13f991b28 (diff) | |
download | numpy-6b1a1205eac6fe5d162f16155d500765e8bca53c.tar.gz |
BUG: reject too large seeds to RandomState
mtrand accepts seeds larger than 32 bit but silently truncates them back
to 32 bit. This can lead to accidentally getting the same random stream
for two different seeds, e.g. 1 and 1 + 2**40.
Diffstat (limited to 'numpy/random')
-rw-r--r-- | numpy/random/mtrand/mtrand.pyx | 11 | ||||
-rw-r--r-- | numpy/random/mtrand/numpy.pxd | 2 | ||||
-rw-r--r-- | numpy/random/tests/test_random.py | 29 |
3 files changed, 40 insertions, 2 deletions
diff --git a/numpy/random/mtrand/mtrand.pyx b/numpy/random/mtrand/mtrand.pyx index 3f9dcb687..c2603543d 100644 --- a/numpy/random/mtrand/mtrand.pyx +++ b/numpy/random/mtrand/mtrand.pyx @@ -628,6 +628,7 @@ cdef class RandomState: ---------- seed : int or array_like, optional Seed for `RandomState`. + Must be convertable to 32 bit unsigned integers. See Also -------- @@ -640,9 +641,15 @@ cdef class RandomState: if seed is None: errcode = rk_randomseed(self.internal_state) else: - rk_seed(operator.index(seed), self.internal_state) + idx = operator.index(seed) + if idx > int(2**32 - 1) or idx < 0: + raise ValueError("Seed must be between 0 and 4294967295") + rk_seed(idx, self.internal_state) except TypeError: - obj = <ndarray>PyArray_ContiguousFromObject(seed, NPY_LONG, 1, 1) + obj = np.asarray(seed).astype(np.int64, casting='safe') + if ((obj > int(2**32 - 1)) | (obj < 0)).any(): + raise ValueError("Seed must be between 0 and 4294967295") + obj = obj.astype('L', casting='unsafe') init_by_array(self.internal_state, <unsigned long *>PyArray_DATA(obj), PyArray_DIM(obj, 0)) diff --git a/numpy/random/mtrand/numpy.pxd b/numpy/random/mtrand/numpy.pxd index 6812cc164..c54f79c0a 100644 --- a/numpy/random/mtrand/numpy.pxd +++ b/numpy/random/mtrand/numpy.pxd @@ -121,6 +121,8 @@ cdef extern from "numpy/arrayobject.h": object PyArray_IterNew(object arr) void PyArray_ITER_NEXT(flatiter it) nogil + dtype PyArray_DescrFromType(int) + void import_array() # include functions that were once macros in the new api diff --git a/numpy/random/tests/test_random.py b/numpy/random/tests/test_random.py index b6c4fe3af..ef4e7b127 100644 --- a/numpy/random/tests/test_random.py +++ b/numpy/random/tests/test_random.py @@ -7,6 +7,35 @@ from numpy.testing import ( from numpy import random from numpy.compat import asbytes +class TestSeed(TestCase): + def test_scalar(self): + s = np.random.RandomState(0) + assert_equal(s.randint(1000), 684) + s = np.random.RandomState(4294967295) + assert_equal(s.randint(1000), 419) + + def test_array(self): + s = np.random.RandomState(range(10)) + assert_equal(s.randint(1000), 468) + s = np.random.RandomState(np.arange(10)) + assert_equal(s.randint(1000), 468) + s = np.random.RandomState([0]) + assert_equal(s.randint(1000), 973) + s = np.random.RandomState([4294967295]) + assert_equal(s.randint(1000), 265) + + def test_invalid_scalar(self): + # seed must be a unsigned 32 bit integers + assert_raises(TypeError, np.random.RandomState, -0.5) + assert_raises(ValueError, np.random.RandomState, -1) + + def test_invalid_array(self): + # seed must be a unsigned 32 bit integers + assert_raises(TypeError, np.random.RandomState, [-0.5]) + assert_raises(ValueError, np.random.RandomState, [-1]) + assert_raises(ValueError, np.random.RandomState, [4294967296]) + assert_raises(ValueError, np.random.RandomState, [1, 2, 4294967296]) + assert_raises(ValueError, np.random.RandomState, [1, -2, 4294967296]) class TestBinomial(TestCase): def test_n_zero(self): |