random.hpp

00001 /*
00002 This file is part of GraphLab.
00003 
00004 GraphLab is free software: you can redistribute it and/or modify
00005 it under the terms of the GNU Lesser General Public License as 
00006 published by the Free Software Foundation, either version 3 of 
00007 the License, or (at your option) any later version.
00008 
00009 GraphLab is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU Lesser General Public License for more details.
00013 
00014 You should have received a copy of the GNU Lesser General Public 
00015 License along with GraphLab.  If not, see <http://www.gnu.org/licenses/>.
00016 */
00017 
00018 #ifndef GRAPHLAB_RANDOM_HPP
00019 #define GRAPHLAB_RANDOM_HPP
00020 
00021 #include <cstdlib>
00022 #include <stdint.h>
00023 
00024 
00025 #include <vector>
00026 #include <limits>
00027 #include <algorithm>
00028 
00029 #include <boost/random.hpp>
00030 #include <graphlab/util/timer.hpp>
00031 #include <graphlab/parallel/pthread_tools.hpp>
00032 
00033 namespace graphlab {
00034 
00035   /**
00036    * \ingroup random
00037    * A collection of thread safe random number routines.  Each thread
00038    * is assigned its own generator however assigning a seed affects
00039    * all current and future generators.
00040    */
00041   namespace random {        
00042 
00043 
00044     ///////////////////////////////////////////////////////////////////////
00045     //// Underlying generator definition
00046 
00047 
00048 
00049     namespace distributions {
00050       /**
00051        * The uniform distribution struct is used for partial function
00052        * specialization. Generating uniform random real numbers is
00053        * accomplished slightly differently than for integers.
00054        * Therefore the base case is for integers and we then
00055        * specialize the two real number types (floats and doubles).
00056        */
00057       template<typename IntType>
00058       struct uniform {
00059         typedef boost::uniform_int<IntType> distribution_type;
00060         template<typename RealRNG, typename DiscreteRNG>
00061         static inline IntType sample(RealRNG& real_rng, 
00062                                      DiscreteRNG& discrete_rng, 
00063                                      const IntType& min, const IntType& max) {
00064           return distribution_type(min, max)(discrete_rng);
00065         }
00066       };
00067       template<>
00068       struct uniform<double> {
00069         typedef boost::uniform_real<double> distribution_type;
00070         template<typename RealRNG, typename DiscreteRNG>
00071         static inline double sample(RealRNG& real_rng, 
00072                                     DiscreteRNG& discrete_rng, 
00073                                     const double& min, const double& max) {
00074           return distribution_type(min, max)(real_rng);
00075         }
00076       };
00077       template<>
00078       struct uniform<float> {
00079         typedef boost::uniform_real<float> distribution_type;
00080         template<typename RealRNG, typename DiscreteRNG>
00081         static inline float sample(RealRNG& real_rng, 
00082                                   DiscreteRNG& discrete_rng, 
00083                                   const float& min, const float& max) {
00084           return distribution_type(min, max)(real_rng);
00085         }
00086       };
00087 
00088     };
00089 
00090     /**
00091      * The generator class is the base underlying type used to
00092      * generate random numbers.  User threads should use the functions
00093      * provided in the random namespace.
00094      */
00095     class generator {
00096     public:
00097       // base Generator types
00098       typedef boost::lagged_fibonacci607 real_rng_type;
00099       typedef boost::mt11213b            discrete_rng_type;  
00100       typedef boost::rand48              fast_discrete_rng_type;       
00101     
00102       //! Seed the generator using the default seed
00103       inline void seed() {
00104         mut.lock();
00105         real_rng.seed();
00106         discrete_rng.seed();
00107         fast_discrete_rng.seed();
00108         mut.unlock();
00109       }
00110 
00111       //! Seed the generator nondeterministically
00112       void nondet_seed();
00113 
00114 
00115       //! Seed the generator using the current time in microseconds
00116       inline void time_seed() {
00117         seed( graphlab::timer::usec_of_day() );
00118       }
00119 
00120       //! Seed the random number generator based on a number
00121       void seed(size_t number) {
00122         mut.lock();
00123         fast_discrete_rng.seed(number);
00124         real_rng.seed(fast_discrete_rng);
00125         discrete_rng.seed(fast_discrete_rng);
00126         mut.unlock();
00127       }
00128       
00129       //! Seed the generator using another generator
00130       void seed(generator& other){
00131         mut.lock();
00132         real_rng.seed(other.real_rng);
00133         discrete_rng.seed(other.discrete_rng);
00134         fast_discrete_rng.seed(other.fast_discrete_rng());
00135         mut.unlock();
00136       } 
00137    
00138       /**
00139        * Generate a random number in the uniform real with range [min,
00140        * max) or [min, max] if the number type is discrete.
00141        */
00142       template<typename NumType>
00143       inline NumType uniform(const NumType min, const NumType max) { 
00144         mut.lock();
00145         const NumType result = distributions::uniform<NumType>::
00146           sample(real_rng, discrete_rng, min, max);
00147         mut.unlock();
00148         return result;
00149       } // end of uniform
00150 
00151       /**
00152        * Generate a random number in the uniform real with range [min,
00153        * max) or [min, max] if the number type is discrete.
00154        */
00155       template<typename NumType>
00156       inline NumType fast_uniform(const NumType min, const NumType max) { 
00157         mut.lock();
00158         const NumType result = distributions::uniform<NumType>::
00159           sample(real_rng, fast_discrete_rng, min, max);
00160         mut.unlock();
00161         return result;
00162       } // end of fast_uniform
00163 
00164 
00165       /**
00166        * Generate a random number in the uniform real with range [min,
00167        * max);
00168        */
00169       inline double gamma(const double alpha = double(1)) {
00170         boost::gamma_distribution<double> gamma_dist(alpha);
00171         mut.lock();
00172         const double result = gamma_dist(real_rng);
00173         mut.unlock();
00174         return result;
00175       } // end of gamma
00176 
00177       /**
00178        * Generate a gaussian random variable with zero mean and unit
00179        * variance.
00180        */
00181       inline double gaussian(const double mean = double(0), 
00182                              const double var = double(1)) {
00183         boost::normal_distribution<double> normal_dist(mean,var);
00184         mut.lock();
00185         const double result = normal_dist(real_rng);
00186         mut.unlock();
00187         return result;
00188       } // end of gaussian
00189 
00190       inline bool bernoulli(const double p = double(0.5)) {
00191         boost::bernoulli_distribution<double> dist(p);
00192         mut.lock();
00193         const double result(dist(discrete_rng));
00194         mut.unlock();
00195         return result;
00196       } // end of bernoulli
00197 
00198       inline bool fast_bernoulli(const double p = double(0.5)) {
00199         boost::bernoulli_distribution<double> dist(p);
00200         mut.lock();
00201         const double result(dist(fast_discrete_rng));
00202         mut.unlock();
00203         return result;
00204       } // end of bernoulli
00205 
00206 
00207       /**
00208        * Draw a random number from a multinomial
00209        */
00210       size_t multinomial(const std::vector<double>& prb) {
00211         ASSERT_GT(prb.size(),0);
00212         if (prb.size() == 1) { return 0; }
00213         double sum(0);
00214         for(size_t i = 0; i < prb.size(); ++i) {
00215           ASSERT_GE(prb[i], 0); // Each entry must be P[i] >= 0
00216           sum += prb[i];
00217         }
00218         ASSERT_GT(sum, 0); // Normalizer must be positive
00219         // actually draw the random number
00220         const double rnd(uniform<double>(0,1));
00221         size_t ind = 0;
00222         for(double cumsum(prb[ind]/sum); 
00223             rnd > cumsum && (ind+1) < prb.size(); 
00224             cumsum += (prb[++ind]/sum));
00225         return ind;
00226       } // end of multinomial
00227 
00228       
00229       /** 
00230        * Shuffle a standard vector
00231        */ 
00232       template<typename T>
00233       void shuffle(std::vector<T>& vec) { shuffle(vec.begin(), vec.end()); }
00234 
00235       /** 
00236        * Shuffle a range using the begin and end iterators
00237        */ 
00238       template<typename Iterator>
00239       void shuffle(Iterator begin, Iterator end) {
00240         mut.lock();
00241         shuffle_functor functor(*this);
00242         std::random_shuffle(begin, end, functor);
00243         mut.unlock();
00244       }
00245 
00246     private:
00247       //////////////////////////////////////////////////////
00248       /// Data members
00249       struct shuffle_functor {
00250         generator& gen;
00251         inline shuffle_functor(generator& gen) : gen(gen) { }
00252         inline std::ptrdiff_t operator()(std::ptrdiff_t end) {
00253           return distributions::uniform<ptrdiff_t>::
00254             sample(gen.real_rng, gen.fast_discrete_rng, 0, end-1);
00255         }
00256       };
00257 
00258       
00259       //! The real random number generator
00260       real_rng_type real_rng;
00261       //! The discrete random number generator
00262       discrete_rng_type discrete_rng;
00263       //! The fast discrete random number generator
00264       fast_discrete_rng_type fast_discrete_rng;
00265       //! lock used to access local members
00266       mutex mut;      
00267     }; // end of class generator
00268 
00269 
00270 
00271 
00272 
00273 
00274 
00275 
00276 
00277 
00278 
00279 
00280 
00281 
00282 
00283     /**
00284      * \ingroup random
00285      * Seed all generators using the default seed
00286      */
00287     void seed();
00288 
00289     /**
00290      * \ingroup random
00291      * Seed all generators using an integer
00292      */
00293     void seed(size_t seed_value);
00294 
00295     /**
00296      * \ingroup random
00297      * Seed all generators using a nondeterministic source
00298      */
00299     void nondet_seed();
00300 
00301     /**
00302      * \ingroup random
00303      * Seed all generators using the current time in microseconds
00304      */
00305     void time_seed();
00306     
00307 
00308     /**
00309      * \ingroup random
00310      * Get the local generator
00311      */
00312     generator& get_source();
00313 
00314     /**
00315      * \ingroup random
00316      * Generate a random number in the uniform real with range [min,
00317      * max) or [min, max] if the number type is discrete.
00318      */
00319     template<typename NumType>
00320     inline NumType uniform(const NumType min, const NumType max) { 
00321       return get_source().uniform<NumType>(min, max);
00322     } // end of uniform
00323     
00324     /**
00325      * \ingroup random
00326      * Generate a random number in the uniform real with range [min,
00327      * max) or [min, max] if the number type is discrete.
00328      */
00329     template<typename NumType>
00330     inline NumType fast_uniform(const NumType min, const NumType max) { 
00331       return get_source().fast_uniform<NumType>(min, max);
00332     } // end of fast_uniform
00333     
00334     /**
00335      * \ingroup random
00336      * Generate a random number between 0 and 1
00337      */
00338     inline double rand01() { return uniform<double>(0, 1); }
00339 
00340     /**
00341      * \ingroup random
00342      * Simulates the standard rand function as defined in cstdlib
00343      */
00344     inline int rand() { return fast_uniform(0, RAND_MAX); }
00345 
00346 
00347     /**
00348      * \ingroup random
00349      * Generate a random number from a gamma distribution.
00350      */
00351     inline double gamma(const double alpha = double(1)) {
00352       return get_source().gamma(alpha);
00353     }
00354 
00355 
00356 
00357     /**
00358      * \ingroup random
00359      * Generate a gaussian random variable with zero mean and unit
00360      * variance.
00361      */
00362     inline double gaussian(const double mean = double(0), 
00363                     const double var = double(1)) {
00364       return get_source().gaussian(mean, var);
00365     }
00366 
00367     /**
00368      * \ingroup random
00369      * Draw a sample from a bernoulli distribution
00370      */
00371     inline bool bernoulli(const double p = double(0.5)) {
00372       return get_source().bernoulli(p);
00373     }
00374 
00375     /**
00376      * \ingroup random
00377      * Draw a sample form a bernoulli distribution using the faster generator
00378      */
00379     inline bool fast_bernoulli(const double p = double(0.5)) {
00380       return get_source().fast_bernoulli(p);
00381     }
00382 
00383     /**
00384      * \ingroup random
00385      * Generate a draw from a multinomial.  This function
00386      * automatically normalizes as well.
00387      */
00388     inline size_t multinomial(const std::vector<double>& prb) {
00389       return get_source().multinomial(prb);
00390     }
00391 
00392 
00393     /** 
00394      * \ingroup random
00395      * Shuffle a standard vector
00396      */ 
00397     template<typename T>
00398     void shuffle(std::vector<T>& vec) { 
00399       get_source().shuffle(vec); 
00400     }
00401 
00402     /** 
00403      * \ingroup random
00404      * Shuffle a range using the begin and end iterators
00405      */ 
00406     template<typename Iterator>
00407     void shuffle(Iterator begin, Iterator end) {
00408       get_source().shuffle(begin, end);
00409     }
00410 
00411   }; // end of random 
00412 }; // end of graphlab
00413 
00414 
00415 #endif
00416