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