librandom 2.0 ============= [NOTE: This file is somewhat outdated; HEP 2010-05-28.] Key files --------- randomgen.h : all that's needed to draw random numbers gslrandomgen.h : required for creating rngs randomdev.h : non-uniform random deviates Usage ----- Files that only use random generators need only include randomgen.h. Files that implement RNG allocation and thus need access to the GslRandomGen class directly, need gslrandomgen.h as well. This will work even if GSL is not installed. Class RandomGen : abstract class allowing seeding and drawing GslRandomGen: derived class implementing GSL-style RNGs RandomDev : abstract class allowing drawing only GammaRandomDev : Gamma random dev NormalRandomDev: Normal random dev PoissonRanDev : Poisson random dev ExpRanDev : Exponential random dev Note: parameters influencing that shape of distributions, such as gamma order, can be set via set_* methods. Pure scaling paramaters must be applied to the deviate by the calling routine. Accessing RNGs -------------- All GSL and GSL-style RNGs are included in a list librandom::GslRandomGen::RngList . Before this list is accessed for the first time, it has to initialized by a call to librandom::GslRandomGen::build_rng_list() . If this function is called more than once during a program run, only the first call will build the list, all further calls do nothing. The list contains gsl_rng_type*, which can be used to access the pointers. The use is illustrated by randomtest.cpp (see also sli/rangen.cc): #include "gslrandomgen.h" librandom::GslRngListType::iterator t; for (t = librandom::GslRandomGen::RngList.begin(); t != librandom::GslRandomGen::RngList.end(); ++t ) { std::cout << std::setw(17) << (*t)->name << " : "; rng = new librandom::GslRandomGen(*t, seed); rungen(rng, Ngen); // run generator test delete(rng); } Note that functions using librandom do not have to worry about the presence or absence of the GSL: all GSL dependencies are completely contained within librandom. The only difference will be that far fewer RNG are available when the GSL is missing or too old. Random Deviates --------------- In non-threaded environments (SLI interpreter, bkernel), random deviate sources should be created with an RngPtr to a random generator as argument, e.g., new NormalRandomDev(myrng). A pointer to the RNG is then stored in the RandomDev object, and the RNG is used for all subsequent RNG generation. In threaded environments (nest), each call to a random deviate source may come from a different thread, and consequently, a different RngClient has to be used on each call. Therefore, create the RandomDev object WITHOUT an rng pointer and rather pass the pointer to the threads RngClient object each time you draw a random number. See nest/noise_generator.cpp for an example. Verification ------------ Mersenne Twister only. Generated four streams of 20MB ulrands each (using gsl_rng_get) in four-thread nest-kernel. Ran DIEHARD on all streams. None showed problems. Computed covariance of streams (up to lag 1000) and found no correlations. Exponential, gamma and normal deviates with MT backend tested: 1000x1000 random number Kolmogorov-Smirnov test with meta-test on resulting 1000 p-values. The Poisson deviate was similarly tested with 1000x1000 Chi2 tests. All tests were fine. Threads: Random Random Numbers ------------------------------ Threading leads to a further randomization of random numbers: if you are running N threads and set up N nodes that each dump RNGs to files, then each THREAD will have an RN buffer, which is refilled from the central RNG when necessary. On each time step, each node is assigned to some thread essentially randomly, so the association between RN buffers and nodes may change during simulations and will almost surely be different from run to run [unless you put your machine into identical initial conditions, including resetting the system clock (cron jobs!) ...]. There is just no way to exactly reproduce multi-threaded noisy runs! If you want to check the RNG mechnisms for principal reproducibility, you could dump large amounts of random numbers, and then pool across all nodes, sort, and compare those lists across trials. You should see considerable overlap, but still no perfect agreement, if, say, thread 2 gets used heavily in run 1, and thread 3 in run 2. If you want to be really sure, try dumping thread numbers along with the RNs and then re-sort by thread. Each thread should produce the same stream on each run, if the RNG seed is held constant. For the RNGs this means that any randomly selected subset of RNs from an RNG should pass all the statistical tests that the entire sequence passes [any good RNG should do so, but still ...]. Integration with GSL -------------------- GSL 1.2 or newer should be installed to get a choice of random generators. If GSL is not installed, only the MersenneTwister RNG will be available from the GSL. The MT is the default generator in any case. To allow for smooth integration in presence/absence of the GSL, core GSL files have been copied to librandom. These files are prefixed with gsl_*. They have been slightly adapted from their GSL originals and are "emptied" by #ifndef's if GSL is available. GSL header files are placed in synod2/include/gsl_gsl_*.h, since they might be used by other parts of NEST that make use of the GSL. The GSL-replacement code is compiled only if no acceptable GSL is available. Random deviates cannot be implemented by linking against the GSL, since the pertaining GSL functions require a gsl_rng* as input. This cannot be reconciled with the necessity of RNG streaming in multithreaded NEST. We therefore have to adapt GSL code to take RandomGen objects instead of gsl_rng*. gsl_gsl_errno.h can be used to map all GSL_ERROR and _WARNING calls to failing asserts as a quick hack when making this adaptation. Adding Non-GSL Random Generators -------------------------------- Further RNGs can be added, but should have the same interface as GSL RNGs. Such RNGs are called GSL-style RNGs. An example is gsl_knuthlfg. All GSL-style RNGs must provide an extern const gsl_rng_type * pointer to the generator in their *.h file, and this pointer must be entered in GslRandomGen::build_rng_list() to make the RNG available. Integration with SLI -------------------- The SLI module RandomNumbers, defined in rangen.*, creates a dictionray rngdict, which contains all defined GSL rng types. Elements of this dict have type RngTypeDatum, with name "rngtypetype", corresponding to or _gt_ for tries. The actual RNG objects created from RngTypeDatum and a seed are of type RngDatum, name "rngtyp", or _g_ for tries; they are lockptr objects.