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 */ 017 018package org.apache.commons.math4.legacy.analysis.solvers; 019 020import org.apache.commons.math4.legacy.exception.NoBracketingException; 021import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException; 022import org.apache.commons.math4.core.jdkmath.JdkMath; 023 024/** 025 * Implements the <em>Secant</em> method for root-finding (approximating a 026 * zero of a univariate real function). The solution that is maintained is 027 * not bracketed, and as such convergence is not guaranteed. 028 * 029 * <p>Implementation based on the following article: M. Dowell and P. Jarratt, 030 * <em>A modified regula falsi method for computing the root of an 031 * equation</em>, BIT Numerical Mathematics, volume 11, number 2, 032 * pages 168-174, Springer, 1971.</p> 033 * 034 * <p>Note that since release 3.0 this class implements the actual 035 * <em>Secant</em> algorithm, and not a modified one. As such, the 3.0 version 036 * is not backwards compatible with previous versions. To use an algorithm 037 * similar to the pre-3.0 releases, use the 038 * {@link IllinoisSolver <em>Illinois</em>} algorithm or the 039 * {@link PegasusSolver <em>Pegasus</em>} algorithm.</p> 040 * 041 */ 042public class SecantSolver extends AbstractUnivariateSolver { 043 044 /** Default absolute accuracy. */ 045 protected static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6; 046 047 /** Construct a solver with default accuracy (1e-6). */ 048 public SecantSolver() { 049 super(DEFAULT_ABSOLUTE_ACCURACY); 050 } 051 052 /** 053 * Construct a solver. 054 * 055 * @param absoluteAccuracy absolute accuracy 056 */ 057 public SecantSolver(final double absoluteAccuracy) { 058 super(absoluteAccuracy); 059 } 060 061 /** 062 * Construct a solver. 063 * 064 * @param relativeAccuracy relative accuracy 065 * @param absoluteAccuracy absolute accuracy 066 */ 067 public SecantSolver(final double relativeAccuracy, 068 final double absoluteAccuracy) { 069 super(relativeAccuracy, absoluteAccuracy); 070 } 071 072 /** {@inheritDoc} */ 073 @Override 074 protected final double doSolve() 075 throws TooManyEvaluationsException, 076 NoBracketingException { 077 // Get initial solution 078 double x0 = getMin(); 079 double x1 = getMax(); 080 double f0 = computeObjectiveValue(x0); 081 double f1 = computeObjectiveValue(x1); 082 083 // If one of the bounds is the exact root, return it. Since these are 084 // not under-approximations or over-approximations, we can return them 085 // regardless of the allowed solutions. 086 if (f0 == 0.0) { 087 return x0; 088 } 089 if (f1 == 0.0) { 090 return x1; 091 } 092 093 // Verify bracketing of initial solution. 094 verifyBracketing(x0, x1); 095 096 // Get accuracies. 097 final double ftol = getFunctionValueAccuracy(); 098 final double atol = getAbsoluteAccuracy(); 099 final double rtol = getRelativeAccuracy(); 100 101 // Keep finding better approximations. 102 while (true) { 103 // Calculate the next approximation. 104 final double x = x1 - ((f1 * (x1 - x0)) / (f1 - f0)); 105 final double fx = computeObjectiveValue(x); 106 107 // If the new approximation is the exact root, return it. Since 108 // this is not an under-approximation or an over-approximation, 109 // we can return it regardless of the allowed solutions. 110 if (fx == 0.0) { 111 return x; 112 } 113 114 // Update the bounds with the new approximation. 115 x0 = x1; 116 f0 = f1; 117 x1 = x; 118 f1 = fx; 119 120 // If the function value of the last approximation is too small, 121 // given the function value accuracy, then we can't get closer to 122 // the root than we already are. 123 if (JdkMath.abs(f1) <= ftol) { 124 return x1; 125 } 126 127 // If the current interval is within the given accuracies, we 128 // are satisfied with the current approximation. 129 if (JdkMath.abs(x1 - x0) < JdkMath.max(rtol * JdkMath.abs(x1), atol)) { 130 return x1; 131 } 132 } 133 } 134}