poisson_optimisation.qbk 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. [sect:optim Optimisation Examples]
  2. [h4 Poisson Distribution - Optimization and Accuracy is quite complicated.
  3. The general formula for calculating the CDF uses the incomplete gamma thus:
  4. return gamma_q(k+1, mean);
  5. But the case of small integral k is *very* common, so it is worth considering optimisation.
  6. The first obvious step is to use a finite sum of each PDF (Probability *density* function)
  7. for each value of k to build up the CDF (*cumulative* distribution function).
  8. This could be done using the PDF function for the distribution,
  9. for which there are two equivalent formulae:
  10. return exp(-mean + log(mean) * k - lgamma(k+1));
  11. return gamma_p_derivative(k+1, mean);
  12. The pdf would probably be more accurate using `gamma_p_derivative`.
  13. The reason is that the expression:
  14. -mean + log(mean) * k - lgamma(k+1)
  15. Will produce a value much smaller than the largest of the terms, so you get
  16. cancellation error: and then when you pass the result to `exp()` which
  17. converts the absolute error in its argument to a relative error in the
  18. result (explanation available if required), you effectively amplify the
  19. error further still.
  20. `gamma_p_derivative` is just a thin wrapper around some of the internals of
  21. the incomplete gamma, it does its utmost to avoid issues like this, because
  22. this function is responsible for virtually all of the error in the result.
  23. Hopefully further advances in the future might improve things even further
  24. (what is really needed is an 'accurate' `pow(1+x)` function, but that's a whole
  25. other story!).
  26. But calling `pdf` function makes repeated, redundant, checks on the value of `mean` and `k`,
  27. result += pdf(dist, i);
  28. so it may be faster to substitute the formula for the pdf in a summation loop
  29. result += exp(-mean) * pow(mean, i) / unchecked_factorial(i);
  30. (simplified by removing casting from RealType).
  31. Of course, mean is unchanged during this summation,
  32. so exp(mean) should only be calculated once, outside the loop.
  33. Optimising compilers 'might' do this, but one can easily ensure this.
  34. Obviously too, k must be small enough that unchecked_factorial is OK:
  35. 34 is an obvious choice as the limit for 32-bit float.
  36. For larger k, the number of iterations is like to be uneconomic.
  37. Only experiment can determine the optimum value of k
  38. for any particular RealType (float, double...)
  39. But also note that
  40. The incomplete gamma already optimises this case
  41. (argument "a" is a small int),
  42. although only when the result q (1-p) would be < 0.5.
  43. And moreover, in the above series, each term can be calculated
  44. from the previous one much more efficiently:
  45. cdf = sum from 0 to k of C[k]
  46. with:
  47. C[0] = exp(-mean)
  48. C[N+1] = C[N] * mean / (N+1)
  49. So hopefully that's:
  50. {
  51. RealType result = exp(-mean);
  52. RealType term = result;
  53. for(int i = 1; i <= k; ++i)
  54. { // cdf is sum of pdfs.
  55. term *= mean / i;
  56. result += term;
  57. }
  58. return result;
  59. }
  60. This is exactly the same finite sum as used by `gamma_p/gamma_q` internally.
  61. As explained previously it's only used when the result
  62. p > 0.5 or 1-p = q < 0.5.
  63. The slight danger when using the sum directly like this, is that if
  64. the mean is small and k is large then you're calculating a value ~1, so
  65. conceivably you might overshoot slightly. For this and other reasons in the
  66. case when p < 0.5 and q > 0.5 `gamma_p/gamma_q` use a different (infinite but
  67. rapidly converging) sum, so that danger isn't present since you always
  68. calculate the smaller of p and q.
  69. So... it's tempting to suggest that you just call `gamma_p/gamma_q` as
  70. required. However, there is a slight benefit for the k = 0 case because you
  71. avoid all the internal logic inside `gamma_p/gamma_q` trying to figure out
  72. which method to use etc.
  73. For the incomplete beta function, there are no simple finite sums
  74. available (that I know of yet anyway), so when there's a choice between a
  75. finite sum of the PDF and an incomplete beta call, the finite sum may indeed
  76. win out in that case.
  77. [endsect] [/sect:optim Optimisation Examples]
  78. [/
  79. Copyright 2006 John Maddock and Paul A. Bristow.
  80. Distributed under the Boost Software License, Version 1.0.
  81. (See accompanying file LICENSE_1_0.txt or copy at
  82. http://www.boost.org/LICENSE_1_0.txt).
  83. ]