Skip to content

Commit c48f994

Browse files
authored
Merge pull request #959 from jkalias/robust-newton-raphson-root-on-the-bound
Robust Newton Raphson root on the bound
2 parents 035a1dc + 963ef16 commit c48f994

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

src/Numerics.Tests/RootFindingTests/RobustNewtonRaphsonTest.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,58 @@ public void Cubic()
115115
Assert.AreEqual(4.0254088477485, RobustNewtonRaphson.FindRoot(f2, df2, 3, 5, 1e-10, 100, 20), 1e-6);
116116
}
117117

118+
[Test]
119+
public void InfinitelyManyRoots()
120+
{
121+
// degenerate case with infinitely many roots
122+
Func<double, double> f1 = x => 0.0;
123+
Func<double, double> df1 = x => 0.0;
124+
Assert.AreEqual(-50, RobustNewtonRaphson.FindRoot(f1, df1, -200, 100), 1e-6);
125+
}
126+
127+
[Test]
128+
public void InfinitelyManyRootsWithGivenAccuracy()
129+
{
130+
// degenerate case with infinitely many roots
131+
Func<double, double> f1 = x => 1e-10;
132+
Func<double, double> df1 = x => 1e-6;
133+
Assert.AreEqual(-50, RobustNewtonRaphson.FindRoot(f1, df1, -200, 100, 1e-8), 1e-6);
134+
}
135+
136+
[Test]
137+
public void BoundsNearMaxValueNoOverflow()
138+
{
139+
Func<double, double> f1 = x => 0.0;
140+
Func<double, double> df1 = x => 0.0;
141+
Assert.AreEqual(double.MaxValue, RobustNewtonRaphson.FindRoot(f1, df1, double.MaxValue - 2, double.MaxValue - 1), 1e-6);
142+
}
143+
144+
[Test]
145+
public void BoundsNearMinValueNoUnderflow()
146+
{
147+
Func<double, double> f1 = x => 0.0;
148+
Func<double, double> df1 = x => 0.0;
149+
Assert.AreEqual(double.MinValue, RobustNewtonRaphson.FindRoot(f1, df1, double.MinValue + 1, double.MinValue + 2), 1e-6);
150+
}
151+
152+
[Test]
153+
public void InfiniteLowerBound()
154+
{
155+
Func<double, double> f1 = x => 0.0;
156+
Func<double, double> df1 = x => 0.0;
157+
Assert.That(() => RobustNewtonRaphson.FindRoot(f1, df1, double.NegativeInfinity, 5), Throws.TypeOf<ArgumentOutOfRangeException>());
158+
Assert.That(() => RobustNewtonRaphson.FindRoot(f1, df1, double.PositiveInfinity, 5), Throws.TypeOf<ArgumentOutOfRangeException>());
159+
}
160+
161+
[Test]
162+
public void InfiniteUpperBound()
163+
{
164+
Func<double, double> f1 = x => 0.0;
165+
Func<double, double> df1 = x => 0.0;
166+
Assert.That(() => RobustNewtonRaphson.FindRoot(f1, df1, -5, double.NegativeInfinity), Throws.TypeOf<ArgumentOutOfRangeException>());
167+
Assert.That(() => RobustNewtonRaphson.FindRoot(f1, df1, -5, double.PositiveInfinity), Throws.TypeOf<ArgumentOutOfRangeException>());
168+
}
169+
118170
[Test]
119171
public void NoRoot()
120172
{

src/Numerics/RootFinding/RobustNewtonRaphson.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ public static double FindRoot(Func<double, double> f, Func<double, double> df, d
6060
/// <summary>Find a solution of the equation f(x)=0.</summary>
6161
/// <param name="f">The function to find roots from.</param>
6262
/// <param name="df">The first derivative of the function to find roots from.</param>
63-
/// <param name="lowerBound">The low value of the range where the root is supposed to be.</param>
64-
/// <param name="upperBound">The high value of the range where the root is supposed to be.</param>
63+
/// <param name="lowerBound">The low value of the range where the root is supposed to be (finite number).</param>
64+
/// <param name="upperBound">The high value of the range where the root is supposed to be (finite number).</param>
6565
/// <param name="accuracy">Desired accuracy. The root will be refined until the accuracy or the maximum number of iterations is reached. Example: 1e-14. Must be greater than 0.</param>
6666
/// <param name="maxIterations">Maximum number of iterations. Example: 100.</param>
6767
/// <param name="subdivision">How many parts an interval should be split into for zero crossing scanning in case of lacking bracketing. Example: 20.</param>
@@ -74,6 +74,23 @@ public static bool TryFindRoot(Func<double, double> f, Func<double, double> df,
7474
throw new ArgumentOutOfRangeException(nameof(accuracy), "Must be greater than zero.");
7575
}
7676

77+
if (double.IsInfinity(lowerBound))
78+
{
79+
throw new ArgumentOutOfRangeException(nameof(lowerBound), "Must be a finite number.");
80+
}
81+
82+
if (double.IsInfinity(upperBound))
83+
{
84+
throw new ArgumentOutOfRangeException(nameof(upperBound), "Must be a finite number.");
85+
}
86+
87+
root = lowerBound + 0.5 * (upperBound - lowerBound);
88+
double fx = f(root);
89+
if (Math.Abs(fx) < accuracy)
90+
{
91+
return true;
92+
}
93+
7794
double fmin = f(lowerBound);
7895
double fmax = f(upperBound);
7996

@@ -89,13 +106,6 @@ public static bool TryFindRoot(Func<double, double> f, Func<double, double> df,
89106
return true;
90107
}
91108

92-
root = 0.5*(lowerBound + upperBound);
93-
double fx = f(root);
94-
if (fx == 0.0)
95-
{
96-
return true;
97-
}
98-
99109
double lastStep = Math.Abs(upperBound - lowerBound);
100110
for (int i = 0; i < maxIterations; i++)
101111
{

0 commit comments

Comments
 (0)