tandem rng tandem-java GitHub

API

Use

import io.github.tandemrng.Tandem;

Tandem rng = Tandem.seed(42);                  // 128-bit seed with whitening, default K
double x = rng.nextDouble();
int[] words = new int[1 << 20];
rng.fill(words);                               // the values of repeated nextInt()
int i = rng.nextInt(10);                       // uniform in [0, 10), Lemire
double g = rng.nextGaussian();                 // 1024-layer ziggurat of one long draw
double e = rng.nextExponential();              // -ln(1 - u) from one double draw
Tandem worker = rng.split(7);                  // by index, from the key alone
Tandem[] kids = rng.fork(4);                   // from the current block, parent moves on
double y = rng.atDouble(1000);                 // element 1000 of the fill from here, no move

Reference

Details

More use

Raw keys and other chunk lengths:

Tandem a = new Tandem(new int[] {1, 2, 3, 4}, 0L, 8);   // key words, position, K
Tandem b = Tandem.seed(lo, hi, 64);                     // 128-bit seed as two longs, K = 64

More draws and accessors:

double[] z = rng.nextComplexDouble();          // {re, im}, two double draws
float[] ex = new float[1 << 20];
rng.fillExponential(ex);                       // the values of repeated nextExponentialFloat()
ChoiceTable t = new ChoiceTable(new double[] {1, 2, 3, 4});
int[] picks = new int[1000];
rng.fillChoice(picks, t);                      // index i with probability w[i] / sum(w)
int[] key = rng.key();
long position = rng.position();
int k = rng.chunkLength();

With the JDK and Commons RNG:

import java.util.random.RandomGenerator;
import org.apache.commons.rng.UniformRandomProvider;

RandomGenerator r = Tandem.seed(42);
double[] samples = r.doubles(1000).toArray();
Collections.shuffle(list, r);                  // RandomGenerator overload
UniformRandomProvider p = new TandemProvider(Tandem.seed(42));

Positions are unsigned 64-bit values held in a long. Tandem.seed(long) reads its argument as an unsigned 64-bit seed. belowU32 and belowU64 take their bound as an unsigned value, and a bound of 0 returns 0 and consumes one draw (specification Appendix A), so they reach ranges above Integer.MAX_VALUE and Long.MAX_VALUE.

Parallel use

Element i of a fill is draw i, so ranks, threads or devices that start at the position of their first element, or draw from split(task), reproduce a serial run for any decomposition (Appendix B of the spec).

A generator is not thread-safe. Give each thread its own child from split or fork.