Reproducible random numbers#
Fill an array with seeded normal draws, and show that the same seed reproduces the same stream – the exact sequence of numbers it draws.
Time: ~2 min · Runs on: CPU · You need: Random numbers
running on: cpu
Generator(seed) plus .normal<Real, D>(mean, sd) draws a
D-component normal sample.
Each draw is addressed by a SampleIndex – SampleIndex::make(idx)
wraps a plain index so a View can be indexed by it (va[i]), the same
identity a GPU kernel would use per thread.
Two different Generator values, same seed, must draw the identical
stream:
RNG_TOP = r"""
using aether::Array;
using aether::SampleIndex;
using aether::random::Generator;
constexpr std::size_t N = 5;
"""
out = run_cpp(r"""
Array<double, 3> a(N);
auto va = a.hostView();
Generator rng1(42);
for (std::size_t idx = 0; idx < N; ++idx)
va[SampleIndex::make(idx)] = rng1.normal<double, 3>(0.0, 1.0);
std::printf("sample 0: (%+.6f, %+.6f, %+.6f)\n", va(0, 0), va(1, 0), va(2, 0));
""", top=RNG_TOP)
print(out)
sample 0: (-0.341393, +0.165468, +0.343431)
out = run_cpp(r"""
Array<double, 3> a(N), b(N);
auto va = a.hostView(), vb = b.hostView();
Generator rng1(42), rng2(42); // two independent values, same seed
bool identical = true;
for (std::size_t idx = 0; idx < N; ++idx) {
va[SampleIndex::make(idx)] = rng1.normal<double, 3>(0.0, 1.0);
vb[SampleIndex::make(idx)] = rng2.normal<double, 3>(0.0, 1.0);
for (std::size_t c = 0; c < 3; ++c)
identical &= (va(c, idx) == vb(c, idx));
}
std::printf("two Generators, same seed, same stream: %s\n", identical ? "true" : "false");
""", top=RNG_TOP)
print(out)
two Generators, same seed, same stream: true
Two independent Generator values, same seed, draw bit-for-bit the same
stream. For a genuinely different second draw per sample,
rng.substream(k) derives an independent sub-stream from the same seed
– the sub-counter its identity is built from (seed, sample id,
sub-counter).