BrentSolver.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.analysis.solvers;


  22. import org.hipparchus.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.exception.MathIllegalStateException;
  25. import org.hipparchus.util.FastMath;
  26. import org.hipparchus.util.Precision;

  27. /**
  28.  * This class implements the <a href="http://mathworld.wolfram.com/BrentsMethod.html">
  29.  * Brent algorithm</a> for finding zeros of real univariate functions.
  30.  * The function should be continuous but not necessarily smooth.
  31.  * The {@code solve} method returns a zero {@code x} of the function {@code f}
  32.  * in the given interval {@code [a, b]} to within a tolerance
  33.  * {@code 2 eps abs(x) + t} where {@code eps} is the relative accuracy and
  34.  * {@code t} is the absolute accuracy.
  35.  * <p>The given interval must bracket the root.</p>
  36.  * <p>
  37.  *  The reference implementation is given in chapter 4 of
  38.  *  <blockquote>
  39.  *   <b>Algorithms for Minimization Without Derivatives</b>,
  40.  *   <em>Richard P. Brent</em>,
  41.  *   Dover, 2002
  42.  *  </blockquote>
  43.  *
  44.  * @see BaseAbstractUnivariateSolver
  45.  */
  46. public class BrentSolver extends AbstractUnivariateSolver {

  47.     /** Default absolute accuracy. */
  48.     private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;

  49.     /**
  50.      * Construct a solver with default absolute accuracy (1e-6).
  51.      */
  52.     public BrentSolver() {
  53.         this(DEFAULT_ABSOLUTE_ACCURACY);
  54.     }
  55.     /**
  56.      * Construct a solver.
  57.      *
  58.      * @param absoluteAccuracy Absolute accuracy.
  59.      */
  60.     public BrentSolver(double absoluteAccuracy) {
  61.         super(absoluteAccuracy);
  62.     }
  63.     /**
  64.      * Construct a solver.
  65.      *
  66.      * @param relativeAccuracy Relative accuracy.
  67.      * @param absoluteAccuracy Absolute accuracy.
  68.      */
  69.     public BrentSolver(double relativeAccuracy,
  70.                        double absoluteAccuracy) {
  71.         super(relativeAccuracy, absoluteAccuracy);
  72.     }
  73.     /**
  74.      * Construct a solver.
  75.      *
  76.      * @param relativeAccuracy Relative accuracy.
  77.      * @param absoluteAccuracy Absolute accuracy.
  78.      * @param functionValueAccuracy Function value accuracy.
  79.      *
  80.      * @see BaseAbstractUnivariateSolver#BaseAbstractUnivariateSolver(double,double,double)
  81.      */
  82.     public BrentSolver(double relativeAccuracy,
  83.                        double absoluteAccuracy,
  84.                        double functionValueAccuracy) {
  85.         super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
  86.     }

  87.     /**
  88.      * {@inheritDoc}
  89.      */
  90.     @Override
  91.     protected double doSolve()
  92.         throws MathIllegalArgumentException, MathIllegalStateException {
  93.         double min = getMin();
  94.         double max = getMax();
  95.         final double initial = getStartValue();
  96.         final double functionValueAccuracy = getFunctionValueAccuracy();

  97.         verifySequence(min, initial, max);

  98.         // Return the initial guess if it is good enough.
  99.         double yInitial = computeObjectiveValue(initial);
  100.         if (FastMath.abs(yInitial) <= functionValueAccuracy) {
  101.             return initial;
  102.         }

  103.         // Return the first endpoint if it is good enough.
  104.         double yMin = computeObjectiveValue(min);
  105.         if (FastMath.abs(yMin) <= functionValueAccuracy) {
  106.             return min;
  107.         }

  108.         // Reduce interval if min and initial bracket the root.
  109.         if (yInitial * yMin < 0) {
  110.             return brent(min, initial, yMin, yInitial);
  111.         }

  112.         // Return the second endpoint if it is good enough.
  113.         double yMax = computeObjectiveValue(max);
  114.         if (FastMath.abs(yMax) <= functionValueAccuracy) {
  115.             return max;
  116.         }

  117.         // Reduce interval if initial and max bracket the root.
  118.         if (yInitial * yMax < 0) {
  119.             return brent(initial, max, yInitial, yMax);
  120.         }

  121.         throw new MathIllegalArgumentException(LocalizedCoreFormats.NOT_BRACKETING_INTERVAL,
  122.                                                min, max, yMin, yMax);
  123.     }

  124.     /**
  125.      * Search for a zero inside the provided interval.
  126.      * This implementation is based on the algorithm described at page 58 of
  127.      * the book
  128.      * <blockquote>
  129.      *  <b>Algorithms for Minimization Without Derivatives</b>,
  130.      *  <it>Richard P. Brent</it>,
  131.      *  Dover 0-486-41998-3
  132.      * </blockquote>
  133.      *
  134.      * @param lo Lower bound of the search interval.
  135.      * @param hi Higher bound of the search interval.
  136.      * @param fLo Function value at the lower bound of the search interval.
  137.      * @param fHi Function value at the higher bound of the search interval.
  138.      * @return the value where the function is zero.
  139.      */
  140.     private double brent(double lo, double hi,
  141.                          double fLo, double fHi) {
  142.         double a = lo;
  143.         double fa = fLo;
  144.         double b = hi;
  145.         double fb = fHi;
  146.         double c = a;
  147.         double fc = fa;
  148.         double d = b - a;
  149.         double e = d;

  150.         final double t = getAbsoluteAccuracy();
  151.         final double eps = getRelativeAccuracy();

  152.         while (true) {
  153.             if (FastMath.abs(fc) < FastMath.abs(fb)) {
  154.                 a = b;
  155.                 b = c;
  156.                 c = a;
  157.                 fa = fb;
  158.                 fb = fc;
  159.                 fc = fa;
  160.             }

  161.             final double tol = 2 * eps * FastMath.abs(b) + t;
  162.             final double m = 0.5 * (c - b);

  163.             if (FastMath.abs(m) <= tol ||
  164.                 Precision.equals(fb, 0))  {
  165.                 return b;
  166.             }
  167.             if (FastMath.abs(e) < tol ||
  168.                 FastMath.abs(fa) <= FastMath.abs(fb)) {
  169.                 // Force bisection.
  170.                 d = m;
  171.                 e = d;
  172.             } else {
  173.                 double s = fb / fa;
  174.                 double p;
  175.                 double q;
  176.                 // The equality test (a == c) is intentional,
  177.                 // it is part of the original Brent's method and
  178.                 // it should NOT be replaced by proximity test.
  179.                 if (a == c) {
  180.                     // Linear interpolation.
  181.                     p = 2 * m * s;
  182.                     q = 1 - s;
  183.                 } else {
  184.                     // Inverse quadratic interpolation.
  185.                     q = fa / fc;
  186.                     final double r = fb / fc;
  187.                     p = s * (2 * m * q * (q - r) - (b - a) * (r - 1));
  188.                     q = (q - 1) * (r - 1) * (s - 1);
  189.                 }
  190.                 if (p > 0) {
  191.                     q = -q;
  192.                 } else {
  193.                     p = -p;
  194.                 }
  195.                 s = e;
  196.                 e = d;
  197.                 if (p >= 1.5 * m * q - FastMath.abs(tol * q) ||
  198.                     p >= FastMath.abs(0.5 * s * q)) {
  199.                     // Inverse quadratic interpolation gives a value
  200.                     // in the wrong direction, or progress is slow.
  201.                     // Fall back to bisection.
  202.                     d = m;
  203.                     e = d;
  204.                 } else {
  205.                     d = p / q;
  206.                 }
  207.             }
  208.             a = b;
  209.             fa = fb;

  210.             if (FastMath.abs(d) > tol) {
  211.                 b += d;
  212.             } else if (m > 0) {
  213.                 b += tol;
  214.             } else {
  215.                 b -= tol;
  216.             }
  217.             fb = computeObjectiveValue(b);
  218.             if ((fb > 0 && fc > 0) ||
  219.                 (fb <= 0 && fc <= 0)) {
  220.                 c = a;
  221.                 fc = fa;
  222.                 d = b - a;
  223.                 e = d;
  224.             }
  225.         }
  226.     }
  227. }