summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/random/common.pxd2
-rw-r--r--numpy/random/common.pyx10
-rw-r--r--numpy/random/pcg32.pyx20
-rw-r--r--numpy/random/pcg64.pyx22
-rw-r--r--numpy/random/philox.pyx8
-rw-r--r--numpy/random/tests/test_direct.py14
-rw-r--r--numpy/random/threefry.pyx8
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)