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/11/22 13:11:13 UTC

[commons-statistics] 02/03: Add probability range implementation for continuous uniform 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 07a9de308dca81a210d35052c4f91fa6d28bb5c1
Author: aherbert <ah...@apache.org>
AuthorDate: Mon Nov 22 12:30:38 2021 +0000

    Add probability range implementation for continuous uniform distribution
---
 .../UniformContinuousDistribution.java             | 30 +++++++---
 .../UniformContinuousDistributionTest.java         | 67 ++++++++++++++++++++++
 .../test.uniformcontinuous.1.properties            |  5 ++
 3 files changed, 94 insertions(+), 8 deletions(-)

diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/UniformContinuousDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/UniformContinuousDistribution.java
index 48145de..ce1c67a 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/UniformContinuousDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/UniformContinuousDistribution.java
@@ -77,6 +77,28 @@ public final class UniformContinuousDistribution extends AbstractContinuousDistr
 
     /** {@inheritDoc} */
     @Override
+    public double probability(double x0,
+                              double x1) {
+        if (x0 > x1) {
+            throw new DistributionException(DistributionException.INVALID_RANGE_LOW_GT_HIGH, x0, x1);
+        }
+        if (x0 >= upper || x1 <= lower) {
+            // (x0, x1] does not overlap [lower, upper]
+            return 0;
+        }
+
+        // x0 < upper
+        // x1 >= lower
+
+        // Find the range between x0 and x1 that is within [lower, upper].
+        final double l = Math.max(lower, x0);
+        final double u = Math.min(upper, x1);
+
+        return (u - l) / upperMinusLower;
+    }
+
+    /** {@inheritDoc} */
+    @Override
     public double logDensity(double x) {
         if (x < lower ||
             x > upper) {
@@ -187,14 +209,6 @@ public final class UniformContinuousDistribution extends AbstractContinuousDistr
 
     /** {@inheritDoc} */
     @Override
-    protected double getMedian() {
-        // Overridden for the probability(double, double) method.
-        // This is intentionally not a public method.
-        return getMean();
-    }
-
-    /** {@inheritDoc} */
-    @Override
     public ContinuousDistribution.Sampler createSampler(final UniformRandomProvider rng) {
         // Uniform distribution sampler.
         return ContinuousUniformSampler.of(rng, lower, upper)::sample;
diff --git a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/UniformContinuousDistributionTest.java b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/UniformContinuousDistributionTest.java
index 862d17e..28b384a 100644
--- a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/UniformContinuousDistributionTest.java
+++ b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/UniformContinuousDistributionTest.java
@@ -17,8 +17,14 @@
 
 package org.apache.commons.statistics.distribution;
 
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.sampling.distribution.ContinuousSampler;
+import org.apache.commons.rng.sampling.distribution.ContinuousUniformSampler;
+import org.apache.commons.rng.simple.RandomSource;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 
 /**
  * Test cases for {@link UniformContinuousDistribution}.
@@ -83,4 +89,65 @@ class UniformContinuousDistributionTest extends BaseContinuousDistributionTest {
         Assertions.assertEquals(-7.5e-10, dist2.inverseCumulativeProbability(0.25), Math.ulp(-7.5e-10));
         Assertions.assertEquals(-upper + tiny * upper, dist2.inverseCumulativeProbability(tiny));
     }
+
+    /**
+     * Test the probability in a range uses the exact computation of
+     * {@code (x1 - x0) / (upper - lower)} assuming x0 and x1 are within [lower, upper].
+     * This test will fail if the distribution uses the default implementation in
+     * {@link AbstractContinuousDistribution}.
+     */
+    @ParameterizedTest
+    @CsvSource(value = {
+        "-1.6358421681, -0.566237287234",
+        "-10.23678, 234.234",
+        "234.2342, 54322342.13",
+    })
+    void testProbabilityRange(double lower, double upper) {
+        final UniformContinuousDistribution dist = UniformContinuousDistribution.of(lower, upper);
+        final double r = upper - lower;
+        final UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_128_PP.create();
+        final ContinuousSampler sampler = ContinuousUniformSampler.of(rng, lower, upper);
+        for (int i = 0; i < 100; i++) {
+            double x0 = sampler.sample();
+            double x1 = sampler.sample();
+            if (x1 < x0) {
+                final double tmp = x0;
+                x1 = x0;
+                x0 = tmp;
+            }
+            Assertions.assertEquals((x1 - x0) / r, dist.probability(x0, x1));
+        }
+    }
+
+    @Test
+    void testProbabilityRangeEdgeCases() {
+        final UniformContinuousDistribution dist = UniformContinuousDistribution.of(0, 11);
+
+        Assertions.assertThrows(DistributionException.class, () -> dist.probability(4, 3));
+
+        // x0 >= upper
+        Assertions.assertEquals(0, dist.probability(11, 16));
+        Assertions.assertEquals(0, dist.probability(15, 16));
+        // x1 < lower
+        Assertions.assertEquals(0, dist.probability(-3, -1));
+
+        // x0 == x1
+        Assertions.assertEquals(0, dist.probability(4.12, 4.12));
+        Assertions.assertEquals(0, dist.probability(5.68, 5.68));
+
+        // x1 > upper
+        Assertions.assertEquals(1, dist.probability(0, 16));
+        Assertions.assertEquals((11 - 3.45) / 11, dist.probability(3.45, 16));
+        Assertions.assertEquals((11 - 4.89) / 11, dist.probability(4.89, 16));
+        Assertions.assertEquals(0, dist.probability(11, 16));
+
+        // x0 < lower
+        Assertions.assertEquals(2.0 / 11, dist.probability(-2, 2));
+        Assertions.assertEquals(3.0 / 11, dist.probability(-2, 3));
+        Assertions.assertEquals(4.0 / 11, dist.probability(-2, 4));
+        Assertions.assertEquals(1.0, dist.probability(-2, 11));
+
+        // x1 > upper && x0 < lower
+        Assertions.assertEquals(1, dist.probability(-2, 16));
+    }
 }
diff --git a/commons-statistics-distribution/src/test/resources/org/apache/commons/statistics/distribution/test.uniformcontinuous.1.properties b/commons-statistics-distribution/src/test/resources/org/apache/commons/statistics/distribution/test.uniformcontinuous.1.properties
index 922fae4..aee6b1e 100644
--- a/commons-statistics-distribution/src/test/resources/org/apache/commons/statistics/distribution/test.uniformcontinuous.1.properties
+++ b/commons-statistics-distribution/src/test/resources/org/apache/commons/statistics/distribution/test.uniformcontinuous.1.properties
@@ -17,6 +17,11 @@ parameters = -0.5 1.25
 # Lower tolerance to pass survival function test.
 # scipy uniform does not appear to compute sf to high precision.
 tolerance.relative = 1e-14
+# probability(x0, x1) test expects this to be cdf(x1) - cdf(x0).
+# The computation is 0.5 ulp exact using (x1 - x0) / (upper - lower).
+# Configure the absolute error to allow this.
+tolerance.absolute = 5e-17
+
 # Computed using scipy.stats uniform(-0.5, 1.75)
 mean = 0.375
 variance = 0.2552083333333333