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()