From c0b4954297ce01b64487c09a1dcfcc0124f7053b Mon Sep 17 00:00:00 2001 From: Gilles Sadowski Date: Fri, 2 Sep 2011 11:09:59 +0000 Subject: [PATCH] MATH-631 Early detection of "Regula Falsi" algorithm being stuck due to finite precision. Javadoc makes it clear that either the Pegasus or the Illinois solver should be preferred over the Regula Falsi one (due to D. Hendriks). git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1164474 13f79535-47bb-0310-9956-ffa450edef68 --- .../analysis/solvers/BaseSecantSolver.java | 17 +++++---- .../math/analysis/solvers/IllinoisSolver.java | 15 ++++---- .../math/analysis/solvers/PegasusSolver.java | 22 ++++++------ .../analysis/solvers/RegulaFalsiSolver.java | 35 ++++++++++++++----- .../solvers/RegulaFalsiSolverTest.java | 4 +-- 5 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/BaseSecantSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/BaseSecantSolver.java index c781a9011..034752553 100644 --- a/src/main/java/org/apache/commons/math/analysis/solvers/BaseSecantSolver.java +++ b/src/main/java/org/apache/commons/math/analysis/solvers/BaseSecantSolver.java @@ -19,6 +19,7 @@ package org.apache.commons.math.analysis.solvers; import org.apache.commons.math.util.FastMath; import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.ConvergenceException; import org.apache.commons.math.exception.MathInternalError; /** @@ -61,8 +62,8 @@ public abstract class BaseSecantSolver /** * Construct a solver. * - * @param absoluteAccuracy absolute accuracy - * @param method Secant-based root-finding method to use + * @param absoluteAccuracy Absolute accuracy. + * @param method Secant-based root-finding method to use. */ protected BaseSecantSolver(final double absoluteAccuracy, final Method method) { super(absoluteAccuracy); @@ -73,9 +74,9 @@ public abstract class BaseSecantSolver /** * Construct a solver. * - * @param relativeAccuracy relative accuracy - * @param absoluteAccuracy absolute accuracy - * @param method Secant-based root-finding method to use + * @param relativeAccuracy Relative accuracy. + * @param absoluteAccuracy Absolute accuracy. + * @param method Secant-based root-finding method to use. */ protected BaseSecantSolver(final double relativeAccuracy, final double absoluteAccuracy, @@ -183,7 +184,11 @@ public abstract class BaseSecantSolver f0 *= f1 / (f1 + fx); break; case REGULA_FALSI: - // Nothing. + // Detect early that algorithm is stuck, instead of waiting + // for the maximum number of iterations to be exceeded. + if (x == x1) { + throw new ConvergenceException(); + } break; default: // Should never happen. diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/IllinoisSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/IllinoisSolver.java index ede0c2792..47c340c01 100644 --- a/src/main/java/org/apache/commons/math/analysis/solvers/IllinoisSolver.java +++ b/src/main/java/org/apache/commons/math/analysis/solvers/IllinoisSolver.java @@ -26,7 +26,9 @@ package org.apache.commons.math.analysis.solvers; *

Like the Regula Falsi method, convergence is guaranteed by * maintaining a bracketed solution. The Illinois method however, * should converge much faster than the original Regula Falsi - * method.

+ * method. Furthermore, this implementation of the Illinois method + * should not suffer from the same implementation issues as the Regula + * Falsi method, which may fail to convergence in certain cases.

* *

The Illinois method assumes that the function is continuous, * but not necessarily smooth.

@@ -49,7 +51,7 @@ public class IllinoisSolver extends BaseSecantSolver { /** * Construct a solver. * - * @param absoluteAccuracy absolute accuracy + * @param absoluteAccuracy Absolute accuracy. */ public IllinoisSolver(final double absoluteAccuracy) { super(absoluteAccuracy, Method.ILLINOIS); @@ -58,8 +60,8 @@ public class IllinoisSolver extends BaseSecantSolver { /** * Construct a solver. * - * @param relativeAccuracy relative accuracy - * @param absoluteAccuracy absolute accuracy + * @param relativeAccuracy Relative accuracy. + * @param absoluteAccuracy Absolute accuracy. */ public IllinoisSolver(final double relativeAccuracy, final double absoluteAccuracy) { @@ -69,8 +71,8 @@ public class IllinoisSolver extends BaseSecantSolver { /** * Construct a solver. * - * @param relativeAccuracy relative accuracy - * @param absoluteAccuracy absolute accuracy + * @param relativeAccuracy Relative accuracy. + * @param absoluteAccuracy Absolute accuracy. * @param functionValueAccuracy Maximum function value error. */ public IllinoisSolver(final double relativeAccuracy, @@ -78,5 +80,4 @@ public class IllinoisSolver extends BaseSecantSolver { final double functionValueAccuracy) { super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy, Method.PEGASUS); } - } diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/PegasusSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/PegasusSolver.java index 08fb0c7d0..457c9580d 100644 --- a/src/main/java/org/apache/commons/math/analysis/solvers/PegasusSolver.java +++ b/src/main/java/org/apache/commons/math/analysis/solvers/PegasusSolver.java @@ -24,10 +24,13 @@ package org.apache.commons.math.analysis.solvers; * *

Like the Regula Falsi method, convergence is guaranteed by * maintaining a bracketed solution. The Pegasus method however, - * should converge much faster than the original Regula Falsi method. - * Furthermore, it should converge faster than the - * {@link IllinoisSolver Illinois} method, another - * Regula Falsi-based method.

+ * should converge much faster than the original Regula Falsi + * method. Furthermore, this implementation of the Pegasus method + * should not suffer from the same implementation issues as the Regula + * Falsi method, which may fail to convergence in certain cases. Also, + * the Pegasus method should converge faster than the + * {@link IllinoisSolver Illinois} method, another Regula + * Falsi-based method.

* *

The Pegasus method assumes that the function is continuous, * but not necessarily smooth.

@@ -50,7 +53,7 @@ public class PegasusSolver extends BaseSecantSolver { /** * Construct a solver. * - * @param absoluteAccuracy absolute accuracy + * @param absoluteAccuracy Absolute accuracy. */ public PegasusSolver(final double absoluteAccuracy) { super(absoluteAccuracy, Method.PEGASUS); @@ -59,8 +62,8 @@ public class PegasusSolver extends BaseSecantSolver { /** * Construct a solver. * - * @param relativeAccuracy relative accuracy - * @param absoluteAccuracy absolute accuracy + * @param relativeAccuracy Relative accuracy. + * @param absoluteAccuracy Absolute accuracy. */ public PegasusSolver(final double relativeAccuracy, final double absoluteAccuracy) { @@ -70,8 +73,8 @@ public class PegasusSolver extends BaseSecantSolver { /** * Construct a solver. * - * @param relativeAccuracy relative accuracy - * @param absoluteAccuracy absolute accuracy + * @param relativeAccuracy Relative accuracy. + * @param absoluteAccuracy Absolute accuracy. * @param functionValueAccuracy Maximum function value error. */ public PegasusSolver(final double relativeAccuracy, @@ -79,5 +82,4 @@ public class PegasusSolver extends BaseSecantSolver { final double functionValueAccuracy) { super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy, Method.PEGASUS); } - } diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/RegulaFalsiSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/RegulaFalsiSolver.java index 89c50dfd0..71224a0da 100644 --- a/src/main/java/org/apache/commons/math/analysis/solvers/RegulaFalsiSolver.java +++ b/src/main/java/org/apache/commons/math/analysis/solvers/RegulaFalsiSolver.java @@ -17,13 +17,30 @@ package org.apache.commons.math.analysis.solvers; - /** * Implements the Regula Falsi or False position method for * root-finding (approximating a zero of a univariate real function). It is a - * modified {@link SecantSolver Secant} method. Unlike the - * Secant method, convergence is guaranteed by maintaining a - * bracketed solution. + * modified {@link SecantSolver Secant} method. + * + *

The Regula Falsi method is included for completeness, for + * testing purposes, for educational purposes, for comparison to other + * algorithms, etc. It is however not intended to be used + * for actual problems, as one of the bounds often remains fixed, resulting + * in very slow convergence. Instead, one of the well-known modified + * Regula Falsi algorithms can be used ({@link IllinoisSolver + * Illinois} or {@link PegasusSolver Pegasus}). These two + * algorithms solve the fundamental issues of the original Regula + * Falsi algorithm, and greatly out-performs it for most, if not all, + * (practical) functions. + * + *

Unlike the Secant method, the Regula Falsi guarantees + * convergence, by maintaining a bracketed solution. Note however, that due to + * the finite/limited precision of Java's {@link Double double} type, which is + * used in this implementation, the algorithm may get stuck in a situation + * where it no longer makes any progress. Such cases are detected and result + * in a {@code ConvergenceException} exception being thrown. In other words, + * the algorithm theoretically guarantees convergence, but the implementation + * does not.

* *

The Regula Falsi method assumes that the function is continuous, * but not necessarily smooth.

@@ -46,7 +63,7 @@ public class RegulaFalsiSolver extends BaseSecantSolver { /** * Construct a solver. * - * @param absoluteAccuracy absolute accuracy + * @param absoluteAccuracy Absolute accuracy. */ public RegulaFalsiSolver(final double absoluteAccuracy) { super(absoluteAccuracy, Method.REGULA_FALSI); @@ -55,8 +72,8 @@ public class RegulaFalsiSolver extends BaseSecantSolver { /** * Construct a solver. * - * @param relativeAccuracy relative accuracy - * @param absoluteAccuracy absolute accuracy + * @param relativeAccuracy Relative accuracy. + * @param absoluteAccuracy Absolute accuracy. */ public RegulaFalsiSolver(final double relativeAccuracy, final double absoluteAccuracy) { @@ -66,8 +83,8 @@ public class RegulaFalsiSolver extends BaseSecantSolver { /** * Construct a solver. * - * @param relativeAccuracy relative accuracy - * @param absoluteAccuracy absolute accuracy + * @param relativeAccuracy Relative accuracy. + * @param absoluteAccuracy Absolute accuracy. * @param functionValueAccuracy Maximum function value error. */ public RegulaFalsiSolver(final double relativeAccuracy, diff --git a/src/test/java/org/apache/commons/math/analysis/solvers/RegulaFalsiSolverTest.java b/src/test/java/org/apache/commons/math/analysis/solvers/RegulaFalsiSolverTest.java index fea0abcc8..db098975f 100644 --- a/src/test/java/org/apache/commons/math/analysis/solvers/RegulaFalsiSolverTest.java +++ b/src/test/java/org/apache/commons/math/analysis/solvers/RegulaFalsiSolverTest.java @@ -18,7 +18,7 @@ package org.apache.commons.math.analysis.solvers; import org.apache.commons.math.analysis.UnivariateRealFunction; -import org.apache.commons.math.exception.TooManyEvaluationsException; +import org.apache.commons.math.exception.ConvergenceException; import org.junit.Test; import org.junit.Assert; @@ -41,7 +41,7 @@ public final class RegulaFalsiSolverTest extends BaseSecantSolverAbstractTest { return new int[] {3, 7, 8, 19, 18, 11, 67, 55, 288, 151, -1}; } - @Test(expected=TooManyEvaluationsException.class) + @Test(expected=ConvergenceException.class) public void testIssue631() { final UnivariateRealFunction f = new UnivariateRealFunction() { /** {@inheritDoc} */