generalized_inverse_gaussian_distribution.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /* boost random/generalized_inverse_gaussian_distribution.hpp header file
  2. *
  3. * Copyright Young Geun Kim 2025
  4. * Distributed under the Boost Software License, Version 1.0. (See
  5. * accompanying file LICENSE_1_0.txt or copy at
  6. * http://www.boost.org/LICENSE_1_0.txt)
  7. *
  8. * See http://www.boost.org for most recent version including documentation.
  9. *
  10. * $Id$
  11. */
  12. #ifndef BOOST_GENERALIZED_RANDOM_INVERSE_GAUSSIAN_DISTRIBUTION_HPP
  13. #define BOOST_GENERALIZED_RANDOM_INVERSE_GAUSSIAN_DISTRIBUTION_HPP
  14. #include <boost/config/no_tr1/cmath.hpp>
  15. #include <istream>
  16. #include <iosfwd>
  17. #include <limits>
  18. #include <boost/assert.hpp>
  19. #include <boost/limits.hpp>
  20. #include <boost/random/detail/config.hpp>
  21. #include <boost/random/detail/operators.hpp>
  22. #include <boost/random/uniform_01.hpp>
  23. namespace boost {
  24. namespace random {
  25. /**
  26. * The generalized inverse gaussian distribution is a real-valued distribution with
  27. * three parameters p, a, and b. It produced values > 0.
  28. *
  29. * It has
  30. * \f$\displaystyle p(x) = \frac{(a / b)^{p / 2}}{2 K_{p}(\sqrt{a b})} x^{p - 1} e^{-(a x + b / 2) / 2}\f$.
  31. * where \f$\displaystyle K_{p}\f$ is a modified Bessel function of the second kind.
  32. *
  33. * The algorithm used is from
  34. *
  35. * @blockquote
  36. * "Random variate generation for the generalized inverse Gaussian distribution",
  37. * Luc Devroye,
  38. * Statistics and Computing,
  39. * Volume 24, 2014, Pages 236 - 246
  40. * @endblockquote
  41. */
  42. template<class RealType = double>
  43. class generalized_inverse_gaussian_distribution
  44. {
  45. public:
  46. typedef RealType result_type;
  47. typedef RealType input_type;
  48. class param_type {
  49. public:
  50. typedef generalized_inverse_gaussian_distribution distribution_type;
  51. /**
  52. * Constructs a @c param_type object from the "p", "a", and "b"
  53. * parameters.
  54. *
  55. * Requires:
  56. * a > 0 && b >= 0 if p > 0,
  57. * a > 0 && b > 0 if p == 0,
  58. * a >= 0 && b > 0 if p < 0
  59. */
  60. explicit param_type(RealType p_arg = RealType(1.0),
  61. RealType a_arg = RealType(1.0),
  62. RealType b_arg = RealType(1.0))
  63. : _p(p_arg), _a(a_arg), _b(b_arg)
  64. {
  65. BOOST_ASSERT(
  66. (p_arg > RealType(0) && a_arg > RealType(0) && b_arg >= RealType(0)) ||
  67. (p_arg == RealType(0) && a_arg > RealType(0) && b_arg > RealType(0)) ||
  68. (p_arg < RealType(0) && a_arg >= RealType(0) && b_arg > RealType(0))
  69. );
  70. }
  71. /** Returns the "p" parameter of the distribution. */
  72. RealType p() const { return _p; }
  73. /** Returns the "a" parameter of the distribution. */
  74. RealType a() const { return _a; }
  75. /** Returns the "b" parameter of the distribution. */
  76. RealType b() const { return _b; }
  77. /** Writes a @c param_type to a @c std::ostream. */
  78. BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
  79. {
  80. os << parm._p << ' ' << parm._a << ' ' << parm._b;
  81. return os;
  82. }
  83. /** Reads a @c param_type from a @c std::istream. */
  84. BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
  85. {
  86. is >> parm._p >> std::ws >> parm._a >> std::ws >> parm._b;
  87. return is;
  88. }
  89. /** Returns true if the two sets of parameters are the same. */
  90. BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
  91. { return lhs._p == rhs._p && lhs._a == rhs._a && lhs._b == rhs._b; }
  92. /** Returns true if the two sets of parameters are different. */
  93. BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
  94. private:
  95. RealType _p;
  96. RealType _a;
  97. RealType _b;
  98. };
  99. #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
  100. BOOST_STATIC_ASSERT(!std::numeric_limits<RealType>::is_integer);
  101. #endif
  102. /**
  103. * Constructs an @c generalized_inverse_gaussian_distribution from its "p", "a", and "b" parameters.
  104. *
  105. * Requires:
  106. * a > 0 && b >= 0 if p > 0,
  107. * a > 0 && b > 0 if p == 0,
  108. * a >= 0 && b > 0 if p < 0
  109. */
  110. explicit generalized_inverse_gaussian_distribution(RealType p_arg = RealType(1.0),
  111. RealType a_arg = RealType(1.0),
  112. RealType b_arg = RealType(1.0))
  113. : _p(p_arg), _a(a_arg), _b(b_arg)
  114. {
  115. BOOST_ASSERT(
  116. (p_arg > RealType(0) && a_arg > RealType(0) && b_arg >= RealType(0)) ||
  117. (p_arg == RealType(0) && a_arg > RealType(0) && b_arg > RealType(0)) ||
  118. (p_arg < RealType(0) && a_arg >= RealType(0) && b_arg > RealType(0))
  119. );
  120. init();
  121. }
  122. /** Constructs an @c generalized_inverse_gaussian_distribution from its parameters. */
  123. explicit generalized_inverse_gaussian_distribution(const param_type& parm)
  124. : _p(parm.p()), _a(parm.a()), _b(parm.b())
  125. {
  126. init();
  127. }
  128. /**
  129. * Returns a random variate distributed according to the
  130. * generalized inverse gaussian distribution.
  131. */
  132. template<class URNG>
  133. RealType operator()(URNG& urng) const
  134. {
  135. #ifndef BOOST_NO_STDC_NAMESPACE
  136. using std::sqrt;
  137. using std::log;
  138. using std::min;
  139. using std::exp;
  140. using std::cosh;
  141. #endif
  142. RealType t = result_type(1);
  143. RealType s = result_type(1);
  144. RealType log_concave = -psi(result_type(1));
  145. if (log_concave >= result_type(.5) && log_concave <= result_type(2)) {
  146. t = result_type(1);
  147. } else if (log_concave > result_type(2)) {
  148. t = sqrt(result_type(2) / (_alpha + _abs_p));
  149. } else if (log_concave < result_type(.5)) {
  150. t = log(result_type(4) / (_alpha + result_type(2) * _abs_p));
  151. }
  152. log_concave = -psi(result_type(-1));
  153. if (log_concave >= result_type(.5) && log_concave <= result_type(2)) {
  154. s = result_type(1);
  155. } else if (log_concave > result_type(2)) {
  156. s = sqrt(result_type(4) / (_alpha * cosh(1) + _abs_p));
  157. } else if (log_concave < result_type(.5)) {
  158. s = min(result_type(1) / _abs_p, log(result_type(1) + result_type(1) / _alpha + sqrt(result_type(1) / (_alpha * _alpha) + result_type(2) / _alpha)));
  159. }
  160. RealType eta = -psi(t);
  161. RealType zeta = -psi_deriv(t);
  162. RealType theta = -psi(-s);
  163. RealType xi = psi_deriv(-s);
  164. RealType p = result_type(1) / xi;
  165. RealType r = result_type(1) / zeta;
  166. RealType t_deriv = t - r * eta;
  167. RealType s_deriv = s - p * theta;
  168. RealType q = t_deriv + s_deriv;
  169. RealType u = result_type(0);
  170. RealType v = result_type(0);
  171. RealType w = result_type(0);
  172. RealType cand = result_type(0);
  173. do
  174. {
  175. u = uniform_01<RealType>()(urng);
  176. v = uniform_01<RealType>()(urng);
  177. w = uniform_01<RealType>()(urng);
  178. if (u < q / (p + q + r)) {
  179. cand = -s_deriv + q * v;
  180. } else if (u < (q + r) / (p + q + r)) {
  181. cand = t_deriv - r * log(v);
  182. } else {
  183. cand = -s_deriv + p * log(v);
  184. }
  185. } while (w * chi(cand, s, t, s_deriv, t_deriv, eta, zeta, theta, xi) > exp(psi(cand)));
  186. cand = (_abs_p / _omega + sqrt(result_type(1) + _abs_p * _abs_p / (_omega * _omega))) * exp(cand);
  187. return _p > 0 ? cand * sqrt(_b / _a) : sqrt(_b / _a) / cand;
  188. }
  189. /**
  190. * Returns a random variate distributed accordint to the beta
  191. * distribution with parameters specified by @c param.
  192. */
  193. template<class URNG>
  194. result_type operator()(URNG& urng, const param_type& parm) const
  195. {
  196. return generalized_inverse_gaussian_distribution(parm)(urng);
  197. }
  198. /** Returns the "p" parameter of the distribution. */
  199. RealType p() const { return _p; }
  200. /** Returns the "a" parameter of the distribution. */
  201. RealType a() const { return _a; }
  202. /** Returns the "b" parameter of the distribution. */
  203. RealType b() const { return _b; }
  204. /** Returns the smallest value that the distribution can produce. */
  205. RealType min BOOST_PREVENT_MACRO_SUBSTITUTION () const
  206. { return RealType(0.0); }
  207. /** Returns the largest value that the distribution can produce. */
  208. RealType max BOOST_PREVENT_MACRO_SUBSTITUTION () const
  209. { return (std::numeric_limits<RealType>::infinity)(); }
  210. /** Returns the parameters of the distribution. */
  211. param_type param() const { return param_type(_p, _a, _b); }
  212. /** Sets the parameters of the distribution. */
  213. void param(const param_type& parm)
  214. {
  215. _p = parm.p();
  216. _a = parm.a();
  217. _b = parm.b();
  218. init();
  219. }
  220. /**
  221. * Effects: Subsequent uses of the distribution do not depend
  222. * on values produced by any engine prior to invoking reset.
  223. */
  224. void reset() { }
  225. /** Writes an @c generalized_inverse_gaussian_distribution to a @c std::ostream. */
  226. BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, generalized_inverse_gaussian_distribution, wd)
  227. {
  228. os << wd.param();
  229. return os;
  230. }
  231. /** Reads an @c generalized_inverse_gaussian_distribution from a @c std::istream. */
  232. BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, generalized_inverse_gaussian_distribution, wd)
  233. {
  234. param_type parm;
  235. if(is >> parm) {
  236. wd.param(parm);
  237. }
  238. return is;
  239. }
  240. /**
  241. * Returns true if the two instances of @c generalized_inverse_gaussian_distribution will
  242. * return identical sequences of values given equal generators.
  243. */
  244. BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(generalized_inverse_gaussian_distribution, lhs, rhs)
  245. { return lhs._p == rhs._p && lhs._a == rhs._a && lhs._b == rhs._b; }
  246. /**
  247. * Returns true if the two instances of @c generalized_inverse_gaussian_distribution will
  248. * return different sequences of values given equal generators.
  249. */
  250. BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(generalized_inverse_gaussian_distribution)
  251. private:
  252. RealType _p;
  253. RealType _a;
  254. RealType _b;
  255. // some data precomputed from the parameters
  256. RealType _abs_p;
  257. RealType _omega;
  258. RealType _alpha;
  259. /// \cond hide_private_members
  260. void init()
  261. {
  262. #ifndef BOOST_NO_STDC_NAMESPACE
  263. using std::abs;
  264. using std::sqrt;
  265. #endif
  266. _abs_p = abs(_p);
  267. _omega = sqrt(_a * _b); // two-parameter representation (p, omega)
  268. _alpha = sqrt(_omega * _omega + _abs_p * _abs_p) - _abs_p;
  269. }
  270. result_type psi(const RealType& x) const
  271. {
  272. #ifndef BOOST_NO_STDC_NAMESPACE
  273. using std::cosh;
  274. using std::exp;
  275. #endif
  276. return -_alpha * (cosh(x) - result_type(1)) - _abs_p * (exp(x) - x - result_type(1));
  277. }
  278. result_type psi_deriv(const RealType& x) const
  279. {
  280. #ifndef BOOST_NO_STDC_NAMESPACE
  281. using std::sinh;
  282. using std::exp;
  283. #endif
  284. return -_alpha * sinh(x) - _abs_p * (exp(x) - result_type(1));
  285. }
  286. static result_type chi(const RealType& x,
  287. const RealType& s, const RealType& t,
  288. const RealType& s_deriv, const RealType& t_deriv,
  289. const RealType& eta, const RealType& zeta, const RealType& theta, const RealType& xi)
  290. {
  291. #ifndef BOOST_NO_STDC_NAMESPACE
  292. using std::exp;
  293. #endif
  294. if (x >= -s_deriv && x <= t_deriv) {
  295. return result_type(1);
  296. } else if (x > t_deriv) {
  297. return exp(-eta - zeta * (x - t));
  298. }
  299. return exp(-theta + xi * (x + s));
  300. }
  301. /// \endcond
  302. };
  303. } // namespace random
  304. using random::generalized_inverse_gaussian_distribution;
  305. } // namespace boost
  306. #endif // BOOST_GENERALIZED_RANDOM_INVERSE_GAUSSIAN_DISTRIBUTION_HPP