Initialize multiple distributions using the same seed or same random stream?

295 views Asked by At

I have a Java simulation in which I need to use Uniform, Exponential, Poisson, and Gamma distributions - and I need to initialize the random stream and/or each of these distributions with the same seed (so that I can exactly reproduce a trajectory given a fixed seed).

I am using Parallel Colt (which is a multithreaded version of Colt).

For Uniform, I could properly seed a DoubleUniform object (after importing from cern.jet.random.tdouble.DoubleUniform) as:

int fixedSeed = 12345;
doubleUniformDist = new DoubleUniform (0.0, 1.0, fixedSeed);

However, for Exponential, Poisson, and Gamma distributions (all in cern.jet.random.tdouble), I cannot do the same by passing the fixedSeed - because they expect a DoubleRandomEngine object to be passed:

Constructor Summary

Exponential(double lambda, DoubleRandomEngine randomGenerator)
Constructs a Negative Exponential distribution.

Poisson(double mean, DoubleRandomEngine randomGenerator)
Constructs a poisson distribution.

Gamma(double alpha, double lambda, DoubleRandomEngine randomGenerator)
Constructs a Gamma distribution.

Is there a way to initialize these (Exponential, Poisson, and Gamma) the same way as I did with Uniform? Or should I instantiate a parent/base class (if so, how?) in cern.jet.random.tdouble from which all these classes have been extended?

Notes:

  1. Again, I'd like to have a single random stream (so that all my distributions could use random numbers from that stream) - this is very important for reproducibility.
  2. An example simulation may need to sample these distributions millions of times (in total) - so performance/speed is always an issue.
3

There are 3 answers

4
Andrew Rueckert On

It looks like DoubleMersenneTwister, which extends DoubleRandomEngine, allows you to set a seed in its constructor.

5
pjs On

Random variate generation of all sorts begins with Uniform(0,1) random numbers. The last argument in the constructors for Exponential, Poisson, and Gamma is for you to provide a source of Uniform(0,1)'s which extends the DoubleRandomEngine abstract class. It looks like Parallel Colt provides half a dozen such implementations. You need to pick one of those, create a separate but identically seeded instance for each of the distribution you want to generate, and then construct each your distribution objects with one of the DoubleRandomEngine instances you created. That way the underlying uniform value streams will be identical, and can be suitably transformed into the distributions you want.

Here's a fairly minimalist implementation:

import cern.jet.random.tdouble.*;
import cern.jet.random.tdouble.engine.*;

public class RNG {
  public static void main(String[] args) {
    // create two instances of Mersenne Twister, seeded identically
    DoubleRandomEngine twister1 = new DoubleMersenneTwister(42);
    DoubleRandomEngine twister2 = new DoubleMersenneTwister(42);
    // print ten values from each, to show they produce identical U(0,1)'s'
    for(int i = 0; i < 10; ++i) {
      System.out.println(twister1.nextDouble() + ", " + twister2.nextDouble());
    }
    System.out.println("\nNow for some exponentials...\n");
    // instantiate two exponentials using our two twisters as the
    // underlying random engine
    Exponential exp1 = new Exponential(3, twister1);
    Exponential exp2 = new Exponential(3, twister2);
    // and print 10 of 'em to show they're synchronized.
    for(int i = 0; i < 10; ++i) {
      System.out.println(exp1.nextDouble() + ", " + exp2.nextDouble());
    }
  }
}

Since the Exponential class transforms the supplied stream of uniforms produced by the "engine" into exponential RVs, and the uniforms are being identically seeded, the exponentials are de facto identically seeded.

0
YaleBD On

All, thank you - I got it fixed. Looks like the varying numbers I was getting earlier were due to my use of staticNextDouble() methods - which was not right for my purpose, because staticNextDouble() uses the random number generator's internal/default seed, bypassing the fixed external seed I was supplying.

After replacing the staticNextDouble() calls with just nextDouble(), I can now exactly reproduce a trajectory given a fixed external seed.