| /* |
| * 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.apache.commons.math.analysis.solvers; |
| |
| |
| import org.apache.commons.math.FunctionEvaluationException; |
| import org.apache.commons.math.MathRuntimeException; |
| import org.apache.commons.math.MaxIterationsExceededException; |
| import org.apache.commons.math.analysis.UnivariateRealFunction; |
| import org.apache.commons.math.exception.util.LocalizedFormats; |
| import org.apache.commons.math.util.FastMath; |
| |
| /** |
| * Implements the <a href="http://mathworld.wolfram.com/BrentsMethod.html"> |
| * Brent algorithm</a> for finding zeros of real univariate functions. |
| * <p> |
| * The function should be continuous but not necessarily smooth.</p> |
| * |
| * @version $Revision:670469 $ $Date:2008-06-23 10:01:38 +0200 (lun., 23 juin 2008) $ |
| */ |
| public class BrentSolver extends UnivariateRealSolverImpl { |
| |
| /** |
| * Default absolute accuracy |
| * @since 2.1 |
| */ |
| public static final double DEFAULT_ABSOLUTE_ACCURACY = 1E-6; |
| |
| /** Default maximum number of iterations |
| * @since 2.1 |
| */ |
| public static final int DEFAULT_MAXIMUM_ITERATIONS = 100; |
| |
| /** Serializable version identifier */ |
| private static final long serialVersionUID = 7694577816772532779L; |
| |
| /** |
| * Construct a solver for the given function. |
| * |
| * @param f function to solve. |
| * @deprecated as of 2.0 the function to solve is passed as an argument |
| * to the {@link #solve(UnivariateRealFunction, double, double)} or |
| * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} |
| * method. |
| */ |
| @Deprecated |
| public BrentSolver(UnivariateRealFunction f) { |
| super(f, DEFAULT_MAXIMUM_ITERATIONS, DEFAULT_ABSOLUTE_ACCURACY); |
| } |
| |
| /** |
| * Construct a solver with default properties. |
| * @deprecated in 2.2 (to be removed in 3.0). |
| */ |
| @Deprecated |
| public BrentSolver() { |
| super(DEFAULT_MAXIMUM_ITERATIONS, DEFAULT_ABSOLUTE_ACCURACY); |
| } |
| |
| /** |
| * Construct a solver with the given absolute accuracy. |
| * |
| * @param absoluteAccuracy lower bound for absolute accuracy of solutions returned by the solver |
| * @since 2.1 |
| */ |
| public BrentSolver(double absoluteAccuracy) { |
| super(DEFAULT_MAXIMUM_ITERATIONS, absoluteAccuracy); |
| } |
| |
| /** |
| * Contstruct a solver with the given maximum iterations and absolute accuracy. |
| * |
| * @param maximumIterations maximum number of iterations |
| * @param absoluteAccuracy lower bound for absolute accuracy of solutions returned by the solver |
| * @since 2.1 |
| */ |
| public BrentSolver(int maximumIterations, double absoluteAccuracy) { |
| super(maximumIterations, absoluteAccuracy); |
| } |
| |
| /** {@inheritDoc} */ |
| @Deprecated |
| public double solve(double min, double max) |
| throws MaxIterationsExceededException, FunctionEvaluationException { |
| return solve(f, min, max); |
| } |
| |
| /** {@inheritDoc} */ |
| @Deprecated |
| public double solve(double min, double max, double initial) |
| throws MaxIterationsExceededException, FunctionEvaluationException { |
| return solve(f, min, max, initial); |
| } |
| |
| /** |
| * Find a zero in the given interval with an initial guess. |
| * <p>Throws <code>IllegalArgumentException</code> if the values of the |
| * function at the three points have the same sign (note that it is |
| * allowed to have endpoints with the same sign if the initial point has |
| * opposite sign function-wise).</p> |
| * |
| * @param f function to solve. |
| * @param min the lower bound for the interval. |
| * @param max the upper bound for the interval. |
| * @param initial the start value to use (must be set to min if no |
| * initial point is known). |
| * @return the value where the function is zero |
| * @throws MaxIterationsExceededException the maximum iteration count is exceeded |
| * @throws FunctionEvaluationException if an error occurs evaluating the function |
| * @throws IllegalArgumentException if initial is not between min and max |
| * (even if it <em>is</em> a root) |
| * @deprecated in 2.2 (to be removed in 3.0). |
| */ |
| @Deprecated |
| public double solve(final UnivariateRealFunction f, |
| final double min, final double max, final double initial) |
| throws MaxIterationsExceededException, FunctionEvaluationException { |
| |
| clearResult(); |
| if ((initial < min) || (initial > max)) { |
| throw MathRuntimeException.createIllegalArgumentException( |
| LocalizedFormats.INVALID_INTERVAL_INITIAL_VALUE_PARAMETERS, |
| min, initial, max); |
| } |
| |
| // return the initial guess if it is good enough |
| double yInitial = f.value(initial); |
| if (FastMath.abs(yInitial) <= functionValueAccuracy) { |
| setResult(initial, 0); |
| return result; |
| } |
| |
| // return the first endpoint if it is good enough |
| double yMin = f.value(min); |
| if (FastMath.abs(yMin) <= functionValueAccuracy) { |
| setResult(min, 0); |
| return result; |
| } |
| |
| // reduce interval if min and initial bracket the root |
| if (yInitial * yMin < 0) { |
| return solve(f, min, yMin, initial, yInitial, min, yMin); |
| } |
| |
| // return the second endpoint if it is good enough |
| double yMax = f.value(max); |
| if (FastMath.abs(yMax) <= functionValueAccuracy) { |
| setResult(max, 0); |
| return result; |
| } |
| |
| // reduce interval if initial and max bracket the root |
| if (yInitial * yMax < 0) { |
| return solve(f, initial, yInitial, max, yMax, initial, yInitial); |
| } |
| |
| throw MathRuntimeException.createIllegalArgumentException( |
| LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, min, max, yMin, yMax); |
| |
| } |
| |
| /** |
| * Find a zero in the given interval with an initial guess. |
| * <p>Throws <code>IllegalArgumentException</code> if the values of the |
| * function at the three points have the same sign (note that it is |
| * allowed to have endpoints with the same sign if the initial point has |
| * opposite sign function-wise).</p> |
| * |
| * @param f function to solve. |
| * @param min the lower bound for the interval. |
| * @param max the upper bound for the interval. |
| * @param initial the start value to use (must be set to min if no |
| * initial point is known). |
| * @param maxEval Maximum number of evaluations. |
| * @return the value where the function is zero |
| * @throws MaxIterationsExceededException the maximum iteration count is exceeded |
| * @throws FunctionEvaluationException if an error occurs evaluating the function |
| * @throws IllegalArgumentException if initial is not between min and max |
| * (even if it <em>is</em> a root) |
| */ |
| @Override |
| public double solve(int maxEval, final UnivariateRealFunction f, |
| final double min, final double max, final double initial) |
| throws MaxIterationsExceededException, FunctionEvaluationException { |
| setMaximalIterationCount(maxEval); |
| return solve(f, min, max, initial); |
| } |
| |
| /** |
| * Find a zero in the given interval. |
| * <p> |
| * Requires that the values of the function at the endpoints have opposite |
| * signs. An <code>IllegalArgumentException</code> is thrown if this is not |
| * the case.</p> |
| * |
| * @param f the function to solve |
| * @param min the lower bound for the interval. |
| * @param max the upper bound for the interval. |
| * @return the value where the function is zero |
| * @throws MaxIterationsExceededException if the maximum iteration count is exceeded |
| * @throws FunctionEvaluationException if an error occurs evaluating the function |
| * @throws IllegalArgumentException if min is not less than max or the |
| * signs of the values of the function at the endpoints are not opposites |
| * @deprecated in 2.2 (to be removed in 3.0). |
| */ |
| @Deprecated |
| public double solve(final UnivariateRealFunction f, |
| final double min, final double max) |
| throws MaxIterationsExceededException, FunctionEvaluationException { |
| |
| clearResult(); |
| verifyInterval(min, max); |
| |
| double ret = Double.NaN; |
| |
| double yMin = f.value(min); |
| double yMax = f.value(max); |
| |
| // Verify bracketing |
| double sign = yMin * yMax; |
| if (sign > 0) { |
| // check if either value is close to a zero |
| if (FastMath.abs(yMin) <= functionValueAccuracy) { |
| setResult(min, 0); |
| ret = min; |
| } else if (FastMath.abs(yMax) <= functionValueAccuracy) { |
| setResult(max, 0); |
| ret = max; |
| } else { |
| // neither value is close to zero and min and max do not bracket root. |
| throw MathRuntimeException.createIllegalArgumentException( |
| LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, min, max, yMin, yMax); |
| } |
| } else if (sign < 0){ |
| // solve using only the first endpoint as initial guess |
| ret = solve(f, min, yMin, max, yMax, min, yMin); |
| } else { |
| // either min or max is a root |
| if (yMin == 0.0) { |
| ret = min; |
| } else { |
| ret = max; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Find a zero in the given interval. |
| * <p> |
| * Requires that the values of the function at the endpoints have opposite |
| * signs. An <code>IllegalArgumentException</code> is thrown if this is not |
| * the case.</p> |
| * |
| * @param f the function to solve |
| * @param min the lower bound for the interval. |
| * @param max the upper bound for the interval. |
| * @param maxEval Maximum number of evaluations. |
| * @return the value where the function is zero |
| * @throws MaxIterationsExceededException if the maximum iteration count is exceeded |
| * @throws FunctionEvaluationException if an error occurs evaluating the function |
| * @throws IllegalArgumentException if min is not less than max or the |
| * signs of the values of the function at the endpoints are not opposites |
| */ |
| @Override |
| public double solve(int maxEval, final UnivariateRealFunction f, |
| final double min, final double max) |
| throws MaxIterationsExceededException, FunctionEvaluationException { |
| setMaximalIterationCount(maxEval); |
| return solve(f, min, max); |
| } |
| |
| /** |
| * Find a zero starting search according to the three provided points. |
| * @param f the function to solve |
| * @param x0 old approximation for the root |
| * @param y0 function value at the approximation for the root |
| * @param x1 last calculated approximation for the root |
| * @param y1 function value at the last calculated approximation |
| * for the root |
| * @param x2 bracket point (must be set to x0 if no bracket point is |
| * known, this will force starting with linear interpolation) |
| * @param y2 function value at the bracket point. |
| * @return the value where the function is zero |
| * @throws MaxIterationsExceededException if the maximum iteration count is exceeded |
| * @throws FunctionEvaluationException if an error occurs evaluating the function |
| */ |
| private double solve(final UnivariateRealFunction f, |
| double x0, double y0, |
| double x1, double y1, |
| double x2, double y2) |
| throws MaxIterationsExceededException, FunctionEvaluationException { |
| |
| double delta = x1 - x0; |
| double oldDelta = delta; |
| |
| int i = 0; |
| while (i < maximalIterationCount) { |
| if (FastMath.abs(y2) < FastMath.abs(y1)) { |
| // use the bracket point if is better than last approximation |
| x0 = x1; |
| x1 = x2; |
| x2 = x0; |
| y0 = y1; |
| y1 = y2; |
| y2 = y0; |
| } |
| if (FastMath.abs(y1) <= functionValueAccuracy) { |
| // Avoid division by very small values. Assume |
| // the iteration has converged (the problem may |
| // still be ill conditioned) |
| setResult(x1, i); |
| return result; |
| } |
| double dx = x2 - x1; |
| double tolerance = |
| FastMath.max(relativeAccuracy * FastMath.abs(x1), absoluteAccuracy); |
| if (FastMath.abs(dx) <= tolerance) { |
| setResult(x1, i); |
| return result; |
| } |
| if ((FastMath.abs(oldDelta) < tolerance) || |
| (FastMath.abs(y0) <= FastMath.abs(y1))) { |
| // Force bisection. |
| delta = 0.5 * dx; |
| oldDelta = delta; |
| } else { |
| double r3 = y1 / y0; |
| double p; |
| double p1; |
| // the equality test (x0 == x2) is intentional, |
| // it is part of the original Brent's method, |
| // it should NOT be replaced by proximity test |
| if (x0 == x2) { |
| // Linear interpolation. |
| p = dx * r3; |
| p1 = 1.0 - r3; |
| } else { |
| // Inverse quadratic interpolation. |
| double r1 = y0 / y2; |
| double r2 = y1 / y2; |
| p = r3 * (dx * r1 * (r1 - r2) - (x1 - x0) * (r2 - 1.0)); |
| p1 = (r1 - 1.0) * (r2 - 1.0) * (r3 - 1.0); |
| } |
| if (p > 0.0) { |
| p1 = -p1; |
| } else { |
| p = -p; |
| } |
| if (2.0 * p >= 1.5 * dx * p1 - FastMath.abs(tolerance * p1) || |
| p >= FastMath.abs(0.5 * oldDelta * p1)) { |
| // Inverse quadratic interpolation gives a value |
| // in the wrong direction, or progress is slow. |
| // Fall back to bisection. |
| delta = 0.5 * dx; |
| oldDelta = delta; |
| } else { |
| oldDelta = delta; |
| delta = p / p1; |
| } |
| } |
| // Save old X1, Y1 |
| x0 = x1; |
| y0 = y1; |
| // Compute new X1, Y1 |
| if (FastMath.abs(delta) > tolerance) { |
| x1 = x1 + delta; |
| } else if (dx > 0.0) { |
| x1 = x1 + 0.5 * tolerance; |
| } else if (dx <= 0.0) { |
| x1 = x1 - 0.5 * tolerance; |
| } |
| y1 = f.value(x1); |
| if ((y1 > 0) == (y2 > 0)) { |
| x2 = x0; |
| y2 = y0; |
| delta = x1 - x0; |
| oldDelta = delta; |
| } |
| i++; |
| } |
| throw new MaxIterationsExceededException(maximalIterationCount); |
| } |
| } |