File indexing completed on 2025-06-21 08:09:44
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include <cmath>
0012 #include <limits>
0013 #include <random>
0014
0015 namespace ActsFatras::detail {
0016
0017
0018
0019
0020
0021 class FpeSafeGammaDistribution {
0022 public:
0023
0024 struct param_type {
0025
0026 using distribution_type = FpeSafeGammaDistribution;
0027
0028
0029
0030
0031 double alpha = 0.5;
0032
0033 double theta = 1.0;
0034
0035
0036 param_type(double alpha_, double theta_) : alpha(alpha_), theta(theta_) {}
0037
0038 param_type() = default;
0039 param_type(const param_type &) = default;
0040 param_type(param_type &&) = default;
0041 param_type &operator=(const param_type &) = default;
0042 param_type &operator=(param_type &&) = default;
0043
0044
0045 friend bool operator==(const param_type &lhs, const param_type &rhs) {
0046 return (lhs.alpha == rhs.alpha) && (lhs.theta == rhs.theta);
0047 }
0048 };
0049
0050 using result_type = double;
0051
0052
0053 FpeSafeGammaDistribution(double alpha, double theta) : m_cfg(alpha, theta) {}
0054
0055 explicit FpeSafeGammaDistribution(const param_type &cfg) : m_cfg(cfg) {}
0056
0057 FpeSafeGammaDistribution() = default;
0058 FpeSafeGammaDistribution(const FpeSafeGammaDistribution &) = default;
0059 FpeSafeGammaDistribution(FpeSafeGammaDistribution &&) = default;
0060 FpeSafeGammaDistribution &operator=(const FpeSafeGammaDistribution &) =
0061 default;
0062 FpeSafeGammaDistribution &operator=(FpeSafeGammaDistribution &&) = default;
0063
0064
0065 void reset() {}
0066
0067 param_type param() const { return m_cfg; }
0068
0069 void param(const param_type &cfg) { m_cfg = cfg; }
0070
0071
0072 result_type min() const { return 0.; }
0073
0074 result_type max() const { return std::numeric_limits<double>::infinity(); }
0075
0076
0077 template <typename Generator>
0078 result_type operator()(Generator &generator) {
0079 return (*this)(generator, m_cfg);
0080 }
0081
0082 template <typename Generator>
0083 result_type operator()(Generator &generator, const param_type ¶ms) {
0084 if (params.alpha >= 1.) {
0085 std::gamma_distribution<double> gDist(params.alpha, params.theta);
0086 return gDist(generator);
0087 } else if (params.alpha <= 0.) {
0088 return 0.;
0089 }
0090
0091
0092 std::gamma_distribution<double> gDistPlus1(params.alpha + 1., params.theta);
0093
0094 const auto uPlus1 = gDistPlus1(generator);
0095 std::uniform_real_distribution<double> uDist(0., 1.);
0096 double u2 = uDist(generator);
0097 while (u2 == 0.) {
0098 u2 = uDist(generator);
0099 }
0100
0101 const double invAlpha = 1. / params.alpha;
0102 if (std::log(u2) * invAlpha + std::log(uPlus1) <
0103 std::log(std::numeric_limits<double>::min())) {
0104
0105 return 0.;
0106 } else {
0107 return uPlus1 * std::pow(u2, invAlpha);
0108 }
0109 }
0110
0111
0112 friend bool operator==(const FpeSafeGammaDistribution &lhs,
0113 const FpeSafeGammaDistribution &rhs) {
0114 return lhs.m_cfg == rhs.m_cfg;
0115 }
0116
0117 private:
0118 param_type m_cfg;
0119 };
0120
0121 }