Program Listing for File AgentRandom.cuh
↰ Return to documentation for file (include/flamegpu/runtime/random/AgentRandom.cuh
)
#ifndef INCLUDE_FLAMEGPU_RUNTIME_RANDOM_AGENTRANDOM_CUH_
#define INCLUDE_FLAMEGPU_RUNTIME_RANDOM_AGENTRANDOM_CUH_
#include <limits>
#include "flamegpu/detail/curand.cuh"
#include "flamegpu/detail/StaticAssert.h"
#include "flamegpu/exception/FLAMEGPUDeviceException.cuh"
namespace flamegpu {
class AgentRandom {
public:
__forceinline__ __device__ AgentRandom(detail::curandState *d_rng);
template<typename T>
__forceinline__ __device__ T uniform() const;
template<typename T>
__forceinline__ __device__ T normal() const;
template<typename T>
__forceinline__ __device__ T logNormal(T mean, T stddev) const;
__forceinline__ __device__ unsigned int poisson(double mean = 1.0f) const;
template<typename T>
__forceinline__ __device__ T uniform(T min, T max) const;
private:
detail::curandState *d_random_state;
};
__forceinline__ __device__ AgentRandom::AgentRandom(detail::curandState *d_rng) : d_random_state(d_rng) { }
template<>
__forceinline__ __device__ float AgentRandom::uniform() const {
// curand naturally generates the range (0, 1], we want [0, 1)
// https://github.com/pytorch/pytorch/blob/059aa34b124916dfd761f3cbdb5fa97d7a01fc93/aten/src/ATen/native/cuda/Distributions.cu#L71-L77
uint32_t val = curand(d_random_state); // need just bits
constexpr auto MASK = static_cast<uint32_t>((static_cast<uint64_t>(1) << std::numeric_limits<float>::digits) - 1);
constexpr auto DIVISOR = static_cast<float>(1) / (static_cast<uint32_t>(1) << std::numeric_limits<float>::digits);
return (val & MASK) * DIVISOR;
}
template<>
__forceinline__ __device__ double AgentRandom::uniform() const {
// curand naturally generates the range (0, 1], we want [0, 1)
// Conversion of High-Period Random Numbers to Floating Point - Jurgen A Doornik
// Based on: https://www.doornik.com/research/randomdouble.pdf
const uint32_t iRan1 = curand(d_random_state);
const uint32_t iRan2 = curand(d_random_state);
constexpr double M_RAN_INVM32 = 2.32830643653869628906e-010;
constexpr double M_RAN_INVM52 = 2.22044604925031308085e-016;
return (static_cast<int>(iRan1)*M_RAN_INVM32 + (0.5 + M_RAN_INVM52 / 2) + static_cast<int>((iRan2) & 0x000FFFFF) * M_RAN_INVM52);
}
template<>
__forceinline__ __device__ float AgentRandom::normal() const {
return curand_normal(d_random_state);
}
template<>
__forceinline__ __device__ double AgentRandom::normal() const {
return curand_normal_double(d_random_state);
}
template<>
__forceinline__ __device__ float AgentRandom::logNormal(const float mean, const float stddev) const {
return curand_log_normal(d_random_state, mean, stddev);
}
template<>
__forceinline__ __device__ double AgentRandom::logNormal(const double mean, const double stddev) const {
return curand_log_normal_double(d_random_state, mean, stddev);
}
__forceinline__ __device__ unsigned int AgentRandom::poisson(const double mean) const {
return curand_poisson(d_random_state, mean);
}
template<typename T>
__forceinline__ __device__ T AgentRandom::uniform(T min, T max) const {
static_assert(detail::StaticAssert::_Is_IntType<T>::value, "Invalid template argument for AgentRandom::uniform(T lowerBound, T max)");
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
if (min > max) {
DTHROW("Invalid arguments passed to AgentRandom::uniform(), %lld > %lld\n", static_cast<int64_t>(min), static_cast<int64_t>(max));
}
#endif
return static_cast<T>(min + (1 + max - min) * uniform<float>());
}
template<>
__forceinline__ __device__ int64_t AgentRandom::uniform(const int64_t min, const int64_t max) const {
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
if (min > max) {
DTHROW("Invalid arguments passed to AgentRandom::uniform(), %lld > %lld\n", static_cast<int64_t>(min), static_cast<int64_t>(max));
}
#endif
return static_cast<int64_t>(min + (1 + max - min) * uniform<double>());
}
template<>
__forceinline__ __device__ uint64_t AgentRandom::uniform(const uint64_t min, const uint64_t max) const {
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
if (min > max) {
DTHROW("Invalid arguments passed to AgentRandom::uniform(), %lld > %lld\n", static_cast<int64_t>(min), static_cast<int64_t>(max));
}
#endif
return static_cast<uint64_t>(min + (1 + max - min) * uniform<double>());
}
template<>
__forceinline__ __device__ float AgentRandom::uniform(const float min, const float max) const {
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
if (min > max) {
DTHROW("Invalid arguments passed to AgentRandom::uniform(), %f > %f\n", min, max);
}
#endif
return min + (max - min) * uniform<float>();
}
template<>
__forceinline__ __device__ double AgentRandom::uniform(const double min, const double max) const {
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
if (min > max) {
DTHROW("Invalid arguments passed to AgentRandom::uniform(), %f > %f\n", min, max);
}
#endif
return min + (max - min) * uniform<double>();
}
} // namespace flamegpu
#endif // INCLUDE_FLAMEGPU_RUNTIME_RANDOM_AGENTRANDOM_CUH_