It is currently Mon Apr 24, 2017 12:29 am

All times are UTC - 5 hours




 Page 1 of 1 [ 1 post ] 
Author Message
 Post subject: c++11 Random Engines and Random Distributions
PostPosted: Fri Apr 15, 2016 7:38 pm 

Joined: Sat Aug 16, 2008 7:58 am
Posts: 447
I've been reading up on the standard library for c++11 about random generators and I noticed that within the Shader Engine we started to use one of these provided engines as opposed to using rand & srand that was used in the Game Engine. After reading several different websites, I've decided to create a nice little abstraction wrapper class to incorporate the various random engines and distributions. This is a header only class and this is what I've come up with.

#ifndef RANDOM_GENERATOR_H
#define RANDOM_GENERATOR_H

#include <limits>
#include <chrono>
#include <random>

class RandomEngine {
public:
   using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
      std::chrono::high_resolution_clock,
      std::chrono::steady_clock>;

   // Used To Determine Which Seeding Process To Use
   enum SeedType {
      USE_CHRONO_CLOCK,
      USE_RANDOM_DEVICE,
      USE_SEED_VALUE,   
      USE_SEED_SEQ,
   }; // SeedType

protected:
   RandomEngine(){}

   // Internal Helper Function
   // ---------------------------------------------------------------------------
   // getRandomDevice()
   static std::random_device& getRandomDevice() {
      static std::random_device device{};
      return device;
   } // getRandomDevice

public:
   // ---------------------------------------------------------------------------
   // getTimeNow()
   static unsigned int getTimeNow() {
      unsigned int now = static_cast<unsigned int>(Clock::now().time_since_epoch().count());
      return now;
   } // getTimeNow

   // ---------------------------------------------------------------------------
   // getDefaultRandomEngine()
   static std::default_random_engine& getDefaultRandomEngine( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
      static std::default_random_engine engine{};

      switch ( type ) {
         case USE_CHRONO_CLOCK: {
            engine.seed( getTimeNow() );
            break;
         }
         case USE_SEED_VALUE: {
            engine.seed( seedValue );
            break;
         }
         case USE_SEED_SEQ: {
            engine.seed( seq );
            break;
         }
         default: {
            engine.seed( getRandomDevice()() );
            break;
         }
      }

      return engine;
   } // getDefaultRandomEngine

   // ---------------------------------------------------------------------------
   // getMinStd_Rand0()
   static std::minstd_rand0& getMinStd_Rand0( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
      static std::minstd_rand0 engine{};

      switch ( type ) {
         case USE_CHRONO_CLOCK: {
            engine.seed( getTimeNow() );
            break;
         }
         case USE_SEED_VALUE: {
            engine.seed( seedValue );
            break;
         }
         case USE_SEED_SEQ: {
            engine.seed( seq );
            break;
         }
         default: {
            engine.seed( getRandomDevice()() );
            break;
         }
      }

      return engine;
   } // getMinStd_Rand0

   // ---------------------------------------------------------------------------
   // getMinStd_Rand()
   static std::minstd_rand& getMinStd_Rand( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
      static std::minstd_rand engine{};

      switch( type ) {
         case USE_CHRONO_CLOCK: {
            engine.seed( getTimeNow() );
            break;
         }
         case USE_SEED_VALUE: {
            engine.seed( seedValue );
            break;
         }
         case USE_SEED_SEQ: {
            engine.seed(seq);
            break;
         }
         default: {
            engine.seed( getRandomDevice()() );
            break;
         }
      }

      return engine;
   } // getMinStd_Rand

   // ---------------------------------------------------------------------------
   // getMt19937()
   static std::mt19937& getMt19937( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
      static std::mt19937 engine{};

      switch ( type ) {
         case USE_CHRONO_CLOCK: {
            engine.seed( getTimeNow() );
            break;
         }
         case USE_SEED_VALUE: {
            engine.seed( seedValue );
            break;
         }
         case USE_SEED_SEQ: {
            engine.seed( seq );
            break;
         }
         default: {
            engine.seed( getRandomDevice()() );
            break;
         }
      }

      return engine;
   } //getMt19937

   // ---------------------------------------------------------------------------
   // getMt19937_64()
   static std::mt19937_64& getMt19937_64( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
      static std::mt19937_64 engine{};

      switch ( type ) {
         case USE_CHRONO_CLOCK: {
            engine.seed( getTimeNow() );
            break;
         }
         case USE_SEED_VALUE: {
            engine.seed( seedValue );
            break;
         }
         case USE_SEED_SEQ: {
            engine.seed( seq );
            break;
         }
         default: {
            engine.seed( getRandomDevice()() );
            break;
         }
      }

      return engine;
   } // getMt19937_64

   // ---------------------------------------------------------------------------
   // getRanLux24_base()
   static std::ranlux24_base& getRanLux24_base( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
      static std::ranlux24_base engine{};

      switch ( type ) {
         case USE_CHRONO_CLOCK: {
            engine.seed( getTimeNow() );
            break;
         }
         case USE_SEED_VALUE: {
            engine.seed( seedValue );
            break;
         }
         case USE_SEED_SEQ: {
            engine.seed( seq );
            break;
         }
         default: {
            engine.seed( getRandomDevice()() );
            break;
         }
      }

      return engine;
   } // getRanLux24_base

   // ---------------------------------------------------------------------------
   // getRanLux48_base()
   static std::ranlux48_base& getRanLux48_base( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
      static std::ranlux48_base engine{};

      switch ( type ) {
         case USE_CHRONO_CLOCK: {
            engine.seed( getTimeNow() );
            break;
         }
         case USE_SEED_VALUE: {
            engine.seed( seedValue );
            break;
         }
         case USE_SEED_SEQ: {
            engine.seed( seq );
            break;
         }
         default: {
            engine.seed( getRandomDevice()() );
            break;
         }
      }

      return engine;
   } // getRanLux48_base

   // ---------------------------------------------------------------------------
   // getRanLux24()
   static std::ranlux24& getRanLux24( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
      static std::ranlux24 engine{};

      switch ( type ) {
         case USE_CHRONO_CLOCK: {
            engine.seed( getTimeNow() );
            break;
         }
         case USE_SEED_VALUE: {
            engine.seed( seedValue );
            break;
         }
         case USE_SEED_SEQ: {
            engine.seed( seq );
            break;
         }
         default: {
            engine.seed( getRandomDevice()() );
            break;
         }
      }

      return engine;
   } // getRanLux24

   // ---------------------------------------------------------------------------
   // getRanLux48()
   static std::ranlux48& getRanLux48( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
      static std::ranlux48 engine{};

      switch ( type ) {
         case USE_CHRONO_CLOCK: {
            engine.seed( getTimeNow() );
            break;
         }
         case USE_SEED_VALUE: {
            engine.seed( seedValue );
            break;
         }
         case USE_SEED_SEQ: {
            engine.seed( seq );
            break;
         }
         default: {
            engine.seed( getRandomDevice()() );
            break;
         }
      }

      return engine;
   } //getRanLux48

}; // RandomEngine

class RandomDistribution {
public:
   // UNIFORM DISTRIBUTIONS

   // ---------------------------------------------------------------------------
   // getUniformIntDistribution()
   template<class IntType = int>
   static std::uniform_int_distribution<IntType>& getUniformIntDistribution( IntType lowerBound = 0, IntType upperBound = (std::numeric_limits<IntType>::max)() ) {
      static std::uniform_int_distribution<IntType> dist( lowerBound, upperBound );
      return dist;
   } // getUniformIntDistribution

   // ---------------------------------------------------------------------------
   // getUniformRealDistribution()
   template<class RealType = double>
   static std::uniform_real_distribution<RealType>& getUniformRealDistribution( RealType lowerBound = 0.0, RealType upperBound = 1.0 ) {
      static std::uniform_real_distribution<RealType> dist( lowerBound, upperBound );
      return dist;
   } // getUniformRealDistribution

   

   // BERNOULLI DISTRIBUTIONS

   // ---------------------------------------------------------------------------
   // getBernoulliDistribution()
   static std::bernoulli_distribution& getBernoulliDistribution( double probability = 0.5 ) {
      static std::bernoulli_distribution dist( probability );
      return dist;
   } // getBernoulliDistribution

   // ---------------------------------------------------------------------------
   // getBinomialDistribution()
   template<class IntType = int>
   static std::binomial_distribution<IntType>& getBinomialDistribution( IntType numTrials = 1, double probability = 0.5 ) {
      static std::binomial_distribution<IntType> dist( numTrials, probability );
      return dist;
   } // getBinomialDistribution

   // ---------------------------------------------------------------------------
   // getNegativeBinomialDistribution()
   template<class IntType = int>
   static std::negative_binomial_distribution<IntType>& getNegativeBinomialDistribution( IntType numTrialFailures = 1, double probability = 0.5 ) {
      static std::negative_binomial_distribution<IntType> dist( numTrialFailures, probability );
      return dist;
   } // getNegativeBinomialDistribution

   // ---------------------------------------------------------------------------
    // getGeometricDistribution()
   template<class IntType = int>
   static std::geometric_distribution<IntType>& getGeometricDistribution( double probability = 0.5 ) {
      static std::geometric_distribution<IntType> dist( probability );
      return dist;
   } // getGeometricDistribution



   // POISSON DISTRIBUTIONS

   // ---------------------------------------------------------------------------
   // getPoissonDistribution()
   template<class IntType = int>
   static std::poisson_distribution<IntType>& getPoissonDistribution( double mean = 1.0 ) {
      static std::poisson_distribution<IntType> dist( mean );
      return dist;
   } // getPoissonDistribution

   // ---------------------------------------------------------------------------
   // getExponentialDistribution()
   template<class RealType = double>
   static std::exponential_distribution<RealType>& getExponentialDistribution( RealType rate = 1.0 ) {
      static std::exponential_distribution<RealType> dist( rate );
      return dist;
   } // getExponentialDistribution

   // ---------------------------------------------------------------------------
   // getGammDistribution()
   template<class RealType = double>
   static std::gamma_distribution<RealType>& getGammaDistribution( RealType alpha_shape = 1.0, RealType beta_scale = 1.0 ) {
      static std::gamma_distribution<RealType> dist( alpha_shape, beta_scale );
      return dist;
   } // getGammaDistribution

   // ---------------------------------------------------------------------------
   // getWeibullDistribution()
   template<class RealType = double>
   static std::weibull_distribution<RealType>& getWeibullDistribution( RealType alpha_shape = 1.0, RealType beta_scale = 1.0 ) {
      static std::weibull_distribution<RealType> dist( alpha_shape, beta_scale );
      return dist;
   } // getWeibullDistribution

   // ---------------------------------------------------------------------------
   // getExtremeValueDistribution()
   template<class RealType = double>
   static std::extreme_value_distribution<RealType>& getExtremeValueDistribution( RealType location = 0.0, RealType scale = 1.0 ) {
      static std::extreme_value_distribution<RealType> dist( location, scale );
      return dist;
   } // getExtremeValueDistribution
   

   // NORMAL DISTRIBUTIONS

   // ---------------------------------------------------------------------------
   // getNormalDistribution()
   template<class RealType = double>
   static std::normal_distribution<RealType>& getNormalDistribution( RealType mean = 0.0, RealType stddev = 1.0 ) {
      static std::normal_distribution<RealType> dist( mean, stddev );
      return dist;
   } // getNormaDistribution

   // ---------------------------------------------------------------------------
   // getLogNormalDistribution()
   template<class RealType = double>
   static std::lognormal_distribution<RealType>& getLogNormalDistribution( RealType logScale = 0.0, RealType shape = 1.0 ) {
      static std::lognormal_distribution<RealType> dist( logScale, shape );
      return dist;
   } // getLogNormalDistribution

   // ---------------------------------------------------------------------------
   // getChiSquaredDistribution()
   template<class RealType = double>
   static std::chi_squared_distribution<RealType>& getChiSquaredDistribution( RealType degreesOfFreedom = 1.0 ) {
      static std::chi_squared_distribution<RealType> dist( degreesOfFreedom );
      return dist;
   } // getChiSquaredDistribution

   // ---------------------------------------------------------------------------
   // getCauchyDistribution()
   template<class RealType = double>
   static std::cauchy_distribution<RealType>& getCauchyDistribution( RealType location = 0.0, RealType scale = 1.0 ) {
      static std::cauchy_distribution<RealType> dist( location, scale );
      return dist;
   } // getCauchyDistribution

   // ---------------------------------------------------------------------------
   // getFisherFDistribution() Both m,n are degress of freedom
   template<class RealType = double>
   static std::fisher_f_distribution<RealType>& getFisherFDistribution( RealType m = 1.0, RealType n = 1.0 ) {
      static std::fisher_f_distribution<RealType> dist( m, n );
      return dist;
   } // getFisherFDistribution

   // ---------------------------------------------------------------------------
   // getStudentTDistribution()
   template<class RealType = double>
   static std::student_t_distribution<RealType>& getStudentTDistribution( RealType degreesOfFreedom = 1.0 ) {
      static std::student_t_distribution<RealType> dist( degreesOfFreedom );
      return dist;
   } // getStudentTDistribution


   // SAMPLING DISTRIBUTIONS

   // ---------------------------------------------------------------------------
   //  getDiscreteDistribution()
   template<class IntType = int>
   static std::discrete_distribution<IntType>& getDiscreteDistribution() {
      static std::discrete_distribution<IntType> dist;
      return dist;
   } // getDiscreteDistribution

   // ---------------------------------------------------------------------------
   //  getDiscreteDistribution()
   template<class IntType = int, class InputIt>
   static std::discrete_distribution<IntType>& getDiscreteDistribution( InputIt first, InputIt last ) {
      static std::discrete_distribution<IntType> dist( first, last );
      return dist;
   } // getDiscreteDistribution

   // ---------------------------------------------------------------------------
   //  getDiscreteDistribution()
   template<class IntType = int>
   static std::discrete_distribution<IntType>& getDiscreteDistribution( std::initializer_list<double> weights ) {
      static std::discrete_distribution<IntType> dist( weights );
      return dist;
   } // getDiscreteDistribution

   // ---------------------------------------------------------------------------
   //  getDiscreteDistribution()
   template<class IntType = int, class UnaryOperation>
   static std::discrete_distribution<IntType>& getDiscreteDistribution( std::size_t count, double xmin, double xmax, UnaryOperation unary_op ) {
      static std::discrete_distribution<IntType> dist( count, xmin, xmax, unary_op );
      return dist;
   } // getDiscreteDistribution

   // ---------------------------------------------------------------------------
   // getPiecewiseConstantDistribution()
   template<class RealType = double>
   static std::piecewise_constant_distribution<RealType>& getPiecewiseConstantDistribution() {
      static std::piecewise_constant_distribution<RealType> dist;
      return dist;
   } // getPiecewiseConstantDistribution

   // ---------------------------------------------------------------------------
   // getPiecewiseConstantDistribution()
   template<class RealType = double, class InputIt1, class InputIt2>
   static std::piecewise_constant_distribution<RealType>& getPiecewiseConstantDistribution( InputIt1 first_i, InputIt1 last_i, InputIt2 first_w ) {
      static std::piecewise_constant_distribution<RealType> dist( first_i, last_i, first_w );
      return dist;
   } // getPiecewiseConstantDistribution

   // ---------------------------------------------------------------------------
   // getPiecewiseConstantDistribution()
   template<class RealType = double, class UnaryOperation>
   static std::piecewise_constant_distribution<RealType>& getPiecewiseConstantDistribution( std::initializer_list<RealType> bl, UnaryOperation fw ) {
      static std::piecewise_constant_distribution<RealType> dist( bl, fw );
      return dist;
   } // getPiecewiseConstantDistribution

   // ---------------------------------------------------------------------------
   // getPiecewiseConstantDistribution()
   template<class RealType = double, class UnaryOperation>
   static std::piecewise_constant_distribution<RealType>& getPiecewiseConstantDistribution( std::size_t nw, RealType xmin, RealType xmax, UnaryOperation fw ) {
      static std::piecewise_constant_distribution<RealType> dist( nw, xmin, xmax, fw );
      return dist;
   } // getPiecewiseConstantDistribution

     // ---------------------------------------------------------------------------
     // getPiecewiseLinearDistribution()
   template<class RealType = double>
   static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution() {
      static std::piecewise_linear_distribution<RealType> dist;
      return dist;
   } // getPiecewiseLinearDistribution

     // ---------------------------------------------------------------------------
     // getPiecewiseLinearDistribution()
   template<class RealType = double, class InputIt1, class InputIt2>
   static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution(InputIt1 first_i, InputIt1 last_i, InputIt2 first_w) {
      static std::piecewise_linear_distribution<RealType> dist(first_i, last_i, first_w);
      return dist;
   } // getPiecewiseLinearDistribution

     // ---------------------------------------------------------------------------
     // getPiecewiseLinearDistribution()
   template<class RealType = double, class UnaryOperation>
   static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution(std::initializer_list<RealType> bl, UnaryOperation fw) {
      static std::piecewise_linear_distribution<RealType> dist(bl, fw);
      return dist;
   } // getPiecewiseLinearDistribution

     // ---------------------------------------------------------------------------
     // getPiecewiseLinearDistribution()
   template<class RealType = double, class UnaryOperation>
   static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution(std::size_t nw, RealType xmin, RealType xmax, UnaryOperation fw) {
      static std::piecewise_linear_distribution<RealType> dist(nw, xmin, xmax, fw);
      return dist;
   } // getPiecewiseLinearDistribution

protected:
   RandomDistribution(){}

}; // RandomDistribution

typedef RandomEngine RE;
typedef RandomDistribution RD;

#endif // RANDOM_GENERATOR_H


There are a few different uses or ways to use it. You can create an instance to each type and assign it from the static function calls or you can use it directly in line. Some of the latter distributions have multiple constructors and I have incorporated each of them as well. The one caveat is that you can not create an instance of a random_device and save it to a member variable so I made the method a protected member for internal use, because of this you can just define that variable directly from the <random> header, but it is not necessary, for each of my engines have enumerated types of how you want to seed the engine. You can seed them in 4 different ways. You can seed by random_device, you can seed by deterministic seed value, you can seed by chrono's high precision clock now, and you can seed by seed_seq. Now as for the getTimeNow() method I left that as public since it is a convenient method to get the time on demand in your application. In the header file for each of the engines, you can modify the SeedType parameter to be a default to any one of the 4 if you want like for your own preference. I did not do that since some systems may behave different on different types. Some may not fully support the random_device, while others may not support chromos high precision clock, so I left that up to the user who decides to use this library class. I've also typedef both classes to make the access faster. Here are some use cases:

#include <string>
#include <iostream>
#include <iomanip>
#include <vector>
#include <map>
#include "RandomGenerator.h"

int main() {
   // Default Random Engine with 4 different Seeding methods, the last two parameters for
   // seed by value and seed by seed_seq are defaulted. If using seed_seq, seed value will
   // be ignored.
   std::default_random_engine eng1 = RE::getDefaultRandomEngine( RE::USE_RANDOM_DEVICE );
   std::default_random_engine eng2 = RE::getDefaultRandomEngine( RE::USE_CHRONO_CLOCK );
   std::default_random_engine eng3 = RE::getDefaultRandomEngine( RE::USE_SEED_VALUE, 4 );
   std::seed_seq seq({ 1.0, 2.0, 3.0, 4.0, 5.0, 4.0, 3.0, 2.0, 1.0});
   std::default_random_engine eng4 = RE::getDefaultRandomEngine( RE::USE_SEED_SEQ, 0, seq );
   
   // Here we can do something similar with our distributions
   std::uniform_int_distribution<> dist1 = RD::getUniformIntDistribution( 1, 10 ); // Default = Int : Must Be Of IntType
   std::uniform_int_distribution<long> dist2 = RD::getUniformIntDistribution<long>( 10, 100 ); // Must Specify Long etc.

   // Here we can use our engine to generate our distriubtion
   dist1( eng1 ); // Using Distribution 1 with Engine 1
   dist1( eng2 ); // Using Distriubtion 1 with Engine 2
   dist1( eng3 ); // Using Distribution 1 with Engine 3
   dist1( eng4 ); // Using Distriubtion 1 with Engine 4

   dist2( eng1 ); // Same As Above
   dist2( eng2 );
   dist2( eng3 );
   dist2( eng4 );

   // Here is another way to use this; where it can be done inline.
   std::cout << RD::getUniformRealDistribution( 0.0, 10.0 )( RE::getMt19937( RE::USE_CHRONO_CLOCK ) ) << std::endl;
   std::cout << RD::getUniformRealDistribution( 100.0, 200.0 )( RE::getRanLux48( RE::USE_SEED_SEQ, 0, seq ) ) << std::endl;   

   
   // Here Is  PieceWise Linear Distribution Using the Mersenne Twister Engine
   std::vector<double> i{0.0, 5.0, 10.0, 15.0};
   std::vector<double> w{0.0, 1.0, 1.0, 0.0 };
   std::map<int,int> hist;
   for ( int n = 0; n < 10000; ++n ) {
      ++hist[RD::getPiecewiseLinearDistribution( i.begin(), i.end(), w.begin() )( RE::getMt19937( RE::USE_RANDOM_DEVICE ) ) ];
   }

   for ( auto p : hist ) {
      std::cout << std::setw(2) << std::setfill('0')
         << p.first << ' ' << std::string( p.second / 100, '*') << '\n';
   }

   // We can do this for any of the avaible engines with any of the avaible distributions

   
   std::cout << std::endl;
   std::cout << "Press any key to quit." << std::endl;
   _getch();
   return 0;
} // main


I hope that this might be of use for you.

One thing that does make this library interesting is that, since the methods are static and the class doesn't hold any member variables it acts similar to our Utility class. And with these methods return a static reference there is only one engine and distribution when used inline, but you can still use the customization of these stand libraries functionality. I have tested each of these and they all appear to be working correctly but if anyone notices any bugs please report them. The one thing to be careful about is that I did not incorporate any TYPE checking on the distributions, so the user is responsible for this; meaning you can not have a uniform_real_distribution and pass it an unsigned. However, most of these errors should be caught during compile or build time through the internal std::implementations.

Let me know what you think.


Offline
 Profile  
 
Display posts from previous:  Sort by  
 Page 1 of 1 [ 1 post ] 

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to:  

cron