Execution Methods#
Execution methods are used to run RNG operations as defined by user with cuRANDDx operators.
Note
Current cuRANDDx release only supports execution on CUDA thread level, i.e., thread execution.
Thread Execute Methods#
The thread execution methods are available if the descriptor has been constructed using the Thread Operator
and is_complete_rand_execution Trait is true
.
State Initialization#
States can be initialized either by using init(...)
function, or using the thread execution constructor (refer to the example).
The function arguments vary depending on the generator type.
using RNG = decltype(curanddx::Thread() + ...);
// Explicit init state function for pseudorandom generators
void init(const unsigned long long seed,
const unsigned long long subsequence,
const typename RNG::offset_type offset);
// Implicit init state function using thread execution constructor for pseudorandom generators
void RNG(const unsigned long long seed,
const unsigned long long subsequence,
const typename RNG::offset_type offset);
// Explicit init function for quasirandom generators
void init(const unsigned int dim,
typename RNG::direction_vector_type* direction_vectors,
const typename RNG::offset_type offset,
const typename RNG::scrambled_const_type* scrambled_consts = nullptr);
// Implicit init function using thread execution constructor for quasirandom generators
void RNG(const unsigned int dim,
typename RNG::direction_vector_type* direction_vectors,
const typename RNG::offset_type offset,
const typename RNG::scrambled_const_type* scrambled_consts = nullptr);
Random Number Generation With Distributions#
Similar to Numpy Random Generator, cuRANDDx uses the RNG
object
to initialize and manage state, generate random bits, which are then transformed into random values from a specified distribution using a distribution object with its own state.
Uniform Bits Distribution#
// Uniform bits distribution
// Equivalent to the random bits generated without transformation
template<typename UIntType = unsigned int>
struct uniform_bits {
using result_type = UIntType;
using result4_type = typename detail::make_vector<UIntType, 4>::type;
// constructor
uniform_bits();
// generate a singel value, invalid for Philox
template<class RNG>
result_type generate(RNG& rng);
// Only used for Philox
template<class RNG>
result4_type generate4(RNG& rng);
}
Warning
cuRANDDx 0.1.0 only supports native Philox4_32 generator, meaning four values are generated at a time.
As a result using generate()
function of a descriptor with Philox4_32 generator is not supported and will lead to compilation errors.
Similarly, using generate4()
function of a descriptor with non-Philox generator will also lead to compilation errors.
Uniform Distribution#
// Uniform distribution
template<typename FPType = float> // Only support float and double
struct uniform {
using result_type = FPType;
using result2_type = typename detail::make_vector<FPType, 2>::type;
using result4_type = typename detail::make_vector<FPType, 4>::type;
// constructor
uniform(FPType min = 0, FPType max = 1);
// generate a singel value, invalid for Philox
template<class RNG>
result_type generate(RNG& rng);
// generate two values, for example uniform_double2 using Philox
template<class RNG>
result2_type generate2(RNG& rng);
// generate four values, for example uniform4 or uniform_double4 using Philox
template<class RNG>
result4_type generate4(RNG& rng);
}
Note
For 32-bit pseudorandom generators, cuRANDDx by default uses a more precise method to compute one double-precision value using two 32-bit values. There is an option to use less precise approach which is compatible with how MRG32K3A generator is implemented in cuRAND host API, curandGenerateUniformDouble
, curandGenerateNormaDouble
, and curandGenerateLogNormalDouble
. It can be enabled by defining CURANDDX_MRG_DOUBLE_DISTRIBUTION_CURAND_COMPATIBLE
preprocessor in your code. Please refer to the example for detail.
Normal Distribution#
// Normal distribution with two implementation methods
enum class normal_method
{
icdf, // Inverse cumulative distribution function (ICDF), generating one value at a time
box_muller // Box-Muller method, requiring two input values and generating two output values
};
template<typename FPType = float, normal_method Method = icdf>
struct normal {
using result_type = FPType; // Only support float and double
using result2_type = typename detail::make_vector<FPType, 2>::type;
using result4_type = typename detail::make_vector<FPType, 4>::type;
// constructor
normal(FPType mean = 0, FPType stddev = 1);
// generate a singel value, invalid for Philox or Box-Muller method
template<class RNG>
result_type generate(RNG& rng);
// Only for Box-Muller used by non-philox generator
// Or Box-Muller used by Philox for two FP64 values
template<class RNG>
result2_type generate2(RNG& rng);
// Only for normal4 or normal4_double for Philox, using either ICDF or Box-Muller
template<class RNG>
result4_type generate4(RNG& rng);
}
Warning
Box-Muller method requires two input values and generates two values. Therefore, using generate()
function for normal distribution with
Box-Muller method is not allowed and will lead to compilation errors.
Log-normal Distribution#
// LogNormal distribution with two implementation methods
template<typename FPType = float, normal_method Method = icdf>
struct log_normal {
using result_type = FPType; // Only support float and double
using result2_type = typename detail::make_vector<FPType, 2>::type;
using result4_type = typename detail::make_vector<FPType, 4>::type;
// constructor
log_normal(FPType mean = 0, FPType stddev = 1);
// generate a singel value, invalid for Philox or Box-Muller method
template<class RNG>
result_type generate(RNG& rng);
// Only for Box-Muller used by non-philox generator
// Or Box-Muller used by Philox for two FP64 values
template<class RNG>
result2_type generate2(RNG& rng);
// Only for log_normal4 or log_normal4_double for Philox, using either ICDF or Box-Muller
template<class RNG>
result4_type generate4(RNG& rng);
}
Poisson Distribution#
// Poisson distribution is a discrete distribution, the output data type is unsigned int
struct poisson {
using result_type = unsigned int;
// constructor
poisson(double lambda = 1);
// generate a singel value
// cuRANDDx current does not support generating Poisson distribution with Philox4_32 generator
result_type generate(RNG& rng);
}
Skip Functions#
The skip_offset
, skip_subsequence
and skip_sequence
functions in cuRANDDx are the updated API based on
skipahead
, skipahead_sequence
, and skipahead_subsequence
cuRAND device API, with difference explained in the comments below.
Users can refer to the example using the skip functions.
using RNG = decltype(curanddx::Thread() + ...);
// Skip offset, valid for all generators except scrambled_sobol32 and scrambled_sobol64
// Similar to skipahead() functions in cuRAND device API
void skip_offset(const typename RNG::offset_type n);
// Skip subsequence, valid for pseudorandom generators
// Similar to skipahead_subsequence() cuRAND device API for MRG32K3A and skipahead_sequence() for XORWOW and Philox
void skip_subsequence(const unsigned long long n);
// Skip sequence, valid for MRG32K3A generator only
// Similar to skipahead_sequence() cuRAND device API for MRG32K3A generator
void skip_sequence(const unsigned long long n);