RandomGen

This package contains replacements for the NumPy RandomState object that allows the core random number generator be be changed.

Quick Start

Like numpy.random, RandomGen can be used at the module level. This uses the default RandomGenerator which uses normals provided by Xoroshiro128.

# As replacement for numpy.random
import randomgen.generator as random
random.standard_normal()

RandomGenerator can also be used as a replacement for RandomState, although the random values are generated by Xoroshiro128. It also isn’t possible to directly seed a RandomGenerator.

# As replacement for RandomState()
from randomgen import RandomGenerator
rg = RandomGenerator()
rg.standard_normal()

Seeds can be passed to any of the basic RNGs. Here MT19937 is used and the RandomGenerator is accessed via the property generator.

from randomgen import MT19937
rg = MT19937(12345).generator
rg.standard_normal()

Introduction

RandomGen takes a different approach to producing random numbers from the numpy.random.RandomState object used in NumPy. Random number generation is separated into two components, a basic RNG and a random generator.

The basic RNG has a limited set of responsibilities – it manages the underlying RNG state and provides functions to produce random doubles and random unsigned 32- and 64-bit values. The basic random generator also handles all seeding since this varies when using alternative basic RNGs.

The random generator (RandomGenerator) takes the basic RNG-provided functions and transforms them into more useful distributions, e.g., simulated normal random values. This structure allows alternative basic RNGs to be used without code duplication.

The RandomGenerator is the user-facing object that is nearly identical to RandomState. The canonical method to initialize a generator passes a basic RNG – MT19937, the underlying RNG in NumPy – as the sole argument. Note that the basic RNG must be instantized.

from randomgen import RandomGenerator, MT19937
rg = RandomGenerator(MT19937())
rg.random_sample()

Seed information is directly passed to the basic RNG.

rg = RandomGenerator(MT19937(12345))
rg.random_sample()

A shorthand method is also available which uses the generator() property from a basic RNG to access an embedded random generator.

rg = MT19937(12345).generator
rg.random_sample()

What’s New or Different

Warning

The Box-Muller method used to produce NumPy’s normals is no longer available in RandomGenerator. It is not possible to reproduce the random values using RandomGenerator for the normal distribution or any other distribution that relies on the normal such as the gamma or student’s t. If you require backward compatibility, a legacy generator, LegacyGenerator, has been created which can fully reproduce the sequence produced by NumPy.

  • The normal, exponential and gamma generators use 256-step Ziggurat methods which are 2-10 times faster than NumPy’s Box-Muller or inverse CDF implementations.
  • Optional dtype argument that accepts np.float32 or np.float64 to produce either single or double prevision uniform random variables for select distributions
  • Optional out argument that allows existing arrays to be filled for select distributions
  • Simulate from the complex normal distribution (complex_normal())
  • random_entropy() provides access to the system source of randomness that is used in cryptographic applications (e.g., /dev/urandom on Unix).
  • All basic random generators functions to produce doubles, uint64s and uint32s via CTypes (ctypes()) and CFFI (cffi()). This allows these basic RNGs to be used in numba.
  • The basic random number generators can be used in downstream projects via Cython.

See What’s New or Different for a complete list of improvements and differences.

Parallel Generation

The included generators can be used in parallel, distributed applications in one of two ways:

Supported Generators

The main innovation is the inclusion of a number of alternative pseudo-random number generators, ‘in addition’ to the standard PRNG in NumPy. The included PRNGs are:

  • MT19937 - The standard NumPy generator. Produces identical results to NumPy using the same seed/state. Adds a jump function that advances the generator as-if 2**128 draws have been made (jump()). See NumPy’s documentation.
  • dSFMT - SSE2 enabled versions of the MT19937 generator. Theoretically the same, but with a different state and so it is not possible to produce a sequence identical to MT19937. Supports jump and so can be used in parallel applications. See the dSFMT authors’ page.
  • XoroShiro128+ - Improved version of XorShift128+ with better performance and statistical quality. Like the XorShift generators, it can be jumped to produce multiple streams in parallel applications. See jump() for details. More information about this PRNG is available at the xorshift and xoroshiro authors’ page.
  • XorShift1024*φ - Vast fast generator based on the XSadd generator. Supports jump and so can be used in parallel applications. See the documentation for jump() for details. More information about these PRNGs is available at the xorshift and xoroshiro authors’ page.
  • PCG-64 - Fast generator that support many parallel streams and can be advanced by an arbitrary amount. See the documentation for advance(). PCG-64 has a period of \(2^{128}\). See the PCG author’s page for more details about this class of PRNG.
  • ThreeFry and Philox - counter-based generators capable of being advanced an arbitrary number of steps or generating independent streams. See the Random123 page for more details about this class of PRNG.