Home | History | Annotate | Download | only in solvers
      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  *      http://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 package org.apache.commons.math.analysis.solvers;
     18 
     19 import org.apache.commons.math.ConvergenceException;
     20 import org.apache.commons.math.FunctionEvaluationException;
     21 import org.apache.commons.math.MathRuntimeException;
     22 import org.apache.commons.math.MaxIterationsExceededException;
     23 import org.apache.commons.math.analysis.UnivariateRealFunction;
     24 import org.apache.commons.math.exception.util.LocalizedFormats;
     25 import org.apache.commons.math.util.FastMath;
     26 
     27 
     28 /**
     29  * Implements a modified version of the
     30  * <a href="http://mathworld.wolfram.com/SecantMethod.html">secant method</a>
     31  * for approximating a zero of a real univariate function.
     32  * <p>
     33  * The algorithm is modified to maintain bracketing of a root by successive
     34  * approximations. Because of forced bracketing, convergence may be slower than
     35  * the unrestricted secant algorithm. However, this implementation should in
     36  * general outperform the
     37  * <a href="http://mathworld.wolfram.com/MethodofFalsePosition.html">
     38  * regula falsi method.</a></p>
     39  * <p>
     40  * The function is assumed to be continuous but not necessarily smooth.</p>
     41  *
     42  * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 fvr. 2011) $
     43  */
     44 public class SecantSolver extends UnivariateRealSolverImpl {
     45 
     46     /**
     47      * Construct a solver for the given function.
     48      * @param f function to solve.
     49      * @deprecated as of 2.0 the function to solve is passed as an argument
     50      * to the {@link #solve(UnivariateRealFunction, double, double)} or
     51      * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
     52      * method.
     53      */
     54     @Deprecated
     55     public SecantSolver(UnivariateRealFunction f) {
     56         super(f, 100, 1E-6);
     57     }
     58 
     59     /**
     60      * Construct a solver.
     61      * @deprecated in 2.2 (to be removed in 3.0).
     62      */
     63     @Deprecated
     64     public SecantSolver() {
     65         super(100, 1E-6);
     66     }
     67 
     68     /** {@inheritDoc} */
     69     @Deprecated
     70     public double solve(final double min, final double max)
     71         throws ConvergenceException, FunctionEvaluationException {
     72         return solve(f, min, max);
     73     }
     74 
     75     /** {@inheritDoc} */
     76     @Deprecated
     77     public double solve(final double min, final double max, final double initial)
     78         throws ConvergenceException, FunctionEvaluationException {
     79         return solve(f, min, max, initial);
     80     }
     81 
     82     /**
     83      * Find a zero in the given interval.
     84      *
     85      * @param f the function to solve
     86      * @param min the lower bound for the interval
     87      * @param max the upper bound for the interval
     88      * @param initial the start value to use (ignored)
     89      * @param maxEval Maximum number of evaluations.
     90      * @return the value where the function is zero
     91      * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
     92      * @throws FunctionEvaluationException if an error occurs evaluating the function
     93      * @throws IllegalArgumentException if min is not less than max or the
     94      * signs of the values of the function at the endpoints are not opposites
     95      */
     96     @Override
     97     public double solve(int maxEval, final UnivariateRealFunction f,
     98                         final double min, final double max, final double initial)
     99         throws MaxIterationsExceededException, FunctionEvaluationException {
    100         setMaximalIterationCount(maxEval);
    101         return solve(f, min, max, initial);
    102     }
    103 
    104     /**
    105      * Find a zero in the given interval.
    106      *
    107      * @param f the function to solve
    108      * @param min the lower bound for the interval
    109      * @param max the upper bound for the interval
    110      * @param initial the start value to use (ignored)
    111      * @return the value where the function is zero
    112      * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
    113      * @throws FunctionEvaluationException if an error occurs evaluating the function
    114      * @throws IllegalArgumentException if min is not less than max or the
    115      * signs of the values of the function at the endpoints are not opposites
    116      * @deprecated in 2.2 (to be removed in 3.0).
    117      */
    118     @Deprecated
    119     public double solve(final UnivariateRealFunction f,
    120                         final double min, final double max, final double initial)
    121         throws MaxIterationsExceededException, FunctionEvaluationException {
    122         return solve(f, min, max);
    123     }
    124 
    125     /**
    126      * Find a zero in the given interval.
    127      * @param f the function to solve
    128      * @param min the lower bound for the interval.
    129      * @param max the upper bound for the interval.
    130      * @param maxEval Maximum number of evaluations.
    131      * @return the value where the function is zero
    132      * @throws MaxIterationsExceededException  if the maximum iteration count is exceeded
    133      * @throws FunctionEvaluationException if an error occurs evaluating the function
    134      * @throws IllegalArgumentException if min is not less than max or the
    135      * signs of the values of the function at the endpoints are not opposites
    136      */
    137     @Override
    138     public double solve(int maxEval, final UnivariateRealFunction f,
    139                         final double min, final double max)
    140         throws MaxIterationsExceededException, FunctionEvaluationException {
    141         setMaximalIterationCount(maxEval);
    142         return solve(f, min, max);
    143     }
    144 
    145     /**
    146      * Find a zero in the given interval.
    147      * @param f the function to solve
    148      * @param min the lower bound for the interval.
    149      * @param max the upper bound for the interval.
    150      * @return the value where the function is zero
    151      * @throws MaxIterationsExceededException  if the maximum iteration count is exceeded
    152      * @throws FunctionEvaluationException if an error occurs evaluating the function
    153      * @throws IllegalArgumentException if min is not less than max or the
    154      * signs of the values of the function at the endpoints are not opposites
    155      * @deprecated in 2.2 (to be removed in 3.0).
    156      */
    157     @Deprecated
    158     public double solve(final UnivariateRealFunction f,
    159                         final double min, final double max)
    160         throws MaxIterationsExceededException, FunctionEvaluationException {
    161 
    162         clearResult();
    163         verifyInterval(min, max);
    164 
    165         // Index 0 is the old approximation for the root.
    166         // Index 1 is the last calculated approximation  for the root.
    167         // Index 2 is a bracket for the root with respect to x0.
    168         // OldDelta is the length of the bracketing interval of the last
    169         // iteration.
    170         double x0 = min;
    171         double x1 = max;
    172         double y0 = f.value(x0);
    173         double y1 = f.value(x1);
    174 
    175         // Verify bracketing
    176         if (y0 * y1 >= 0) {
    177             throw MathRuntimeException.createIllegalArgumentException(
    178                   LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, min, max, y0, y1);
    179         }
    180 
    181         double x2 = x0;
    182         double y2 = y0;
    183         double oldDelta = x2 - x1;
    184         int i = 0;
    185         while (i < maximalIterationCount) {
    186             if (FastMath.abs(y2) < FastMath.abs(y1)) {
    187                 x0 = x1;
    188                 x1 = x2;
    189                 x2 = x0;
    190                 y0 = y1;
    191                 y1 = y2;
    192                 y2 = y0;
    193             }
    194             if (FastMath.abs(y1) <= functionValueAccuracy) {
    195                 setResult(x1, i);
    196                 return result;
    197             }
    198             if (FastMath.abs(oldDelta) <
    199                 FastMath.max(relativeAccuracy * FastMath.abs(x1), absoluteAccuracy)) {
    200                 setResult(x1, i);
    201                 return result;
    202             }
    203             double delta;
    204             if (FastMath.abs(y1) > FastMath.abs(y0)) {
    205                 // Function value increased in last iteration. Force bisection.
    206                 delta = 0.5 * oldDelta;
    207             } else {
    208                 delta = (x0 - x1) / (1 - y0 / y1);
    209                 if (delta / oldDelta > 1) {
    210                     // New approximation falls outside bracket.
    211                     // Fall back to bisection.
    212                     delta = 0.5 * oldDelta;
    213                 }
    214             }
    215             x0 = x1;
    216             y0 = y1;
    217             x1 = x1 + delta;
    218             y1 = f.value(x1);
    219             if ((y1 > 0) == (y2 > 0)) {
    220                 // New bracket is (x0,x1).
    221                 x2 = x0;
    222                 y2 = y0;
    223             }
    224             oldDelta = x2 - x1;
    225             i++;
    226         }
    227         throw new MaxIterationsExceededException(maximalIterationCount);
    228     }
    229 
    230 }
    231