16#ifndef __SIZEOF_INT128__
17#error "clustering::math::rng requires a compiler with __uint128_t support (GCC/Clang)."
46 void seed(std::uint64_t seedValue, std::uint64_t stream = 0) noexcept {
47 static constexpr __uint128_t kMultHi =
48 (
static_cast<__uint128_t
>(2549297995355413924ULL) << 64) | 4865540595714422341ULL;
50 m_inc = (
static_cast<__uint128_t
>(stream) << 1U) | 1U;
64 static constexpr __uint128_t kMult =
65 (
static_cast<__uint128_t
>(2549297995355413924ULL) << 64) | 4865540595714422341ULL;
66 const __uint128_t old = rng.m_state;
67 rng.m_state = (old * kMult) + rng.m_inc;
68 const auto rot =
static_cast<std::uint64_t
>(old >> 122);
69 const auto xorshifted =
static_cast<std::uint64_t
>(old ^ (old >> 64));
70 return (xorshifted >> rot) | (xorshifted << ((-rot) & 63U));
82 std::array<std::uint64_t, 4>
m_s{0, 0, 0, 0};
92 void seed(std::uint64_t seedValue)
noexcept {
93 std::uint64_t z = seedValue;
94 for (
auto &word :
m_s) {
95 z += 0x9E3779B97F4A7C15ULL;
97 x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL;
98 x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL;
112 const auto rotl = [](std::uint64_t x,
int k) -> std::uint64_t {
113 return (x << k) | (x >> (64 - k));
115 const std::uint64_t result = rotl(rng.m_s[1] * 5U, 7) * 9U;
116 const std::uint64_t t = rng.m_s[1] << 17U;
117 rng.m_s[2] ^= rng.m_s[0];
118 rng.m_s[3] ^= rng.m_s[1];
119 rng.m_s[1] ^= rng.m_s[2];
120 rng.m_s[0] ^= rng.m_s[3];
122 rng.m_s[3] = rotl(rng.m_s[3], 45);
133 return static_cast<std::uint32_t
>(
advanceState(rng) >> 32U);
152template <
class T,
class Rng>
inline T
randUnit(Rng &rng)
noexcept {
153 static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>,
154 "randUnit<T> requires T to be float or double");
155 if constexpr (std::is_same_v<T, double>) {
156 return static_cast<double>(
advanceState(rng) >> 11U) * 0x1.0p-53;
158 return static_cast<float>(
advanceState(rng) >> 40U) * 0x1.0p-24F;
178template <
class T, Layout L,
class Rng>
180 static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>,
181 "weightedCategorical<T> requires T to be float or double");
182 const std::size_t n = weights.dim(0);
183 assert(n > 0 &&
"weightedCategorical requires at least one weight");
186 for (std::size_t i = 0; i < n; ++i) {
187 const T w = weights(i);
188 assert(w >= T{0} &&
"weightedCategorical requires non-negative weights");
191 assert(total > T{0} &&
"weightedCategorical requires at least one positive weight");
195 std::size_t lastPositive = 0;
196 for (std::size_t i = 0; i < n; ++i) {
197 const T w = weights(i);
199 if (cumulative > u) {
232template <
class T, Layout L,
class Rng>
234 std::span<std::size_t> outIdx)
noexcept {
235 static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>,
236 "aExpjReservoir<T> requires T to be float or double");
237 const std::size_t n = weights.dim(0);
238 assert(outIdx.size() == k &&
"aExpjReservoir requires outIdx.size() == k");
239 assert(k <= n &&
"aExpjReservoir requires k <= weights.dim(0)");
246 std::vector<std::pair<T, std::size_t>> keyed;
248 for (std::size_t i = 0; i < n; ++i) {
249 const T w = weights(i);
250 assert(w > T{0} &&
"aExpjReservoir requires strictly positive weights");
257 const T key = std::log(u) / w;
258 keyed.emplace_back(key, i);
262 const auto cmp = [](
const std::pair<T, std::size_t> &a,
263 const std::pair<T, std::size_t> &b)
noexcept {
return a.first > b.first; };
264 std::partial_sort(keyed.begin(), keyed.begin() +
static_cast<std::ptrdiff_t
>(k), keyed.end(),
267 for (std::size_t i = 0; i < k; ++i) {
268 outIdx[i] = keyed[i].second;
Represents a multidimensional array (NDArray) of a fixed number of dimensions N and element type T.
T randUnit(Rng &rng) noexcept
Draw a uniform variate in the half-open unit interval [0, 1).
void aExpjReservoir(const NDArray< T, 1, L > &weights, std::size_t k, Rng &rng, std::span< std::size_t > outIdx) noexcept
Efraimidis-Spirakis weighted reservoir sampling (A-Exp variant, log-key form).
std::uint64_t advanceState(pcg64 &rng) noexcept
Advance a pcg64 one step and return the 64-bit XSL-RR output.
std::size_t weightedCategorical(const NDArray< T, 1, L > &weights, Rng &rng) noexcept
Sample one category index proportionally to non-negative weights.
std::uint32_t randUniformU32(Rng &rng) noexcept
Draw a 32-bit unsigned integer uniformly at random from the full u32 range.
std::uint64_t randUniformU64(Rng &rng) noexcept
Draw a 64-bit unsigned integer uniformly at random from the full u64 range.
128-bit state for the PCG-XSL-RR 64-bit output generator (Melissa O'Neill).
__uint128_t m_inc
Stream-encoded odd increment mixed into the LCG step.
__uint128_t m_state
128-bit generator state; advanced by every advanceState call.
void seed(std::uint64_t seedValue, std::uint64_t stream=0) noexcept
Initialize the generator per PCG's canonical seeding procedure.
256-bit state for Vigna & Blackman's xoshiro256** generator.
void seed(std::uint64_t seedValue) noexcept
Initialize the four state words via SplitMix64 diffusion of a single 64-bit seed.
std::array< std::uint64_t, 4 > m_s
Four 64-bit state words; SplitMix64-diffused at seed time.