//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
//
// Copyright (c) 2009-2014 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
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// 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
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using IStation.Numerics.RootFinding;
using Complex = System.Numerics.Complex;
namespace IStation.Numerics
{
public static class FindRoots
{
/// Find a solution of the equation f(x)=0.
/// The function to find roots from.
/// The low value of the range where the root is supposed to be.
/// The high value of the range where the root is supposed to be.
/// Desired accuracy. The root will be refined until the accuracy or the maximum number of iterations is reached. Example: 1e-14.
/// Maximum number of iterations. Example: 100.
public static double OfFunction(Func f, double lowerBound, double upperBound, double accuracy = 1e-8, int maxIterations = 100)
{
if (!ZeroCrossingBracketing.ExpandReduce(f, ref lowerBound, ref upperBound, 1.6, maxIterations, maxIterations*10))
{
throw new NonConvergenceException("The algorithm has failed, exceeded the number of iterations allowed or there is no root within the provided bounds.");
}
if (Brent.TryFindRoot(f, lowerBound, upperBound, accuracy, maxIterations, out var root))
{
return root;
}
if (Bisection.TryFindRoot(f, lowerBound, upperBound, accuracy, maxIterations, out root))
{
return root;
}
throw new NonConvergenceException("The algorithm has failed, exceeded the number of iterations allowed or there is no root within the provided bounds.");
}
/// Find a solution of the equation f(x)=0.
/// The function to find roots from.
/// The first derivative of the function to find roots from.
/// The low value of the range where the root is supposed to be.
/// The high value of the range where the root is supposed to be.
/// Desired accuracy. The root will be refined until the accuracy or the maximum number of iterations is reached. Example: 1e-14.
/// Maximum number of iterations. Example: 100.
public static double OfFunctionDerivative(Func f, Func df, double lowerBound, double upperBound, double accuracy = 1e-8, int maxIterations = 100)
{
double root;
if (RobustNewtonRaphson.TryFindRoot(f, df, lowerBound, upperBound, accuracy, maxIterations, 20, out root))
{
return root;
}
return OfFunction(f, lowerBound, upperBound, accuracy, maxIterations);
}
///
/// Find both complex roots of the quadratic equation c + b*x + a*x^2 = 0.
/// Note the special coefficient order ascending by exponent (consistent with polynomials).
///
public static Tuple Quadratic(double c, double b, double a)
{
if (b == 0d)
{
var t = new Complex(-c/a, 0d).SquareRoot();
return new Tuple(t, -t);
}
var q = b > 0d
? -0.5*(b + new Complex(b*b - 4*a*c, 0d).SquareRoot())
: -0.5*(b - new Complex(b*b - 4*a*c, 0d).SquareRoot());
return new Tuple(q/a, c/q);
}
///
/// Find all three complex roots of the cubic equation d + c*x + b*x^2 + a*x^3 = 0.
/// Note the special coefficient order ascending by exponent (consistent with polynomials).
///
public static Tuple Cubic(double d, double c, double b, double a)
{
return RootFinding.Cubic.Roots(d, c, b, a);
}
///
/// Find all roots of a polynomial by calculating the characteristic polynomial of the companion matrix
///
/// The coefficients of the polynomial in ascending order, e.g. new double[] {5, 0, 2} = "5 + 0 x^1 + 2 x^2"
/// The roots of the polynomial
public static Complex[] Polynomial(double[] coefficients)
{
return new Polynomial(coefficients).Roots();
}
///
/// Find all roots of a polynomial by calculating the characteristic polynomial of the companion matrix
///
/// The polynomial.
/// The roots of the polynomial
public static Complex[] Polynomial(Polynomial polynomial)
{
return polynomial.Roots();
}
///
/// Find all roots of the Chebychev polynomial of the first kind.
///
/// The polynomial order and therefore the number of roots.
/// The real domain interval begin where to start sampling.
/// The real domain interval end where to stop sampling.
/// Samples in [a,b] at (b+a)/2+(b-1)/2*cos(pi*(2i-1)/(2n))
public static double[] ChebychevPolynomialFirstKind(int degree, double intervalBegin = -1d, double intervalEnd = 1d)
{
if (degree < 1)
{
return new double[0];
}
// transform to map to [-1..1] interval
double location = 0.5*(intervalBegin + intervalEnd);
double scale = 0.5*(intervalEnd - intervalBegin);
// evaluate first kind chebychev nodes
double angleFactor = Constants.Pi/(2*degree);
var samples = new double[degree];
for (int i = 0; i < samples.Length; i++)
{
samples[i] = location + scale*Math.Cos(((2*i) + 1)*angleFactor);
}
return samples;
}
///
/// Find all roots of the Chebychev polynomial of the second kind.
///
/// The polynomial order and therefore the number of roots.
/// The real domain interval begin where to start sampling.
/// The real domain interval end where to stop sampling.
/// Samples in [a,b] at (b+a)/2+(b-1)/2*cos(pi*i/(n-1))
public static double[] ChebychevPolynomialSecondKind(int degree, double intervalBegin = -1d, double intervalEnd = 1d)
{
if (degree < 1)
{
return new double[0];
}
// transform to map to [-1..1] interval
double location = 0.5*(intervalBegin + intervalEnd);
double scale = 0.5*(intervalEnd - intervalBegin);
// evaluate second kind chebychev nodes
double angleFactor = Constants.Pi/(degree + 1);
var samples = new double[degree];
for (int i = 0; i < samples.Length; i++)
{
samples[i] = location + scale*Math.Cos((i + 1)*angleFactor);
}
return samples;
}
}
}