/// <summary> /// Performs adaptive Gauss-Kronrod quadrature on function f over the range (a,b) /// </summary> /// <param name="f">The analytic smooth function to integrate</param> /// <param name="intervalBegin">Where the interval starts</param> /// <param name="intervalEnd">Where the interval stops</param> /// <param name="error">The difference between the (N-1)/2 point Gauss approximation and the N-point Gauss-Kronrod approximation</param> /// <param name="L1Norm">The L1 norm of the result, if there is a significant difference between this and the returned value, then the result is likely to be ill-conditioned.</param> /// <param name="targetRelativeError">The maximum relative error in the result</param> /// <param name="maximumDepth">The maximum number of interval splittings permitted before stopping</param> /// <param name="order">The number of Gauss-Kronrod points. Pre-computed for 15, 21, 31, 41, 51 and 61 points</param> public static double Integrate(Func <double, double> f, double intervalBegin, double intervalEnd, out double error, out double L1Norm, double targetRelativeError = 1E-10, int maximumDepth = 15, int order = 15) { // Formula used for variable subsitution from // 1. Shampine, L. F. (2008). Vectorized adaptive quadrature in MATLAB. Journal of Computational and Applied Mathematics, 211(2), 131-140. // 2. quadgk.m, GNU Octave if (f == null) { throw new ArgumentNullException(nameof(f)); } if (intervalBegin > intervalEnd) { return(-Integrate(f, intervalEnd, intervalBegin, out error, out L1Norm, targetRelativeError, maximumDepth, order)); } GaussPointPair gaussKronrodPoint = GaussKronrodPointFactory.GetGaussPoint(order); // (-oo, oo) => [-1, 1] // // integral_(-oo)^(oo) f(x) dx = integral_(-1)^(1) f(g(t)) g'(t) dt // g(t) = t / (1 - t^2) // g'(t) = (1 + t^2) / (1 - t^2)^2 if ((intervalBegin < double.MinValue) && (intervalEnd > double.MaxValue)) { Func <double, double> u = (t) => f(t / (1 - t * t)) * (1 + t * t) / ((1 - t * t) * (1 - t * t)); return(recursive_adaptive_integrate(u, -1, 1, maximumDepth, targetRelativeError, 0, out error, out L1Norm, gaussKronrodPoint)); } // [a, oo) => [0, 1] // // integral_(a)^(oo) f(x) dx = integral_(0)^(oo) f(a + t^2) 2 t dt // = integral_(0)^(1) f(a + g(s)^2) 2 g(s) g'(s) ds // g(s) = s / (1 - s) // g'(s) = 1 / (1 - s)^2 else if (intervalEnd > double.MaxValue) { Func <double, double> u = (s) => 2 * s * f(intervalBegin + (s / (1 - s)) * (s / (1 - s))) / ((1 - s) * (1 - s) * (1 - s)); return(recursive_adaptive_integrate(u, 0, 1, maximumDepth, targetRelativeError, 0, out error, out L1Norm, gaussKronrodPoint)); } // (-oo, b] => [-1, 0] // // integral_(-oo)^(b) f(x) dx = -integral_(-oo)^(0) f(b - t^2) 2 t dt // = -integral_(-1)^(0) f(b - g(s)^2) 2 g(s) g'(s) ds // g(s) = s / (1 + s) // g'(s) = 1 / (1 + s)^2 else if (intervalBegin < double.MinValue) { Func <double, double> u = (s) => - 2 * s * f(intervalEnd - s / (1 + s) * (s / (1 + s))) / ((1 + s) * (1 + s) * (1 + s)); return(recursive_adaptive_integrate(u, -1, 0, maximumDepth, targetRelativeError, 0, out error, out L1Norm, gaussKronrodPoint)); } // [a, b] => [-1, 1] // // integral_(a)^(b) f(x) dx = integral_(-1)^(1) f(g(t)) g'(t) dt // g(t) = (b - a) * t * (3 - t^2) / 4 + (b + a) / 2 // g'(t) = 3 / 4 * (b - a) * (1 - t^2) else { Func <double, double> u = (t) => f((intervalEnd - intervalBegin) / 4 * t * (3 - t * t) + (intervalEnd + intervalBegin) / 2) * 3 * (intervalEnd - intervalBegin) / 4 * (1 - t * t); return(recursive_adaptive_integrate(u, -1, 1, maximumDepth, targetRelativeError, 0d, out error, out L1Norm, gaussKronrodPoint)); } }
public GaussKronrodRule(int order) { _gaussKronrodPoint = GaussKronrodPointFactory.GetGaussPoint(order); }