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