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:48 UTC

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

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;