Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members  

Tina's Random Number Generators' User Manual

Tina's Random Number Generators (TRNG) is a C++ parallel pseudo random number generators package.

Introduction

A lot of tasks in scientific computing can only be tackled by the power of parallel computers. Especially Monte Carlo simmulations are often well suited for parallel computers because of the inherent parallelism of these problems.

A straight forward way to generate pseudo random numbers in a parallel environment is to use the same serial pseudo random number generator on every processor but with (randomly chosen) different seeds or the same generator type but with different parameter sets. These methods are quiet arbitrary and it is not possible to enshure in every case that there are no correlations between the pseudo random number sequences.

To create a good pseudo random number generator for parallel applications we take an excellent sequential pseudo random number generator and distribute its numbers in a well defined way over the parallel jobs. There are two different approaches in distributing the numbers.

The two figures below illustrate these different methods.

A sequential pseudo random number generator's parallelisation using sequence splitting.

A sequential pseudo random number generator's parallelisation using leapfrog method.

A way to implement the leapfrog method could be to dedicate one processor to random number generation and spread these numbers via a network to the other processors. But this wold be a very inefficient way.

TRNG (Tina's Random Number Generators) is a library that gives you a powerfull tool for Monte Carlo simmulations on parallel computers. TRNG is a collection of random number generators specially designed for the needs of parallel Monte Carlo simmulations. With TRNG you can generate parallel streams of pseudo random numbers (with leapfrog or sequence splitting method) without any communication. Tina's Random Number Generators have a highly tuned implementation, have a long period and are empirically tested. Tina's Random Number Generators are easy to use and can be extendend by the user. It is a tool for your everyday work.

Software Distribution and Instalation

Tina's Random Number Generators are copyrighted by Heiko Bauke. You can contact the author of TRNG via electronic mail to heiko.bauke@physik.uni-magdeburg.de and get the software from http://tina.nat.uni-magdeburg.de/TRNG.

TRNG is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. This program is distributed without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the GNU General Public License for more details.

Download the latest tar file from http://tina.nat.uni-magdeburg.de/TRNG and unpack and untar the file. Change into the directory trng and type make. After you have become root type make install. That's all!

If you don't have the GNU compiler but the SUN-Workshop compiler use the makefile Makefile.SW and type make -f Makefile.SW and make -f Makefile.SW install.

If you have installed TRNG successfully you can compile programs that use Tina's Random Number Generators, just include the header file trng.h and use the linker flag -ltrng.

TRNG comes with some example- and test-programs. You may compile these programs with make examples (or make -f Makefile.SW examples). The binaries are located in the directory bin. To compile the MPI-examples you need a MPI-Implementation with C++-support like LAM (http://www.lam-mpi.org). Start these programs without commandline arguments to get a short description. The reader should investigate the following programs.

The author hopes that TRNG is a useful tool that fulfils your needs for parallel Monte Carlo simmulations. Please send feedback and bug reports to heiko.bauke@physik.uni-magdeburg.de.

An Example

In the following program we compute by a Monte Carlo simmulation. The program uses MPI.

// *************************************************************
// 
// Monte-Carlo-pi-Calulation
// 
// *************************************************************


#include <cstdlib>
#include <cmath>
#include <iostream>
#include <trng.h>
#include <mpi.h>

using namespace TRNG;
using namespace std;

int main(int argc, char *argv[]) {

  // number of points in quare
  const long all_samples=1000000l;

  // pseudo random number generator object
  LCG64 r;
  
  // MPI initialisation
  MPI::Init(argc, argv);

  // get rank and number of processes
  int size=MPI::COMM_WORLD.Get_size();
  int rank=MPI::COMM_WORLD.Get_rank();
  
  // split sequence of pseudo random numbers by leapfrog method
  r.split(size, rank);
  
  // no points in the quare
  long in=0l;

  // compute number of points per processor
  long num_samples=all_samples/size;
  // all_samples is not a multiple of size
  if (all_samples%size>rank)
    ++num_samples;

  for (long i=0l; i<num_samples; ++i) {
    double x=r.uniform();
    double y=r.uniform();
    // is point in square
    if (x*x+y*y<=1.0)
      // yes? increment in
      ++in;
  }

  // collect results and print pi
  long in_all;
  MPI::COMM_WORLD.Reduce(&in, &in_all, 1, MPI::LONG, MPI::SUM, 0);
  if (rank==0) {
    double pi=4.0*static_cast<double>(in_all)/static_cast<double>(all_samples);
    cout << "pi = " << pi << endl;
  }

  // quit MPI 
  MPI::Finalize();
  
  return EXIT_SUCCESS;
}

The first version of our program gives not rusults independent of number of processors. The second version shows how to make the program independent of the number of used processors. The trick is to use different generators to calculate the - and -coordinates.

// *************************************************************
//
// Monte-Carlo-pi-Calulation
// 
// *************************************************************


#include <cstdlib>
#include <cmath>
#include <iostream>
#include <trng.h>
#include <mpi.h>

using namespace TRNG;
using namespace std;

int main(int argc, char *argv[]) {

  // number of points in quare
  const long all_samples=1000000l;

  // pseudo random number generator object
  LCG64 rx;
  LCG64 ry;
  // jump 2^26 steps ahead; 2^26 >> all_samples
  ry.jump2(26);
  
  // MPI initialisation
  MPI::Init(argc, argv);

  // get rank and number of processes
  int size=MPI::COMM_WORLD.Get_size();
  int rank=MPI::COMM_WORLD.Get_rank();
  
  // split sequence of pseudo random numbers by leapfrog method
  rx.split(size, rank);
  ry.split(size, rank);
  
  // no points in the quare
  long in=0l;

  // compute number of points per processor
  long num_samples=all_samples/size;
  // all_samples is not a multiple of size
  if (all_samples%size>rank)
    ++num_samples;

  for (long i=0l; i<num_samples; ++i) {
    double x=rx.uniform();
    double y=ry.uniform();
    // is point in square
    if (x*x+y*y<=1.0)
      // yes? increment in
      ++in;
  }

  // collect results and print pi
  long in_all;
  MPI::COMM_WORLD.Reduce(&in, &in_all, 1, MPI::LONG, MPI::SUM, 0);
  if (rank==0) {
    double pi=4.0*static_cast<double>(in_all)/static_cast<double>(all_samples);
    cout << "pi = " << pi << endl;
  }

  // quit MPI 
  MPI::Finalize();
  
  return EXIT_SUCCESS;
}

If a TRNG function is called with an invalid argument this function throws an exception TRNG::error. The next example shows how to handle these exceptions.

#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <trng.h>

using namespace std;
using namespace TRNG;


int main(void) {
  ParkMiller R;

  cout << R.normal_dist() << endl;  
  try {
    R.jump2(16l);
    cout << "jumped forward" << endl;
    // you can't jump backwards
    R.jump2(-16l);
    cout << "jumped backward" << endl;
  }
  catch (exception &e) {
    cerr << "oops!! " << e.what() << endl;
  }
  catch (...) {
    cerr << "something else went wrong" << endl;
  }
  return EXIT_SUCCESS;
}

Sometimes it is desired to use a random number generator object as a function argument. Tina's random number genrators are implemented by a template technique. The next program is an example with a function with a random number generator as an argument.

#include <cstdlib>
#include <iostream>
#include <trng.h>

using namespace std;
using namespace TRNG;

template<class RNG_type>
void foo(RNG_type &R) {
  cout << R.rand() << " is a random number from generator " 
       << R.name() << endl;
}

int main(void) {
  ParkMiller R1;
  LCG64 R2;
  foo(R1);
  foo(R2);  
  return EXIT_SUCCESS;
}

How to extent TRNG

Youd cant find your favourite Tina's Random Number Generators? No problem! Yust extend TRNG to your needs. In the following example we implement an exclusive or lagged fibonacci generator. This example shows only how TRNG could be extented in principle. Sequence splitting and leapfrog method are implemented in a very stupid way. Unused values are just thrown away. Don't uses this generator in your applications. Doun't use exclusive or lagged fibonacci generator at all.

// ---------------------------------------------------------------------
// Time-stamp: <Montag, 21.04.2003, 14:27:38; edited by bauke>
// 
// Tina's random number generators TRNG
//
// lagged Fibonacci generator
//
// Copyright (C) 2001, 2002 Heiko Bauke
//
// heiko.bauke@physik.uni-magdeburg.de
//
// TRNG is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation. This program
// is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// ---------------------------------------------------------------------

#ifndef FIBONACCI_H
#define FIBONACCI_H

#include <trng.h>
#include <strstream>


// This is an example for extending Tina's random number generators.
// We implement a exclusive or lagged Fibonacci generator. 
//
//   r_i=r_(i-q) xor r_(i-p); p>q
//
// Sequence splitting and leapfrog method are implemented in a very stupid 
// way. Unused values are just thrown away. Don't use this generator in
// your applications.

// the template parametr determine the lags
template<long p1, long q1>
class Fibonacci : public TRNG::RNG<Fibonacci<p1, q1> > {
private:
  std::vector<long> r;
  long p, q, pointer, steps;
  TRNG::ParkMiller R_init;
public:
  // TRNG::user1_t for a user implemeneted rng
  static const TRNG::RNG_type type=TRNG::user1_t;

  const char * name(void) {
    std::ostrstream o;
    o << "Fibonacci(" << q << "," << p << ")";
    return o.str();
  }

  void reset(void) {
    steps=1l;
    max_val=0x7fffffffl;
    max_val2=max_val/2l;
  }

  void seed(long s) {
    R_init.seed(s);
    for (int i=0; i<p; ++i)
      r[i]=R_init.rand();
    pointer=0;
  }
  
  // calculate the next random number
  long rand(void) {
    long t;
    ++pointer;
    if (pointer==p)
      pointer=0l;
    r[pointer]=r[pointer]^r[(pointer+q)<p ? (pointer+q) : (pointer+q-p)];
    t=r[pointer];
    for (long i=1l; i<steps; ++i) {
      ++pointer;
      if (pointer==p)
        pointer=0l;
      r[pointer]=r[pointer]^r[(pointer+q)<p ? (pointer+q) : (pointer+q-p)];
    }
    return t;
  }
  
  void split(long s, long n) {
    if (s<1l || n>s || n<0l)
      throw TRNG::error("invalid arguments for Fibonacci::split");
    if (s>1l) {
      for (long i=0l; i<n; ++i)
        rand();
      steps*=s;
    }
  }

  void jump2(long s) {
    if (s<0l || s>63l)
      throw TRNG::error("invalid argument for Fibonacci::split");
    unsigned long long to=1ull<<s;
    for (unsigned long long i=0ull; i<to; ++i)
      rand();
  }

  void save_status(std::vector<long> &s) {
    s.resize(p+5);
    s[0]=type;
    s[1]=q;
    s[2]=p;
    s[3]=pointer;
    s[4]=steps;
    for (int i=0; i<p; ++i)
      s[i+5]=r[i];
  }

  void load_status(const std::vector<long> &s) {
    if (s[0]!=type)
      throw TRNG::error("Fibonacci::load_status wrong parameter");
    q=s[1];
    p=s[2];
    pointer=s[3];
    steps=s[4];
    r.resize(p);
      for (int i=0; i<p; ++i)
        r[i]=s[i+5];
  }

  Fibonacci & Fibonacci::operator=(TRNG::RNG<Fibonacci> &other) {
    if (this!=&other) {
      std::vector<long> s;
      other.save_status(s);
      load_status(s);
    }
    return *this;
  }
  
  Fibonacci(long seed_=0l) : r() {
    if (p1<0l || q1<0l || p1==q1)
      throw TRNG::error("bad arguments for Fibonacci::Fibonacci");
    if (p1>q1) {
      p=p1;
      q=q1;
    } else {
      p=q1;
      q=p1;
    }
    r.resize(p);
    reset();
    seed(seed_);
  }

  virtual ~Fibonacci() {};
};


#endif


Generated on Mon Apr 21 16:27:50 2003 for Tina's Random Number Generators by doxygen1.2.15