1

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.
Kevin J. Chase
  • 3,856
  • 4
  • 21
  • 43
YaleBD
  • 143
  • 12

3 Answers3

0

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

Andrew Rueckert
  • 4,858
  • 1
  • 33
  • 44
  • Yes it does, and so does `MersenneTwister64`. But neither of these seem to have Exponential, Poisson, and Gamma distributions - so no way to seed those using the same seed that I could use to seed a `DoubleMersenneTwister`. – YaleBD Apr 04 '17 at 23:27
  • Er, I believe that you seed the DoubleMersenneTwister with a known seed, and then use _that_ to construct your Exponential, Poisson, and Gamma number generators. Or am I misunderstanding what you need? – Andrew Rueckert Apr 04 '17 at 23:31
  • Yes I can seed the `DoubleMersenneTwister` with a known seed, but cannot use that to construct an `Exponential` - because the `Exponential constructor` expects a `RandomEngine` object, and if I pass the `DoubleMersenneTwister`, the `Exponential` seems not to be seeded properly (I get different results with the same seed - which is wrong). – YaleBD Apr 05 '17 at 00:09
  • I think you understood my question perfectly - it's just that I am getting unexpected results doing it this way - so wondering what I am doing wrong? – YaleBD Apr 05 '17 at 00:12
0

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.

pjs
  • 18,696
  • 4
  • 27
  • 56
  • I tried this... picked MersenneTwister64: `MersenneTwister64 myMT = new MersenneTwister64 (seed);` Then created an Exponential as: `Exponential expDist = new Exponential (0.5, myMT)`; Hoping above would create a "separate but identically seeded instance" for the Exponential... – YaleBD Apr 05 '17 at 00:05
  • but then, I cannot reproduce an identical stream of random numbers. My guess is that the last argument in the Exponential constructor expects a RandomEngine object, but I am passing a MersenneTwister64 object – YaleBD Apr 05 '17 at 00:06
  • , which derives from RandomEngine. Not sure how to correctly transform the MersenneTwister64 (myMT) into a RandomEngine object before passing it to the Exponential constructor? Could you provide a minimal example? – YaleBD Apr 05 '17 at 00:06
  • So is there some reason this is not working for you? – pjs Apr 09 '17 at 15:30
  • thank you again - I got it fixed. Looks like the varying numbers were due to my use of `staticNextDouble()` methods - which was not right for my purpose, because `staticNextDouble()` uses the rng's internal/default seed, bypassing the seed I was supplying. @pjs – YaleBD Apr 14 '17 at 01:54
0

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.

YaleBD
  • 143
  • 12