diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2017-09-13 11:08:07 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2017-09-13 11:09:00 +0900 |
commit | b5c87084afaef42b2d058f68091be31988a6a874 (patch) | |
tree | adef9a65870a41181687e11d57fdf98e7629de3c /boost/random/normal_distribution.hpp | |
parent | 34bd32e225e2a8a94104489b31c42e5801cc1f4a (diff) | |
download | boost-b5c87084afaef42b2d058f68091be31988a6a874.tar.gz boost-b5c87084afaef42b2d058f68091be31988a6a874.tar.bz2 boost-b5c87084afaef42b2d058f68091be31988a6a874.zip |
Imported Upstream version 1.64.0upstream/1.64.0
Change-Id: Id9212edd016dd55f21172c427aa7894d1d24148b
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
Diffstat (limited to 'boost/random/normal_distribution.hpp')
-rw-r--r-- | boost/random/normal_distribution.hpp | 163 |
1 files changed, 65 insertions, 98 deletions
diff --git a/boost/random/normal_distribution.hpp b/boost/random/normal_distribution.hpp index 6fffc4aef5..b7ff3eba9b 100644 --- a/boost/random/normal_distribution.hpp +++ b/boost/random/normal_distribution.hpp @@ -23,17 +23,11 @@ #include <boost/assert.hpp> #include <boost/limits.hpp> #include <boost/static_assert.hpp> -#include <boost/integer.hpp> -#include <boost/integer/integer_mask.hpp> -#include <boost/type_traits/is_integral.hpp> -#include <boost/type_traits/make_unsigned.hpp> #include <boost/random/detail/config.hpp> #include <boost/random/detail/operators.hpp> -#include <boost/random/detail/integer_log2.hpp> +#include <boost/random/detail/int_float_pair.hpp> #include <boost/random/uniform_01.hpp> -#include <boost/random/uniform_int_distribution.hpp> #include <boost/random/exponential_distribution.hpp> -#include <boost/mpl/bool.hpp> namespace boost { namespace random { @@ -121,89 +115,6 @@ const RealType normal_table<RealType>::table_y[129] = { 1 }; -template<class Engine> -inline typename boost::make_unsigned<typename Engine::result_type>::type -generate_one_digit(Engine& eng, std::size_t bits) -{ - typedef typename Engine::result_type base_result; - typedef typename boost::make_unsigned<base_result>::type base_unsigned; - - base_unsigned range = - detail::subtract<base_result>()((eng.max)(), (eng.min)()); - base_unsigned y0_mask = (base_unsigned(2) << (bits - 1)) - 1; - base_unsigned y0 = (range + 1) & ~y0_mask; - base_unsigned u; - do { - u = detail::subtract<base_result>()(eng(), (eng.min)()); - } while(y0 != 0 && u > base_unsigned(y0 - 1)); - return u & y0_mask; -} - -template<class RealType, std::size_t w, class Engine> -std::pair<RealType, int> generate_int_float_pair(Engine& eng, boost::mpl::true_) -{ - typedef typename Engine::result_type base_result; - typedef typename boost::make_unsigned<base_result>::type base_unsigned; - - base_unsigned range = - detail::subtract<base_result>()((eng.max)(), (eng.min)()); - - std::size_t m = - (range == (std::numeric_limits<base_unsigned>::max)()) ? - std::numeric_limits<base_unsigned>::digits : - detail::integer_log2(range + 1); - - int bucket = 0; - // process as many full digits as possible into the int part - for(std::size_t i = 0; i < w/m; ++i) { - base_unsigned u = generate_one_digit(eng, m); - bucket = (bucket << m) | u; - } - RealType r; - - const std::size_t digits = std::numeric_limits<RealType>::digits; - { - base_unsigned u = generate_one_digit(eng, m); - base_unsigned mask = (base_unsigned(1) << (w%m)) - 1; - bucket = (bucket << (w%m)) | (mask & u); - const RealType mult = RealType(1)/RealType(base_unsigned(1) << (m - w%m)); - // zero out unused bits - if (m - w%m > digits) { - u &= ~(base_unsigned(1) << (m - digits)); - } - r = RealType(u >> (w%m)) * mult; - } - for(std::size_t i = m - w%m; i + m < digits; ++i) { - base_unsigned u = generate_one_digit(eng, m); - r += u; - r *= RealType(0.5)/RealType(base_unsigned(1) << (m - 1)); - } - if (m - w%m < digits) - { - const std::size_t remaining = (digits - m + w%m) % m; - base_unsigned u = generate_one_digit(eng, m); - r += u & ((base_unsigned(2) << (remaining - 1)) - 1); - const RealType mult = RealType(0.5)/RealType(base_unsigned(1) << (remaining - 1)); - r *= mult; - } - return std::make_pair(r, bucket); -} - -template<class RealType, std::size_t w, class Engine> -inline std::pair<RealType, int> generate_int_float_pair(Engine& eng, boost::mpl::false_) -{ - int bucket = uniform_int_distribution<>(0, (1 << w) - 1)(eng); - RealType r = uniform_01<RealType>()(eng); - return std::make_pair(r, bucket); -} - -template<class RealType, std::size_t w, class Engine> -inline std::pair<RealType, int> generate_int_float_pair(Engine& eng) -{ - typedef typename Engine::result_type base_result; - return generate_int_float_pair<RealType, w>(eng, - boost::is_integral<base_result>()); -} template<class RealType = double> struct unit_normal_distribution @@ -220,29 +131,77 @@ struct unit_normal_distribution RealType x = vals.first * RealType(table_x[i]); if(x < table_x[i + 1]) return x * sign; if(i == 0) return generate_tail(eng) * sign; - RealType y = RealType(table_y[i]) + uniform_01<RealType>()(eng) * RealType(table_y[i + 1] - table_y[i]); - if (y < f(x)) return x * sign; + + RealType y01 = uniform_01<RealType>()(eng); + RealType y = RealType(table_y[i]) + y01 * RealType(table_y[i + 1] - table_y[i]); + + // These store the value y - bound, or something proportional to that difference: + RealType y_above_ubound, y_above_lbound; + + // There are three cases to consider: + // - convex regions (where x[i] > x[j] >= 1) + // - concave regions (where 1 <= x[i] < x[j]) + // - region containing the inflection point (where x[i] > 1 > x[j]) + // For convex (concave), exp^(-x^2/2) is bounded below (above) by the tangent at + // (x[i],y[i]) and is bounded above (below) by the diagonal line from (x[i+1],y[i+1]) to + // (x[i],y[i]). + // + // *If* the inflection point region satisfies slope(x[i+1]) < slope(diagonal), then we + // can treat the inflection region as a convex region: this condition is necessary and + // sufficient to ensure that the curve lies entirely below the diagonal (that, in turn, + // also implies that it will be above the tangent at x[i]). + // + // For the current table size (128), this is satisfied: slope(x[i+1]) = -0.60653 < + // slope(diag) = -0.60649, and so we have only two cases below instead of three. + + if (table_x[i] >= 1) { // convex (incl. inflection) + y_above_ubound = RealType(table_x[i] - table_x[i+1]) * y01 - (RealType(table_x[i]) - x); + y_above_lbound = y - (RealType(table_y[i]) + (RealType(table_x[i]) - x) * RealType(table_y[i]) * RealType(table_x[i])); + } + else { // concave + y_above_lbound = RealType(table_x[i] - table_x[i+1]) * y01 - (RealType(table_x[i]) - x); + y_above_ubound = y - (RealType(table_y[i]) + (RealType(table_x[i]) - x) * RealType(table_y[i]) * RealType(table_x[i])); + } + + if (y_above_ubound < 0 // if above the upper bound reject immediately + && + ( + y_above_lbound < 0 // If below the lower bound accept immediately + || + y < f(x) // Otherwise it's between the bounds and we need a full check + ) + ) { + return x * sign; + } } } static RealType f(RealType x) { using std::exp; - return exp(-x*x/2); + return exp(-(x*x/2)); } + // Generate from the tail using rejection sampling from the exponential(x_1) distribution, + // shifted by x_1. This looks a little different from the usual rejection sampling because it + // transforms the condition by taking the log of both sides, thus avoiding the costly exp() call + // on the RHS, then takes advantage of the fact that -log(unif01) is simply generating an + // exponential (by inverse cdf sampling) by replacing the log(unif01) on the LHS with a + // exponential(1) draw, y. template<class Engine> RealType generate_tail(Engine& eng) { - boost::random::exponential_distribution<RealType> exponential; const RealType tail_start = RealType(normal_table<double>::table_x[1]); + boost::random::exponential_distribution<RealType> exp_x(tail_start); + boost::random::exponential_distribution<RealType> exp_y; for(;;) { - RealType x = exponential(eng)/tail_start; - RealType y = exponential(eng); + RealType x = exp_x(eng); + RealType y = exp_y(eng); + // If we were doing non-transformed rejection sampling, this condition would be: + // if (unif01 < exp(-.5*x*x)) return x + tail_start; if(2*y > x*x) return x + tail_start; } } }; -} +} // namespace detail -// deterministic Box-Muller method, uses trigonometric functions /** * Instantiations of class template normal_distribution model a @@ -252,6 +211,14 @@ struct unit_normal_distribution * \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{(x-\mu)^2}{2\sigma^2}} * \f$, * where mean and sigma are the parameters of the distribution. + * + * The implementation uses the "ziggurat" algorithm, as described in + * + * @blockquote + * "The Ziggurat Method for Generating Random Variables", + * George Marsaglia and Wai Wan Tsang, Journal of Statistical Software, + * Volume 5, Number 8 (2000), 1-7. + * @endblockquote */ template<class RealType = double> class normal_distribution |