/// <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);
                }
        }
Exemple #2
0
        /// <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);
        }