forked from tsai/mathnet-numerics
committed by
Christoph Ruegg
3 changed files with 313 additions and 0 deletions
@ -0,0 +1,114 @@ |
|||
using System; |
|||
|
|||
namespace MathNet.Numerics.RootFinders |
|||
{ |
|||
public class BrentRootFinder : RootFinder |
|||
{ |
|||
public BrentRootFinder() : base() |
|||
{ |
|||
} |
|||
public BrentRootFinder(int numIters, double accuracy) : base(numIters, accuracy) |
|||
{ |
|||
} |
|||
|
|||
protected override double Find() |
|||
{ |
|||
/* The implementation of the algorithm was inspired by |
|||
Press, Teukolsky, Vetterling, and Flannery, |
|||
"Numerical Recipes in C", 2nd edition, Cambridge |
|||
University Press |
|||
*/ |
|||
|
|||
double min1, min2; |
|||
double p, q, r, s, xAcc1, xMid = 0; |
|||
double d = 0.0, e = 0.0; |
|||
|
|||
// set up
|
|||
double xmin = XMin; |
|||
double fxmin = Func(XMin); |
|||
double xmax = XMax; |
|||
double fxmax = Func(XMax); |
|||
|
|||
double root = xmax; |
|||
double froot = fxmax; |
|||
|
|||
// solve
|
|||
int i = 0; |
|||
for (; i <= Iterations; i++) |
|||
{ |
|||
if (Math.Sign(froot) == Math.Sign(fxmax)) |
|||
{ |
|||
// Rename xMin_, root_, xMax_ and adjust bounds
|
|||
xmax = xmin; |
|||
fxmax = fxmin; |
|||
e = d = root - xmin; |
|||
} |
|||
if (Math.Abs(fxmax) < Math.Abs(froot)) |
|||
{ |
|||
xmin = root; |
|||
root = xmax; |
|||
xmax = xmin; |
|||
fxmin = froot; |
|||
froot = fxmax; |
|||
fxmax = fxmin; |
|||
} |
|||
// Convergence check
|
|||
xAcc1 = 2.0 * DOUBLE_ACCURACY * Math.Abs(root) + 0.5 * Accuracy; |
|||
xMid = (xmax - root) / 2.0; |
|||
if (Math.Abs(xMid) <= xAcc1 || Close(froot, 0.0)) |
|||
{ |
|||
return root; |
|||
} |
|||
if (Math.Abs(e) >= xAcc1 && |
|||
Math.Abs(fxmin) > Math.Abs(froot)) |
|||
{ |
|||
|
|||
// Attempt inverse quadratic interpolation
|
|||
s = froot / fxmin; |
|||
if (Close(xmin, xmax)) |
|||
{ |
|||
p = 2.0 * xMid * s; |
|||
q = 1.0 - s; |
|||
} |
|||
else |
|||
{ |
|||
q = fxmin / fxmax; |
|||
r = froot / fxmax; |
|||
p = s * (2.0 * xMid * q * (q - r) - (root - xmin) * (r - 1.0)); |
|||
q = (q - 1.0) * (r - 1.0) * (s - 1.0); |
|||
} |
|||
if (p > 0.0) q = -q; // Check whether in bounds
|
|||
p = Math.Abs(p); |
|||
min1 = 3.0 * xMid * q - Math.Abs(xAcc1 * q); |
|||
min2 = Math.Abs(e * q); |
|||
if (2.0 * p < Math.Min(min1, min2)) |
|||
{ |
|||
e = d; // Accept interpolation
|
|||
d = p / q; |
|||
} |
|||
else |
|||
{ |
|||
d = xMid; // Interpolation failed, use bisection
|
|||
e = d; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// Bounds decreasing too slowly, use bisection
|
|||
d = xMid; |
|||
e = d; |
|||
} |
|||
xmin = root; |
|||
fxmin = froot; |
|||
if (Math.Abs(d) > xAcc1) |
|||
root += d; |
|||
else |
|||
root += Sign(xAcc1, xMid); |
|||
froot = Func(root); |
|||
} |
|||
|
|||
// The algorithm has exceeded the number of iterations allowed
|
|||
throw new RootFinderException(ACCURACY_NOT_REACHED, i, new Range(XMin, XMax), Math.Abs(xMid)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,175 @@ |
|||
using System; |
|||
|
|||
namespace MathNet.Numerics.RootFinders |
|||
{ |
|||
public struct Range |
|||
{ |
|||
double Min, Max; |
|||
|
|||
public Range(double min, double max) |
|||
{ |
|||
Min = min; Max = max; |
|||
} |
|||
} |
|||
public class RootFinderException : Exception |
|||
{ |
|||
private int m_Iteration; |
|||
private Range m_Range; |
|||
private double m_Accuracy; |
|||
|
|||
public RootFinderException(string message, int iteration, Range range, double accuracy) |
|||
: base(message) |
|||
{ |
|||
m_Iteration = iteration; |
|||
m_Range = range; |
|||
m_Accuracy = accuracy; |
|||
} |
|||
|
|||
public int Iteration |
|||
{ |
|||
get { return m_Iteration; } |
|||
set { m_Iteration = value; } |
|||
} |
|||
|
|||
public Range Range |
|||
{ |
|||
get { return m_Range; } |
|||
set { m_Range = value; } |
|||
} |
|||
|
|||
public double Accuracy { set; get; } |
|||
} |
|||
public abstract class RootFinder |
|||
{ |
|||
|
|||
protected const string INVALID_RANGE="Invalid range while finding root"; |
|||
protected const string ACCURACY_NOT_REACHED = "The accuracy couldn't be reached with the specified number of iterations"; |
|||
protected const string ROOT_NOT_FOUND = "The algorithm ended without root in the range"; |
|||
protected const string ROOT_NOT_BRACKETED = "The algorithm could not start because the root seemed not to be bracketed"; |
|||
protected const string INVALID_ALGORITHM = "This algorithm is not able to solve this equation"; |
|||
protected const double DOUBLE_ACCURACY = 9.99200722162641E-16; |
|||
private const int DEFAULT_MAX_ITERATIONS = 30; |
|||
private const double DEFAULT_ACCURACY = 1e-8; |
|||
|
|||
//protected int _maxNumIters;
|
|||
//protected double _xmin = double.MinValue;
|
|||
//protected double _xmax = double.MaxValue;
|
|||
//protected double _accuracy;
|
|||
//protected Func<double, double> _func;
|
|||
//protected Func<double, double> m_Of;
|
|||
int _maxNumIters; |
|||
double _xmin = double.MinValue; |
|||
double _xmax = double.MaxValue; |
|||
double _accuracy; |
|||
Func<double, double> _func; |
|||
private double bracketingFactor = 1.6; |
|||
|
|||
|
|||
|
|||
/// <summary>Constructor.</summary>
|
|||
/// <param name="f">A continuous function.</param>
|
|||
public RootFinder() : this(DEFAULT_MAX_ITERATIONS, DEFAULT_ACCURACY) |
|||
{ |
|||
} |
|||
|
|||
public RootFinder(int numIters, double accuracy) |
|||
{ |
|||
_maxNumIters = numIters; |
|||
_accuracy = accuracy; |
|||
} |
|||
|
|||
#region Properties
|
|||
protected double XMin { get { return _xmin; } } |
|||
protected double XMax { get { return _xmax; } } |
|||
|
|||
public Func<double, double> Func |
|||
{ |
|||
get { return _func; } |
|||
set { _func = value; } |
|||
} |
|||
public double BracketingFactor |
|||
{ |
|||
get { return bracketingFactor; } |
|||
set |
|||
{ |
|||
if (value <= 0.0) throw new ArgumentOutOfRangeException(); |
|||
bracketingFactor = value; |
|||
} |
|||
} |
|||
public int Iterations |
|||
{ |
|||
set |
|||
{ |
|||
if (value <= 0) throw new ArgumentOutOfRangeException(); |
|||
_maxNumIters = value; |
|||
} |
|||
protected get { return _maxNumIters; } |
|||
} |
|||
public double Accuracy |
|||
{ |
|||
get { return _accuracy; } |
|||
set { _accuracy = value; } |
|||
} |
|||
#endregion Properties
|
|||
|
|||
/// <summary>Detect a range containing at least one root.</summary>
|
|||
/// <param name="xmin">Lower value of the range.</param>
|
|||
/// <param name="xmax">Upper value of the range</param>
|
|||
/// <param name="factor">The growing factor of research. Usually 1.6.</param>
|
|||
/// <returns>True if the bracketing operation succeeded, else otherwise.</returns>
|
|||
/// <remarks>This iterative methods stops when two values with opposite signs are found.</remarks>
|
|||
public bool SearchBracketsOutward(ref double xmin, ref double xmax, double factor) |
|||
{ |
|||
if (xmin >= xmax) |
|||
{ |
|||
throw new RootFinderException(INVALID_RANGE, 0, new Range(xmin, xmax), 0.0); |
|||
} |
|||
|
|||
double fmin = _func(xmin); |
|||
double fmax = _func(xmax); |
|||
|
|||
int i = 0; |
|||
while (i++ < _maxNumIters) |
|||
{ |
|||
if (Math.Sign(fmin) != Math.Sign(fmax)) return true; |
|||
if (Math.Abs(fmin) < Math.Abs(fmax)) |
|||
{ |
|||
xmin += factor * (xmin - xmax); |
|||
fmin = _func(xmin); |
|||
} |
|||
else |
|||
{ |
|||
xmax += factor * (xmax - xmin); |
|||
fmax = _func(xmax); |
|||
} |
|||
} |
|||
|
|||
throw new RootFinderException(ROOT_NOT_FOUND, i, new Range(fmin, fmax), 0.0); |
|||
} |
|||
|
|||
/// <summary>Prototype algorithm for solving the equation f(x)=0.</summary>
|
|||
/// <param name="x1">The low value of the range where the root is supposed to be.</param>
|
|||
/// <param name="x2">The high value of the range where the root is supposed to be.</param>
|
|||
/// <returns>Returns the root with the specified accuracy.</returns>
|
|||
public virtual double Solve(double x1, double x2) |
|||
{ |
|||
_xmin = x1; |
|||
_xmax = x2; |
|||
return Find(); |
|||
} |
|||
|
|||
protected abstract double Find(); |
|||
|
|||
/// <summary>Helper method useful for preventing rounding errors.</summary>
|
|||
/// <returns>a*sign(b)</returns>
|
|||
protected static double Sign(double a, double b) |
|||
{ |
|||
return b >= 0 ? (a >= 0 ? a : -a) : (a >= 0 ? -a : a); |
|||
} |
|||
|
|||
protected static bool Close(double d1, double d2) |
|||
{ |
|||
return Math.Abs(d1 - d2) <= double.Epsilon; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
namespace MathNet.Numerics.RootFinders |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using MbUnit.Framework; |
|||
using Gallio.Framework; |
|||
using SuntrustPortfolio.Numerics; |
|||
|
|||
[TestFixture] |
|||
public class RootFinderTest |
|||
{ |
|||
BrentRootFinder _solver = new BrentRootFinder(100, 1e-14); |
|||
|
|||
[Test] |
|||
public void MultipleRoots() |
|||
{ |
|||
Func<double, double> f = (x) => { return x * x - 4; }; |
|||
_solver.Func = f; |
|||
double root = _solver.Solve(-5, 5); |
|||
|
|||
Assert.AreEqual(0, f(root)); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue