/*---------------------------------------------------------------------------* * IT++ * *---------------------------------------------------------------------------* * Copyright (c) 1995-2004 by Tony Ottosson, Thomas Eriksson, Pål Frenger, * * Tobias Ringström, and Jonas Samuelsson. * * * * Permission to use, copy, modify, and distribute this software and its * * documentation under the terms of the GNU General Public License is hereby * * granted. No representations are made about the suitability of this * * software for any purpose. It is provided "as is" without expressed or * * implied warranty. See the GNU General Public License for more details. * *---------------------------------------------------------------------------*/ /*! \file \brief Definition of classes for random number generators \author Tony Ottosson The Random_Generator class is built on the MersenneTwister MTRand class code by Richard J. Wagner. See http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html for details. The code is distributed under the GPL license. The Mersenne Twister is an algorithm for generating random numbers. It was designed with consideration of the flaws in various other generators. The period, 2^19937-1, and the order of equidistribution, 623 dimensions, are far greater. The generator is also fast; it avoids multiplication and division, and it benefits from caches and pipelines. For more information see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html Reference: M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. Copyright (C) 2001 Richard J. Wagner This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA The original code included the following notice: Copyright (C) 1997, 1999 Makoto Matsumoto and Takuji Nishimura. When you use this, send an email to: matumoto@math.keio.ac.jp with an appropriate reference to your work. It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu when you write. 1.24 2004/09/30 10:21:23 */ #ifndef __random_h #define __random_h #include #include "base/binary.h" #include "base/vec.h" #include "base/mat.h" #include "base/scalfunc.h" #include "base/operators.h" namespace itpp { //! Definition of M_2PI = 2*pi #define M_2PI 6.28318530717958647692 /*! \defgroup randgen Random Number Generation */ /*! \brief Base class for random (stochastic) sources. \ingroup randgen Uses Marsienne Twister random generator. */ class Random_Generator { public: //! Construct a new Random_Generator. Random_Generator(); //! Set the seed to a semi-random value (based on time and process id). void randomize(); //! Reset the source. The same sequance will be generated as the last time. void reset(); //! Reset the source after setting the seed to seed. void reset(unsigned int seed); //! Return a uniformly distributed [0,2^32-1] integer. unsigned int random_int() { if( left == 0 ) reload(); --left; register unsigned int s1; s1 = *pNext++; s1 ^= (s1 >> 11); s1 ^= (s1 << 7) & 0x9d2c5680U; s1 ^= (s1 << 15) & 0xefc60000U; return ( s1 ^ (s1 >> 18) ); } //! Return a uniformly distributed (0,1) value. [2^¯33,1-2^-33] in steps of 2^-32. double random_01() { return ( (double(random_int()) + 0.5) * 2.3283064365386963e-10 ); } //*2^-32+2^-33 //! Return a uniformly distributed [0,1] value. double random_01_closed() { return ( double(random_int()) * 2.3283064370807974e-10 ); } // * 1/(2^32-1) //! Save current full state of generator in memory void get_state(ivec &out_state); //! Resume the state saved in memory. Clears memory. void set_state(ivec &new_state); protected: private: //! unsigned long lastSeed; //! static const unsigned int MAXINT; // largest value from randInt() //! static const unsigned int MAGIC; // magic constant //! static unsigned int state[624]; // internal state //! static unsigned int *pNext; // next value to get from state //! static int left; // number of values left before reload needed //! void set_seed( unsigned int oneSeed ); //! void set_seed( unsigned int *const bigSeed ); //! void reload() { // Generate N new values in state // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) register unsigned int *p = state; register int i; for( i = 624 - 397; i--; ++p ) *p = twist( p[397], p[0], p[1] ); for( i = 397; --i; ++p ) *p = twist( p[397-624], p[0], p[1] ); *p = twist( p[397-624], p[0], state[0] ); left = 624, pNext = state; } //! unsigned int hiBit( const unsigned int& u ) const { return u & 0x80000000U; } //! unsigned int loBit( const unsigned int& u ) const { return u & 0x00000001U; } //! unsigned int loBits( const unsigned int& u ) const { return u & 0x7fffffffU; } //! unsigned int mixBits( const unsigned int& u, const unsigned int& v ) const { return hiBit(u) | loBits(v); } //! unsigned int twist( const unsigned int& m, const unsigned int& s0, const unsigned int& s1 ) const { return m ^ (mixBits(s0,s1)>>1) ^ (loBit(s1) ? MAGIC : 0U); } // static unsigned int hash( time_t t, clock_t c ); }; //! \addtogroup randgen //!@{ //! Set the seed of the Global Random Number Generator void RNG_reset(unsigned long seed); //! Set the seed of the Global Random Number Generator to the same as last reset/init void RNG_reset(); //! Set a random seed for the Global Random Number Generator. void RNG_randomize(); //! Save current full state of generator in memory void RNG_get_state(ivec &state); //! Resume the state saved in memory void RNG_set_state(ivec &state); //!@} /*! \brief Bernoulli distribution \ingroup randgen */ class Bernoulli_RNG { public: //! Binary source with probability prob for a 1 Bernoulli_RNG(double prob) { setup(prob); } //! Binary source with probability prob for a 1 Bernoulli_RNG() { p=0.5; } //! set the probability void setup(double prob) { it_assert(prob>=0.0 && prob<=1.0, "The bernoulli source probability must be between 0 and 1"); p = prob; } //! return the probability double get_setup() const { return p; } //! Get one sample. bin operator()() { return sample(); } //! Get a sample vector. bvec operator()(int n) { bvec temp(n); sample_vector(n, temp); return temp; } //! Get a sample matrix. bmat operator()(int h, int w) { bmat temp(h, w); sample_matrix(h, w, temp); return temp; } //! Get a sample bin sample() { return bin( RNG.random_01() < p ? 1 : 0 ); } //! Get a sample vector. void sample_vector(int size, bvec &out) { out.set_size(size, false); for (int i=0; i= 1. || r2 < 1E-30); r2 = std::sqrt(std::log(r2)*(-2./r2)); x *= r2; y *= r2; odd = 1; return x; } //! Get a Normal distributed (0,1) vector void sample_vector(int size, vec &out) { out.set_size(size, false); for (int i=0; i mean, double variance) { setup(mean, variance); } //! Constructor. Set mean and variance. Complex_Normal_RNG() { m = 0.0; sigma=1.0; } //! Set mean and variance void setup(complex mean, double variance) { m = mean; sigma = std::sqrt(variance); } //! Get mean and variance void get_setup(complex &mean, double &variance) { mean = m; variance = sigma*sigma; } //! Get one sample. complex operator()() { return sigma*sample()+m; } //! Get a sample vector. cvec operator()(int n) { cvec temp(n); sample_vector(n, temp); return (sigma*temp+m); } //! Get a sample matrix. cmat operator()(int h, int w) { cmat temp(h,w); sample_matrix(h,w, temp); return (sigma*temp+m); } //! Get a Complex Normal (0,1) distributed sample complex sample() { double a = M_2PI*RNG.random_01(), b = std::sqrt(-std::log(RNG.random_01())); return complex(b * std::cos(a), b * std::sin(a)); } //! Get a Complex Normal (0,1) distributed vector void sample_vector(int size, cvec &out) { out.set_size(size, false); for (int i=0; i m; //! double sigma; //! Random_Generator RNG; }; /*! \brief Filtered normal distribution \ingroup randgen */ class AR1_Normal_RNG { public: //! Constructor. Set mean, variance, and correlation. AR1_Normal_RNG(double meanval=0.0, double variance=1.0, double rho=0.0); //! Set mean, variance, and correlation void setup(double meanval, double variance, double rho); //! Get mean, variance and correlation void get_setup(double &meanval, double &variance, double &rho) const; //! Set memory contents to zero void reset(); //! Get a single random sample double operator()() { return sample(); } //! Get a sample vector. vec operator()(int n); //! Get a sample matrix. mat operator()(int h, int w); protected: private: //! double sample(); //! double my_mean, mem, r, factr, mean, var, r1, r2; //! bool odd; //! Random_Generator RNG; }; /*! \brief Gauss_RNG is the same as Normal Source \ingroup randgen */ typedef Normal_RNG Gauss_RNG; /*! \brief AR1_Gauss_RNG is the same as AR1_Normal_RNG \ingroup randgen */ typedef AR1_Normal_RNG AR1_Gauss_RNG; /*! \brief Weibull distribution \ingroup randgen */ class Weibull_RNG { public: //! Constructor. Set lambda and beta. Weibull_RNG(double lambda=1.0, double beta=1.0); //! Set lambda, and beta void setup(double lambda, double beta); //! Get lambda and beta void get_setup(double &lambda, double &beta) { lambda=l; beta=b; } //! Get one sample. double operator()() { return sample(); } //! Get a sample vector. vec operator()(int n); //! Get a sample matrix. mat operator()(int h, int w); protected: private: //! double sample(); //! double l, b; //! double mean, var; //! Random_Generator RNG; }; /*! \brief Rayleigh distribution \ingroup randgen */ class Rayleigh_RNG { public: //! Constructor. Set sigma. Rayleigh_RNG(double sigma=1.0); //! Set sigma void setup(double sigma) { sig=sigma; } //! Get sigma double get_setup() { return sig; } //! Get one sample. double operator()() { return sample(); } //! Get a sample vector. vec operator()(int n); //! Get a sample matrix. mat operator()(int h, int w); protected: private: //! double sample(); //! double sig; //! Random_Generator RNG; }; /*! \brief Rice distribution \ingroup randgen */ class Rice_RNG { public: //! Constructor. Set sigma, and s. Rice_RNG(double sigma=1.0, double _s=1.0); //! Set sigma, and s void setup(double sigma, double _s) { sig=sigma; s=_s; } //! Get parameters void get_setup(double &sigma, double &_s) { sigma=sig; _s=s; } //! Get one sample. double operator()() { return sample(); } //! Get a sample vector. vec operator()(int n); //! Get a sample matrix. mat operator()(int h, int w); protected: private: //! double sample(); //! double sig, s; //! Random_Generator RNG; }; //! \addtogroup randgen //!@{ //! Generates a random bit (equally likely 0s and 1s) inline bin randb(void) { Bernoulli_RNG src; return src.sample(); } //! Generates a random bit vector (equally likely 0s and 1s) inline void randb(int size, bvec &out) { Bernoulli_RNG src; src.sample_vector(size, out); } //! Generates a random bit vector (equally likely 0s and 1s) inline bvec randb(int size) { bvec temp; randb(size, temp); return temp; } //! Generates a random bit matrix (equally likely 0s and 1s) inline void randb(int rows, int cols, bmat &out) { Bernoulli_RNG src; src.sample_matrix(rows, cols, out); } //! Generates a random bit matrix (equally likely 0s and 1s) inline bmat randb(int rows, int cols){ bmat temp; randb(rows, cols, temp); return temp; } //! Generates a random uniform (0,1) number inline double randu(void) { Uniform_RNG src; return src.sample(); } //! Generates a random uniform (0,1) vector inline void randu(int size, vec &out) { Uniform_RNG src; src.sample_vector(size, out); } //! Generates a random uniform (0,1) vector inline vec randu(int size){ vec temp; randu(size, temp); return temp; } //! Generates a random uniform (0,1) matrix inline void randu(int rows, int cols, mat &out) { Uniform_RNG src; src.sample_matrix(rows, cols, out); } //! Generates a random uniform (0,1) matrix inline mat randu(int rows, int cols) { mat temp; randu(rows, cols, temp); return temp; } //! Generates a random integer in the interval [low,high] inline int randi(int low, int high) { I_Uniform_RNG src; src.setup(low, high); return src(); } //! Generates a random ivec with elements in the interval [low,high] inline ivec randi(int size, int low, int high) { I_Uniform_RNG src; src.setup(low, high); return src(size); } //! Generates a random imat with elements in the interval [low,high] inline imat randi(int rows, int cols, int low, int high) { I_Uniform_RNG src; src.setup(low, high); return src(rows, cols); } //! Generates a random Rayleigh vector inline vec randray(int size, double sigma = 1.0) { Rayleigh_RNG src; src.setup(sigma); return src(size); } //! Generates a random Rice vector (See J.G. Poakis, "Digital Communications, 3rd ed." p.47) inline vec randrice(int size, double sigma = 1.0, double s = 1.0) { Rice_RNG src; src.setup(sigma, s); return src(size); } //! Generates a random complex Gaussian vector inline vec randexp(int size, double lambda = 1.0) { Exponential_RNG src; src.setup(lambda); return src(size); } //! Generates a random Gaussian (0,1) variable inline double randn(void) { Normal_RNG src; return src.sample(); } //! Generates a random Gaussian (0,1) vector inline void randn(int size, vec &out) { Normal_RNG src; src.sample_vector(size, out); } //! Generates a random Gaussian (0,1) vector inline vec randn(int size) { vec temp; randn(size, temp); return temp; } //! Generates a random Gaussian (0,1) matrix inline void randn(int rows, int cols, mat &out) { Normal_RNG src; src.sample_matrix(rows, cols, out); } //! Generates a random Gaussian (0,1) matrix inline mat randn(int rows, int cols){ mat temp; randn(rows, cols, temp); return temp; } //! Generates a random complex Gaussian (0,1) variable inline complex randn_c(void) { Complex_Normal_RNG src; return src.sample(); } //! Generates a random complex Gaussian (0,1) vector inline void randn_c(int size, cvec &out) { Complex_Normal_RNG src; src.sample_vector(size, out); } //! Generates a random complex Gaussian (0,1) vector inline cvec randn_c(int size){ cvec temp; randn_c(size, temp); return temp; } //! Generates a random complex Gaussian (0,1) matrix inline void randn_c(int rows, int cols, cmat &out) { Complex_Normal_RNG src; src.sample_matrix(rows, cols, out); } //! Generates a random complex Gaussian (0,1) matrix inline cmat randn_c(int rows, int cols) { cmat temp; randn_c(rows, cols, temp); return temp; } //!@} // -------------------- INLINES ---------------------------------------------- inline void Random_Generator::reset() { set_seed(lastSeed); } inline void Random_Generator::reset(unsigned int seed) { lastSeed = seed; set_seed(lastSeed); } inline void Random_Generator::set_seed( unsigned int oneSeed ) { // Seed the generator with a simple uint32 register unsigned int *s; register int i; for( i = 624, s = state; i--; *s = oneSeed & 0xffff0000, *s++ |= ( (oneSeed *= 69069U)++ & 0xffff0000 ) >> 16, (oneSeed *= 69069U)++ ) {} // hard to read, but fast reload(); } inline void Random_Generator::set_seed( unsigned int *const bigSeed ) { // Seed the generator with an array of 624 uint32's // There are 2^19937-1 possible initial states. This function // allows any one of those to be chosen by providing 19937 bits. // Theoretically, the array can contain any values except all zeroes. // Just call seed() if you want to get array from /dev/urandom register unsigned int *s = state, *b = bigSeed; register int i = 624; for( ; i--; *s++ = *b++ & 0xffffffff ) {} reload(); } inline double AR1_Normal_RNG::sample() { double s; if (odd) { r1 = RNG.random_01(); r2 = RNG.random_01(); s = std::sqrt(factr * std::log(r2)) * std::cos(M_2PI * r1); } else s = std::sqrt(factr * std::log(r2)) * std::sin(M_2PI * r1); odd = !odd; mem = s + r * mem; s = mem + mean; return s; } inline double Weibull_RNG::sample() { return ( std::pow(-std::log(RNG.random_01()), 1.0/b) / l ); } inline double Rayleigh_RNG::sample() { double r1, r2; double s1, s2, samp; r1 = RNG.random_01(); r2 = RNG.random_01(); s1 = std::sqrt(-2.0 * std::log(r2)) * std::cos(M_2PI * r1); s2 = std::sqrt(-2.0 * std::log(r2)) * std::sin(M_2PI * r1); // s1 and s2 are N(0,1) and independent samp = sig * hypot(s1, s2); return samp; } inline double Rice_RNG::sample() { double r1, r2; double s1, s2, samp; double m1 = 0.0; double m2 = std::sqrt(s*s - m1*m1); r1 = RNG.random_01(); r2 = RNG.random_01(); s1 = std::sqrt(-2.0 * std::log(r2)) * std::cos(M_2PI * r1); s2 = std::sqrt(-2.0 * std::log(r2)) * std::sin(M_2PI * r1); // s1 and s2 are N(0,1) and independent samp = hypot(sig*s1+m1, sig*s2+m2); return samp; } } //namespace itpp #endif // __random_h