diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/random/common.pxd | 2 | ||||
-rw-r--r-- | numpy/random/common.pyx | 10 | ||||
-rw-r--r-- | numpy/random/pcg32.pyx | 20 | ||||
-rw-r--r-- | numpy/random/pcg64.pyx | 22 | ||||
-rw-r--r-- | numpy/random/philox.pyx | 8 | ||||
-rw-r--r-- | numpy/random/tests/test_direct.py | 14 | ||||
-rw-r--r-- | numpy/random/threefry.pyx | 8 |
7 files changed, 70 insertions, 14 deletions
diff --git a/numpy/random/common.pxd b/numpy/random/common.pxd index 4636fd2f0..4508c90be 100644 --- a/numpy/random/common.pxd +++ b/numpy/random/common.pxd @@ -74,6 +74,8 @@ cdef object float_fill(void *func, bitgen_t *state, object size, object lock, ob cdef object float_fill_from_double(void *func, bitgen_t *state, object size, object lock, object out) +cdef object wrap_int(object val, object bits) + cdef np.ndarray int_to_array(object value, object name, object bits, object uint_size) cdef object cont(void *func, void *state, object size, object lock, int narg, diff --git a/numpy/random/common.pyx b/numpy/random/common.pyx index 0fd2d154a..f1af4986b 100644 --- a/numpy/random/common.pyx +++ b/numpy/random/common.pyx @@ -182,6 +182,16 @@ cdef double kahan_sum(double *darr, np.npy_intp n): sum = t return sum + +cdef object wrap_int(object val, object bits): + """Wraparound to place an integer into the interval [0, 2**bits)""" + upper = int(2)**int(bits) + if not 0<= val < upper: + divisor = val // upper + val = val - upper * divisor + return val + + cdef np.ndarray int_to_array(object value, object name, object bits, object uint_size): """Convert a large integer to an array of unsigned integers""" len = bits // uint_size diff --git a/numpy/random/pcg32.pyx b/numpy/random/pcg32.pyx index b57f8e04e..407e5057e 100644 --- a/numpy/random/pcg32.pyx +++ b/numpy/random/pcg32.pyx @@ -248,15 +248,20 @@ cdef class PCG32: cdef jump_inplace(self, iter): """ Jump state in-place - + Not part of public API - + Parameters ---------- iter : integer, positive Number of times to jump the state of the rng. + + Notes + ----- + The step size is phi when divided by the period 2**64 """ - self.advance(iter * 2**32) + step = int(0x9e3779b97f4a7c16) + self.advance(iter * step) def jumped(self, iter=1): """ @@ -264,8 +269,8 @@ cdef class PCG32: Returns a new bit generator with the state jumped - The state of the returned big generator is jumped as-if - 2**(32 * iter) random numbers have been generated. + Jumps the state as-if 11400714819323198486 random numbers + have been generated. Parameters ---------- @@ -276,6 +281,10 @@ cdef class PCG32: ------- bit_generator : PCG32 New instance of generator jumped iter times + + Notes + ----- + The jump size is phi-1 when divided by the period 2**64 """ cdef PCG32 bit_generator @@ -344,6 +353,7 @@ cdef class PCG32: RNG. For example, two 16-bit integer values can be simulated from a single draw of a 32-bit RNG. """ + delta = wrap_int(delta, 64) pcg32_advance_state(&self.rng_state, <uint64_t>delta) return self diff --git a/numpy/random/pcg64.pyx b/numpy/random/pcg64.pyx index 7610fb203..712c4c932 100644 --- a/numpy/random/pcg64.pyx +++ b/numpy/random/pcg64.pyx @@ -264,8 +264,17 @@ cdef class PCG64: ---------- iter : integer, positive Number of times to jump the state of the rng. + + Notes + ----- + The step size is phi-1 when divided by 2**128 where phi is the + golden number. """ - self.advance(iter * 2**64) + step = 0x9e3779b97f4a7c15f39cc0605cedc834 + step *= int(iter) + divisor = step // 2**128 + step -= 2**128 * divisor + self.advance(step) def jumped(self, iter=1): """ @@ -273,8 +282,8 @@ cdef class PCG64: Returns a new bit generator with the state jumped - The state of the returned big generator is jumped as-if - 2**(64 * iter) random numbers have been generated. + Jumps the state as-if 210306068529402873165736369884012333108 + random numbers have been generated. Parameters ---------- @@ -285,6 +294,11 @@ cdef class PCG64: ------- bit_generator : PCG64 New instance of generator jumped iter times + + Notes + ----- + The step size is phi-1 when divided by 2**128 where phi is the + golden number. """ cdef PCG64 bit_generator @@ -379,6 +393,8 @@ cdef class PCG64: Advancing the RNG state resets any pre-computed random numbers. This is required to ensure exact reproducibility. """ + delta = wrap_int(delta, 128) + cdef np.ndarray d = np.empty(2, dtype=np.uint64) d[0] = delta // 2**64 d[1] = delta % 2**64 diff --git a/numpy/random/philox.pyx b/numpy/random/philox.pyx index 45b4087ac..830e63e38 100644 --- a/numpy/random/philox.pyx +++ b/numpy/random/philox.pyx @@ -343,7 +343,7 @@ cdef class Philox: self.rng_state.uinteger = value['uinteger'] self.rng_state.buffer_pos = value['buffer_pos'] - cdef jump_inplace(self, np.npy_intp iter): + cdef jump_inplace(self, iter): """ Jump state in-place @@ -354,9 +354,9 @@ cdef class Philox: iter : integer, positive Number of times to jump the state of the rng. """ - self.advance(iter * 2 ** 128) + self.advance(iter * int(2 ** 128)) - def jumped(self, np.npy_intp iter=1): + def jumped(self, iter=1): """ jumped(iter=1) @@ -419,6 +419,8 @@ cdef class Philox: Advancing the RNG state resets any pre-computed random numbers. This is required to ensure exact reproducibility. """ + delta = wrap_int(delta, 256) + cdef np.ndarray delta_a delta_a = int_to_array(delta, 'step', 256, 64) philox_advance(<uint64_t *> delta_a.data, &self.rng_state) diff --git a/numpy/random/tests/test_direct.py b/numpy/random/tests/test_direct.py index 4ab521d1b..722d5e680 100644 --- a/numpy/random/tests/test_direct.py +++ b/numpy/random/tests/test_direct.py @@ -420,6 +420,20 @@ class TestPCG64(Base): [2 ** (2 * self.bits + 1)]) assert_raises(self.seed_error_type, rs.bit_generator.seed, [-1]) + def test_advance_symmetry(self): + rs = Generator(self.bit_generator(*self.data1['seed'])) + state = rs.bit_generator.state + step = -0x9e3779b97f4a7c150000000000000000 + rs.bit_generator.advance(step) + val_neg = rs.integers(10) + rs.bit_generator.state = state + rs.bit_generator.advance(2**128 + step) + val_pos = rs.integers(10) + rs.bit_generator.state = state + rs.bit_generator.advance(10 * 2**128 + step) + val_big = rs.integers(10) + assert val_neg == val_pos + assert val_big == val_pos class TestPCG32(TestPCG64): @classmethod diff --git a/numpy/random/threefry.pyx b/numpy/random/threefry.pyx index 5e5b9cf7a..8c349b2e1 100644 --- a/numpy/random/threefry.pyx +++ b/numpy/random/threefry.pyx @@ -337,7 +337,7 @@ cdef class ThreeFry: self.rng_state.uinteger = value['uinteger'] self.rng_state.buffer_pos = value['buffer_pos'] - cdef jump_inplace(self, np.npy_intp iter): + cdef jump_inplace(self, iter): """ Jump state in-place @@ -348,9 +348,9 @@ cdef class ThreeFry: iter : integer, positive Number of times to jump the state of the rng. """ - self.advance(iter * 2 ** 128) + self.advance(iter * int(2**128)) - def jumped(self, np.npy_intp iter=1): + def jumped(self, iter=1): """ jumped(iter=1) @@ -413,6 +413,8 @@ cdef class ThreeFry: Advancing the RNG state resets any pre-computed random numbers. This is required to ensure exact reproducibility. """ + delta = wrap_int(delta, 256) + cdef np.ndarray delta_a delta_a = int_to_array(delta, 'step', 256, 64) threefry_advance(<uint64_t *>delta_a.data, &self.rng_state) |