• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //  Copyright 2014 Marco Guazzone (marco.guazzone@gmail.com)
2 //
3 //  Use, modification and distribution are subject to the
4 //  Boost Software License, Version 1.0. (See accompanying file
5 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // This module implements the Hyper-Exponential distribution.
8 //
9 // References:
10 // - "Queueing Theory in Manufacturing Systems Analysis and Design" by H.T. Papadopolous, C. Heavey and J. Browne (Chapman & Hall/CRC, 1993)
11 // - http://reference.wolfram.com/language/ref/HyperexponentialDistribution.html
12 // - http://en.wikipedia.org/wiki/Hyperexponential_distribution
13 //
14 
15 #ifndef BOOST_MATH_DISTRIBUTIONS_HYPEREXPONENTIAL_HPP
16 #define BOOST_MATH_DISTRIBUTIONS_HYPEREXPONENTIAL_HPP
17 
18 
19 #include <boost/config.hpp>
20 #include <boost/math/tools/cxx03_warn.hpp>
21 #include <boost/math/distributions/complement.hpp>
22 #include <boost/math/distributions/detail/common_error_handling.hpp>
23 #include <boost/math/distributions/exponential.hpp>
24 #include <boost/math/policies/policy.hpp>
25 #include <boost/math/special_functions/fpclassify.hpp>
26 #include <boost/math/tools/precision.hpp>
27 #include <boost/math/tools/roots.hpp>
28 #include <boost/range/begin.hpp>
29 #include <boost/range/end.hpp>
30 #include <boost/range/size.hpp>
31 #include <boost/type_traits/has_pre_increment.hpp>
32 #include <cstddef>
33 #include <iterator>
34 #include <limits>
35 #include <numeric>
36 #include <utility>
37 #include <vector>
38 
39 #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
40 # include <initializer_list>
41 #endif
42 
43 #ifdef _MSC_VER
44 # pragma warning (push)
45 # pragma warning(disable:4127) // conditional expression is constant
46 # pragma warning(disable:4389) // '==' : signed/unsigned mismatch in test_tools
47 #endif // _MSC_VER
48 
49 namespace boost { namespace math {
50 
51 namespace detail {
52 
53 template <typename Dist>
54 typename Dist::value_type generic_quantile(const Dist& dist, const typename Dist::value_type& p, const typename Dist::value_type& guess, bool comp, const char* function);
55 
56 } // Namespace detail
57 
58 
59 template <typename RealT, typename PolicyT>
60 class hyperexponential_distribution;
61 
62 
63 namespace /*<unnamed>*/ { namespace hyperexp_detail {
64 
65 template <typename T>
normalize(std::vector<T> & v)66 void normalize(std::vector<T>& v)
67 {
68    if(!v.size())
69       return;  // Our error handlers will get this later
70     const T sum = std::accumulate(v.begin(), v.end(), static_cast<T>(0));
71     T final_sum = 0;
72     const typename std::vector<T>::iterator end = --v.end();
73     for (typename std::vector<T>::iterator it = v.begin();
74          it != end;
75          ++it)
76     {
77         *it /= sum;
78         final_sum += *it;
79     }
80     *end = 1 - final_sum;  // avoids round off errors, ensures the probs really do sum to 1.
81 }
82 
83 template <typename RealT, typename PolicyT>
check_probabilities(char const * function,std::vector<RealT> const & probabilities,RealT * presult,PolicyT const & pol)84 bool check_probabilities(char const* function, std::vector<RealT> const& probabilities, RealT* presult, PolicyT const& pol)
85 {
86     BOOST_MATH_STD_USING
87     const std::size_t n = probabilities.size();
88     RealT sum = 0;
89     for (std::size_t i = 0; i < n; ++i)
90     {
91         if (probabilities[i] < 0
92             || probabilities[i] > 1
93             || !(boost::math::isfinite)(probabilities[i]))
94         {
95             *presult = policies::raise_domain_error<RealT>(function,
96                                                            "The elements of parameter \"probabilities\" must be >= 0 and <= 1, but at least one of them was: %1%.",
97                                                            probabilities[i],
98                                                            pol);
99             return false;
100         }
101         sum += probabilities[i];
102     }
103 
104     //
105     // We try to keep phase probabilities correctly normalized in the distribution constructors,
106     // however in practice we have to allow for a very slight divergence from a sum of exactly 1:
107     //
108     if (fabs(sum - 1) > tools::epsilon<RealT>() * 2)
109     {
110         *presult = policies::raise_domain_error<RealT>(function,
111                                                        "The elements of parameter \"probabilities\" must sum to 1, but their sum is: %1%.",
112                                                        sum,
113                                                        pol);
114         return false;
115     }
116 
117     return true;
118 }
119 
120 template <typename RealT, typename PolicyT>
check_rates(char const * function,std::vector<RealT> const & rates,RealT * presult,PolicyT const & pol)121 bool check_rates(char const* function, std::vector<RealT> const& rates, RealT* presult, PolicyT const& pol)
122 {
123     const std::size_t n = rates.size();
124     for (std::size_t i = 0; i < n; ++i)
125     {
126         if (rates[i] <= 0
127             || !(boost::math::isfinite)(rates[i]))
128         {
129             *presult = policies::raise_domain_error<RealT>(function,
130                                                            "The elements of parameter \"rates\" must be > 0, but at least one of them is: %1%.",
131                                                            rates[i],
132                                                            pol);
133             return false;
134         }
135     }
136     return true;
137 }
138 
139 template <typename RealT, typename PolicyT>
check_dist(char const * function,std::vector<RealT> const & probabilities,std::vector<RealT> const & rates,RealT * presult,PolicyT const & pol)140 bool check_dist(char const* function, std::vector<RealT> const& probabilities, std::vector<RealT> const& rates, RealT* presult, PolicyT const& pol)
141 {
142     BOOST_MATH_STD_USING
143     if (probabilities.size() != rates.size())
144     {
145         *presult = policies::raise_domain_error<RealT>(function,
146                                                        "The parameters \"probabilities\" and \"rates\" must have the same length, but their size differ by: %1%.",
147                                                        fabs(static_cast<RealT>(probabilities.size())-static_cast<RealT>(rates.size())),
148                                                        pol);
149         return false;
150     }
151 
152     return check_probabilities(function, probabilities, presult, pol)
153            && check_rates(function, rates, presult, pol);
154 }
155 
156 template <typename RealT, typename PolicyT>
check_x(char const * function,RealT x,RealT * presult,PolicyT const & pol)157 bool check_x(char const* function, RealT x, RealT* presult, PolicyT const& pol)
158 {
159     if (x < 0 || (boost::math::isnan)(x))
160     {
161         *presult = policies::raise_domain_error<RealT>(function, "The random variable must be >= 0, but is: %1%.", x, pol);
162         return false;
163     }
164     return true;
165 }
166 
167 template <typename RealT, typename PolicyT>
check_probability(char const * function,RealT p,RealT * presult,PolicyT const & pol)168 bool check_probability(char const* function, RealT p, RealT* presult, PolicyT const& pol)
169 {
170     if (p < 0 || p > 1 || (boost::math::isnan)(p))
171     {
172         *presult = policies::raise_domain_error<RealT>(function, "The probability be >= 0 and <= 1, but is: %1%.", p, pol);
173         return false;
174     }
175     return true;
176 }
177 
178 template <typename RealT, typename PolicyT>
quantile_impl(hyperexponential_distribution<RealT,PolicyT> const & dist,RealT const & p,bool comp)179 RealT quantile_impl(hyperexponential_distribution<RealT, PolicyT> const& dist, RealT const& p, bool comp)
180 {
181     // Don't have a closed form so try to numerically solve the inverse CDF...
182 
183     typedef typename policies::evaluation<RealT, PolicyT>::type value_type;
184     typedef typename policies::normalise<PolicyT,
185                                          policies::promote_float<false>,
186                                          policies::promote_double<false>,
187                                          policies::discrete_quantile<>,
188                                          policies::assert_undefined<> >::type forwarding_policy;
189 
190     static const char* function = comp ? "boost::math::quantile(const boost::math::complemented2_type<boost::math::hyperexponential_distribution<%1%>, %1%>&)"
191                                        : "boost::math::quantile(const boost::math::hyperexponential_distribution<%1%>&, %1%)";
192 
193     RealT result = 0;
194 
195     if (!check_probability(function, p, &result, PolicyT()))
196     {
197         return result;
198     }
199 
200     const std::size_t n = dist.num_phases();
201     const std::vector<RealT> probs = dist.probabilities();
202     const std::vector<RealT> rates = dist.rates();
203 
204     // A possible (but inaccurate) approximation is given below, where the
205     // quantile is given by the weighted sum of exponential quantiles:
206     RealT guess = 0;
207     if (comp)
208     {
209         for (std::size_t i = 0; i < n; ++i)
210         {
211             const exponential_distribution<RealT,PolicyT> exp(rates[i]);
212 
213             guess += probs[i]*quantile(complement(exp, p));
214         }
215     }
216     else
217     {
218         for (std::size_t i = 0; i < n; ++i)
219         {
220             const exponential_distribution<RealT,PolicyT> exp(rates[i]);
221 
222             guess += probs[i]*quantile(exp, p);
223         }
224     }
225 
226     // Fast return in case the Hyper-Exponential is essentially an Exponential
227     if (n == 1)
228     {
229         return guess;
230     }
231 
232     value_type q;
233     q = detail::generic_quantile(hyperexponential_distribution<RealT,forwarding_policy>(probs, rates),
234                                  p,
235                                  guess,
236                                  comp,
237                                  function);
238 
239     result = policies::checked_narrowing_cast<RealT,forwarding_policy>(q, function);
240 
241     return result;
242 }
243 
244 }} // Namespace <unnamed>::hyperexp_detail
245 
246 
247 template <typename RealT = double, typename PolicyT = policies::policy<> >
248 class hyperexponential_distribution
249 {
250     public: typedef RealT value_type;
251     public: typedef PolicyT policy_type;
252 
253 
hyperexponential_distribution()254     public: hyperexponential_distribution()
255     : probs_(1, 1),
256       rates_(1, 1)
257     {
258         RealT err;
259         hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
260                                     probs_,
261                                     rates_,
262                                     &err,
263                                     PolicyT());
264     }
265 
266     // Four arg constructor: no ambiguity here, the arguments must be two pairs of iterators:
267     public: template <typename ProbIterT, typename RateIterT>
hyperexponential_distribution(ProbIterT prob_first,ProbIterT prob_last,RateIterT rate_first,RateIterT rate_last)268             hyperexponential_distribution(ProbIterT prob_first, ProbIterT prob_last,
269                                           RateIterT rate_first, RateIterT rate_last)
270     : probs_(prob_first, prob_last),
271       rates_(rate_first, rate_last)
272     {
273         hyperexp_detail::normalize(probs_);
274         RealT err;
275         hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
276                                     probs_,
277                                     rates_,
278                                     &err,
279                                     PolicyT());
280     }
281 
282     // Two arg constructor from 2 ranges, we SFINAE this out of existence if
283     // either argument type is incrementable as in that case the type is
284     // probably an iterator:
285     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)286             hyperexponential_distribution(ProbRangeT const& prob_range,
287                                           RateRangeT const& rate_range,
288                                           typename boost::disable_if_c<boost::has_pre_increment<ProbRangeT>::value || boost::has_pre_increment<RateRangeT>::value>::type* = 0)
289     : probs_(boost::begin(prob_range), boost::end(prob_range)),
290       rates_(boost::begin(rate_range), boost::end(rate_range))
291     {
292         hyperexp_detail::normalize(probs_);
293 
294         RealT err;
295         hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
296                                     probs_,
297                                     rates_,
298                                     &err,
299                                     PolicyT());
300     }
301 
302     // Two arg constructor for a pair of iterators: we SFINAE this out of
303     // existence if neither argument types are incrementable.
304     // Note that we allow different argument types here to allow for
305     // construction from an array plus a pointer into that array.
306     public: template <typename RateIterT, typename RateIterT2>
hyperexponential_distribution(RateIterT const & rate_first,RateIterT2 const & rate_last,typename boost::enable_if_c<boost::has_pre_increment<RateIterT>::value||boost::has_pre_increment<RateIterT2>::value>::type * =0)307             hyperexponential_distribution(RateIterT const& rate_first,
308                                           RateIterT2 const& rate_last,
309                                           typename boost::enable_if_c<boost::has_pre_increment<RateIterT>::value || boost::has_pre_increment<RateIterT2>::value>::type* = 0)
310     : probs_(std::distance(rate_first, rate_last), 1), // will be normalized below
311       rates_(rate_first, rate_last)
312     {
313         hyperexp_detail::normalize(probs_);
314 
315         RealT err;
316         hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
317                                     probs_,
318                                     rates_,
319                                     &err,
320                                     PolicyT());
321     }
322 
323 #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
324       // Initializer list constructor: allows for construction from array literals:
hyperexponential_distribution(std::initializer_list<RealT> l1,std::initializer_list<RealT> l2)325 public: hyperexponential_distribution(std::initializer_list<RealT> l1, std::initializer_list<RealT> l2)
326       : probs_(l1.begin(), l1.end()),
327         rates_(l2.begin(), l2.end())
328       {
329          hyperexp_detail::normalize(probs_);
330 
331          RealT err;
332          hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
333             probs_,
334             rates_,
335             &err,
336             PolicyT());
337       }
338 
hyperexponential_distribution(std::initializer_list<RealT> l1)339 public: hyperexponential_distribution(std::initializer_list<RealT> l1)
340       : probs_(l1.size(), 1),
341         rates_(l1.begin(), l1.end())
342       {
343          hyperexp_detail::normalize(probs_);
344 
345          RealT err;
346          hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
347             probs_,
348             rates_,
349             &err,
350             PolicyT());
351       }
352 #endif // !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
353 
354     // Single argument constructor: argument must be a range.
355     public: template <typename RateRangeT>
hyperexponential_distribution(RateRangeT const & rate_range)356     hyperexponential_distribution(RateRangeT const& rate_range)
357     : probs_(boost::size(rate_range), 1), // will be normalized below
358       rates_(boost::begin(rate_range), boost::end(rate_range))
359     {
360         hyperexp_detail::normalize(probs_);
361 
362         RealT err;
363         hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
364                                     probs_,
365                                     rates_,
366                                     &err,
367                                     PolicyT());
368     }
369 
probabilities() const370     public: std::vector<RealT> probabilities() const
371     {
372         return probs_;
373     }
374 
rates() const375     public: std::vector<RealT> rates() const
376     {
377         return rates_;
378     }
379 
num_phases() const380     public: std::size_t num_phases() const
381     {
382         return rates_.size();
383     }
384 
385 
386     private: std::vector<RealT> probs_;
387     private: std::vector<RealT> rates_;
388 }; // class hyperexponential_distribution
389 
390 
391 // Convenient type synonym for double.
392 typedef hyperexponential_distribution<double> hyperexponential;
393 
394 
395 // Range of permissible values for random variable x
396 template <typename RealT, typename PolicyT>
range(hyperexponential_distribution<RealT,PolicyT> const &)397 std::pair<RealT,RealT> range(hyperexponential_distribution<RealT,PolicyT> const&)
398 {
399     if (std::numeric_limits<RealT>::has_infinity)
400     {
401         return std::make_pair(static_cast<RealT>(0), std::numeric_limits<RealT>::infinity()); // 0 to +inf.
402     }
403 
404     return std::make_pair(static_cast<RealT>(0), tools::max_value<RealT>()); // 0 to +<max value>
405 }
406 
407 // Range of supported values for random variable x.
408 // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
409 template <typename RealT, typename PolicyT>
support(hyperexponential_distribution<RealT,PolicyT> const &)410 std::pair<RealT,RealT> support(hyperexponential_distribution<RealT,PolicyT> const&)
411 {
412     return std::make_pair(tools::min_value<RealT>(), tools::max_value<RealT>()); // <min value> to +<max value>.
413 }
414 
415 template <typename RealT, typename PolicyT>
pdf(hyperexponential_distribution<RealT,PolicyT> const & dist,RealT const & x)416 RealT pdf(hyperexponential_distribution<RealT, PolicyT> const& dist, RealT const& x)
417 {
418     BOOST_MATH_STD_USING
419     RealT result = 0;
420 
421     if (!hyperexp_detail::check_x("boost::math::pdf(const boost::math::hyperexponential_distribution<%1%>&, %1%)", x, &result, PolicyT()))
422     {
423         return result;
424     }
425 
426     const std::size_t n = dist.num_phases();
427     const std::vector<RealT> probs = dist.probabilities();
428     const std::vector<RealT> rates = dist.rates();
429 
430     for (std::size_t i = 0; i < n; ++i)
431     {
432         const exponential_distribution<RealT,PolicyT> exp(rates[i]);
433 
434         result += probs[i]*pdf(exp, x);
435         //result += probs[i]*rates[i]*exp(-rates[i]*x);
436     }
437 
438     return result;
439 }
440 
441 template <typename RealT, typename PolicyT>
cdf(hyperexponential_distribution<RealT,PolicyT> const & dist,RealT const & x)442 RealT cdf(hyperexponential_distribution<RealT, PolicyT> const& dist, RealT const& x)
443 {
444     RealT result = 0;
445 
446     if (!hyperexp_detail::check_x("boost::math::cdf(const boost::math::hyperexponential_distribution<%1%>&, %1%)", x, &result, PolicyT()))
447     {
448         return result;
449     }
450 
451     const std::size_t n = dist.num_phases();
452     const std::vector<RealT> probs = dist.probabilities();
453     const std::vector<RealT> rates = dist.rates();
454 
455     for (std::size_t i = 0; i < n; ++i)
456     {
457         const exponential_distribution<RealT,PolicyT> exp(rates[i]);
458 
459         result += probs[i]*cdf(exp, x);
460     }
461 
462     return result;
463 }
464 
465 template <typename RealT, typename PolicyT>
quantile(hyperexponential_distribution<RealT,PolicyT> const & dist,RealT const & p)466 RealT quantile(hyperexponential_distribution<RealT, PolicyT> const& dist, RealT const& p)
467 {
468     return hyperexp_detail::quantile_impl(dist, p , false);
469 }
470 
471 template <typename RealT, typename PolicyT>
cdf(complemented2_type<hyperexponential_distribution<RealT,PolicyT>,RealT> const & c)472 RealT cdf(complemented2_type<hyperexponential_distribution<RealT,PolicyT>, RealT> const& c)
473 {
474     RealT const& x = c.param;
475     hyperexponential_distribution<RealT,PolicyT> const& dist = c.dist;
476 
477     RealT result = 0;
478 
479     if (!hyperexp_detail::check_x("boost::math::cdf(boost::math::complemented2_type<const boost::math::hyperexponential_distribution<%1%>&, %1%>)", x, &result, PolicyT()))
480     {
481         return result;
482     }
483 
484     const std::size_t n = dist.num_phases();
485     const std::vector<RealT> probs = dist.probabilities();
486     const std::vector<RealT> rates = dist.rates();
487 
488     for (std::size_t i = 0; i < n; ++i)
489     {
490         const exponential_distribution<RealT,PolicyT> exp(rates[i]);
491 
492         result += probs[i]*cdf(complement(exp, x));
493     }
494 
495     return result;
496 }
497 
498 
499 template <typename RealT, typename PolicyT>
quantile(complemented2_type<hyperexponential_distribution<RealT,PolicyT>,RealT> const & c)500 RealT quantile(complemented2_type<hyperexponential_distribution<RealT, PolicyT>, RealT> const& c)
501 {
502     RealT const& p = c.param;
503     hyperexponential_distribution<RealT,PolicyT> const& dist = c.dist;
504 
505     return hyperexp_detail::quantile_impl(dist, p , true);
506 }
507 
508 template <typename RealT, typename PolicyT>
mean(hyperexponential_distribution<RealT,PolicyT> const & dist)509 RealT mean(hyperexponential_distribution<RealT, PolicyT> const& dist)
510 {
511     RealT result = 0;
512 
513     const std::size_t n = dist.num_phases();
514     const std::vector<RealT> probs = dist.probabilities();
515     const std::vector<RealT> rates = dist.rates();
516 
517     for (std::size_t i = 0; i < n; ++i)
518     {
519         const exponential_distribution<RealT,PolicyT> exp(rates[i]);
520 
521         result += probs[i]*mean(exp);
522     }
523 
524     return result;
525 }
526 
527 template <typename RealT, typename PolicyT>
variance(hyperexponential_distribution<RealT,PolicyT> const & dist)528 RealT variance(hyperexponential_distribution<RealT, PolicyT> const& dist)
529 {
530     RealT result = 0;
531 
532     const std::size_t n = dist.num_phases();
533     const std::vector<RealT> probs = dist.probabilities();
534     const std::vector<RealT> rates = dist.rates();
535 
536     for (std::size_t i = 0; i < n; ++i)
537     {
538         result += probs[i]/(rates[i]*rates[i]);
539     }
540 
541     const RealT mean = boost::math::mean(dist);
542 
543     result = 2*result-mean*mean;
544 
545     return result;
546 }
547 
548 template <typename RealT, typename PolicyT>
skewness(hyperexponential_distribution<RealT,PolicyT> const & dist)549 RealT skewness(hyperexponential_distribution<RealT,PolicyT> const& dist)
550 {
551     BOOST_MATH_STD_USING
552     const std::size_t n = dist.num_phases();
553     const std::vector<RealT> probs = dist.probabilities();
554     const std::vector<RealT> rates = dist.rates();
555 
556     RealT s1 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i}
557     RealT s2 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^2}
558     RealT s3 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^3}
559     for (std::size_t i = 0; i < n; ++i)
560     {
561         const RealT p = probs[i];
562         const RealT r = rates[i];
563         const RealT r2 = r*r;
564         const RealT r3 = r2*r;
565 
566         s1 += p/r;
567         s2 += p/r2;
568         s3 += p/r3;
569     }
570 
571     const RealT s1s1 = s1*s1;
572 
573     const RealT num = (6*s3 - (3*(2*s2 - s1s1) + s1s1)*s1);
574     const RealT den = (2*s2 - s1s1);
575 
576     return num / pow(den, static_cast<RealT>(1.5));
577 }
578 
579 template <typename RealT, typename PolicyT>
kurtosis(hyperexponential_distribution<RealT,PolicyT> const & dist)580 RealT kurtosis(hyperexponential_distribution<RealT,PolicyT> const& dist)
581 {
582     const std::size_t n = dist.num_phases();
583     const std::vector<RealT> probs = dist.probabilities();
584     const std::vector<RealT> rates = dist.rates();
585 
586     RealT s1 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i}
587     RealT s2 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^2}
588     RealT s3 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^3}
589     RealT s4 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^4}
590     for (std::size_t i = 0; i < n; ++i)
591     {
592         const RealT p = probs[i];
593         const RealT r = rates[i];
594         const RealT r2 = r*r;
595         const RealT r3 = r2*r;
596         const RealT r4 = r3*r;
597 
598         s1 += p/r;
599         s2 += p/r2;
600         s3 += p/r3;
601         s4 += p/r4;
602     }
603 
604     const RealT s1s1 = s1*s1;
605 
606     const RealT num = (24*s4 - 24*s3*s1 + 3*(2*(2*s2 - s1s1) + s1s1)*s1s1);
607     const RealT den = (2*s2 - s1s1);
608 
609     return num/(den*den);
610 }
611 
612 template <typename RealT, typename PolicyT>
kurtosis_excess(hyperexponential_distribution<RealT,PolicyT> const & dist)613 RealT kurtosis_excess(hyperexponential_distribution<RealT,PolicyT> const& dist)
614 {
615     return kurtosis(dist) - 3;
616 }
617 
618 template <typename RealT, typename PolicyT>
mode(hyperexponential_distribution<RealT,PolicyT> const &)619 RealT mode(hyperexponential_distribution<RealT,PolicyT> const& /*dist*/)
620 {
621     return 0;
622 }
623 
624 }} // namespace boost::math
625 
626 #ifdef BOOST_MSVC
627 #pragma warning (pop)
628 #endif
629 // This include must be at the end, *after* the accessors
630 // for this distribution have been defined, in order to
631 // keep compilers that support two-phase lookup happy.
632 #include <boost/math/distributions/detail/derived_accessors.hpp>
633 #include <boost/math/distributions/detail/generic_quantile.hpp>
634 
635 #endif // BOOST_MATH_DISTRIBUTIONS_HYPEREXPONENTIAL
636