hyperexponential_distribution.hpp 36 KB

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