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/12/16 12:17:46 UTC

[commons-statistics] branch master updated (e83dec2 -> d0c550b)

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/commons-statistics.git.


    from e83dec2  Fix typo
     new 5bc2f6c  Ensure default inverse probability is robust to distribution truncation
     new e17a87a  PMD fix: Refactor adjustment of infinite bounds to methods
     new aaf91eb  Avoid overflow in the continuous uniform distribution
     new 19a8138  Reduce getMedian to package-private
     new d0c550b  Reduce isSupportConnected to package-private

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../AbstractContinuousDistribution.java            | 143 ++++++++++++++-----
 .../distribution/CauchyDistribution.java           |   2 +-
 .../distribution/ExponentialDistribution.java      |   2 +-
 .../distribution/GumbelDistribution.java           |   2 +-
 .../distribution/LaplaceDistribution.java          |   2 +-
 .../statistics/distribution/LevyDistribution.java  |   2 +-
 .../distribution/LogisticDistribution.java         |   2 +-
 .../statistics/distribution/TDistribution.java     |   2 +-
 .../UniformContinuousDistribution.java             |  17 ++-
 .../AbstractContinuousDistributionTest.java        | 153 ++++++++++++++++++++-
 .../UniformContinuousDistributionTest.java         |   9 ++
 11 files changed, 283 insertions(+), 53 deletions(-)

[commons-statistics] 04/05: Reduce getMedian to package-private

Posted by ah...@apache.org.
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 19a8138288a9003b224b2eee99e08530dd42aa41
Author: aherbert <ah...@apache.org>
AuthorDate: Thu Dec 16 11:44:49 2021 +0000

    Reduce getMedian to package-private
    
    If protected this is exposed in javadoc. It is a package level
    implementation detail.
---
 .../commons/statistics/distribution/AbstractContinuousDistribution.java | 2 +-
 .../org/apache/commons/statistics/distribution/CauchyDistribution.java  | 2 +-
 .../apache/commons/statistics/distribution/ExponentialDistribution.java | 2 +-
 .../org/apache/commons/statistics/distribution/GumbelDistribution.java  | 2 +-
 .../org/apache/commons/statistics/distribution/LaplaceDistribution.java | 2 +-
 .../org/apache/commons/statistics/distribution/LevyDistribution.java    | 2 +-
 .../apache/commons/statistics/distribution/LogisticDistribution.java    | 2 +-
 .../java/org/apache/commons/statistics/distribution/TDistribution.java  | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
index 1a95654..69e6be1 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
@@ -100,7 +100,7 @@ abstract class AbstractContinuousDistribution
      *
      * @return the median
      */
-    protected double getMedian() {
+    double getMedian() {
         double m = median;
         if (Double.isNaN(m)) {
             median = m = inverseCumulativeProbability(0.5);
diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/CauchyDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/CauchyDistribution.java
index 60ed069..ca766be 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/CauchyDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/CauchyDistribution.java
@@ -192,7 +192,7 @@ public final class CauchyDistribution extends AbstractContinuousDistribution {
 
     /** {@inheritDoc} */
     @Override
-    protected double getMedian() {
+    double getMedian() {
         // Overridden for the probability(double, double) method.
         // This is intentionally not a public method.
         return location;
diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/ExponentialDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/ExponentialDistribution.java
index fb0a409..1f04867 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/ExponentialDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/ExponentialDistribution.java
@@ -186,7 +186,7 @@ public final class ExponentialDistribution extends AbstractContinuousDistributio
 
     /** {@inheritDoc} */
     @Override
-    protected double getMedian() {
+    double getMedian() {
         // Overridden for the probability(double, double) method.
         // This is intentionally not a public method.
         // ln(2) / rate = mean * ln(2)
diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/GumbelDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/GumbelDistribution.java
index 76c0349..59127b3 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/GumbelDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/GumbelDistribution.java
@@ -196,7 +196,7 @@ public final class GumbelDistribution extends AbstractContinuousDistribution {
 
     /** {@inheritDoc} */
     @Override
-    protected double getMedian() {
+    double getMedian() {
         // Overridden for the probability(double, double) method.
         // This is intentionally not a public method.
         // u - beta * ln(ln(2))
diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LaplaceDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LaplaceDistribution.java
index 5b70a78..c66a481 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LaplaceDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LaplaceDistribution.java
@@ -181,7 +181,7 @@ public final class LaplaceDistribution extends AbstractContinuousDistribution {
 
     /** {@inheritDoc} */
     @Override
-    protected double getMedian() {
+    double getMedian() {
         // Overridden for the probability(double, double) method.
         // This is intentionally not a public method.
         return mu;
diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LevyDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LevyDistribution.java
index 57bf4da..d4ab4cf 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LevyDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LevyDistribution.java
@@ -216,7 +216,7 @@ public final class LevyDistribution extends AbstractContinuousDistribution {
 
     /** {@inheritDoc} */
     @Override
-    protected double getMedian() {
+    double getMedian() {
         // Overridden for the probability(double, double) method.
         // This is intentionally not a public method.
         // u + c / 2(erfc^-1 (0.5))^2
diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LogisticDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LogisticDistribution.java
index 5bf8527..5ccd850 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LogisticDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/LogisticDistribution.java
@@ -203,7 +203,7 @@ public final class LogisticDistribution extends AbstractContinuousDistribution {
 
     /** {@inheritDoc} */
     @Override
-    protected double getMedian() {
+    double getMedian() {
         // Overridden for the probability(double, double) method.
         // This is intentionally not a public method.
         return mu;
diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/TDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/TDistribution.java
index 2832f37..beecfa7 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/TDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/TDistribution.java
@@ -222,7 +222,7 @@ public abstract class TDistribution extends AbstractContinuousDistribution {
 
         /** {@inheritDoc} */
         @Override
-        protected double getMedian() {
+        double getMedian() {
             // Overridden for the probability(double, double) method.
             // This is intentionally not a public method.
             return 0;

[commons-statistics] 03/05: Avoid overflow in the continuous uniform distribution

Posted by ah...@apache.org.
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 aaf91eb83012564a9f1f1c67c0f1d20ebf7921a6
Author: aherbert <ah...@apache.org>
AuthorDate: Thu Dec 16 11:18:25 2021 +0000

    Avoid overflow in the continuous uniform distribution
    
    The range (upper - lower) must be finite for the current implementation
    to function. If infinite (or nan) then throw an exception.
    
    Ensure the mean is robust to overflow if (lower + upper) is infinite.
---
 .../distribution/UniformContinuousDistribution.java     | 17 +++++++++++------
 .../distribution/UniformContinuousDistributionTest.java |  9 +++++++++
 2 files changed, 20 insertions(+), 6 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 84b35f7..d9acb29 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
@@ -54,7 +54,8 @@ public final class UniformContinuousDistribution extends AbstractContinuousDistr
      * @param lower Lower bound of this distribution (inclusive).
      * @param upper Upper bound of this distribution (inclusive).
      * @return the distribution
-     * @throws IllegalArgumentException if {@code lower >= upper}.
+     * @throws IllegalArgumentException if {@code lower >= upper} or the range between the bounds
+     * is not finite
      */
     public static UniformContinuousDistribution of(double lower,
                                                    double upper) {
@@ -62,6 +63,9 @@ public final class UniformContinuousDistribution extends AbstractContinuousDistr
             throw new DistributionException(DistributionException.INVALID_RANGE_LOW_GTE_HIGH,
                                             lower, upper);
         }
+        if (!Double.isFinite(upper - lower)) {
+            throw new DistributionException("Range %s is not finite", upper - lower);
+        }
         return new UniformContinuousDistribution(lower, upper);
     }
 
@@ -150,19 +154,20 @@ public final class UniformContinuousDistribution extends AbstractContinuousDistr
     /**
      * {@inheritDoc}
      *
-     * <p>For lower bound {@code lower} and upper bound {@code upper}, the mean is
-     * {@code 0.5 * (lower + upper)}.
+     * <p>For lower bound {@code a} and upper bound {@code b}, the mean is
+     * {@code 0.5 * (a + b)}.
      */
     @Override
     public double getMean() {
-        return 0.5 * (lower + upper);
+        // Avoid overflow
+        return 0.5 * lower + 0.5 * upper;
     }
 
     /**
      * {@inheritDoc}
      *
-     * <p>For lower bound {@code lower} and upper bound {@code upper}, the
-     * variance is {@code (upper - lower)^2 / 12}.
+     * <p>For lower bound {@code a} and upper bound {@code b}, the
+     * variance is {@code (b - a)^2 / 12}.
      */
     @Override
     public double getVariance() {
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 28b384a..c56fde0 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
@@ -48,6 +48,10 @@ class UniformContinuousDistributionTest extends BaseContinuousDistributionTest {
         return new Object[][] {
             {0.0, 0.0},
             {1.0, 0.0},
+            // Range not finite
+            {-Double.MAX_VALUE, Double.MAX_VALUE},
+            {Double.NaN, 1.0},
+            {0.0, Double.NaN},
         };
     }
 
@@ -69,6 +73,11 @@ class UniformContinuousDistributionTest extends BaseContinuousDistributionTest {
         dist = UniformContinuousDistribution.of(-1.5, 0.6);
         Assertions.assertEquals(-0.45, dist.getMean());
         Assertions.assertEquals(0.3675, dist.getVariance());
+
+        // Overflow of 0.5 * (lower + upper)
+        dist = UniformContinuousDistribution.of(Double.MAX_VALUE / 2, Double.MAX_VALUE);
+        Assertions.assertEquals(Double.MAX_VALUE - Double.MAX_VALUE / 4, dist.getMean());
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, dist.getVariance());
     }
 
     /**

[commons-statistics] 01/05: Ensure default inverse probability is robust to distribution truncation

Posted by ah...@apache.org.
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 5bc2f6c514976212713a790f041ab508800538db
Author: aherbert <ah...@apache.org>
AuthorDate: Thu Dec 16 10:53:22 2021 +0000

    Ensure default inverse probability is robust to distribution truncation
    
    This was discovered as the F-distribution with numerator DF=1 and
    denominator DF=2 computed a survival probability:
    
    sf(infinity) = 0.0
    sf(max_value) = 5.56e-309
    
    If the inverse SF was called with a p-value lower than 5.56e-309 (e.g.
    Double.MIN_VALUE) the required value of x is outside the finite range of
    a double.
---
 .../AbstractContinuousDistribution.java            |  32 +++++-
 .../AbstractContinuousDistributionTest.java        | 109 +++++++++++++++++++++
 2 files changed, 138 insertions(+), 3 deletions(-)

diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
index 4d8b5f8..5e26812 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
@@ -269,15 +269,39 @@ abstract class AbstractContinuousDistribution
             }
         }
 
+        // Here the bracket [lower, upper] uses finite values. If the support
+        // is infinite the bracket can truncate the distribution and the target
+        // probability can be outside the range of [lower, upper].
+        if (upperBound == Double.MAX_VALUE) {
+            if (complement) {
+                if (survivalProbability(upperBound) > q) {
+                    return Double.POSITIVE_INFINITY;
+                }
+            } else if (cumulativeProbability(upperBound) < p) {
+                return Double.POSITIVE_INFINITY;
+            }
+        }
+        if (lowerBound == -Double.MAX_VALUE) {
+            if (complement) {
+                if (survivalProbability(lowerBound) < q) {
+                    return Double.NEGATIVE_INFINITY;
+                }
+            } else if (cumulativeProbability(lowerBound) > p) {
+                return Double.NEGATIVE_INFINITY;
+            }
+        }
+
         final DoubleUnaryOperator fun = complement ?
             arg -> survivalProbability(arg) - q :
             arg -> cumulativeProbability(arg) - p;
+        // Note the initial value is robust to overflow.
+        // Do not use 0.5 * (lowerBound + upperBound).
         final double x = new BrentSolver(SOLVER_RELATIVE_ACCURACY,
                                          SOLVER_ABSOLUTE_ACCURACY,
                                          SOLVER_FUNCTION_VALUE_ACCURACY)
             .findRoot(fun,
                       lowerBound,
-                      0.5 * (lowerBound + upperBound),
+                      lowerBound + 0.5 * (upperBound - lowerBound),
                       upperBound);
 
         if (!isSupportConnected()) {
@@ -331,8 +355,10 @@ abstract class AbstractContinuousDistribution
      * @return the infimum y
      */
     private double searchPlateau(boolean complement, double lower, final double x) {
-        /* Test for plateau. Lower the value x if the probability is the same. */
-        final double dx = SOLVER_ABSOLUTE_ACCURACY;
+        // Test for plateau. Lower the value x if the probability is the same.
+        // Ensure the step is robust to the solver accuracy being less
+        // than 1 ulp of x (e.g. dx=0 will infinite loop)
+        final double dx = Math.max(SOLVER_ABSOLUTE_ACCURACY, Math.ulp(x));
         if (x - dx >= lower) {
             final DoubleUnaryOperator fun = complement ?
                 this::survivalProbability :
diff --git a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java
index 317f95b..e72698a 100644
--- a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java
+++ b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java
@@ -371,4 +371,113 @@ class AbstractContinuousDistributionTest {
         Assertions.assertEquals(10 - x, distribution.inverseSurvivalProbability(0.25), tolerance, "Inverse SF");
         Assertions.assertEquals(Double.POSITIVE_INFINITY, distribution.inverseSurvivalProbability(0), "Inverse CDF");
     }
+
+    /**
+     * Create a distribution near positive infinity so that it is truncated by MAX_VALUE.
+     */
+    @Test
+    void testTruncatedDistributionAtPositiveInfinity() {
+        final double mean = Double.MAX_VALUE;
+        final double width = Double.MAX_VALUE / 2;
+        final CentredUniformDistribution dist = new CentredUniformDistribution(mean, width);
+        final double x = mean - width / 2;
+        Assertions.assertEquals(0, dist.cumulativeProbability(x));
+        Assertions.assertTrue(dist.cumulativeProbability(Math.nextUp(x)) > 0);
+        Assertions.assertEquals(0.25, dist.cumulativeProbability(mean - width / 4));
+        Assertions.assertEquals(0.5, dist.cumulativeProbability(mean));
+
+        // Truncated
+        Assertions.assertEquals(1.0, dist.cumulativeProbability(Math.nextUp(mean)));
+
+        // Inversion should be robust to return the upper infinite bound
+        Assertions.assertEquals(mean, dist.inverseCumulativeProbability(0.5));
+        Assertions.assertEquals(mean, dist.inverseSurvivalProbability(0.5));
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, dist.inverseCumulativeProbability(0.75));
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, dist.inverseSurvivalProbability(0.25));
+    }
+
+    /**
+     * Create a distribution near negative infinity so that it is truncated by -MAX_VALUE.
+     */
+    @Test
+    void testTruncatedDistributionAtNegativeInfinity() {
+        final double mean = -Double.MAX_VALUE;
+        final double width = Double.MAX_VALUE / 2;
+        final CentredUniformDistribution dist = new CentredUniformDistribution(mean, width);
+        final double x = mean + width / 2;
+        Assertions.assertEquals(1, dist.cumulativeProbability(x));
+        Assertions.assertTrue(dist.cumulativeProbability(Math.nextDown(x)) < 1);
+        Assertions.assertEquals(0.75, dist.cumulativeProbability(mean + width / 4));
+        Assertions.assertEquals(0.5, dist.cumulativeProbability(mean));
+
+        // Truncated
+        Assertions.assertEquals(0.0, dist.cumulativeProbability(Math.nextDown(mean)));
+
+        // Inversion should be robust to return the lower infinite bound
+        Assertions.assertEquals(mean, dist.inverseCumulativeProbability(0.5));
+        Assertions.assertEquals(mean, dist.inverseSurvivalProbability(0.5));
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, dist.inverseCumulativeProbability(0.25));
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, dist.inverseSurvivalProbability(0.75));
+    }
+
+    /**
+     * Uniform distribution described with a centre and width.
+     * This can be use to place the centre of the distribution at the limit of a finite double
+     * value so that the distribution is truncated.
+     */
+    static final class CentredUniformDistribution extends AbstractContinuousDistribution {
+        private final double mean;
+        private final double width;
+        private final double density;
+        private final double lo;
+        private final double hi;
+
+        /**
+         * @param mean Mean
+         * @param width Width
+         */
+        CentredUniformDistribution(double mean, double width) {
+            this.mean = mean;
+            this.width = width;
+            density = 1 / width;
+            lo = mean - width / 2;
+            hi = mean + width / 2;
+        }
+
+        @Override
+        public double density(double x) {
+            return density;
+        }
+
+        @Override
+        public double cumulativeProbability(double x) {
+            if (x <= lo) {
+                return 0;
+            }
+            if (x >= hi) {
+                return 1;
+            }
+            return 0.5 + density * (x - mean);
+        }
+
+        @Override
+        public double getMean() {
+            return mean;
+        }
+
+        @Override
+        public double getVariance() {
+            return width * width / 12;
+        }
+
+        @Override
+        public double getSupportLowerBound() {
+            return lo;
+        }
+
+        @Override
+        public double getSupportUpperBound() {
+            return hi;
+        }
+    }
 }

[commons-statistics] 02/05: PMD fix: Refactor adjustment of infinite bounds to methods

Posted by ah...@apache.org.
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 e17a87ac678b796aa720fa6882986a5f4b540556
Author: aherbert <ah...@apache.org>
AuthorDate: Thu Dec 16 12:12:54 2021 +0000

    PMD fix: Refactor adjustment of infinite bounds to methods
    
    This reduces complexity in the inverseProbability method
---
 .../AbstractContinuousDistribution.java            | 121 ++++++++++++++-------
 .../AbstractContinuousDistributionTest.java        |  54 +++++++--
 2 files changed, 128 insertions(+), 47 deletions(-)

diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
index 5e26812..1a95654 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
@@ -228,45 +228,11 @@ abstract class AbstractContinuousDistribution
                                          ArgumentUtils.isFiniteStrictlyPositive(sig);
 
         if (lowerBound == Double.NEGATIVE_INFINITY) {
-            if (chebyshevApplies) {
-                lowerBound = mu - sig * Math.sqrt(q / p);
-            }
-            // Bound may have been set as infinite
-            if (lowerBound == Double.NEGATIVE_INFINITY) {
-                lowerBound = Math.min(-1, upperBound);
-                if (complement) {
-                    while (survivalProbability(lowerBound) < q) {
-                        lowerBound *= 2;
-                    }
-                } else {
-                    while (cumulativeProbability(lowerBound) >= p) {
-                        lowerBound *= 2;
-                    }
-                }
-                // Ensure finite
-                lowerBound = Math.max(lowerBound, -Double.MAX_VALUE);
-            }
+            lowerBound = createFiniteLowerBound(p, q, complement, upperBound, mu, sig, chebyshevApplies);
         }
 
         if (upperBound == Double.POSITIVE_INFINITY) {
-            if (chebyshevApplies) {
-                upperBound = mu + sig * Math.sqrt(p / q);
-            }
-            // Bound may have been set as infinite
-            if (upperBound == Double.POSITIVE_INFINITY) {
-                upperBound = Math.max(1, lowerBound);
-                if (complement) {
-                    while (survivalProbability(upperBound) >= q) {
-                        upperBound *= 2;
-                    }
-                } else {
-                    while (cumulativeProbability(upperBound) < p) {
-                        upperBound *= 2;
-                    }
-                }
-                // Ensure finite
-                upperBound = Math.min(upperBound, Double.MAX_VALUE);
-            }
+            upperBound = createFiniteUpperBound(p, q, complement, lowerBound, mu, sig, chebyshevApplies);
         }
 
         // Here the bracket [lower, upper] uses finite values. If the support
@@ -275,19 +241,19 @@ abstract class AbstractContinuousDistribution
         if (upperBound == Double.MAX_VALUE) {
             if (complement) {
                 if (survivalProbability(upperBound) > q) {
-                    return Double.POSITIVE_INFINITY;
+                    return getSupportUpperBound();
                 }
             } else if (cumulativeProbability(upperBound) < p) {
-                return Double.POSITIVE_INFINITY;
+                return getSupportUpperBound();
             }
         }
         if (lowerBound == -Double.MAX_VALUE) {
             if (complement) {
                 if (survivalProbability(lowerBound) < q) {
-                    return Double.NEGATIVE_INFINITY;
+                    return getSupportLowerBound();
                 }
             } else if (cumulativeProbability(lowerBound) > p) {
-                return Double.NEGATIVE_INFINITY;
+                return getSupportLowerBound();
             }
         }
 
@@ -310,6 +276,81 @@ abstract class AbstractContinuousDistribution
         return x;
     }
 
+    /**
+     * Create a finite lower bound. Assumes the current lower bound is negative infinity.
+     *
+     * @param p Cumulative probability.
+     * @param q Survival probability.
+     * @param complement Set to true to compute the inverse survival probability
+     * @param upperBound Current upper bound
+     * @param mu Mean
+     * @param sig Standard deviation
+     * @param chebyshevApplies True if the Chebyshev inequality applies (mean is finite and {@code sig > 0}}
+     * @return the finite lower bound
+     */
+    private double createFiniteLowerBound(final double p, final double q, boolean complement,
+        double upperBound, final double mu, final double sig, final boolean chebyshevApplies) {
+        double lowerBound;
+        if (chebyshevApplies) {
+            lowerBound = mu - sig * Math.sqrt(q / p);
+        } else {
+            lowerBound = Double.NEGATIVE_INFINITY;
+        }
+        // Bound may have been set as infinite
+        if (lowerBound == Double.NEGATIVE_INFINITY) {
+            lowerBound = Math.min(-1, upperBound);
+            if (complement) {
+                while (survivalProbability(lowerBound) < q) {
+                    lowerBound *= 2;
+                }
+            } else {
+                while (cumulativeProbability(lowerBound) >= p) {
+                    lowerBound *= 2;
+                }
+            }
+            // Ensure finite
+            lowerBound = Math.max(lowerBound, -Double.MAX_VALUE);
+        }
+        return lowerBound;
+    }
+
+    /**
+     * Create a finite upper bound. Assumes the current upper bound is positive infinity.
+     *
+     * @param p Cumulative probability.
+     * @param q Survival probability.
+     * @param complement Set to true to compute the inverse survival probability
+     * @param lowerBound Current lower bound
+     * @param mu Mean
+     * @param sig Standard deviation
+     * @param chebyshevApplies True if the Chebyshev inequality applies (mean is finite and {@code sig > 0}}
+     * @return the finite lower bound
+     */
+    private double createFiniteUpperBound(final double p, final double q, boolean complement,
+        double lowerBound, final double mu, final double sig, final boolean chebyshevApplies) {
+        double upperBound;
+        if (chebyshevApplies) {
+            upperBound = mu + sig * Math.sqrt(p / q);
+        } else {
+            upperBound = Double.POSITIVE_INFINITY;
+        }
+        // Bound may have been set as infinite
+        if (upperBound == Double.POSITIVE_INFINITY) {
+            upperBound = Math.max(1, lowerBound);
+            if (complement) {
+                while (survivalProbability(upperBound) >= q) {
+                    upperBound *= 2;
+                }
+            } else {
+                while (cumulativeProbability(upperBound) < p) {
+                    upperBound *= 2;
+                }
+            }
+            // Ensure finite
+            upperBound = Math.min(upperBound, Double.MAX_VALUE);
+        }
+        return upperBound;
+    }
 
     /**
      * Indicates whether the support is connected, i.e. whether all values between the
diff --git a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java
index e72698a..3cce2e0 100644
--- a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java
+++ b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java
@@ -374,12 +374,32 @@ class AbstractContinuousDistributionTest {
 
     /**
      * Create a distribution near positive infinity so that it is truncated by MAX_VALUE.
+     * This distribution reports the upper bound as infinite.
      */
     @Test
     void testTruncatedDistributionAtPositiveInfinity() {
+        assertTruncatedDistributionAtPositiveInfinity(false);
+    }
+
+    /**
+     * Create a distribution near positive infinity so that it is truncated by MAX_VALUE.
+     * This distribution reports the upper bound as finite.
+     */
+    @Test
+    void testTruncatedDistributionAtPositiveInfinity2() {
+        assertTruncatedDistributionAtPositiveInfinity(true);
+    }
+
+    private static void assertTruncatedDistributionAtPositiveInfinity(boolean finiteBound) {
         final double mean = Double.MAX_VALUE;
         final double width = Double.MAX_VALUE / 2;
-        final CentredUniformDistribution dist = new CentredUniformDistribution(mean, width);
+        final double bound = finiteBound ? Double.MAX_VALUE : Double.POSITIVE_INFINITY;
+        final CentredUniformDistribution dist = new CentredUniformDistribution(mean, width) {
+            @Override
+            public double getSupportUpperBound() {
+                return bound;
+            }
+        };
         final double x = mean - width / 2;
         Assertions.assertEquals(0, dist.cumulativeProbability(x));
         Assertions.assertTrue(dist.cumulativeProbability(Math.nextUp(x)) > 0);
@@ -392,18 +412,38 @@ class AbstractContinuousDistributionTest {
         // Inversion should be robust to return the upper infinite bound
         Assertions.assertEquals(mean, dist.inverseCumulativeProbability(0.5));
         Assertions.assertEquals(mean, dist.inverseSurvivalProbability(0.5));
-        Assertions.assertEquals(Double.POSITIVE_INFINITY, dist.inverseCumulativeProbability(0.75));
-        Assertions.assertEquals(Double.POSITIVE_INFINITY, dist.inverseSurvivalProbability(0.25));
+        Assertions.assertEquals(bound, dist.inverseCumulativeProbability(0.75));
+        Assertions.assertEquals(bound, dist.inverseSurvivalProbability(0.25));
     }
 
     /**
      * Create a distribution near negative infinity so that it is truncated by -MAX_VALUE.
+     * This distribution reports the lower bound as infinite.
      */
     @Test
     void testTruncatedDistributionAtNegativeInfinity() {
+        assertTruncatedDistributionAtNegativeInfinity(false);
+    }
+
+    /**
+     * Create a distribution near negative infinity so that it is truncated by -MAX_VALUE.
+     * This distribution reports the lower bound as finite.
+     */
+    @Test
+    void testTruncatedDistributionAtNegativeInfinity2() {
+        assertTruncatedDistributionAtNegativeInfinity(true);
+    }
+
+    private static void assertTruncatedDistributionAtNegativeInfinity(boolean finiteBound) {
         final double mean = -Double.MAX_VALUE;
         final double width = Double.MAX_VALUE / 2;
-        final CentredUniformDistribution dist = new CentredUniformDistribution(mean, width);
+        final double bound = finiteBound ? -Double.MAX_VALUE : Double.NEGATIVE_INFINITY;
+        final CentredUniformDistribution dist = new CentredUniformDistribution(mean, width) {
+            @Override
+            public double getSupportLowerBound() {
+                return bound;
+            }
+        };
         final double x = mean + width / 2;
         Assertions.assertEquals(1, dist.cumulativeProbability(x));
         Assertions.assertTrue(dist.cumulativeProbability(Math.nextDown(x)) < 1);
@@ -416,8 +456,8 @@ class AbstractContinuousDistributionTest {
         // Inversion should be robust to return the lower infinite bound
         Assertions.assertEquals(mean, dist.inverseCumulativeProbability(0.5));
         Assertions.assertEquals(mean, dist.inverseSurvivalProbability(0.5));
-        Assertions.assertEquals(Double.NEGATIVE_INFINITY, dist.inverseCumulativeProbability(0.25));
-        Assertions.assertEquals(Double.NEGATIVE_INFINITY, dist.inverseSurvivalProbability(0.75));
+        Assertions.assertEquals(bound, dist.inverseCumulativeProbability(0.25));
+        Assertions.assertEquals(bound, dist.inverseSurvivalProbability(0.75));
     }
 
     /**
@@ -425,7 +465,7 @@ class AbstractContinuousDistributionTest {
      * This can be use to place the centre of the distribution at the limit of a finite double
      * value so that the distribution is truncated.
      */
-    static final class CentredUniformDistribution extends AbstractContinuousDistribution {
+    static class CentredUniformDistribution extends AbstractContinuousDistribution {
         private final double mean;
         private final double width;
         private final double density;

[commons-statistics] 05/05: Reduce isSupportConnected to package-private

Posted by ah...@apache.org.
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 d0c550b0250f8952dde3819e22ca977e7bce7de5
Author: aherbert <ah...@apache.org>
AuthorDate: Thu Dec 16 12:16:30 2021 +0000

    Reduce isSupportConnected to package-private
    
    It is a package level implementation detail.
---
 .../statistics/distribution/AbstractContinuousDistribution.java       | 2 +-
 .../statistics/distribution/AbstractContinuousDistributionTest.java   | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
index 69e6be1..a691f3b 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/AbstractContinuousDistribution.java
@@ -377,7 +377,7 @@ abstract class AbstractContinuousDistribution
      * @return whether the support is connected.
      * @see <a href="https://issues.apache.org/jira/browse/MATH-699">MATH-699</a>
      */
-    protected boolean isSupportConnected() {
+    boolean isSupportConnected() {
         return true;
     }
 
diff --git a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java
index 3cce2e0..1671278 100644
--- a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java
+++ b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/AbstractContinuousDistributionTest.java
@@ -94,7 +94,7 @@ class AbstractContinuousDistributionTest {
             }
 
             @Override
-            public boolean isSupportConnected() {
+            boolean isSupportConnected() {
                 // This is deliberately false; the functionality is the subject of this test
                 return false;
             }
@@ -200,7 +200,7 @@ class AbstractContinuousDistributionTest {
             }
 
             @Override
-            public boolean isSupportConnected() {
+            boolean isSupportConnected() {
                 // This is deliberately false; the functionality is the subject of this test
                 return false;
             }