/***************************************************************************
* GRan.cpp - Random number generator class *
* ----------------------------------------------------------------------- *
* copyright (C) 2011-2014 by Juergen Knoedlseder *
* ----------------------------------------------------------------------- *
* *
* This program 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, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
***************************************************************************/
/**
* @file GRan.cpp
* @brief Random number generator class implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "GRan.hpp"
#include "GTools.hpp"
#include "GMath.hpp"
#include "GException.hpp"
/* __ Method name definitions ____________________________________________ */
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/* __ Constants __________________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GRan::GRan(void)
{
// Initialise private members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Seed constructor
*
* @param[in] seed Random number generator seed.
***************************************************************************/
GRan::GRan(unsigned long long int seed)
{
// Initialise private
init_members(seed);
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] ran Random number generator.
***************************************************************************/
GRan::GRan(const GRan& ran)
{
// Initialise private
init_members();
// Copy members
copy_members(ran);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GRan::~GRan(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] ran Random number generator.
* @return Random number generator.
***************************************************************************/
GRan& GRan::operator=(const GRan& ran)
{
// Execute only if object is not identical
if (this != &ran) {
// Free members
free_members();
// Initialise private members for clean destruction
init_members();
// Copy members
copy_members(ran);
} // endif: object was not identical
// Return
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear random number generator
*
* This method properly resets the object to an initial state using the
* default seed.
***************************************************************************/
void GRan::clear(void)
{
// Free class members
free_members();
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone random number generator
*
* @return Pointer to deep copy of random number generator.
**************************************************************************/
GRan* GRan::clone(void) const
{
return new GRan(*this);
}
/***********************************************************************//**
* @brief Set seed of random number generator
*
* @param[in] seed Random number generator seed.
*
* This method properly resets the object to an initial state using the
* specified seed.
***************************************************************************/
void GRan::seed(unsigned long long int seed)
{
// Free class members
free_members();
// Initialise members
init_members(seed);
// Return
return;
}
/***********************************************************************//**
* @brief Return 32-bit random unsigned integer
***************************************************************************/
unsigned long int GRan::int32(void)
{
// Return random value
return ((unsigned long int)int64());
}
/***********************************************************************//**
* @brief Return 64-bit random unsigned integer
***************************************************************************/
unsigned long long int GRan::int64(void)
{
// Update u
m_value1 = m_value1 * 2862933555777941757LL + 7046029254386353087LL;
// Shuffle v
m_value2 ^= m_value2 >> 17;
m_value2 ^= m_value2 << 31;
m_value2 ^= m_value2 >> 8;
// Update w
m_value3 = 4294957665U * (m_value3 & 0xffffffff) + (m_value3 >> 32);
// Compute x
unsigned long long int x = m_value1 ^ (m_value1 << 21);
x ^= x >> 35;
x ^= x << 4;
// Return random value
return ((x + m_value2) ^ m_value3);
}
/***********************************************************************//**
* @brief Returns random double precision floating value in range 0 to 1
***************************************************************************/
double GRan::uniform(void)
{
// Return random value
return (5.42101086242752217e-20 * int64());
}
/***********************************************************************//**
* @brief Returns normal deviates
*
* Method Used: Box-Muller transform, outlined here:
* http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
*
* Code from: http://www.design.caltech.edu/erik/Misc/Gaussian.html
***************************************************************************/
double GRan::normal(void)
{
// Get random value
double x1;
double w;
do {
x1 = 2.0 * uniform() - 1.0;
double x2 = 2.0 * uniform() - 1.0;
w = x1 * x1 + x2 * x2;
} while (w >= 1.0);
// Compute random value
double value = x1 * std::sqrt((-2.0 * std::log(w)) / w);
// Return random value
return value;
}
/***********************************************************************//**
* @brief Returns exponential deviates
*
* @param[in] lambda Mean rate.
*
* Returns exponential deviates from the probability distribution
* \f[p(x) = \lambda \exp( -\lambda x )\f]
* where
* \f$\lambda\f$ is the parameter of the distribution.
* This method may be used to simulate the occurence time of an event, where
* \f$\lambda\f$ is the mean event rate. Convsersely, \f$1/\lambda\f$ is the
* mean waiting time between events.
*
* @todo Check that \f$\lambda>0\f$.
***************************************************************************/
double GRan::exp(const double& lambda)
{
// Allocate argument
double x;
// Get non-zero uniform deviate
do {
x = uniform();
} while (x == 0.0);
// Return random value
return (-std::log(x)/lambda);
}
/***********************************************************************//**
* @brief Returns Poisson deviates
*
* @param[in] lambda Expectation value.
*
* Returns Poisson deviates for an expectation value.
*
* This method may be used to simulate the number of events in case that a
* given mean number of events is expected.
***************************************************************************/
double GRan::poisson(const double& lambda)
{
// Declare result
double value;
// Use direct method for small numbers ...
if (lambda < 12.0) {
if (lambda != m_old_lambda) {
m_old_lambda = lambda;
m_exp_lambda = std::exp(-lambda);
}
value = -1.0;
double tmp = 1.0;
do {
value += 1.0;
tmp *= uniform();
} while (tmp > m_exp_lambda);
} // endif: direct method used
// ... otherwise use rejection method
else {
double tmp;
if (lambda != m_old_lambda) {
m_old_lambda = lambda;
m_sqrt_lambda = std::sqrt(2.0*lambda);
m_log_lambda = std::log(lambda);
m_exp_lambda = lambda * m_log_lambda - gammalib::gammln(lambda+1.0);
}
do {
double factor;
do {
factor = std::tan(gammalib::pi * uniform());
value = m_sqrt_lambda * factor + lambda;
} while (value < 0.0);
value = floor(value);
tmp = 0.9*(1.0+factor*factor) *
std::exp(value*m_log_lambda - gammalib::gammln(value+1.0)-m_exp_lambda);
} while (uniform() > tmp);
}
// Return random deviate
return value;
}
/***********************************************************************//**
* @brief Returns Chi2 deviates for 2 degrees of freedom
*
* Returns exponential deviates from the probability distribution
* \f[p(x) = \frac{1}{2\pi} x \exp( -\frac{1}{2} x^2 )\f]
* This method can be used to simulate the random radial offset of a measured
* source position from the true source position, assuming an azimuthally
* symmetric 2D Gaussian probability distribution.
***************************************************************************/
double GRan::chisq2(void)
{
// Allocate argument
double x;
// Get uniform deviate < 1
do {
x = uniform();
} while (x == 1.0);
// Return random value
return (std::sqrt(-2.0*std::log(1.0-x)));
}
/***********************************************************************//**
* @brief Random sampling from a cumulative density function
*
* @param[in] cdf Array containing cumulative density function
***************************************************************************/
int GRan::cdf(const std::vector& cdf)
{
// Get uniform random number
double u = uniform();
// Get pixel index according to random number. We use a bi-section
// method to find the corresponding pixel index
int low = 0;
int high = cdf.size();
while ((high - low) > 1) {
int mid = (low+high) / 2;
if (u < cdf[mid]) {
high = mid;
}
else if (cdf[mid] <= u) {
low = mid;
}
}
// Return index
return low;
}
/***********************************************************************//**
* @brief Random sampling from a cumulative density function
*
* @param[in] cdf Vector containing cumulative density function
*
* @todo Somehow merge with the above method to reduce code repetition
***************************************************************************/
int GRan::cdf(const GVector& cdf)
{
// Get uniform random number
double u = uniform();
// Get pixel index according to random number. We use a bi-section
// method to find the corresponding pixel index
int low = 0;
int high = cdf.size();
while ((high - low) > 1) {
int mid = (low+high) / 2;
if (u < cdf[mid]) {
high = mid;
}
else if (cdf[mid] <= u) {
low = mid;
}
}
// Return index
return low;
}
/***********************************************************************//**
* @brief Print random number generator information
*
* @param[in] chatter Chattiness (defaults to NORMAL).
* @return String containing random number generator information.
***************************************************************************/
std::string GRan::print(const GChatter& chatter) const
{
// Initialise result string
std::string result;
// Continue only if chatter is not silent
if (chatter != SILENT) {
// Append header
result.append("=== GRan ===");
// Append information
result.append("\n"+gammalib::parformat("Seed")+gammalib::str(m_seed));
result.append("\n"+gammalib::parformat("Value 1")+gammalib::str(m_value1));
result.append("\n"+gammalib::parformat("Value 2")+gammalib::str(m_value2));
result.append("\n"+gammalib::parformat("Value 3")+gammalib::str(m_value3));
} // endif: chatter was not silent
// Return result
return result;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GRan::init_members(unsigned long long int seed)
{
// Initialise members
m_seed = seed;
m_value2 = 4101842887655102017LL;
m_value3 = 1;
m_old_lambda = -1.0;
m_sqrt_lambda = 0.0;
m_log_lambda = 0.0;
m_exp_lambda = 0.0;
// Initialise values
m_value1 = m_seed ^ m_value2;
int64();
m_value2 = m_value1;
int64();
m_value3 = m_value2;
int64();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] ran Random number generator.
***************************************************************************/
void GRan::copy_members(const GRan& ran)
{
// Copy members
m_seed = ran.m_seed;
m_value1 = ran.m_value1;
m_value2 = ran.m_value2;
m_value3 = ran.m_value3;
m_old_lambda = ran.m_old_lambda;
m_sqrt_lambda = ran.m_sqrt_lambda;
m_log_lambda = ran.m_log_lambda;
m_exp_lambda = ran.m_exp_lambda;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GRan::free_members(void)
{
// Return
return;
}