|
| 1 | +Parallel Random Number Generation |
| 2 | +================================= |
| 3 | + |
| 4 | +There are three strategies implemented that can be used to produce |
| 5 | +repeatable pseudo-random numbers across multiple processes (local |
| 6 | +or distributed). |
| 7 | + |
| 8 | +.. _independent-streams: |
| 9 | + |
| 10 | +Independent Streams |
| 11 | +------------------- |
| 12 | + |
| 13 | +Currently only ``pcg32`` and ``pcg64`` support independent streams, and, |
| 14 | +due to the limited period of ``pcg32`` (:math:`2^{64}`), only ``pcg64`` |
| 15 | +should be used. This example shows how many streams can be created by |
| 16 | +passing in different index values in the second input while using the |
| 17 | +same seed in the first. |
| 18 | + |
| 19 | +:: |
| 20 | + |
| 21 | + from randomstate.entropy import random_entropy |
| 22 | + import randomstate.prng.pcg64 as pcg64 |
| 23 | + |
| 24 | + entropy = random_entropy(4) |
| 25 | + # 128-bit number as a seed |
| 26 | + seed = reduce(lambda x, y: x + y, [long(entropy[i]) * 2 ** (32 * i) for i in range(4)]) |
| 27 | + streams = [pcg64.RandomState(seed, stream) for stream in range(10)] |
| 28 | + |
| 29 | + |
| 30 | +.. _jump-and-advance: |
| 31 | + |
| 32 | +Jump/Advance the PRNG state |
| 33 | +--------------------------- |
| 34 | + |
| 35 | +``jump`` advances the state of the PRNG *as-if* a large number of random |
| 36 | +number have been drawn. The specific number of draws varies by PRNG, and |
| 37 | +ranges from :math:`2^{64}` to :math:`2^{512}`. Additionally, the *as-if* |
| 38 | +draws also depend on the size of the default random number produced by the |
| 39 | +specific PRNG. The PRNGs that support ``jump``, along with the period of |
| 40 | +the PRNG, the size of the jump and the bits in the default unsigned random |
| 41 | +are listed below. |
| 42 | + |
| 43 | ++-----------------+-------------------------+-------------------------+-------------------------+ |
| 44 | +| PRNG | Period | Jump Size | Bits | |
| 45 | ++=================+=========================+=========================+=========================+ |
| 46 | +| dsfmt | :math:`2^{19937}` | :math:`2^{128}` | 53 | |
| 47 | ++-----------------+-------------------------+-------------------------+-------------------------+ |
| 48 | +| mrg32k3a | :math:`\approx 2^{191}` | :math:`2^{127}` | 32 | |
| 49 | ++-----------------+-------------------------+-------------------------+-------------------------+ |
| 50 | +| xorshift128 | :math:`2^{128}` | :math:`2^{64}` | 64 | |
| 51 | ++-----------------+-------------------------+-------------------------+-------------------------+ |
| 52 | +| xorshift1024 | :math:`2^{1024}` | :math:`2^{512}` | 64 | |
| 53 | ++-----------------+-------------------------+-------------------------+-------------------------+ |
| 54 | + |
| 55 | +``jump`` can be used to produce long blocks which should be long enough to not |
| 56 | +overlap. |
| 57 | + |
| 58 | +:: |
| 59 | + |
| 60 | + from randomstate.entropy import random_entropy |
| 61 | + import randomstate.prng.xorshift1024 as xorshift1024 |
| 62 | + |
| 63 | + entropy = random_entropy(2).astype(np.uint64) |
| 64 | + # 64-bit number as a seed |
| 65 | + seed = entropy[0] * 2**32 + entropy[1] |
| 66 | + blocks = [] |
| 67 | + for i in range(10): |
| 68 | + block = xorshift1024.RandomState(seed) |
| 69 | + block.jump(i) |
| 70 | + blocks.append(block) |
| 71 | + |
| 72 | + |
| 73 | +``advance`` can be used to jump the state an arbitrary number of steps, and so |
| 74 | +is a more general approach than ``jump``. Only ``pcg32`` and ``pcg64`` |
| 75 | +support ``advance``, and since these also support independent streams, it is |
| 76 | +not usually necessary to use ``advance``. |
0 commit comments