There are three strategies implemented that can be used to produce repeatable pseudo-random numbers across multiple processes (local or distributed).
Currently only pcg32
and pcg64
support independent streams, and,
due to the limited period of pcg32
(\(2^{64}\)), only pcg64
should be used. This example shows how many streams can be created by
passing in different index values in the second input while using the
same seed in the first.
from randomstate.entropy import random_entropy
import randomstate.prng.pcg64 as pcg64
entropy = random_entropy(4)
# 128-bit number as a seed
seed = reduce(lambda x, y: x + y, [long(entropy[i]) * 2 ** (32 * i) for i in range(4)])
streams = [pcg64.RandomState(seed, stream) for stream in range(10)]
jump
advances the state of the PRNG as-if a large number of random
numbers have been drawn. The specific number of draws varies by PRNG, and
ranges from \(2^{64}\) to \(2^{512}\). Additionally, the as-if
draws also depend on the size of the default random number produced by the
specific PRNG. The PRNGs that support jump
, along with the period of
the PRNG, the size of the jump and the bits in the default unsigned random
are listed below.
PRNG | Period | Jump Size | Bits |
---|---|---|---|
dsfmt | \(2^{19937}\) | \(2^{128}\) | 53 |
mrg32k3a | \(\approx 2^{191}\) | \(2^{127}\) | 32 |
xorshift128 | \(2^{128}\) | \(2^{64}\) | 64 |
xorshift1024 | \(2^{1024}\) | \(2^{512}\) | 64 |
mt19937 | \(2^{19937}\) | \(2^{128}\) | 32 |
jump
can be used to produce long blocks which should be long enough to not
overlap.
from randomstate.entropy import random_entropy
import randomstate.prng.xorshift1024 as xorshift1024
entropy = random_entropy(2).astype(np.uint64)
# 64-bit number as a seed
seed = entropy[0] * 2**32 + entropy[1]
blocks = []
for i in range(10):
block = xorshift1024.RandomState(seed)
block.jump(i)
blocks.append(block)
advance
can be used to jump the state an arbitrary number of steps, and so
is a more general approach than jump
. Only pcg32
and pcg64
support advance
, and since these also support independent streams, it is
not usually necessary to use advance
.
Advancing a PRNG updates the underlying PRNG state as-if a given number of calls to the underlying PRNG have been made. In general there is not a one-to-one relationship between the number output random values from a particular distribution and the number of draws from the core PRNG. This occurs for two reasons:
Advancing the PRNG state resets any pre-computed random numbers. This is required to ensure exact reproducibility.
This example uses advance
to advance a pcg64
generator 2 ** 127
steps to set a sequence of random number generators.
import randomstate.prng.pcg64 as pcg64
rs = pcg64.RandomState()
rs_gen = pcg64.RandomState()
rs_gen.set_state(rs.get_state())
advance = 2**127
rngs = [rs]
for _ in range(9):
rs_gen.advance(advance)
rng = pcg64.RandomState()
rng.set_state(rs_gen.get_state())
rngs.append(rng)