hyperexponential_distribution.hpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  1. /* boost random/hyperexponential_distribution.hpp header file
  2. *
  3. * Copyright Marco Guazzone 2014
  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. * Much of the code here taken by boost::math::hyperexponential_distribution.
  11. * To this end, we would like to thank Paul Bristow and John Maddock for their
  12. * valuable feedback.
  13. *
  14. * \author Marco Guazzone (marco.guazzone@gmail.com)
  15. */
  16. #ifndef BOOST_RANDOM_HYPEREXPONENTIAL_DISTRIBUTION_HPP
  17. #define BOOST_RANDOM_HYPEREXPONENTIAL_DISTRIBUTION_HPP
  18. #include <boost/config.hpp>
  19. #include <boost/core/cmath.hpp>
  20. #include <boost/random/detail/operators.hpp>
  21. #include <boost/random/detail/vector_io.hpp>
  22. #include <boost/random/discrete_distribution.hpp>
  23. #include <boost/random/exponential_distribution.hpp>
  24. #include <boost/range/begin.hpp>
  25. #include <boost/range/end.hpp>
  26. #include <boost/range/size.hpp>
  27. #include <boost/type_traits/has_pre_increment.hpp>
  28. #include <cassert>
  29. #include <cmath>
  30. #include <cstddef>
  31. #include <iterator>
  32. #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
  33. # include <initializer_list>
  34. #endif // BOOST_NO_CXX11_HDR_INITIALIZER_LIST
  35. #include <iostream>
  36. #include <limits>
  37. #include <numeric>
  38. #include <vector>
  39. namespace boost { namespace random {
  40. namespace hyperexp_detail {
  41. template <typename T>
  42. std::vector<T>& normalize(std::vector<T>& v)
  43. {
  44. if (v.size() == 0)
  45. {
  46. return v;
  47. }
  48. const T sum = std::accumulate(v.begin(), v.end(), static_cast<T>(0));
  49. T final_sum = 0;
  50. const typename std::vector<T>::iterator end = --v.end();
  51. for (typename std::vector<T>::iterator it = v.begin();
  52. it != end;
  53. ++it)
  54. {
  55. *it /= sum;
  56. final_sum += *it;
  57. }
  58. *end = 1-final_sum; // avoids round off errors thus ensuring the probabilities really sum to 1
  59. return v;
  60. }
  61. template <typename RealT>
  62. bool check_probabilities(std::vector<RealT> const& probabilities)
  63. {
  64. const std::size_t n = probabilities.size();
  65. RealT sum = 0;
  66. for (std::size_t i = 0; i < n; ++i)
  67. {
  68. if (probabilities[i] < 0
  69. || probabilities[i] > 1
  70. || !(boost::core::isfinite)(probabilities[i]))
  71. {
  72. return false;
  73. }
  74. sum += probabilities[i];
  75. }
  76. //NOTE: the check below seems to fail on some architectures.
  77. // So we commented it.
  78. //// - We try to keep phase probabilities correctly normalized in the distribution constructors
  79. //// - However in practice we have to allow for a very slight divergence from a sum of exactly 1:
  80. ////if (std::abs(sum-1) > (std::numeric_limits<RealT>::epsilon()*2))
  81. //// This is from Knuth "The Art of Computer Programming: Vol.2, 3rd Ed", and can be used to
  82. //// check is two numbers are approximately equal
  83. //const RealT one = 1;
  84. //const RealT tol = std::numeric_limits<RealT>::epsilon()*2.0;
  85. //if (std::abs(sum-one) > (std::max(std::abs(sum), std::abs(one))*tol))
  86. //{
  87. // return false;
  88. //}
  89. return true;
  90. }
  91. template <typename RealT>
  92. bool check_rates(std::vector<RealT> const& rates)
  93. {
  94. const std::size_t n = rates.size();
  95. for (std::size_t i = 0; i < n; ++i)
  96. {
  97. if (rates[i] <= 0
  98. || !(boost::core::isfinite)(rates[i]))
  99. {
  100. return false;
  101. }
  102. }
  103. return true;
  104. }
  105. template <typename RealT>
  106. bool check_params(std::vector<RealT> const& probabilities, std::vector<RealT> const& rates)
  107. {
  108. if (probabilities.size() != rates.size())
  109. {
  110. return false;
  111. }
  112. return check_probabilities(probabilities)
  113. && check_rates(rates);
  114. }
  115. } // Namespace hyperexp_detail
  116. /**
  117. * The hyperexponential distribution is a real-valued continuous distribution
  118. * with two parameters, the <em>phase probability vector</em> \c probs and the
  119. * <em>rate vector</em> \c rates.
  120. *
  121. * A \f$k\f$-phase hyperexponential distribution is a mixture of \f$k\f$
  122. * exponential distributions.
  123. * For this reason, it is also referred to as <em>mixed exponential
  124. * distribution</em> or <em>parallel \f$k\f$-phase exponential
  125. * distribution</em>.
  126. *
  127. * A \f$k\f$-phase hyperexponential distribution is characterized by two
  128. * parameters, namely a <em>phase probability vector</em> \f$\mathbf{\alpha}=(\alpha_1,\ldots,\alpha_k)\f$ and a <em>rate vector</em> \f$\mathbf{\lambda}=(\lambda_1,\ldots,\lambda_k)\f$.
  129. *
  130. * A \f$k\f$-phase hyperexponential distribution is frequently used in
  131. * <em>queueing theory</em> to model the distribution of the superposition of
  132. * \f$k\f$ independent events, like, for instance, the service time distribution
  133. * of a queueing station with \f$k\f$ servers in parallel where the \f$i\f$-th
  134. * server is chosen with probability \f$\alpha_i\f$ and its service time
  135. * distribution is an exponential distribution with rate \f$\lambda_i\f$
  136. * (Allen,1990; Papadopolous et al.,1993; Trivedi,2002).
  137. *
  138. * For instance, CPUs service-time distribution in a computing system has often
  139. * been observed to possess such a distribution (Rosin,1965).
  140. * Also, the arrival of different types of customer to a single queueing station
  141. * is often modeled as a hyperexponential distribution (Papadopolous et al.,1993).
  142. * Similarly, if a product manufactured in several parallel assemply lines and
  143. * the outputs are merged, the failure density of the overall product is likely
  144. * to be hyperexponential (Trivedi,2002).
  145. *
  146. * Finally, since the hyperexponential distribution exhibits a high Coefficient
  147. * of Variation (CoV), that is a CoV > 1, it is especially suited to fit
  148. * empirical data with large CoV (Feitelson,2014; Wolski et al.,2013) and to
  149. * approximate <em>long-tail probability distributions</em> (Feldmann et al.,1998).
  150. *
  151. * See (Boost,2014) for more information and examples.
  152. *
  153. * A \f$k\f$-phase hyperexponential distribution has a probability density
  154. * function
  155. * \f[
  156. * f(x) = \sum_{i=1}^k \alpha_i \lambda_i e^{-x\lambda_i}
  157. * \f]
  158. * where:
  159. * - \f$k\f$ is the <em>number of phases</em> and also the size of the input
  160. * vector parameters,
  161. * - \f$\mathbf{\alpha}=(\alpha_1,\ldots,\alpha_k)\f$ is the <em>phase probability
  162. * vector</em> parameter, and
  163. * - \f$\mathbf{\lambda}=(\lambda_1,\ldots,\lambda_k)\f$ is the <em>rate vector</em>
  164. * parameter.
  165. * .
  166. *
  167. * Given a \f$k\f$-phase hyperexponential distribution with phase probability
  168. * vector \f$\mathbf{\alpha}\f$ and rate vector \f$\mathbf{\lambda}\f$, the
  169. * random variate generation algorithm consists of the following steps (Tyszer,1999):
  170. * -# Generate a random variable \f$U\f$ uniformly distribution on the interval \f$(0,1)\f$.
  171. * -# Use \f$U\f$ to select the appropriate \f$\lambda_i\f$ (e.g., the
  172. * <em>alias method</em> can possibly be used for this step).
  173. * -# Generate an exponentially distributed random variable \f$X\f$ with rate parameter \f$\lambda_i\f$.
  174. * -# Return \f$X\f$.
  175. * .
  176. *
  177. * References:
  178. * -# A.O. Allen, <em>Probability, Statistics, and Queuing Theory with Computer Science Applications, Second Edition</em>, Academic Press, 1990.
  179. * -# Boost C++ Libraries, <em>Boost.Math / Statistical Distributions: Hyperexponential Distribution</em>, Online: http://www.boost.org/doc/libs/release/libs/math/doc/html/dist.html , 2014.
  180. * -# D.G. Feitelson, <em>Workload Modeling for Computer Systems Performance Evaluation</em>, Cambridge University Press, 2014
  181. * -# A. Feldmann and W. Whitt, <em>Fitting mixtures of exponentials to long-tail distributions to analyze network performance models</em>, Performance Evaluation 31(3-4):245, doi:10.1016/S0166-5316(97)00003-5, 1998.
  182. * -# H.T. Papadopolous, C. Heavey and J. Browne, <em>Queueing Theory in Manufacturing Systems Analysis and Design</em>, Chapman & Hall/CRC, 1993, p. 35.
  183. * -# R.F. Rosin, <em>Determining a computing center environment</em>, Communications of the ACM 8(7):463-468, 1965.
  184. * -# K.S. Trivedi, <em>Probability and Statistics with Reliability, Queueing, and Computer Science Applications</em>, John Wiley & Sons, Inc., 2002.
  185. * -# J. Tyszer, <em>Object-Oriented Computer Simulation of Discrete-Event Systems</em>, Springer, 1999.
  186. * -# Wikipedia, <em>Hyperexponential Distribution</em>, Online: http://en.wikipedia.org/wiki/Hyperexponential_distribution , 2014.
  187. * -# Wolfram Mathematica, <em>Hyperexponential Distribution</em>, Online: http://reference.wolfram.com/language/ref/HyperexponentialDistribution.html , 2014.
  188. * .
  189. *
  190. * \author Marco Guazzone (marco.guazzone@gmail.com)
  191. */
  192. template<class RealT = double>
  193. class hyperexponential_distribution
  194. {
  195. public: typedef RealT result_type;
  196. public: typedef RealT input_type;
  197. /**
  198. * The parameters of a hyperexponential distribution.
  199. *
  200. * Stores the <em>phase probability vector</em> and the <em>rate vector</em>
  201. * of the hyperexponential distribution.
  202. *
  203. * \author Marco Guazzone (marco.guazzone@gmail.com)
  204. */
  205. public: class param_type
  206. {
  207. public: typedef hyperexponential_distribution distribution_type;
  208. /**
  209. * Constructs a \c param_type with the default parameters
  210. * of the distribution.
  211. */
  212. public: param_type()
  213. : probs_(1, 1),
  214. rates_(1, 1)
  215. {
  216. }
  217. /**
  218. * Constructs a \c param_type from the <em>phase probability vector</em>
  219. * and <em>rate vector</em> parameters of the distribution.
  220. *
  221. * The <em>phase probability vector</em> parameter is given by the range
  222. * defined by [\a prob_first, \a prob_last) iterator pair, and the
  223. * <em>rate vector</em> parameter is given by the range defined by
  224. * [\a rate_first, \a rate_last) iterator pair.
  225. *
  226. * \tparam ProbIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
  227. * \tparam RateIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
  228. *
  229. * \param prob_first The iterator to the beginning of the range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
  230. * \param prob_last The iterator to the ending of the range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
  231. * \param rate_first The iterator to the beginning of the range of non-negative real elements representing the rates.
  232. * \param rate_last The iterator to the ending of the range of non-negative real elements representing the rates.
  233. *
  234. * References:
  235. * -# ISO, <em>ISO/IEC 14882-2014: Information technology - Programming languages - C++</em>, 2014
  236. * .
  237. */
  238. public: template <typename ProbIterT, typename RateIterT>
  239. param_type(ProbIterT prob_first, ProbIterT prob_last,
  240. RateIterT rate_first, RateIterT rate_last)
  241. : probs_(prob_first, prob_last),
  242. rates_(rate_first, rate_last)
  243. {
  244. hyperexp_detail::normalize(probs_);
  245. assert( hyperexp_detail::check_params(probs_, rates_) );
  246. }
  247. /**
  248. * Constructs a \c param_type from the <em>phase probability vector</em>
  249. * and <em>rate vector</em> parameters of the distribution.
  250. *
  251. * The <em>phase probability vector</em> parameter is given by the range
  252. * defined by \a prob_range, and the <em>rate vector</em> parameter is
  253. * given by the range defined by \a rate_range.
  254. *
  255. * \tparam ProbRangeT Must meet the requirements of <a href="boost:/libs/range/doc/html/range/concepts.html">Range</a> concept.
  256. * \tparam RateRangeT Must meet the requirements of <a href="boost:/libs/range/doc/html/range/concepts.html">Range</a> concept.
  257. *
  258. * \param prob_range The range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
  259. * \param rate_range The range of positive real elements representing the rates.
  260. *
  261. * \note
  262. * The final \c disable_if parameter is an implementation detail that
  263. * differentiates between this two argument constructor and the
  264. * iterator-based two argument constructor described below.
  265. */
  266. // We SFINAE this out of existance if either argument type is
  267. // incrementable as in that case the type is probably an iterator:
  268. public: template <typename ProbRangeT, typename RateRangeT>
  269. param_type(ProbRangeT const& prob_range,
  270. RateRangeT const& rate_range,
  271. typename boost::disable_if_c<boost::has_pre_increment<ProbRangeT>::value || boost::has_pre_increment<RateRangeT>::value>::type* = 0)
  272. : probs_(boost::begin(prob_range), boost::end(prob_range)),
  273. rates_(boost::begin(rate_range), boost::end(rate_range))
  274. {
  275. hyperexp_detail::normalize(probs_);
  276. assert( hyperexp_detail::check_params(probs_, rates_) );
  277. }
  278. /**
  279. * Constructs a \c param_type from the <em>rate vector</em> parameter of
  280. * the distribution and with equal phase probabilities.
  281. *
  282. * The <em>rate vector</em> parameter is given by the range defined by
  283. * [\a rate_first, \a rate_last) iterator pair, and the <em>phase
  284. * probability vector</em> parameter is set to the equal phase
  285. * probabilities (i.e., to a vector of the same length \f$k\f$ of the
  286. * <em>rate vector</em> and with each element set to \f$1.0/k\f$).
  287. *
  288. * \tparam RateIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
  289. * \tparam RateIterT2 Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
  290. *
  291. * \param rate_first The iterator to the beginning of the range of non-negative real elements representing the rates.
  292. * \param rate_last The iterator to the ending of the range of non-negative real elements representing the rates.
  293. *
  294. * \note
  295. * The final \c disable_if parameter is an implementation detail that
  296. * differentiates between this two argument constructor and the
  297. * range-based two argument constructor described above.
  298. *
  299. * References:
  300. * -# ISO, <em>ISO/IEC 14882-2014: Information technology - Programming languages - C++</em>, 2014
  301. * .
  302. */
  303. // We SFINAE this out of existance if the argument type is
  304. // incrementable as in that case the type is probably an iterator.
  305. public: template <typename RateIterT>
  306. param_type(RateIterT rate_first,
  307. RateIterT rate_last,
  308. typename boost::enable_if_c<boost::has_pre_increment<RateIterT>::value>::type* = 0)
  309. : probs_(std::distance(rate_first, rate_last), 1), // will be normalized below
  310. rates_(rate_first, rate_last)
  311. {
  312. assert(probs_.size() == rates_.size());
  313. }
  314. /**
  315. * Constructs a @c param_type from the "rates" parameters
  316. * of the distribution and with equal phase probabilities.
  317. *
  318. * The <em>rate vector</em> parameter is given by the range defined by
  319. * \a rate_range, and the <em>phase probability vector</em> parameter is
  320. * set to the equal phase probabilities (i.e., to a vector of the same
  321. * length \f$k\f$ of the <em>rate vector</em> and with each element set
  322. * to \f$1.0/k\f$).
  323. *
  324. * \tparam RateRangeT Must meet the requirements of <a href="boost:/libs/range/doc/html/range/concepts.html">Range</a> concept.
  325. *
  326. * \param rate_range The range of positive real elements representing the rates.
  327. */
  328. public: template <typename RateRangeT>
  329. param_type(RateRangeT const& rate_range)
  330. : probs_(boost::size(rate_range), 1), // Will be normalized below
  331. rates_(boost::begin(rate_range), boost::end(rate_range))
  332. {
  333. hyperexp_detail::normalize(probs_);
  334. assert( hyperexp_detail::check_params(probs_, rates_) );
  335. }
  336. #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
  337. /**
  338. * Constructs a \c param_type from the <em>phase probability vector</em>
  339. * and <em>rate vector</em> parameters of the distribution.
  340. *
  341. * The <em>phase probability vector</em> parameter is given by the
  342. * <em>brace-init-list</em> (ISO,2014,sec. 8.5.4 [dcl.init.list])
  343. * defined by \a l1, and the <em>rate vector</em> parameter is given by the
  344. * <em>brace-init-list</em> (ISO,2014,sec. 8.5.4 [dcl.init.list])
  345. * defined by \a l2.
  346. *
  347. * \param l1 The initializer list for inizializing the phase probability vector.
  348. * \param l2 The initializer list for inizializing the rate vector.
  349. *
  350. * References:
  351. * -# ISO, <em>ISO/IEC 14882-2014: Information technology - Programming languages - C++</em>, 2014
  352. * .
  353. */
  354. public: param_type(std::initializer_list<RealT> l1, std::initializer_list<RealT> l2)
  355. : probs_(l1.begin(), l1.end()),
  356. rates_(l2.begin(), l2.end())
  357. {
  358. hyperexp_detail::normalize(probs_);
  359. assert( hyperexp_detail::check_params(probs_, rates_) );
  360. }
  361. /**
  362. * Constructs a \c param_type from the <em>rate vector</em> parameter
  363. * of the distribution and with equal phase probabilities.
  364. *
  365. * The <em>rate vector</em> parameter is given by the
  366. * <em>brace-init-list</em> (ISO,2014,sec. 8.5.4 [dcl.init.list])
  367. * defined by \a l1, and the <em>phase probability vector</em> parameter is
  368. * set to the equal phase probabilities (i.e., to a vector of the same
  369. * length \f$k\f$ of the <em>rate vector</em> and with each element set
  370. * to \f$1.0/k\f$).
  371. *
  372. * \param l1 The initializer list for inizializing the rate vector.
  373. *
  374. * References:
  375. * -# ISO, <em>ISO/IEC 14882-2014: Information technology - Programming languages - C++</em>, 2014
  376. * .
  377. */
  378. public: param_type(std::initializer_list<RealT> l1)
  379. : probs_(std::distance(l1.begin(), l1.end()), 1), // Will be normalized below
  380. rates_(l1.begin(), l1.end())
  381. {
  382. hyperexp_detail::normalize(probs_);
  383. assert( hyperexp_detail::check_params(probs_, rates_) );
  384. }
  385. #endif // BOOST_NO_CXX11_HDR_INITIALIZER_LIST
  386. /**
  387. * Gets the <em>phase probability vector</em> parameter of the distribtuion.
  388. *
  389. * \return The <em>phase probability vector</em> parameter of the distribution.
  390. *
  391. * \note
  392. * The returned probabilities are the normalized version of the ones
  393. * passed at construction time.
  394. */
  395. public: std::vector<RealT> probabilities() const
  396. {
  397. return probs_;
  398. }
  399. /**
  400. * Gets the <em>rate vector</em> parameter of the distribtuion.
  401. *
  402. * \return The <em>rate vector</em> parameter of the distribution.
  403. */
  404. public: std::vector<RealT> rates() const
  405. {
  406. return rates_;
  407. }
  408. /** Writes a \c param_type to a \c std::ostream. */
  409. public: BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, param)
  410. {
  411. detail::print_vector(os, param.probs_);
  412. os << ' ';
  413. detail::print_vector(os, param.rates_);
  414. return os;
  415. }
  416. /** Reads a \c param_type from a \c std::istream. */
  417. public: BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, param)
  418. {
  419. // NOTE: if \c std::ios_base::exceptions is set, the code below may
  420. // throw in case of a I/O failure.
  421. // To prevent leaving the state of \c param inconsistent:
  422. // - if an exception is thrown, the state of \c param is left
  423. // unchanged (i.e., is the same as the one at the beginning
  424. // of the function's execution), and
  425. // - the state of \c param only after reading the whole input.
  426. std::vector<RealT> probs;
  427. std::vector<RealT> rates;
  428. // Reads probability and rate vectors
  429. detail::read_vector(is, probs);
  430. if (!is)
  431. {
  432. return is;
  433. }
  434. is >> std::ws;
  435. detail::read_vector(is, rates);
  436. if (!is)
  437. {
  438. return is;
  439. }
  440. // Update the state of the param_type object
  441. if (probs.size() > 0)
  442. {
  443. param.probs_.swap(probs);
  444. probs.clear();
  445. }
  446. if (rates.size() > 0)
  447. {
  448. param.rates_.swap(rates);
  449. rates.clear();
  450. }
  451. // Adjust vector sizes (if needed)
  452. if (param.probs_.size() != param.rates_.size()
  453. || param.probs_.size() == 0)
  454. {
  455. const std::size_t np = param.probs_.size();
  456. const std::size_t nr = param.rates_.size();
  457. if (np > nr)
  458. {
  459. param.rates_.resize(np, 1);
  460. }
  461. else if (nr > np)
  462. {
  463. param.probs_.resize(nr, 1);
  464. }
  465. else
  466. {
  467. param.probs_.resize(1, 1);
  468. param.rates_.resize(1, 1);
  469. }
  470. }
  471. // Normalize probabilities
  472. // NOTE: this cannot be done earlier since the probability vector
  473. // can be changed due to size conformance
  474. hyperexp_detail::normalize(param.probs_);
  475. //post: vector size conformance
  476. assert(param.probs_.size() == param.rates_.size());
  477. return is;
  478. }
  479. /** Returns true if the two sets of parameters are the same. */
  480. public: BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
  481. {
  482. return lhs.probs_ == rhs.probs_
  483. && lhs.rates_ == rhs.rates_;
  484. }
  485. /** Returns true if the two sets of parameters are the different. */
  486. public: BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
  487. private: std::vector<RealT> probs_; ///< The <em>phase probability vector</em> parameter of the distribution
  488. private: std::vector<RealT> rates_; ///< The <em>rate vector</em> parameter of the distribution
  489. }; // param_type
  490. /**
  491. * Constructs a 1-phase \c hyperexponential_distribution (i.e., an
  492. * exponential distribution) with rate 1.
  493. */
  494. public: hyperexponential_distribution()
  495. : dd_(std::vector<RealT>(1, 1)),
  496. rates_(1, 1)
  497. {
  498. // empty
  499. }
  500. /**
  501. * Constructs a \c hyperexponential_distribution from the <em>phase
  502. * probability vector</em> and <em>rate vector</em> parameters of the
  503. * distribution.
  504. *
  505. * The <em>phase probability vector</em> parameter is given by the range
  506. * defined by [\a prob_first, \a prob_last) iterator pair, and the
  507. * <em>rate vector</em> parameter is given by the range defined by
  508. * [\a rate_first, \a rate_last) iterator pair.
  509. *
  510. * \tparam ProbIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
  511. * \tparam RateIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
  512. *
  513. * \param prob_first The iterator to the beginning of the range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
  514. * \param prob_last The iterator to the ending of the range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
  515. * \param rate_first The iterator to the beginning of the range of non-negative real elements representing the rates.
  516. * \param rate_last The iterator to the ending of the range of non-negative real elements representing the rates.
  517. *
  518. * References:
  519. * -# ISO, <em>ISO/IEC 14882-2014: Information technology - Programming languages - C++</em>, 2014
  520. * .
  521. */
  522. public: template <typename ProbIterT, typename RateIterT>
  523. hyperexponential_distribution(ProbIterT prob_first, ProbIterT prob_last,
  524. RateIterT rate_first, RateIterT rate_last)
  525. : dd_(prob_first, prob_last),
  526. rates_(rate_first, rate_last)
  527. {
  528. assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
  529. }
  530. /**
  531. * Constructs a \c hyperexponential_distribution from the <em>phase
  532. * probability vector</em> and <em>rate vector</em> parameters of the
  533. * distribution.
  534. *
  535. * The <em>phase probability vector</em> parameter is given by the range
  536. * defined by \a prob_range, and the <em>rate vector</em> parameter is
  537. * given by the range defined by \a rate_range.
  538. *
  539. * \tparam ProbRangeT Must meet the requirements of <a href="boost:/libs/range/doc/html/range/concepts.html">Range</a> concept.
  540. * \tparam RateRangeT Must meet the requirements of <a href="boost:/libs/range/doc/html/range/concepts.html">Range</a> concept.
  541. *
  542. * \param prob_range The range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
  543. * \param rate_range The range of positive real elements representing the rates.
  544. *
  545. * \note
  546. * The final \c disable_if parameter is an implementation detail that
  547. * differentiates between this two argument constructor and the
  548. * iterator-based two argument constructor described below.
  549. */
  550. // We SFINAE this out of existance if either argument type is
  551. // incrementable as in that case the type is probably an iterator:
  552. public: template <typename ProbRangeT, typename RateRangeT>
  553. hyperexponential_distribution(ProbRangeT const& prob_range,
  554. RateRangeT const& rate_range,
  555. typename boost::disable_if_c<boost::has_pre_increment<ProbRangeT>::value || boost::has_pre_increment<RateRangeT>::value>::type* = 0)
  556. : dd_(prob_range),
  557. rates_(boost::begin(rate_range), boost::end(rate_range))
  558. {
  559. assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
  560. }
  561. /**
  562. * Constructs a \c hyperexponential_distribution from the <em>rate
  563. * vector</em> parameter of the distribution and with equal phase
  564. * probabilities.
  565. *
  566. * The <em>rate vector</em> parameter is given by the range defined by
  567. * [\a rate_first, \a rate_last) iterator pair, and the <em>phase
  568. * probability vector</em> parameter is set to the equal phase
  569. * probabilities (i.e., to a vector of the same length \f$k\f$ of the
  570. * <em>rate vector</em> and with each element set to \f$1.0/k\f$).
  571. *
  572. * \tparam RateIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
  573. * \tparam RateIterT2 Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
  574. *
  575. * \param rate_first The iterator to the beginning of the range of non-negative real elements representing the rates.
  576. * \param rate_last The iterator to the ending of the range of non-negative real elements representing the rates.
  577. *
  578. * \note
  579. * The final \c disable_if parameter is an implementation detail that
  580. * differentiates between this two argument constructor and the
  581. * range-based two argument constructor described above.
  582. *
  583. * References:
  584. * -# ISO, <em>ISO/IEC 14882-2014: Information technology - Programming languages - C++</em>, 2014
  585. * .
  586. */
  587. // We SFINAE this out of existance if the argument type is
  588. // incrementable as in that case the type is probably an iterator.
  589. public: template <typename RateIterT>
  590. hyperexponential_distribution(RateIterT rate_first,
  591. RateIterT rate_last,
  592. typename boost::enable_if_c<boost::has_pre_increment<RateIterT>::value>::type* = 0)
  593. : dd_(std::vector<RealT>(std::distance(rate_first, rate_last), 1)),
  594. rates_(rate_first, rate_last)
  595. {
  596. assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
  597. }
  598. /**
  599. * Constructs a @c param_type from the "rates" parameters
  600. * of the distribution and with equal phase probabilities.
  601. *
  602. * The <em>rate vector</em> parameter is given by the range defined by
  603. * \a rate_range, and the <em>phase probability vector</em> parameter is
  604. * set to the equal phase probabilities (i.e., to a vector of the same
  605. * length \f$k\f$ of the <em>rate vector</em> and with each element set
  606. * to \f$1.0/k\f$).
  607. *
  608. * \tparam RateRangeT Must meet the requirements of <a href="boost:/libs/range/doc/html/range/concepts.html">Range</a> concept.
  609. *
  610. * \param rate_range The range of positive real elements representing the rates.
  611. */
  612. public: template <typename RateRangeT>
  613. hyperexponential_distribution(RateRangeT const& rate_range)
  614. : dd_(std::vector<RealT>(boost::size(rate_range), 1)),
  615. rates_(boost::begin(rate_range), boost::end(rate_range))
  616. {
  617. assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
  618. }
  619. /**
  620. * Constructs a \c hyperexponential_distribution from its parameters.
  621. *
  622. * \param param The parameters of the distribution.
  623. */
  624. public: explicit hyperexponential_distribution(param_type const& param)
  625. : dd_(param.probabilities()),
  626. rates_(param.rates())
  627. {
  628. assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
  629. }
  630. #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
  631. /**
  632. * Constructs a \c hyperexponential_distribution from the <em>phase
  633. * probability vector</em> and <em>rate vector</em> parameters of the
  634. * distribution.
  635. *
  636. * The <em>phase probability vector</em> parameter is given by the
  637. * <em>brace-init-list</em> (ISO,2014,sec. 8.5.4 [dcl.init.list])
  638. * defined by \a l1, and the <em>rate vector</em> parameter is given by the
  639. * <em>brace-init-list</em> (ISO,2014,sec. 8.5.4 [dcl.init.list])
  640. * defined by \a l2.
  641. *
  642. * \param l1 The initializer list for inizializing the phase probability vector.
  643. * \param l2 The initializer list for inizializing the rate vector.
  644. *
  645. * References:
  646. * -# ISO, <em>ISO/IEC 14882-2014: Information technology - Programming languages - C++</em>, 2014
  647. * .
  648. */
  649. public: hyperexponential_distribution(std::initializer_list<RealT> const& l1, std::initializer_list<RealT> const& l2)
  650. : dd_(l1.begin(), l1.end()),
  651. rates_(l2.begin(), l2.end())
  652. {
  653. assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
  654. }
  655. /**
  656. * Constructs a \c hyperexponential_distribution from the <em>rate
  657. * vector</em> parameter of the distribution and with equal phase
  658. * probabilities.
  659. *
  660. * The <em>rate vector</em> parameter is given by the
  661. * <em>brace-init-list</em> (ISO,2014,sec. 8.5.4 [dcl.init.list])
  662. * defined by \a l1, and the <em>phase probability vector</em> parameter is
  663. * set to the equal phase probabilities (i.e., to a vector of the same
  664. * length \f$k\f$ of the <em>rate vector</em> and with each element set
  665. * to \f$1.0/k\f$).
  666. *
  667. * \param l1 The initializer list for inizializing the rate vector.
  668. *
  669. * References:
  670. * -# ISO, <em>ISO/IEC 14882-2014: Information technology - Programming languages - C++</em>, 2014
  671. * .
  672. */
  673. public: hyperexponential_distribution(std::initializer_list<RealT> const& l1)
  674. : dd_(std::vector<RealT>(std::distance(l1.begin(), l1.end()), 1)),
  675. rates_(l1.begin(), l1.end())
  676. {
  677. assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
  678. }
  679. #endif
  680. /**
  681. * Gets a random variate distributed according to the
  682. * hyperexponential distribution.
  683. *
  684. * \tparam URNG Must meet the requirements of \uniform_random_number_generator.
  685. *
  686. * \param urng A uniform random number generator object.
  687. *
  688. * \return A random variate distributed according to the hyperexponential distribution.
  689. */
  690. public: template<class URNG>\
  691. RealT operator()(URNG& urng) const
  692. {
  693. const int i = dd_(urng);
  694. return boost::random::exponential_distribution<RealT>(rates_[i])(urng);
  695. }
  696. /**
  697. * Gets a random variate distributed according to the hyperexponential
  698. * distribution with parameters specified by \c param.
  699. *
  700. * \tparam URNG Must meet the requirements of \uniform_random_number_generator.
  701. *
  702. * \param urng A uniform random number generator object.
  703. * \param param A distribution parameter object.
  704. *
  705. * \return A random variate distributed according to the hyperexponential distribution.
  706. * distribution with parameters specified by \c param.
  707. */
  708. public: template<class URNG>
  709. RealT operator()(URNG& urng, const param_type& param) const
  710. {
  711. return hyperexponential_distribution(param)(urng);
  712. }
  713. /** Returns the number of phases of the distribution. */
  714. public: std::size_t num_phases() const
  715. {
  716. return rates_.size();
  717. }
  718. /** Returns the <em>phase probability vector</em> parameter of the distribution. */
  719. public: std::vector<RealT> probabilities() const
  720. {
  721. return dd_.probabilities();
  722. }
  723. /** Returns the <em>rate vector</em> parameter of the distribution. */
  724. public: std::vector<RealT> rates() const
  725. {
  726. return rates_;
  727. }
  728. /** Returns the smallest value that the distribution can produce. */
  729. public: RealT min BOOST_PREVENT_MACRO_SUBSTITUTION () const
  730. {
  731. return 0;
  732. }
  733. /** Returns the largest value that the distribution can produce. */
  734. public: RealT max BOOST_PREVENT_MACRO_SUBSTITUTION () const
  735. {
  736. return std::numeric_limits<RealT>::infinity();
  737. }
  738. /** Returns the parameters of the distribution. */
  739. public: param_type param() const
  740. {
  741. std::vector<RealT> probs = dd_.probabilities();
  742. return param_type(probs.begin(), probs.end(), rates_.begin(), rates_.end());
  743. }
  744. /** Sets the parameters of the distribution. */
  745. public: void param(param_type const& param)
  746. {
  747. dd_.param(typename boost::random::discrete_distribution<int,RealT>::param_type(param.probabilities()));
  748. rates_ = param.rates();
  749. }
  750. /**
  751. * Effects: Subsequent uses of the distribution do not depend
  752. * on values produced by any engine prior to invoking reset.
  753. */
  754. public: void reset()
  755. {
  756. // empty
  757. }
  758. /** Writes an @c hyperexponential_distribution to a @c std::ostream. */
  759. public: BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, hyperexponential_distribution, hd)
  760. {
  761. os << hd.param();
  762. return os;
  763. }
  764. /** Reads an @c hyperexponential_distribution from a @c std::istream. */
  765. public: BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, hyperexponential_distribution, hd)
  766. {
  767. param_type param;
  768. if(is >> param)
  769. {
  770. hd.param(param);
  771. }
  772. return is;
  773. }
  774. /**
  775. * Returns true if the two instances of @c hyperexponential_distribution will
  776. * return identical sequences of values given equal generators.
  777. */
  778. public: BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(hyperexponential_distribution, lhs, rhs)
  779. {
  780. return lhs.dd_ == rhs.dd_
  781. && lhs.rates_ == rhs.rates_;
  782. }
  783. /**
  784. * Returns true if the two instances of @c hyperexponential_distribution will
  785. * return different sequences of values given equal generators.
  786. */
  787. public: BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(hyperexponential_distribution)
  788. private: boost::random::discrete_distribution<int,RealT> dd_; ///< The \c discrete_distribution used to sample the phase probability and choose the rate
  789. private: std::vector<RealT> rates_; ///< The <em>rate vector</em> parameter of the distribution
  790. }; // hyperexponential_distribution
  791. }} // namespace boost::random
  792. #endif // BOOST_RANDOM_HYPEREXPONENTIAL_DISTRIBUTION_HPP