package org.spaceroots.mantissa.roots;

import org.spaceroots.mantissa.functions.scalar.ComputableFunction;
import org.spaceroots.mantissa.functions.FunctionException;

/** This class implements the Brent algorithm to compute the roots of
 * a function in an interval.

 * This class is basically a translation in Java of a fortran
 * implementation found at netlib (zeroin.f).

 * @version $Id: BrentSolver.java 1499 2003-03-29 12:50:08Z luc $
 * @author L. Maisonobe

 */

public class BrentSolver implements RootsFinder {

  /** IEEE 754 epsilon . */
  private static final double epsilon = Math.pow(2.0, -52);

  /** Root found. */
  private double root;

  /** Simple constructor.
   * Build a Brent solver
   */
  public BrentSolver() {
    root = Double.NaN;
  }

  /** Solve a function in a given interval known to contain a root.
   * @param function function for which a root should be found
   * @param checker checker for the convergence of the function
   * @param maxIter maximal number of iteration allowed
   * @param x0 abscissa of the lower bound of the interval
   * @param f0 value of the function the lower bound of the interval
   * @param x1 abscissa of the higher bound of the interval
   * @param f1 value of the function the higher bound of the interval
   * @return true if a root has been found in the given interval
   */
  public boolean findRoot(ComputableFunction function,
                          ConvergenceChecker checker,
                          int maxIter,
                          double x0, double f0, double x1, double f1)
    throws FunctionException {

    double a  = x0;
    double fa = f0;
    double b  = x1;
    double fb = f1;

    double c  = a;
    double fc = fa;

    double d  = b - a;
    double e  = d;

    double tolS;
    for (int iter = 0; iter < maxIter; ++iter) {

      if (Math.abs(fc) < Math.abs(fb)) {
        // invert points
        a  = b;
        b  = c;
        c  = a;
        fa = fb;
        fb = fc;
        fc = fa;
      }

      tolS = 2 * epsilon * Math.abs(b);
      double xm = 0.5 * (c - b);

      // convergence test
      double xLow, fLow, xHigh, fHigh;
      if (b < c) {
        xLow   = b;
        fLow   = fb;
        xHigh  = c;
        fHigh  = fc;
      } else {
        xLow   = c;
        fLow   = fc;
        xHigh  = b;
        fHigh  = fb;
      }

      switch (checker.converged(xLow, fLow, xHigh, fHigh)) {
      case ConvergenceChecker.LOW :
        root = xLow;
        return true;
      case ConvergenceChecker.HIGH :
        root = xHigh;
        return true;
      default :
        if ((Math.abs(xm) < tolS) || (Math.abs(fb) < Double.MIN_VALUE)) {
          root = b;
          return true;
        }
      }

      if ((Math.abs(e) < tolS) || (Math.abs(fa) <= Math.abs(fb))) {
        // use bisection method
        d = xm;
        e = d;
      } else {
        // use secant method
        double p, q, r, s;
        s = fb / fa;
        if (Math.abs(a - c) < epsilon * Math.max(Math.abs(a), Math.abs(c))) {
          // linear interpolation using only b and c points
          p = 2.0 * xm * s;
          q = 1.0 - s;
        } else {
          // inverse quadratic interpolation using a, b and c points
          q = fa / fc;
          r = fb / fc;
          p = s * (2.0 * xm * q * (q - r) - (b - a) * (r - 1.0));
          q = (q - 1.0) * (r - 1.0) * (s - 1.0);
        }

        // signs adjustment
        if (p > 0.0) {
          q = -q;
        } else {
          p = -p;
        }

        // is interpolation acceptable ?
        if (((2.0 * p) < (3.0 * xm * q - Math.abs(tolS * q)))
            &&
            (p < Math.abs(0.5 * e * q))) {
          e = d;
          d = p / q;
        } else {
          // no, we need to fall back to bisection
          d = xm;
          e = d;
        }
      }

      // complete step
      a  = b;
      fa = fb;
      b += ((Math.abs(d) > tolS) ? d : (xm > 0.0 ? tolS : -tolS));
      fb = function.valueAt(b);

      if (fb * fc > 0) {
        c  = a;
        fc = fa;
        d  = b - a;
        e  = d;
      }

    }

    // we have exceeded the maximal number of iterations
    return false;

  }

  /** Get the abscissa of the root.
   * @return abscissa of the root
   */
  public double getRoot() {
    return root;
  }

}

 

Ads help maintain this website.