You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2021/09/21 16:32:48 UTC
[commons-statistics] 02/04: Correct inverse CDF for Geometric
distribution.
This is an automated email from the ASF dual-hosted git repository.
aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-statistics.git
commit cc74870d70349f3f6e4325a6f8caec18fcd4d5e7
Author: aherbert <ah...@apache.org>
AuthorDate: Tue Sep 21 13:09:12 2021 +0100
Correct inverse CDF for Geometric distribution.
This requires a check of the result using the cdf and then correction to
round down.
---
.../distribution/GeometricDistribution.java | 9 ++++++++-
.../distribution/GeometricDistributionTest.java | 20 ++++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/GeometricDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/GeometricDistribution.java
index b7be11f..0724715 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/GeometricDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/GeometricDistribution.java
@@ -108,7 +108,14 @@ public class GeometricDistribution extends AbstractDiscreteDistribution {
if (p == 0) {
return 0;
}
- return Math.max(0, (int) Math.ceil(Math.log1p(-p) / log1mProbabilityOfSuccess - 1));
+ final int x = (int) Math.ceil(Math.log1p(-p) / log1mProbabilityOfSuccess - 1);
+ // Note: x may be too high due to floating-point error and rounding up with ceil.
+ // Return the next value down if that is also above the input cumulative probability.
+ // This ensures x == icdf(cdf(x))
+ if (x <= 0) {
+ return 0;
+ }
+ return cumulativeProbability(x - 1) >= p ? x - 1 : x;
}
/**
diff --git a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/GeometricDistributionTest.java b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/GeometricDistributionTest.java
index 41e8e06..9081408 100644
--- a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/GeometricDistributionTest.java
+++ b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/GeometricDistributionTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.commons.statistics.distribution;
+import java.util.Arrays;
import org.apache.commons.rng.simple.RandomSource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
@@ -185,6 +186,25 @@ class GeometricDistributionTest extends DiscreteDistributionAbstractTest {
}
}
+ /**
+ * Test the inverse CDF returns the correct x from the CDF result.
+ * This case was identified using various probabilities to discover a mismatch
+ * of x != icdf(cdf(x)). This occurs due to rounding errors on the inversion.
+ *
+ * @param p Probability of success
+ */
+ @ParameterizedTest
+ @ValueSource(doubles = {0.2, 0.8})
+ void testInverseCDF(double p) {
+ final GeometricDistribution dist = new GeometricDistribution(p);
+ final int[] x = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ final double[] values = Arrays.stream(x).mapToDouble(dist::cumulativeProbability).toArray();
+ setDistribution(dist);
+ setInverseCumulativeTestPoints(values);
+ setInverseCumulativeTestValues(x);
+ verifyInverseCumulativeProbabilities();
+ }
+
@ParameterizedTest
@ValueSource(doubles = {0.1, 0.456, 0.999})
void testParameterAccessors(double p) {