Examples
Complete, runnable examples live under examples/ in the repository.
C++ examples
Basic scalar Xoshiro
// Minimal scalar Xoshiro256++ usage: raw uint64, uniform(), and C++ <random> interop.
#include <iostream>
#include <random>
#include <random/xoshiro.hpp>
int main() {
const auto seed = 42u;
prng::Xoshiro rng(seed);
std::cout << "uint64: " << rng() << '\n';
std::cout << "uniform double: " << rng.uniform() << '\n';
std::uniform_real_distribution<double> dist(0.0, 1.0);
std::cout << "std::uniform_real_distribution: " << dist(rng) << '\n';
return 0;
}
Bulk fill with XoshiroSIMD
// Fill a large std::vector<double> using XoshiroSIMD in a hot loop.
#include <chrono>
#include <iostream>
#include <vector>
#include <random/xoshiro_simd.hpp>
int main() {
constexpr std::size_t N = 1 << 22;
prng::XoshiroSIMD rng(1234u);
std::vector<double> out(N);
const auto t0 = std::chrono::steady_clock::now();
for (auto &x : out) x = rng.uniform();
const auto t1 = std::chrono::steady_clock::now();
const auto ms = std::chrono::duration<double, std::milli>(t1 - t0).count();
std::cout << "Filled " << N << " doubles in " << ms << " ms ("
<< (N / ms / 1e3) << " Msamples/s)\n";
std::cout << "first=" << out.front() << " last=" << out.back() << '\n';
return 0;
}
XoshiroNative with -march=native
// Use XoshiroNative when compiling with -march=native / -mcpu for best performance.
#include <iostream>
#include <random/xoshiro_simd.hpp>
int main() {
prng::XoshiroNative rng(42u);
for (int i = 0; i < 4; ++i) std::cout << rng() << '\n';
std::cout << "uniform: " << rng.uniform() << '\n';
return 0;
}
OpenMP: per-thread streams
// Per-thread independent streams using the (seed, thread_id, cluster_id) constructor.
#include <cstdio>
#include <random/xoshiro.hpp>
#ifdef _OPENMP
#include <omp.h>
#endif
int main() {
#ifdef _OPENMP
#pragma omp parallel
{
prng::Xoshiro rng(42u, omp_get_thread_num());
const auto x = rng();
std::printf("thread %d: %llu\n", omp_get_thread_num(),
static_cast<unsigned long long>(x));
}
#else
prng::Xoshiro rng(42u, 0);
std::printf("single thread: %llu\n",
static_cast<unsigned long long>(rng()));
#endif
return 0;
}
Philox counter-based reproducibility
// Philox is a counter-based PRNG: same key + same counter => same output.
#include <iostream>
#include <random/philox_simd.hpp>
int main() {
using Rng = prng::PhiloxSIMD<4, 64, 10>;
Rng a(42u);
Rng b(42u);
for (int i = 0; i < 4; ++i) {
const auto x = a();
const auto y = b();
std::cout << "a=" << x << " b=" << y
<< (x == y ? " (match)\n" : " (MISMATCH)\n");
}
return 0;
}
Python examples
Quickstart
"""Quickstart: bulk-fill a numpy array and demonstrate reproducibility."""
from __future__ import annotations
import numpy as np
import simdrng
def main() -> None:
rng = simdrng.XoshiroSIMD(42)
assert isinstance(rng, np.random.Generator)
samples = rng.random(1_000_000) # dispatches to C++ bulk fill
print(f"Drew {samples.size} float64 samples; mean={samples.mean():.4f}")
# Reproducibility: re-seeding yields identical output.
a = simdrng.XoshiroSIMD(123).random(8)
b = simdrng.XoshiroSIMD(123).random(8)
assert np.array_equal(a, b), "identical seeds must yield identical streams"
print("Reproducibility check: OK")
# float32 fast path
f32 = rng.random(1_000_000, dtype=np.float32)
print(f"float32 samples dtype={f32.dtype} size={f32.size}")
if __name__ == "__main__":
main()
scipy integration
"""Use a simdrng generator as a drop-in source for scipy.stats distributions."""
from __future__ import annotations
import numpy as np
import simdrng
from scipy import stats
def main() -> None:
rng = simdrng.Philox4x64(seed=2026)
# scipy.stats accepts any numpy.random.Generator via random_state=.
samples = stats.norm.rvs(loc=0.0, scale=1.0, size=100_000, random_state=rng)
print(f"normal samples: mean={samples.mean():.3f} std={samples.std():.3f}")
expo = stats.expon.rvs(scale=2.0, size=100_000, random_state=rng)
print(f"expon samples: mean={expo.mean():.3f} (expected 2.0)")
if __name__ == "__main__":
main()
Benchmark vs numpy
"""Quick timing comparison: simdrng vs numpy.random.default_rng."""
from __future__ import annotations
import time
import numpy as np
import simdrng
N = 10_000_000
def time_it(label: str, fn) -> None:
t0 = time.perf_counter()
out = fn()
dt = time.perf_counter() - t0
rate = out.size / dt / 1e6
print(f"{label:<28s} {dt*1e3:7.2f} ms {rate:8.1f} Msamples/s")
def main() -> None:
np_rng = np.random.default_rng(42)
x_rng = simdrng.XoshiroSIMD(42)
p_rng = simdrng.Philox4x64(42)
time_it("numpy.default_rng", lambda: np_rng.random(N))
time_it("simdrng.XoshiroSIMD", lambda: x_rng.random(N))
time_it("simdrng.Philox4x64", lambda: p_rng.random(N))
if __name__ == "__main__":
main()