001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math4.legacy.analysis.solvers;
018
019import org.apache.commons.math4.legacy.exception.NoBracketingException;
020import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException;
021import org.apache.commons.math4.core.jdkmath.JdkMath;
022
023/**
024 * Implements the <a href="http://mathworld.wolfram.com/RiddersMethod.html">
025 * Ridders' Method</a> for root finding of real univariate functions. For
026 * reference, see C. Ridders, <i>A new algorithm for computing a single root
027 * of a real continuous function </i>, IEEE Transactions on Circuits and
028 * Systems, 26 (1979), 979 - 980.
029 * <p>
030 * The function should be continuous but not necessarily smooth.</p>
031 *
032 * @since 1.2
033 */
034public class RiddersSolver extends AbstractUnivariateSolver {
035    /** Default absolute accuracy. */
036    private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
037
038    /**
039     * Construct a solver with default accuracy (1e-6).
040     */
041    public RiddersSolver() {
042        this(DEFAULT_ABSOLUTE_ACCURACY);
043    }
044    /**
045     * Construct a solver.
046     *
047     * @param absoluteAccuracy Absolute accuracy.
048     */
049    public RiddersSolver(double absoluteAccuracy) {
050        super(absoluteAccuracy);
051    }
052    /**
053     * Construct a solver.
054     *
055     * @param relativeAccuracy Relative accuracy.
056     * @param absoluteAccuracy Absolute accuracy.
057     */
058    public RiddersSolver(double relativeAccuracy,
059                         double absoluteAccuracy) {
060        super(relativeAccuracy, absoluteAccuracy);
061    }
062
063    /**
064     * {@inheritDoc}
065     */
066    @Override
067    protected double doSolve()
068        throws TooManyEvaluationsException,
069               NoBracketingException {
070        double min = getMin();
071        double max = getMax();
072        // [x1, x2] is the bracketing interval in each iteration
073        // x3 is the midpoint of [x1, x2]
074        // x is the new root approximation and an endpoint of the new interval
075        double x1 = min;
076        double y1 = computeObjectiveValue(x1);
077        double x2 = max;
078        double y2 = computeObjectiveValue(x2);
079
080        // check for zeros before verifying bracketing
081        if (y1 == 0) {
082            return min;
083        }
084        if (y2 == 0) {
085            return max;
086        }
087        verifyBracketing(min, max);
088
089        final double absoluteAccuracy = getAbsoluteAccuracy();
090        final double functionValueAccuracy = getFunctionValueAccuracy();
091        final double relativeAccuracy = getRelativeAccuracy();
092
093        double oldx = Double.POSITIVE_INFINITY;
094        while (true) {
095            // calculate the new root approximation
096            final double x3 = 0.5 * (x1 + x2);
097            final double y3 = computeObjectiveValue(x3);
098            if (JdkMath.abs(y3) <= functionValueAccuracy) {
099                return x3;
100            }
101            final double delta = 1 - (y1 * y2) / (y3 * y3);  // delta > 1 due to bracketing
102            final double correction = (JdkMath.signum(y2) * JdkMath.signum(y3)) *
103                                      (x3 - x1) / JdkMath.sqrt(delta);
104            final double x = x3 - correction;                // correction != 0
105            final double y = computeObjectiveValue(x);
106
107            // check for convergence
108            final double tolerance = JdkMath.max(relativeAccuracy * JdkMath.abs(x), absoluteAccuracy);
109            if (JdkMath.abs(x - oldx) <= tolerance) {
110                return x;
111            }
112            if (JdkMath.abs(y) <= functionValueAccuracy) {
113                return x;
114            }
115
116            // prepare the new interval for next iteration
117            // Ridders' method guarantees x1 < x < x2
118            if (correction > 0.0) {             // x1 < x < x3
119                if (JdkMath.signum(y1) + JdkMath.signum(y) == 0.0) {
120                    x2 = x;
121                    y2 = y;
122                } else {
123                    x1 = x;
124                    x2 = x3;
125                    y1 = y;
126                    y2 = y3;
127                }
128            } else {                            // x3 < x < x2
129                if (JdkMath.signum(y2) + JdkMath.signum(y) == 0.0) {
130                    x1 = x;
131                    y1 = y;
132                } else {
133                    x1 = x3;
134                    x2 = x;
135                    y1 = y3;
136                    y2 = y;
137                }
138            }
139            oldx = x;
140        }
141    }
142}