nk 1.0.0
C++ Numerical Computing Runtime
Loading...
Searching...
No Matches
rng_context.hpp
1// ops/include/nk/ops/rng_context.hpp
2//
3// RngContext — the seedable random-number session state. Owns one MATLAB-style
4// MT19937 stream and the rng() control surface (seed / shuffle / state save +
5// restore). The Engine owns one RngContext (engine.rng()); every RNG-using
6// function (rand/randn/randi/randperm + the toolbox *rnd samplers, comm noise,
7// …) takes `RngContext &` and draws from it — so a single session shares ONE
8// reproducible stream (MATLAB `rng(seed)` semantics) WITHOUT a process-global
9// singleton or a mutex. Two Engines have independent streams.
10//
11// It models UniformRandomBitGenerator (forwards operator()/min/max/result_type
12// to the owned MT19937), so the std distributions draw from it directly:
13// std::normal_distribution<double> d; double x = d(rng);
14// An L0.5 ops type (Value + MatlabMT19937 only, no Engine).
15
16#pragma once
17
18#include <nk/ops/matlab_mt19937.hpp>
19#include <nk/value/value.hpp>
20
21#include <cmath> // v4Normal: std::sqrt / std::log
22#include <cstdint>
23#include <memory_resource>
24#include <random> // RngContext models a URBG — callers draw via std::*_distribution(rng)
25
26namespace nk::ops {
27
29{
30public:
31 // ── UniformRandomBitGenerator surface (forwarded to the owned stream) ──
32 // Lets std::*_distribution and the value generators draw via `dist(rng)`.
33 using result_type = MatlabMT19937::result_type;
34 static constexpr result_type min() { return MatlabMT19937::min(); }
35 static constexpr result_type max() { return MatlabMT19937::max(); }
36 result_type operator()() { return gen_(); }
37
39 double genRes53() { return gen_.genRes53(); }
40
41 // ── MATLAB rng() control ──────────────────────────────────────────
43 void seed(std::uint64_t seed)
44 {
45 legacyV4_ = false;
46 gen_.seed(static_cast<std::uint32_t>(seed));
47 }
48
49 void shuffle();
51 Value state(std::pmr::memory_resource *mr = nullptr) const;
53 void restore(const Value &s);
54
55 // ── Legacy v4 generator (rand/randn('seed', S)) ───────────────────
56 // MATLAB's pre-v5 generator, bit-identical (derived + verified vs
57 // R2025b, see bugs/closed/stats/randn-legacy-seed-syntax.md):
58 // x <- 16807*x mod (2^31-1); u = x/(2^31-1)
59 // seed S -> state S*2^16 (S==0 -> the constant 1144108930)
60 // randn: Marsaglia polar on that stream, FIRST of each accepted
61 // pair emitted (the second is discarded — probed; MATLAB's v4
62 // randn wastes half the pairs). ONE shared stream for rand+randn.
63 void setLegacyV4(std::uint64_t seed)
64 {
65 legacyV4_ = true;
66 v4seed_ = seed;
67 v4state_ = seed ? seed * 65536ull : 1144108930ull;
68 }
70 std::uint64_t legacySeed() const noexcept { return v4seed_; }
71 bool legacyV4() const noexcept { return legacyV4_; }
72 double v4Uniform()
73 {
74 v4state_ = 16807ull * v4state_ % 2147483647ull;
75 return static_cast<double>(v4state_) / 2147483647.0;
76 }
77 double v4Normal()
78 {
79 for (;;) {
80 const double v1 = 2.0 * v4Uniform() - 1.0;
81 const double v2 = 2.0 * v4Uniform() - 1.0;
82 const double s = v1 * v1 + v2 * v2;
83 if (s > 0.0 && s < 1.0)
84 return v1 * std::sqrt(-2.0 * std::log(s) / s);
85 }
86 }
87
89 MatlabMT19937 &engine() noexcept { return gen_; }
90 const MatlabMT19937 &engine() const noexcept { return gen_; }
91
92private:
93 MatlabMT19937 gen_; // default-constructed = rng('default')
94 bool legacyV4_ = false;
95 std::uint64_t v4seed_ = 0;
96 std::uint64_t v4state_ = 1;
97};
98
99} // namespace nk::ops
Core MATLAB-compatible multidimensional array and scalar value type.
Definition value.hpp:61
void restore(const Value &s)
rng(s) — restore a state struct captured by state().
double genRes53()
MATLAB-canonical 53-bit double in [0, 1) — what rand uses.
std::uint64_t legacySeed() const noexcept
rand('seed') / randn('seed') — query the last legacy seed.
void shuffle()
rng('shuffle') — seed from a nondeterministic source.
void seed(std::uint64_t seed)
Seed the stream. seed == 0 ≡ rng('default').
Value state(std::pmr::memory_resource *mr=nullptr) const
s = rng() — capture the stream state as a struct (.Type / .State).
MatlabMT19937 & engine() noexcept
Escape hatch: the underlying MT19937 (e.g. for getState/setState).