Enoki ships with a vectorized implementation of the PCG32 random number generator developed by Melissa O’Neill. To use it, include the following header:
#include <enoki/random.h>
The following reference is based on the original PCG32 documentation.
The enoki::PCG32
class takes a single template parameter T
that denotes the “shape” of the output. This can be any scalar type like
uint32_t
, in which case the implementation generates scalar variates:
/* Scalar RNG */
using RNG_1x = PCG32<uint32_t>;
RNG_1x my_rng;
float value = my_rng.next_float32();
Alternatively, it can be an Enoki array, in which case the implementation produces arrays of variates.
using FloatP = Packet<float, 16>;
/* Vector RNG -- generates 16 independent variates at once */
using RNG_16x = PCG32<FloatP>;
RNG_16x my_rng;
FloatP value = my_rng.next_float32();
PCG32 is fast: on a Skylake i7-6920HQ processor, the vectorized implementation provided here generates around 1.4 billion single precision variates per second.
T
>PCG32
¶This class implements the PCG32 pseudorandom number generator. It has a period of \(2^{64}\) and supports \(2^{63}\) separate streams. Each stream produces a different unique sequence of random numbers, which is particularly useful in the context of vectorized computations.
Size
¶Denotes the SIMD width of the random number generator (i.e. how many pseudorandom variates are generated at the same time)
Int64
= int64_array_t<T>¶Type alias for a signed 64-bit integer (or an array thereof).
UInt64
= uint64_array_t<T>¶Type alias for a unsigned 64-bit integer (or an array thereof).
UInt32
= uint32_array_t<T>¶Type alias for a unsigned 32-bit integer (or an array thereof).
Float32
= float32_array_t<T>¶Type alias for a single precision float (or an array thereof).
Float64
= float64_array_t<T>¶Type alias for a double precision float (or an array thereof).
seed
(const UInt64 &initstate, const UInt64 &initseq)¶This function initializes (a.k.a. “seeds”) the random number generator, a required initialization step before the generator can be used. The provided arguments are defined as follows:
initstate
is the starting state for the RNG. Any 64-bit value is
permissible.initseq
selects the output sequence for the RNG. Any 64-bit value is
permissible, although only the low 63 bits are used.For this generator, there are \(2^{63}\) possible sequences of
pseudorandom numbers. Each sequence is entirely distinct and has a period
of \(2^{64}\). The initseq
argument selects which stream is used.
The initstate
argument specifies the location within the \(2^{64}\)
period.
Calling PCG32::seed()
with the same arguments produces the same
output, allowing programs to use random number sequences repeatably.
next_uint32
(const mask_t<UInt64> &mask = true)¶Generate a uniformly distributed unsigned 32-bit random number (i.e. \(x\), where \(0\le x< 2^{32}\))
If a mask parameter is provided, only the pseudorandom number generators of active SIMD lanes are advanced.
next_uint64
(const mask_t<UInt64> &mask = true)¶Generate a uniformly distributed unsigned 64-bit random number (i.e. \(x\), where \(0\le x< 2^{64}\))
If a mask parameter is provided, only the pseudorandom number generators of active SIMD lanes are advanced.
Note
This function performs two internal calls to next_uint32()
.
next_uint32_bound
(uint32_t bound, const mask_t<UInt64> &mask = true)¶Generate a uniformly distributed unsigned 32-bit random number less
than bound
(i.e. \(x\), where \(0\le x<\) bound
)
If a mask parameter is provided, only the pseudorandom number generators of active SIMD lanes are advanced.
Note
This may involve multiple internal calls to
next_uint32()
, in which case the RNG advances by
several steps. This is only relevant when using the
advance()
or operator-()
method.
next_uint64_bound
(uint64_t bound, const mask_t<UInt64> &mask = true)¶Generate a uniformly distributed unsigned 64-bit random number less
than bound
(i.e. \(x\), where \(0\le x<\) bound
)
If a mask parameter is provided, only the pseudorandom number generators of active SIMD lanes are advanced.
Note
This may involve multiple internal calls to
next_uint64()
, in which case the RNG advances by
several steps. This is only relevant when using the
advance()
or operator-()
method.
next_float32
(const mask_t<UInt64> &mask = true)¶Generate a single precision floating point value on the interval \([0, 1)\)
If a mask parameter is provided, only the pseudorandom number generators of active SIMD lanes are advanced.
next_float64
(const mask_t<UInt64> &mask = true)¶Generate a double precision floating point value on the interval \([0, 1)\)
If a mask parameter is provided, only the pseudorandom number generators of active SIMD lanes are advanced.
Warning
Since the underlying random number generator produces 32 bit
output, only the first 32 mantissa bits will be filled (however,
the resolution is still finer than in next_float32()
,
which only uses 23 mantissa bits)
advance
(const Int64 &delta)¶This operation provides jump-ahead; it advances the RNG by delta
steps,
doing so in \(\log(\texttt{delta})\) time. Because of the periodic
nature of generation, advancing by \(2^{64}-d\) (i.e., passing
\(-d\)) is equivalent to backstepping the generator by \(d\) steps.
The following macros are defined in enoki/random.h
:
uint64_t PCG32_DEFAULT_STATE = 0x853c49e6748fea9bULL
Default initialization passed to PCG32::seed()
.
uint64_t PCG32_DEFAULT_STREAM = 0xda3e39cb94b95bdbULL
Default stream index passed to PCG32::seed()
.