You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2019/06/09 08:01:37 UTC

[commons-statistics] branch master updated: "BrentSolver" is moved to "Commons Numbers".

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

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


The following commit(s) were added to refs/heads/master by this push:
     new dce8454  "BrentSolver" is moved to "Commons Numbers".
dce8454 is described below

commit dce8454a29b6fcee7232286518f5c1564eb90929
Author: Gilles Sadowski <gi...@harfang.homelinux.org>
AuthorDate: Sun Jun 9 09:59:20 2019 +0200

    "BrentSolver" is moved to "Commons Numbers".
    
    Cf. JIRA: NUMBERS-112.
---
 commons-statistics-distribution/pom.xml            |   5 +
 .../AbstractContinuousDistribution.java            | 215 +--------------------
 .../distribution/DistributionException.java        |   2 -
 pom.xml                                            |   5 +
 4 files changed, 15 insertions(+), 212 deletions(-)

diff --git a/commons-statistics-distribution/pom.xml b/commons-statistics-distribution/pom.xml
index bc9e3d9..a89b6a9 100644
--- a/commons-statistics-distribution/pom.xml
+++ b/commons-statistics-distribution/pom.xml
@@ -71,6 +71,11 @@
 
     <dependency>
       <groupId>org.apache.commons</groupId>
+      <artifactId>commons-numbers-rootfinder</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
       <artifactId>commons-rng-simple</artifactId>
       <scope>test</scope>
     </dependency>
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 cdbe670..9057cbf 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
@@ -16,8 +16,7 @@
  */
 package org.apache.commons.statistics.distribution;
 
-import java.util.function.DoubleUnaryOperator;
-import org.apache.commons.numbers.core.Precision;
+import org.apache.commons.numbers.rootfinder.BrentSolver;
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.sampling.distribution.InverseTransformContinuousSampler;
 
@@ -128,10 +127,10 @@ abstract class AbstractContinuousDistribution
         final double x = new BrentSolver(SOLVER_RELATIVE_ACCURACY,
                                          SOLVER_ABSOLUTE_ACCURACY,
                                          SOLVER_FUNCTION_VALUE_ACCURACY)
-            .solve(arg -> cumulativeProbability(arg) - p,
-                   lowerBound,
-                   0.5 * (lowerBound + upperBound),
-                   upperBound);
+            .findRoot(arg -> cumulativeProbability(arg) - p,
+                      lowerBound,
+                      0.5 * (lowerBound + upperBound),
+                      upperBound);
 
         if (!isSupportConnected()) {
             /* Test for plateau. */
@@ -178,208 +177,4 @@ abstract class AbstractContinuousDistribution
         // Inversion method distribution sampler.
         return new InverseTransformContinuousSampler(rng, this::inverseCumulativeProbability)::sample;
     }
-
-    /**
-     * This class implements the <a href="http://mathworld.wolfram.com/BrentsMethod.html">
-     * Brent algorithm</a> for finding zeros of real univariate functions.
-     * The function should be continuous but not necessarily smooth.
-     * The {@code solve} method returns a zero {@code x} of the function {@code f}
-     * in the given interval {@code [a, b]} to within a tolerance
-     * {@code 2 eps abs(x) + t} where {@code eps} is the relative accuracy and
-     * {@code t} is the absolute accuracy.
-     * <p>The given interval must bracket the root.</p>
-     * <p>
-     *  The reference implementation is given in chapter 4 of
-     *  <blockquote>
-     *   <b>Algorithms for Minimization Without Derivatives</b>,
-     *   <em>Richard P. Brent</em>,
-     *   Dover, 2002
-     *  </blockquote>
-     *
-     * Used by {@link AbstractContinuousDistribution#inverseCumulativeProbability(double)}.
-     */
-    private static class BrentSolver {
-        /** Relative accuracy. */
-        private final double relativeAccuracy;
-        /** Absolutee accuracy. */
-        private final double absoluteAccuracy;
-        /** Function accuracy. */
-        private final double functionValueAccuracy;
-
-        /**
-         * Construct a solver.
-         *
-         * @param relativeAccuracy Relative accuracy.
-         * @param absoluteAccuracy Absolute accuracy.
-         * @param functionValueAccuracy Function value accuracy.
-         */
-        BrentSolver(double relativeAccuracy,
-                    double absoluteAccuracy,
-                    double functionValueAccuracy) {
-            this.relativeAccuracy = relativeAccuracy;
-            this.absoluteAccuracy = absoluteAccuracy;
-            this.functionValueAccuracy = functionValueAccuracy;
-        }
-
-        /**
-         * @param func Function to solve.
-         * @param min Lower bound.
-         * @param initial Initial guess.
-         * @param max Upper bound.
-         * @return the root.
-         */
-        double solve(DoubleUnaryOperator func,
-                                   double min,
-                                   double initial,
-                                   double max) {
-            if (min > max) {
-                throw new DistributionException(DistributionException.TOO_LARGE, min, max);
-            }
-            if (initial < min ||
-                initial > max) {
-                throw new DistributionException(DistributionException.OUT_OF_RANGE, initial, min, max);
-            }
-
-            // Return the initial guess if it is good enough.
-            final double yInitial = func.applyAsDouble(initial);
-            if (Math.abs(yInitial) <= functionValueAccuracy) {
-                return initial;
-            }
-
-            // Return the first endpoint if it is good enough.
-            final double yMin = func.applyAsDouble(min);
-            if (Math.abs(yMin) <= functionValueAccuracy) {
-                return min;
-            }
-
-            // Reduce interval if min and initial bracket the root.
-            if (yInitial * yMin < 0) {
-                return brent(func, min, initial, yMin, yInitial);
-            }
-
-            // Return the second endpoint if it is good enough.
-            final double yMax = func.applyAsDouble(max);
-            if (Math.abs(yMax) <= functionValueAccuracy) {
-                return max;
-            }
-
-            // Reduce interval if initial and max bracket the root.
-            if (yInitial * yMax < 0) {
-                return brent(func, initial, max, yInitial, yMax);
-            }
-
-            throw new DistributionException(DistributionException.BRACKETING, min, yMin, max, yMax);
-        }
-
-        /**
-         * Search for a zero inside the provided interval.
-         * This implementation is based on the algorithm described at page 58 of
-         * the book
-         * <blockquote>
-         *  <b>Algorithms for Minimization Without Derivatives</b>,
-         *  <i>Richard P. Brent</i>,
-         *  Dover 0-486-41998-3
-         * </blockquote>
-         *
-         * @param func Function to solve.
-         * @param lo Lower bound of the search interval.
-         * @param hi Higher bound of the search interval.
-         * @param fLo Function value at the lower bound of the search interval.
-         * @param fHi Function value at the higher bound of the search interval.
-         * @return the value where the function is zero.
-         */
-        private double brent(DoubleUnaryOperator func,
-                             double lo, double hi,
-                             double fLo, double fHi) {
-            double a = lo;
-            double fa = fLo;
-            double b = hi;
-            double fb = fHi;
-            double c = a;
-            double fc = fa;
-            double d = b - a;
-            double e = d;
-
-            final double t = absoluteAccuracy;
-            final double eps = relativeAccuracy;
-
-            while (true) {
-                if (Math.abs(fc) < Math.abs(fb)) {
-                    a = b;
-                    b = c;
-                    c = a;
-                    fa = fb;
-                    fb = fc;
-                    fc = fa;
-                }
-
-                final double tol = 2 * eps * Math.abs(b) + t;
-                final double m = 0.5 * (c - b);
-
-                if (Math.abs(m) <= tol ||
-                    Precision.equals(fb, 0))  {
-                    return b;
-                }
-                if (Math.abs(e) < tol ||
-                    Math.abs(fa) <= Math.abs(fb)) {
-                    // Force bisection.
-                    d = m;
-                    e = d;
-                } else {
-                    double s = fb / fa;
-                    double p;
-                    double q;
-                    // The equality test (a == c) is intentional,
-                    // it is part of the original Brent's method and
-                    // it should NOT be replaced by proximity test.
-                    if (a == c) {
-                        // Linear interpolation.
-                        p = 2 * m * s;
-                        q = 1 - s;
-                    } else {
-                        // Inverse quadratic interpolation.
-                        q = fa / fc;
-                        final double r = fb / fc;
-                        p = s * (2 * m * q * (q - r) - (b - a) * (r - 1));
-                        q = (q - 1) * (r - 1) * (s - 1);
-                    }
-                    if (p > 0) {
-                        q = -q;
-                    } else {
-                        p = -p;
-                    }
-                    s = e;
-                    e = d;
-                    if (p >= 1.5 * m * q - Math.abs(tol * q) ||
-                        p >= Math.abs(0.5 * s * q)) {
-                        // Inverse quadratic interpolation gives a value
-                        // in the wrong direction, or progress is slow.
-                        // Fall back to bisection.
-                        d = m;
-                        e = d;
-                    } else {
-                        d = p / q;
-                    }
-                }
-                a = b;
-                fa = fb;
-
-                if (Math.abs(d) > tol) {
-                    b += d;
-                } else if (m > 0) {
-                    b += tol;
-                } else {
-                    b -= tol;
-                }
-                fb = func.applyAsDouble(b);
-                if ((fb > 0 && fc > 0) ||
-                    (fb <= 0 && fc <= 0)) {
-                    c = a;
-                    fc = fa;
-                    d = b - a;
-                    e = d;
-                }
-            }
-        }
-    }
 }
diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/DistributionException.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/DistributionException.java
index 86c98f5..4d6d9c5 100644
--- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/DistributionException.java
+++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/DistributionException.java
@@ -32,8 +32,6 @@ class DistributionException extends IllegalArgumentException {
     static final String NEGATIVE = "Number {0} is negative";
     /** Error message for "mismatch" condition. */
     static final String MISMATCH = "Expected {1} but was {0}";
-    /** Error message for "failed bracketing" condition. */
-    static final String BRACKETING = "No bracketing: f({0})={1}, f({2})={3}";
 
     /** Serializable version identifier. */
     private static final long serialVersionUID = 20180119L;
diff --git a/pom.xml b/pom.xml
index 1d0bd58..baf9e41 100644
--- a/pom.xml
+++ b/pom.xml
@@ -161,6 +161,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.commons</groupId>
+        <artifactId>commons-numbers-rootfinder</artifactId>
+        <version>${statistics.commons.numbers.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.commons</groupId>
         <artifactId>commons-math3</artifactId>
         <version>${statistics.commons.math3.version}</version>
       </dependency>