What’s New or Different¶
Differences from NumPy 1.17+¶
UserBitGenerator
allows bit generators to be written in Python (slow, suitable for experiments and testing) or numba (fast, similar speed to compiled C). See the demonstration notebook for examples.PCG64
supports additional variants of PCG64, including the PCG4 2.0 variant (“cm-dxsm”).SFC64
supports optional Weyl sequence increments other than 1 which is the fixed increment innumpy.random.SFC64
.random_entropy()
provides access to the system source of randomness that is used in cryptographic applications (e.g.,/dev/urandom
on Unix).Support broadcasting when producing multivariate Gaussian values (
multivariate_normal()
)Simulate from the complex normal distribution (
complex_normal()
)Direct access to unsigned integers is provided by (
uintegers()
)A wider range of bit generators:
Chaotic mappings
Cryptographic Cipher-based:
Hardware-based:
Mersenne Twisters
Permuted Congruential Generators
Shift/rotate based:
Deprecated
Generator
is deprecated. You should be using numpy.random.Generator
.
randomgen’s
Generator
continues to expose legacy methodsrandom_sample()
,randint()
,random_integers()
,rand()
,randn()
, andtomaxint()
. Note: These should not be used, and their modern replacements are preferred:random_integers()
,randint()
→integers()
tomaxint()
→integers()
withdtype
set toint
randomgen’s bit generators remain seedable and the convenience function
seed()
is exposed as part ofGenerator
. Additionally, the convenience propertystate()
is available to get or set the state of the underlying bit generator.numpy.random.Generator.multivariate_hypergeometric()
was added afterGenerator
was merged into NumPy and will not be ported over.numpy.random.Generator.shuffle()
andnumpy.random.Generator.permutation()
supportaxis
keyword to operator along an axis other than 0.integers()
supports the keyword argumentuse_masked
to switch between masked generation of bounded integers and Lemire’s superior method.
Differences from NumPy before 1.17¶
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()
orstandard_gamma()
.
In [1]: from randomgen import Generator, Xoroshiro128
In [2]: import numpy.random
In [3]: rg = Generator(Xoroshiro128(mode="sequence"))
In [4]: %timeit rg.standard_normal(100000)
...: %timeit numpy.random.standard_normal(100000)
...:
890 us +- 18.4 us per loop (mean +- std. dev. of 7 runs, 1000 loops each)
2.66 ms +- 94.6 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
In [5]: %timeit rg.standard_exponential(100000)
...: %timeit numpy.random.standard_exponential(100000)
...:
443 us +- 13.1 us per loop (mean +- std. dev. of 7 runs, 1000 loops each)
2.01 ms +- 35.7 us 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)
...:
2.67 ms +- 115 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
5.36 ms +- 184 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
The Box-Muller used to produce NumPy’s normals is no longer available.
All bit generators functions to produce doubles, uint64s and uint32s via CTypes (
ctypes()
) and CFFI (cffi()
). This allows the bit generators to be used in numba or in other low-level applicationsThe bit generators can be used in downstream projects via Cython.
Optional
dtype
argument that acceptsnp.float32
ornp.float64
to produce either single or double prevision uniform random variables for select core distributionsNormals (
standard_normal()
andrandn()
)Standard Gammas (
standard_gamma()
)Standard Exponentials (
standard_exponential()
)
In [7]: rg.seed(0)
Out[7]: <randomgen.generator.Generator(Xoroshiro128) object at 0x7fb4a5603220>
In [8]: rg.random(3, dtype='d')
Out[8]: array([0.52785087, 0.70822489, 0.66049255])
In [9]: rg.seed(0)
Out[9]: <randomgen.generator.Generator(Xoroshiro128) object at 0x7fb4a5603220>
In [10]: rg.random(3, dtype='f')
Out[10]: array([0.8648406 , 0.52785087, 0.9834049 ], dtype=float32)
Optional
out
argument that allows existing arrays to be filled for select core distributionsUniforms (
random()
)Normals (
standard_normal()
)Standard Gammas (
standard_gamma()
)Standard Exponentials (
standard_exponential()
)
This allows multithreading to fill large arrays in chunks using suitable PRNGs in parallel.
In [11]: existing = np.zeros(4)
In [12]: rg.random(out=existing[:2])
Out[12]: array([0.66049255, 0.07582601])
In [13]: print(existing)
[0.66049255 0.07582601 0. 0. ]
integers()
supports broadcasting inputs.integers()
supports drawing from open (default,[low, high)
) or closed ([low, high]
) intervals using the keyword argumentendpoint
. Closed intervals are simpler to use when the distribution may include the maximum value of a given integer type.
In [14]: rg.seed(1234)
Out[14]: <randomgen.generator.Generator(Xoroshiro128) object at 0x7fb4a5603220>
In [15]: rg.integers(0, np.iinfo(np.int64).max+1)
Out[15]: 26097719062389013
In [16]: rg.seed(1234)
Out[16]: <randomgen.generator.Generator(Xoroshiro128) object at 0x7fb4a5603220>
In [17]: rg.integers(0, np.iinfo(np.int64).max, endpoint=True)
Out[17]: 26097719062389013
The closed interval is particularly helpful when using arrays since it avoids object-dtype arrays when sampling from the full range.
In [18]: rg.seed(1234)
Out[18]: <randomgen.generator.Generator(Xoroshiro128) object at 0x7fb4a5603220>
In [19]: lower = np.zeros((2, 1), dtype=np.uint64)
In [20]: upper = np.array([10, np.iinfo(np.uint64).max+1], dtype=object)
In [21]: upper
Out[21]: array([10, 18446744073709551616], dtype=object)
In [22]: rg.integers(lower, upper, dtype=np.uint64)
Out[22]:
array([[ 5, 4570805141130348385],
[ 2, 4398458246985300510]], dtype=uint64)
In [23]: rg.seed(1234)
Out[23]: <randomgen.generator.Generator(Xoroshiro128) object at 0x7fb4a5603220>
In [24]: upper = np.array([10, np.iinfo(np.uint64).max], dtype=np.uint64)
In [25]: upper
Out[25]: array([ 10, 18446744073709551615], dtype=uint64)
In [26]: rg.integers(lower, upper, endpoint=True, dtype=np.uint64)
Out[26]:
array([[ 5, 4570805141130348385],
[ 2, 4398458246985300510]], dtype=uint64)
Support for Lemire’s method of generating uniform integers on an arbitrary interval by setting
use_masked=True
in (integers()
).
In [27]: %timeit rg.integers(0, 1535, size=100000, use_masked=False)
....: %timeit numpy.random.randint(0, 1535, size=100000)
....:
370 us +- 12.7 us per loop (mean +- std. dev. of 7 runs, 1000 loops each)
979 us +- 35.3 us per loop (mean +- std. dev. of 7 runs, 1000 loops each)
multinomial()
supports multidimensional values ofn
In [28]: rg.multinomial([10, 100], np.ones(6) / 6.)
Out[28]:
array([[ 1, 2, 2, 2, 2, 1],
[18, 9, 23, 20, 16, 14]])
choice()
is much faster when sampling small amounts from large arrays
In [29]: x = np.arange(1000000)
In [30]: %timeit rg.choice(x, 10)
39.7 us +- 795 ns per loop (mean +- std. dev. of 7 runs, 10000 loops each)
choice()
supports theaxis
keyword to work with multidimensional arrays.
In [31]: x = np.reshape(np.arange(20), (2, 10))
In [32]: rg.choice(x, 2, axis=1)
Out[32]:
array([[ 1, 8],
[11, 18]])
For changes since the previous release, see the Change Log