ThreeFry Counter-based RNG

class randomgen.threefry.ThreeFry(seed=None, *, counter=None, key=None, number=4, width=64, mode=None)

Container for the ThreeFry family of pseudo-random number generators.

seed{None, int, array_like[uint64], SeedSequence}, optional

Entropy initializing the pseudo-random number generator. Can be an integer in [0, 2**64), array of integers in [0, 2**64), a SeedSequence instance or None (the default). If seed is None, data will be read from /dev/urandom (or the Windows analog) if available. If unavailable, a hash of the time and process ID is used.

counter{None, int, array_like[uint64]}, optional

Counter to use in the ThreeFry state. Can be either a Python int in [0, 2**(N*W)) where N is number of W is the width, or a M-element uint64 array where M = N*W // 64. If not provided, the counter is initialized at 0.

key{None, int, array_like[uint64]}, optional

Key to use in the ThreeFry state. Unlike seed, which is run through another RNG before use, the value in key is directly set. Can be either a Python int in [0, 2**(N*W//2)) or a m-element uint64 array where m = N*W // (2 * 64). If number=2 and width=32, then the value must be in [0, 2**32) even if stored in a uint64 array. key and seed cannot both be used.

number{2, 4}, optional

Number of values to produce in a single call. Maps to N in the ThreeFry variant naming scheme ThreeFryNxW.

width{32, 64}, optional

Bit width the values produced. Maps to W in the ThreeFry variant naming scheme ThreeFryNxW.

mode{None, “sequence”, “legacy”}, optional

The seeding mode to use. “legacy” uses the legacy SplitMix64-based initialization. “sequence” uses a SeedSequence to transforms the seed into an initial state. None defaults to “sequence”.


ThreeFry is a 32 or 64-bit PRNG that uses a counter-based design based on weaker (and faster) versions of cryptographic functions [1]. Instances using different values of the key produce distinct sequences. ThreeFry has a period of \(N*2^{N*W}\) and supports arbitrary advancing and jumping the sequence in increments of \(2^{N*W//2}\). These features allow multiple non-overlapping sequences to be generated.

ThreeFry provides a capsule containing function pointers that produce doubles, and unsigned 32 and 64- bit integers. These are not directly consumable in Python and must be consumed by a Generator or similar object that supports low-level access.

See Philox for a closely related PRNG.

State and Seeding

The ThreeFry state vector consists of a (N*W)-bit value and a (N*W//2)-bit value. These are encoded as an n-element w-bit array. One is a counter which is incremented by 1 for every n w-bit randoms produced. The second is a key which determines the sequence produced. Using different keys produces distinct sequences.

When mode is “legacy”, ThreeFry is seeded using either a single 64-bit unsigned integer or a vector of 64-bit unsigned integers. In either case, the seed is used as an input for a second random number generator, SplitMix64, and the output of this PRNG function is used as the initial state. Using a single 64-bit value for the seed can only initialize a small range of the possible initial state values.

Parallel Features

ThreeFry can be used in parallel applications by calling the jump method to advances the state as-if \(2^{N*W//2}\) random numbers have been generated. Alternatively, advance can be used to advance the counter for any positive step in [0, 2**N*W). When using jump, all generators should be initialized with the same seed to ensure that the segments come from the same sequence.

>>> from numpy.random import Generator
>>> from randomgen import ThreeFry
>>> rg = [Generator(ThreeFry(1234)) for _ in range(10)]
# Advance each ThreeFry instance by i jumps
>>> for i in range(10):
...     rg[i].bit_generator.jump(i)

Alternatively, ThreeFry can be used in parallel applications by using a sequence of distinct keys where each instance uses different key.

>>> key = 2**196 + 2**132 + 2**65 + 2**33 + 2**17 + 2**9
>>> rg = [Generator(ThreeFry(key=key+i)) for i in range(10)]

Compatibility Guarantee

ThreeFry makes a guarantee that a fixed seed and will always produce the same random integer stream.



John K. Salmon, Mark A. Moraes, Ron O. Dror, and David E. Shaw, “Parallel Random Numbers: As Easy as 1, 2, 3,” Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis (SC11), New York, NY: ACM, 2011.


>>> from numpy.random import Generator
>>> from randomgen import ThreeFry
>>> rg = Generator(ThreeFry(1234))
>>> rg.standard_normal()
0.123  # random

Lock instance that is shared so that the same bit git generator can be used in multiple Generators without corrupting the state. Code that generates values from a bit generator should hold the bit generator’s lock.

seed_seq{None, SeedSequence}

The SeedSequence instance used to initialize the generator if mode is “sequence” or is seed is a SeedSequence. None if mode is “legacy”.

Seeding and State

seed([seed, counter, key])

Seed the generator


Get or set the PRNG state

Parallel generation

advance(delta[, counter])

Advance the underlying RNG as-if delta draws have occurred.


Jumps the state as-if 2**(W*N/2) random numbers have been generated.


Returns a new bit generator with the state jumped



CFFI interface


ctypes interface


random_raw([size, output])

Return randoms as generated by the underlying BitGenerator