tandem rng tandem-rs GitHub

API

Use

use tandem_rng::{ChoiceTable, Tandem};

let mut rng = Tandem::new(42);                 // 128-bit seed, default K
let x = rng.next_f64();
let mut words = vec![0u32; 1 << 20];
rng.fill_u32(&mut words);
let i = rng.below_u32(10);                     // uniform in 0..10, Lemire
let z = rng.normal_f64();                      // ziggurat from one u64 draw
let e = rng.exponential_f64();                 // -ln(1 - u) from one f64 draw
let table = ChoiceTable::new(&[1.0, 2.0, 3.0, 4.0]).unwrap();
let j = rng.choice(&table);                    // index 3 with probability 0.4
let worker = rng.split(7);                     // by index, from the key alone
let kids: Vec<Tandem> = rng.fork(4).collect(); // from the current block, parent moves on
use rand::{RngExt, SeedableRng};
use rand_distr::StandardNormal;

let mut rng = Tandem::seed_from_u64(42);       // the same generator as Tandem::new(42)
let z: f64 = rng.sample(StandardNormal);

Reference

rand distributions

tandem-rng = { git = "https://github.com/tandem-rng/tandem-rs", features = ["rand"] }
use rand::{RngExt, distr::Distribution};
use tandem_rng::{Below, Exp1, StandardNormal, Tandem};

let mut rng = Tandem::new(42);
let z: f64 = rng.sample(StandardNormal);       // rng.normal_f64()
let p: [f32; 2] = rng.sample(StandardNormal);  // rng.normal2_f32()
let e: f32 = rng.sample(Exp1);                 // rng.exponential_f32()
let i: u64 = rng.sample(Below(10u64));         // rng.below_u64(10)
let zs: Vec<f64> = StandardNormal.sample_iter(&mut rng).take(1000).collect();
Distribution Type Inherent draw
StandardNormal f64 normal_f64
StandardNormal f32 normal_f32
StandardNormal [f32; 2] normal2_f32
Exp1 f64, f32 exponential_f64, exponential_f32
Below(n) u32, u64 below_u32, below_u64
ChoiceTable u32 choice

GPU

tandem-rng = { git = "https://github.com/tandem-rng/tandem-rs", features = ["wgpu"] }
use tandem_rng::{Tandem, gpu::GpuFill};

let gpu = GpuFill::new_default().expect("a wgpu adapter");
let mut rng = Tandem::new(42);
let words: Vec<u32> = gpu.read_u32(&mut rng, 1 << 20);  // the values of rng.fill_u32
let out = gpu.device().create_buffer(&wgpu::BufferDescriptor {
    label: None,
    size: GpuFill::buffer_size(&rng, 32, 1 << 24),
    usage: wgpu::BufferUsages::STORAGE,
    mapped_at_creation: false,
});
let span = gpu.fill_words(&mut rng, &out, 1 << 24);     // stays on the GPU

GpuFill::new takes a device and queue the application owns. Every GPU fill advances the generator exactly as the CPU fill does. src/tandem.wgsl is the shader. WGSL has no 64-bit integers, so it builds the 32x32 to 64-bit product from 16-bit halves, which is exact. The feature pulls in std.

Parallel use

Element i of a fill is draw i, so any decomposition reproduces a serial run. See Appendix B.