diff --git a/src/Numerics/FindRoots.cs b/src/Numerics/FindRoots.cs
index 27a81c22..f07a43b9 100644
--- a/src/Numerics/FindRoots.cs
+++ b/src/Numerics/FindRoots.cs
@@ -49,7 +49,7 @@ namespace MathNet.Numerics
{
double root;
- if (!ZeroCrossingBracketing.Expand(f, ref lowerBound, ref upperBound, 1.6, 100))
+ if (!ZeroCrossingBracketing.ExpandReduce(f, ref lowerBound, ref upperBound, 1.6, maxIterations, maxIterations*10))
{
throw new NonConvergenceException(Resources.RootFindingFailed);
}
diff --git a/src/Numerics/RootFinding/Bisection.cs b/src/Numerics/RootFinding/Bisection.cs
index 074b3d2c..4c2dfda1 100644
--- a/src/Numerics/RootFinding/Bisection.cs
+++ b/src/Numerics/RootFinding/Bisection.cs
@@ -49,7 +49,7 @@ namespace MathNet.Numerics.RootFinding
///
public static double FindRootExpand(Func f, double guessLowerBound, double guessUpperBound, double accuracy = 1e-8, int maxIterations = 100, double expandFactor = 1.6, int maxExpandIteratons = 100)
{
- ZeroCrossingBracketing.Expand(f, ref guessLowerBound, ref guessUpperBound, expandFactor, maxExpandIteratons);
+ ZeroCrossingBracketing.ExpandReduce(f, ref guessLowerBound, ref guessUpperBound, expandFactor, maxExpandIteratons, maxExpandIteratons*10);
return FindRoot(f, guessLowerBound, guessUpperBound, accuracy, maxIterations);
}
diff --git a/src/Numerics/RootFinding/Brent.cs b/src/Numerics/RootFinding/Brent.cs
index 736c0206..7c76f2ed 100644
--- a/src/Numerics/RootFinding/Brent.cs
+++ b/src/Numerics/RootFinding/Brent.cs
@@ -50,7 +50,7 @@ namespace MathNet.Numerics.RootFinding
///
public static double FindRootExpand(Func f, double guessLowerBound, double guessUpperBound, double accuracy = 1e-8, int maxIterations = 100, double expandFactor = 1.6, int maxExpandIteratons = 100)
{
- ZeroCrossingBracketing.Expand(f, ref guessLowerBound, ref guessUpperBound, expandFactor, maxExpandIteratons);
+ ZeroCrossingBracketing.ExpandReduce(f, ref guessLowerBound, ref guessUpperBound, expandFactor, maxExpandIteratons, maxExpandIteratons*10);
return FindRoot(f, guessLowerBound, guessUpperBound, accuracy, maxIterations);
}
diff --git a/src/Numerics/RootFinding/RobustNewtonRaphson.cs b/src/Numerics/RootFinding/RobustNewtonRaphson.cs
index 0fd6b8ef..dcd31d73 100644
--- a/src/Numerics/RootFinding/RobustNewtonRaphson.cs
+++ b/src/Numerics/RootFinding/RobustNewtonRaphson.cs
@@ -2,9 +2,9 @@
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
-//
+//
// Copyright (c) 2009-2013 Math.NET
-//
+//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
@@ -13,10 +13,10 @@
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
-//
+//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
-//
+//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
diff --git a/src/Numerics/RootFinding/ZeroCrossingBracketing.cs b/src/Numerics/RootFinding/ZeroCrossingBracketing.cs
index 126df806..beb2b241 100644
--- a/src/Numerics/RootFinding/ZeroCrossingBracketing.cs
+++ b/src/Numerics/RootFinding/ZeroCrossingBracketing.cs
@@ -35,7 +35,7 @@ namespace MathNet.Numerics.RootFinding
{
public static class ZeroCrossingBracketing
{
- public static IEnumerable> FindIntervalsWithin(Func f, double lowerBound, double upperBound, int parts)
+ public static IEnumerable> FindIntervalsWithin(Func f, double lowerBound, double upperBound, int subdivisions)
{
// TODO: Consider binary-style search instead of linear scan
double fmin = f(lowerBound);
@@ -47,11 +47,11 @@ namespace MathNet.Numerics.RootFinding
yield break;
}
- double subdiv = (upperBound - lowerBound)/parts;
+ double subdiv = (upperBound - lowerBound)/subdivisions;
double smin = lowerBound;
int sign = Math.Sign(fmin);
- for (int k = 0; k < parts; k++)
+ for (int k = 0; k < subdivisions; k++)
{
double smax = smin + subdiv;
double sfmax = f(smax);
@@ -116,5 +116,59 @@ namespace MathNet.Numerics.RootFinding
upperBound = originalUpperBound;
return false;
}
+
+ public static bool Reduce(Func f, ref double lowerBound, ref double upperBound, int subdivisions = 1000)
+ {
+ double originalLowerBound = lowerBound;
+ double originalUpperBound = upperBound;
+
+ if (lowerBound >= upperBound)
+ {
+ throw new ArgumentOutOfRangeException("upperBound", string.Format(Resources.ArgumentOutOfRangeGreater, "xmax", "xmin"));
+ }
+
+ // TODO: Consider binary-style search instead of linear scan
+ double fmin = f(lowerBound);
+ double fmax = f(upperBound);
+
+ if (Math.Sign(fmin) != Math.Sign(fmax))
+ {
+ return true;
+ }
+
+ double subdiv = (upperBound - lowerBound) / subdivisions;
+ double smin = lowerBound;
+ int sign = Math.Sign(fmin);
+
+ for (int k = 0; k < subdivisions; k++)
+ {
+ double smax = smin + subdiv;
+ double sfmax = f(smax);
+ if (double.IsInfinity(sfmax))
+ {
+ // expand interval to include pole
+ smin = smax;
+ continue;
+ }
+
+ if (Math.Sign(sfmax) != sign)
+ {
+ lowerBound = smin;
+ upperBound = smax;
+ return true;
+ }
+
+ smin = smax;
+ }
+
+ lowerBound = originalLowerBound;
+ upperBound = originalUpperBound;
+ return false;
+ }
+
+ public static bool ExpandReduce(Func f, ref double lowerBound, ref double upperBound, double expansionFactor = 1.6, int expansionMaxIterations = 50, int reduceSubdivisions = 100)
+ {
+ return Expand(f, ref lowerBound, ref upperBound, expansionFactor, expansionMaxIterations) || Reduce(f, ref lowerBound, ref upperBound, reduceSubdivisions);
+ }
}
}
diff --git a/src/UnitTests/RootFindingTests/FindRootsTest.cs b/src/UnitTests/RootFindingTests/FindRootsTest.cs
index d9380a83..04c89dfa 100644
--- a/src/UnitTests/RootFindingTests/FindRootsTest.cs
+++ b/src/UnitTests/RootFindingTests/FindRootsTest.cs
@@ -160,6 +160,16 @@ namespace MathNet.Numerics.UnitTests.RootFindingTests
Assert.AreEqual(0, f1(x), 1e-5);
}
+ [Test]
+ public void StackOverflow39935588()
+ {
+ // Roots at -2, 2
+ Func f1 = x => (x - 3.0)*(x - 4.0);
+ Assert.AreEqual(3.0, FindRoots.OfFunction(f1, -2.0, 3.5), 1e-10);
+ Assert.AreEqual(4.0, FindRoots.OfFunction(f1, 3.5, 5.5), 1e-10);
+ Assert.AreEqual(0.0, f1(FindRoots.OfFunction(f1, -2.0, 5.5, 1e-14)), 1e-14);
+ }
+
void AssertComplexEqual(Complex expected, Complex actual, double delta)
{
Assert.AreEqual(expected.Real, actual.Real, delta);