blob: ba84a391a7697b76ce34e28394050717fe6cff7b [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
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 (<a
* href="http://www.netlib.org/fmm/zeroin.f">zeroin.f</a>).
* @version $Id$
* @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;
}
}