/// <summary> /// Adaptive approximation of the definite integral by the trapezium rule. /// </summary> /// <param name="f">The analytic smooth function to integrate.</param> /// <param name="intervalBegin">Where the interval starts, inclusive and finite.</param> /// <param name="intervalEnd">Where the interval stops, inclusive and finite.</param> /// <param name="levelAbscissas">Abscissa vector per level provider.</param> /// <param name="levelWeights">Weight vector per level provider.</param> /// <param name="levelOneStep">First Level Step</param> /// <param name="targetRelativeError">The expected relative accuracy of the approximation.</param> /// <returns>Approximation of the finite integral in the given interval.</returns> public static double IntegrateAdaptiveTransformedOdd( Func <double, double> f, double intervalBegin, double intervalEnd, IEnumerable <double[]> levelAbscissas, IEnumerable <double[]> levelWeights, double levelOneStep, double targetRelativeError) { if (f == null) { throw new ArgumentNullException(nameof(f)); } if (levelAbscissas == null) { throw new ArgumentNullException(nameof(levelAbscissas)); } if (levelWeights == null) { throw new ArgumentNullException(nameof(levelWeights)); } double linearSlope = 0.5 * (intervalEnd - intervalBegin); double linearOffset = 0.5 * (intervalEnd + intervalBegin); targetRelativeError /= 5 * linearSlope; using (var abcissasIterator = levelAbscissas.GetEnumerator()) using (var weightsIterator = levelWeights.GetEnumerator()) { double step = levelOneStep; // First Level abcissasIterator.MoveNext(); weightsIterator.MoveNext(); double[] abcissasL1 = abcissasIterator.Current; double[] weightsL1 = weightsIterator.Current; double sum = f(linearOffset) * weightsL1[0]; for (int i = 1; i < abcissasL1.Length; i++) { sum += weightsL1[i] * (f((linearSlope * abcissasL1[i]) + linearOffset) + f(-(linearSlope * abcissasL1[i]) + linearOffset)); } sum *= step; // Additional Levels double previousDelta = double.MaxValue; for (int level = 1; abcissasIterator.MoveNext() && weightsIterator.MoveNext(); level++) { double[] abcissas = abcissasIterator.Current; double[] weights = weightsIterator.Current; double midpointsum = 0; for (int i = 0; i < abcissas.Length; i++) { midpointsum += weights[i] * (f((linearSlope * abcissas[i]) + linearOffset) + f(-(linearSlope * abcissas[i]) + linearOffset)); } midpointsum *= step; sum = 0.5 * (sum + midpointsum); step *= 0.5; double delta = Math.Abs(sum - midpointsum); if (level == 1) { previousDelta = delta; continue; } double r = Math.Log(delta) / Math.Log(previousDelta); previousDelta = delta; if (r > 1.9 && r < 2.1) { // convergence region delta = Math.Sqrt(delta); } if (sum.AlmostEqualNormRelative(midpointsum, delta, targetRelativeError)) { break; } } return(sum * linearSlope); } }
/// <summary>Find a solution of the equation f(x)=0.</summary> /// <param name="f">The function to find roots from.</param> /// <param name="lowerBound">The low value of the range where the root is supposed to be.</param> /// <param name="upperBound">The high value of the range where the root is supposed to be.</param> /// <param name="accuracy">Desired accuracy. The root will be refined until the accuracy or the maximum number of iterations is reached.</param> /// <param name="maxIterations">Maximum number of iterations. Usually 100.</param> /// <param name="root">The root that was found, if any. Undefined if the function returns false.</param> /// <returns>True if a root with the specified accuracy was found, else false.</returns> public static bool TryFindRoot(Func <double, double> f, double lowerBound, double upperBound, double accuracy, int maxIterations, out double root) { double fmin = f(lowerBound); double fmax = f(upperBound); double froot = fmax; double d = 0.0, e = 0.0; root = upperBound; double xMid = double.NaN; // Root must be bracketed. if (Math.Sign(fmin) == Math.Sign(fmax)) { return(false); } for (int i = 0; i <= maxIterations; i++) { // adjust bounds if (Math.Sign(froot) == Math.Sign(fmax)) { upperBound = lowerBound; fmax = fmin; e = d = root - lowerBound; } if (Math.Abs(fmax) < Math.Abs(froot)) { lowerBound = root; root = upperBound; upperBound = lowerBound; fmin = froot; froot = fmax; fmax = fmin; } // convergence check double xAcc1 = 2.0 * Precision.DoublePrecision * Math.Abs(root) + 0.5 * accuracy; double xMidOld = xMid; xMid = (upperBound - root) / 2.0; if (Math.Abs(xMid) <= xAcc1 || froot.AlmostEqualNormRelative(0, froot, accuracy)) { return(true); } if (xMid == xMidOld) { // accuracy not sufficient, but cannot be improved further return(false); } if (Math.Abs(e) >= xAcc1 && Math.Abs(fmin) > Math.Abs(froot)) { // Attempt inverse quadratic interpolation double s = froot / fmin; double p; double q; if (lowerBound.AlmostEqualRelative(upperBound)) { p = 2.0 * xMid * s; q = 1.0 - s; } else { q = fmin / fmax; double r = froot / fmax; p = s * (2.0 * xMid * q * (q - r) - (root - lowerBound) * (r - 1.0)); q = (q - 1.0) * (r - 1.0) * (s - 1.0); } if (p > 0.0) { // Check whether in bounds q = -q; } p = Math.Abs(p); if (2.0 * p < Math.Min(3.0 * xMid * q - Math.Abs(xAcc1 * q), Math.Abs(e * q))) { // Accept interpolation e = d; d = p / q; } else { // Interpolation failed, use bisection d = xMid; e = d; } } else { // Bounds decreasing too slowly, use bisection d = xMid; e = d; } lowerBound = root; fmin = froot; if (Math.Abs(d) > xAcc1) { root += d; } else { root += Sign(xAcc1, xMid); } froot = f(root); } return(false); }