[
https://issues.apache.org/jira/browse/RNG-91?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16819878#comment-16819878
]
Alex D Herbert commented on RNG-91:
-----------------------------------
Original timings above were using the Kemp method that effectively computes the
cumulative probability of the Poisson distribution until the cumulative
probability is above the value of the random deviate.
The cumulative probability is computed *for each sample*. This is a lot of
repeat computation.
However it should be noted that the value of the random deviate will be above
0.5 half of the time. Therefore for repeat sampling it is worth computing and
storing the cumulative probability and the sample value that corresponds to
approximately 0.5 in the cumulative probability function. This will be known as
the 50% hedge value.
This will *save half of the computation* for *half of the samples*. This is
expected to produce a speed increase of 50% and so *reduce runtime to 2/3* of
that listed above.
I have tested an implementation that uses this 50% hedge.
Here is an output of the raw data for various means using the original Kemp
algorithm and the modified Kemp algorithm. The algorithm is compared to the
current Small and Large mean Poisson sampler.
Note that the XO_RO_SHI_RO_128_PLUS generator is the fastest currently in the
library for double floating point generation. The Small mean sampler is very
dependent on the speed of the generator so a slow algorithm is once again
included to demonstrate this (WELL_44497B).
| |Small| | |OriginalKemp| | |Kemp| | |Large| | |
|mean|Xo|Split|Well|Xo|Split|Well|Xo|Split|Well|Xo|Split|Well|
|0.25|8.63|10.03|30.14|7.87|9.07|22.16|8.13|9.04|22.04| | | |
|0.5|12.31|14.60|37.72|11.34|13.07|25.42|11.06|12.39|25.09| | | |
|1|16.71|19.92|49.53|15.20|17.25|29.59|13.58|15.41|28.04|91.78|100.64|203.57|
|2|21.37|23.14|67.82|20.64|21.00|36.40|16.73|18.70|31.38|88.51|97.72|193.22|
|4|25.94|28.17|101.93|26.91|27.22|43.46|21.52|23.51|39.24|81.67|88.89|172.74|
|8|35.49|37.44|167.94|44.26|44.58|60.72|33.04|36.91|49.21|72.96|79.27|145.84|
|16|52.61|55.00|300.23|78.55|79.28|94.81|53.58|55.03|68.86|64.89|69.99|134.86|
|32|87.17|90.55|562.73|147.19|147.15|162.92|90.86|92.52|107.10|59.94|62.67|146.87|
Sorry for the large table width but the raw data allows a lot of analysis to be
done. Algorithms are:
* Split = SPLIT_MIX_64
* Xo = XO_RO_SHI_RO_128_PLUS
* Well = WELL_44497_B
The LargeMean sampler is invalid at a mean below 1.
*Analysis*
The new hedge computation is very similar to the original Kemp sampler at very
low mean. The added complexity of the algorithm cancels the speed increase of
the 50% hedge. It should also be noted that at a mean of 0.25 the cumulative
probability of a Poisson distribution is 0.779 for n=0. This high skew in the
distribution effectively means the hedge is rarely used and the complexity of
the algorithm is a waste.
When the mean is large the new 50% hedge algorithm does show the approximate
run-time at 2/3 of the original Kemp algorithm. So the hedge algorithm is
working as expected,
When the mean is in the range 0.5 to 8 the new hedged Kemp sampler is the
fastest.
Only at mean 16 and 32 is the current Small mean sampler faster and this is by
less then 5% and requires a very fast generator. When the generator is slow the
new Kemp sampler is faster by 5-fold due to the small sampler requiring
multiple double values from the RNG per poisson sample.
The Large mean sampler is currently used in the library at mean 40. This table
shows that with a fast RNG the sampler starts outperforming the small samplers
around a mean of 32. When the sampler is slow it outperforms the small mean
sampler at mean 8 but is still slower than the new Kemp sampler when the mean
is 32.
Note: The bound of 40 on the large mean poisson sampler may not be based on
performance and instead on the validity of the algorithm. It is based around
approximating the poisson distribution using a normal distribution with
handling of the skew when the mean is low. This sampler may not even be valid
for very low means and the current units tests use a mean of 67.89 to test the
distribution.
This is perhaps best illustrated with a chart (this omits the original Kemp
sampler and only includes the new Kemp sampler with the 50% hedge):
!poisson-samplers.jpg!
The new code for the Kemp sampler and the performance test is submitted in a PR.
A recommendation would be to:
* Switch the code to use the Kemp sampler instead of the Small mean poisson
sampler. Speed is approximately equivalent with the fastest generators in the
library and is much better when the generator is slow as only 1 double from the
RNG is used per sample.
* Maintain the switch point of 40 for the change to the large mean poisson
sampler. This is a valid point on the basis of performance and may be imposed
anyway by the details of the algorithm
> Kemp small mean poisson sampler
> -------------------------------
>
> Key: RNG-91
> URL: https://issues.apache.org/jira/browse/RNG-91
> Project: Commons RNG
> Issue Type: New Feature
> Components: sampling
> Affects Versions: 1.3
> Reporter: Alex D Herbert
> Assignee: Alex D Herbert
> Priority: Minor
> Fix For: 1.3
>
> Attachments: kemp.jpg, poisson-samplers.jpg
>
> Time Spent: 20m
> Remaining Estimate: 0h
>
> The current algorithm for the {{SmallMeanPoissonSampler}} is used to generate
> Poisson samples for any mean up to 40. The sampler requires approximately n
> samples from a RNG to generate 1 Poisson deviate for a mean of n.
> The [Kemp (1981)|https://www.jstor.org/stable/2346348] algorithm requires 1
> sample from the RNG and then accrues a cumulative probability using a
> recurrence relation to compute each successive Poisson probability:
> {noformat}
> p(n+1) = p(n) * mean / (n+1)
> {noformat}
> The full algorithm is here:
> {code:java}
> mean = ...;
> final double p0 = Math.exp(-mean);
> @Override
> public int sample() {
> double u = rng.nextDouble();
> int x = 0;
> double p = p0;
> // The algorithm listed in Kemp (1981) does not check that the
> rolling probability
> // is positive. This check is added to ensure no errors when the
> limit of the summation
> // 1 - sum(p(x)) is above 0 due to cumulative error in floating point
> arithmetic.
> while (u > p && p != 0) {
> u -= p;
> x++;
> p = p * mean / x;
> }
> return x;
> }
> {code}
> The limit for the sampler is set by the ability to compute p0. This is
> approximately 744.440 when Math.exp(-mean) returns 0.
> A conservative limit of 700 sets an initial probability p0 of
> 9.85967654375977E-305. When run through the summation series for the limit (u
> initialised to 1) the result when the summation ends (p is zero) leaves u =
> 3.335439283623915E-15. This is close to the expected tolerance for floating
> point error (Note: 1 - Math.nextDown(1) = 1.1102230246251565E-16).
> Using a mean of 10 leaves u = 4.988586742717954E-17. So smaller means have a
> similar error. The error in the cumulative sum is expected to result in
> truncation of the long tail of the Poisson distribution (which should be
> bounded at infinity).
> This sampler should outperform the current {{SmallMeanPoissonSampler}} as it
> requires 1 uniform deviate per sample.
> Note that the \{[SmallMeanPoissonSampler}} uses a limit for the mean of
> Integer.MAX_VALUE / 2. This should be updated since it also relies on p0
> being above zero.
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)