NumPy

Previous topic

Multithreaded Generation

Next topic

Performance

What’s New or Different

Warning

The Box-Muller method used to produce NumPy’s normals is no longer available in Generator. It is not possible to reproduce the exact random values using Generator for the normal distribution or any other distribution that relies on the normal such as the Generator.gamma or Generator.standard_t. If you require bitwise backward compatible streams, use RandomState, i.e., RandomState.gamma or RandomState.standard_t.

Quick comparison of legacy mtrand to the new Generator

Feature

Older Equivalent

Notes

Generator

RandomState

Generator requires a stream source, called a BitGenerator A number of these are provided. RandomState uses the Mersenne Twister MT19937 by default, but can also be instantiated with any BitGenerator.

random

random_sample, rand

Access the values in a BitGenerator, convert them to float64 in the interval [0.0., `` 1.0)``. In addition to the size kwarg, now supports dtype='d' or dtype='f', and an out kwarg to fill a user- supplied array.

Many other distributions are also supported.

integers

randint, random_integers

Use the endpoint kwarg to adjust the inclusion or exclution of the high interval endpoint

And in more detail:

  • Simulate from the complex normal distribution (complex_normal)

  • The normal, exponential and gamma generators use 256-step Ziggurat methods which are 2-10 times faster than NumPy’s default implementation in standard_normal, standard_exponential or standard_gamma.

  • integers is now the canonical way to generate integer random numbers from a discrete uniform distribution. The rand and randn methods are only available through the legacy RandomState. This replaces both randint and the deprecated random_integers.

  • The Box-Muller method used to produce NumPy’s normals is no longer available.

  • All bit generators can produce doubles, uint64s and uint32s via CTypes (ctypes) and CFFI (cffi). This allows these bit generators to be used in numba.

  • The bit generators can be used in downstream projects via Cython.

In [1]: from  numpy.random import Generator, PCG64

In [2]: import numpy.random

In [3]: rg = Generator(PCG64())

In [4]: %timeit rg.standard_normal(100000)
   ...: %timeit numpy.random.standard_normal(100000)
   ...: 
1 ms +- 2.11 us per loop (mean +- std. dev. of 7 runs, 1000 loops each)
1.81 ms +- 12.7 us per loop (mean +- std. dev. of 7 runs, 1000 loops each)
In [5]: %timeit rg.standard_exponential(100000)
   ...: %timeit numpy.random.standard_exponential(100000)
   ...: 
382 us +- 448 ns per loop (mean +- std. dev. of 7 runs, 1000 loops each)
1.33 ms +- 979 ns per loop (mean +- std. dev. of 7 runs, 1000 loops each)
In [6]: %timeit rg.standard_gamma(3.0, 100000)
   ...: %timeit numpy.random.standard_gamma(3.0, 100000)
   ...: 
1.86 ms +- 1.54 us per loop (mean +- std. dev. of 7 runs, 1000 loops each)
3.6 ms +- 17 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
In [7]: rg = Generator(PCG64(0))

In [8]: rg.random(3, dtype='d')
Out[8]: array([0.63696169, 0.26978671, 0.04097352])

In [9]: rg.random(3, dtype='f')
Out[9]: array([0.07524014, 0.01652753, 0.17526722], dtype=float32)
  • Optional out argument that allows existing arrays to be filled for select distributions

    This allows multithreading to fill large arrays in chunks using suitable BitGenerators in parallel.

In [10]: existing = np.zeros(4)

In [11]: rg.random(out=existing[:2])
Out[11]: array([0.91275558, 0.60663578])

In [12]: print(existing)
[0.91275558 0.60663578 0.         0.        ]