# 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`.

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

    In [2]: import numpy.random

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

    In [4]: %timeit -n 1 rng.standard_normal(100000)
       ...: %timeit -n 1 numpy.random.standard_normal(100000)
       ...:
    1.09 ms +- 23.3 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
    2.08 ms +- 22.6 us per loop (mean +- std. dev. of 7 runs, 1 loop each)

    In [5]: %timeit -n 1 rng.standard_exponential(100000)
       ...: %timeit -n 1 numpy.random.standard_exponential(100000)
       ...:
    599 us +- 4.3 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
    1.51 ms +- 3.95 us per loop (mean +- std. dev. of 7 runs, 1 loop each)

    In [6]: %timeit -n 1 rng.standard_gamma(3.0, 100000)
       ...: %timeit -n 1 numpy.random.standard_gamma(3.0, 100000)
       ...:
    2.22 ms +- 17.2 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
    4.17 ms +- 17.5 us per loop (mean +- std. dev. of 7 runs, 1 loop each)

- `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.

- Optional `dtype` argument that accepts `np.float32` or `np.float64` to produce either single or double prevision uniform random variables for select distributions

  - Uniforms (`random` and `integers`)

  - Normals (`standard_normal`)

  - Standard Gammas (`standard_gamma`)

  - Standard Exponentials (`standard_exponential`)

    In [7]: rng = Generator(PCG64(0))

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

    In [9]: rng.random(3, dtype='f')
    Out[9]: array([0.07524014, 0.01652759, 0.17526728], dtype=float32)

- Optional `out` argument that allows existing arrays to be filled for select distributions

  - Uniforms (`random`)

  - Normals (`standard_normal`)

  - Standard Gammas (`standard_gamma`)

  - Standard Exponentials (`standard_exponential`)

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

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

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

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

- Optional `axis` argument for methods like `choice`, `permutation` and `shuffle` that controls which axis an operation is performed over for multi-dimensional arrays.

    In [13]: rng = Generator(PCG64(123456789))

    In [14]: a = np.arange(12).reshape((3, 4))

    In [15]: a
    Out[15]:
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])

    In [16]: rng.choice(a, axis=1, size=5)
    Out[16]:
    array([[ 3,  0,  2,  3,  1],
           [ 7,  4,  6,  7,  5],
           [11,  8, 10, 11,  9]])

    In [17]: rng.shuffle(a, axis=1)        # Shuffle in-place

    In [18]: a
    Out[18]:
    array([[ 3,  1,  2,  0],
           [ 7,  5,  6,  4],
           [11,  9, 10,  8]])

previous

Multithreaded Generation

next

Performance