/*
Copyright (C) 2008 Richard Gomes
This source code is release under the BSD License.
This file is part of JQuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://jquantlib.org/
JQuantLib is free software: you can redistribute it and/or modify it
under the terms of the JQuantLib license. You should have received a
copy of the license along with this program; if not, please email
. The license is also available online at
.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
JQuantLib is based on QuantLib. http://quantlib.org/
When applicable, the original copyright notice follows this notice.
*/
package org.jquantlib.math.solvers1D;
import org.jquantlib.math.AbstractSolver1D;
import org.jquantlib.math.Closeness;
import org.jquantlib.math.Constants;
import org.jquantlib.math.Ops;
/**
* Brent 1-D solver
*
* The implementation of the algorithm was inspired by
* Press, Teukolsky, Vetterling, and Flannery, "Numerical Recipes in C", 2nd Edition, Cambridge University Press
*
* @author Richard Gomes
**/
public class Brent extends AbstractSolver1D {
/**
* Computes the roots of a function by using the Brent method.
*
* @param f the function
* @param xAccuracy the provided accuracy
* @returns root
*/
@Override
protected double solveImpl(final Ops.DoubleOp f, final double xAccuracy) {
double min1, min2;
double froot, p, q, r, s, xAcc1, xMid;
double d = 0.0, e = 0.0;
root = xMax;
froot = fxMax;
while (evaluationNumber <= getMaxEvaluations()) {
if ((froot > 0.0 && fxMax > 0.0) || (froot < 0.0 && fxMax < 0.0)) {
// Rename xMin, root, xMax and adjust bounds
xMax = xMin;
fxMax = fxMin;
e = d = root - xMin;
}
if (Math.abs(fxMax) < Math.abs(froot)) {
xMin = root;
root = xMax;
xMax = xMin;
fxMin = froot;
froot = fxMax;
fxMax = fxMin;
}
// Convergence check
xAcc1 = 2.0 * Constants.QL_EPSILON * Math.abs(root) + 0.5 * xAccuracy;
xMid = (xMax - root) / 2.0;
if (Math.abs(xMid) <= xAcc1 || froot == 0.0)
return root;
if (Math.abs(e) >= xAcc1 && Math.abs(fxMin) > Math.abs(froot)) {
// Attempt inverse quadratic interpolation
s = froot / fxMin;
if (Closeness.isClose(xMin, xMax)) {
p = 2.0 * xMid * s;
q = 1.0 - s;
} else {
q = fxMin / fxMax;
r = froot / fxMax;
p = s * (2.0 * xMid * q * (q - r) - (root - xMin) * (r - 1.0));
q = (q - 1.0) * (r - 1.0) * (s - 1.0);
}
if (p > 0.0)
q = -q; // Check whether in bounds
p = Math.abs(p);
min1 = 3.0 * xMid * q - Math.abs(xAcc1 * q);
min2 = Math.abs(e * q);
if (2.0 * p < (min1 < min2 ? min1 : min2)) {
e = d; // Accept interpolation
d = p / q;
} else {
d = xMid; // Interpolation failed, use bisection
e = d;
}
} else {
d = xMid; // Bounds decreasing too slowly, use bisection
e = d;
}
xMin = root;
fxMin = froot;
if (Math.abs(d) > xAcc1)
root += d;
else
root += sign(xAcc1, xMid);
froot = f.op(root);
evaluationNumber++;
}
throw new ArithmeticException("maximum number of function evaluations exceeded"); // TODO: message
}
private double sign(final double a, final double b) {
return b >= 0.0 ? Math.abs(a) : -Math.abs(a);
}
}
Ads help maintain this website.