Click here to Skip to main content
15,881,173 members
Articles / Programming Languages / C#

Functional Programming in Numerical Integration

Rate me:
Please Sign up or sign in to vote.
4.67/5 (4 votes)
4 Jun 2014CPOL5 min read 20K   318   24  
The article depicts usage of functional programming for creating indefinite integral in form Func from delegate Func.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FunctionalProgramming
{
    //generally inspired by
    //http://www.codeproject.com/Articles/375166/Functional-programming-in-Csharp
    //and reaction to
    //http://www.codeproject.com/Articles/95073/Using-Nested-Lambda-Functions-to-Implement-Numeric
    //and
    //http://www.codeproject.com/Articles/334308/Numerical-Integration-with-Simpsons-Rule

    public static class Extensions
    {
        /// <summary>
        /// limit for numerical computation
        /// </summary>
        public static double eps = 1e-16;

        /// <summary>
        /// Numerical antiderivation (integral computation)
        /// <para/><see cref="http://en.wikipedia.org/wiki/Antiderivative"/> 
        /// <para/><see cref="https://www.khanacademy.org/math/integral-calculus/indefinite-definite-integrals/indefinite_integrals/v/antiderivatives-and-indefinite-integrals"/>
        /// <para/><see cref="http://www-math.mit.edu/~djk/calculus_beginners/chapter16/section01.html"/>
        /// </summary>
        /// <example>
        /// <code>Func&lt;double, double&gt; sin = Antiderivative(cos, 0, 0, 0.001);</code>
        /// </example>
        /// <param name="df">derivation</param>
        /// <param name="x0">start point</param>
        /// <param name="f0">start value f0 = f(x0)</param>
        /// <param name="stepSize">step size</param>
        /// <returns>integral of input function</returns>
        public static Func<double, double> Antiderivative(this Func<double, double> df, double x0, double f0, double stepSize)
        {
            //save values - state for function which will be returned
            double currentx = x0;
            double currentf = f0;
            double currentdf = df(x0);

            //create function
            return (x) =>
            {
                //not implemented for left values, can be done in future
                if (x < x0)
                    throw new ArgumentException("Cannot compute");

                //test relation between currentx (state value) and wanted x | f(x)
                if (currentx > x)
                {
                    //reset state if needed
                    //this can be improved by values caching this implementation is simplified
                    currentx = x0;
                    currentf = f0;
                    currentdf = df(x0);
                }

                double newLimit = x - stepSize;
                //make loop till the distance is less than deltax
                while (currentx < newLimit)
                {
                    //*
                    double k1 = stepSize * currentdf; //it is same as k1 = deltax * df(currentx);
                    double k2 = stepSize * df(currentx + 0.5 * stepSize);
                    double k3 = k2; //it is same as k3 = deltax * df(currentx + 0.5 * deltax);
                    currentdf = df(currentx + stepSize);
                    double k4 = stepSize * currentdf;
                    currentf = currentf + 1 / 6.0 * (k1 + 2 * k2 + 2 * k3 + k4);
                    //*/

                    currentx = currentx + stepSize;
                }

                //now compute last step, if it is needed
                double lastStepSize = x - currentx;
                if (lastStepSize > eps)
                {
                    //*
                    double k1 = lastStepSize * currentdf; //it is same as k1 = deltax * df(currentx);
                    double k2 = lastStepSize * df(currentx + 0.5 * lastStepSize);
                    double k3 = k2; //it is same as k3 = deltax * df(currentx + 0.5 * deltax);
                    currentdf = df(currentx + lastStepSize);
                    double k4 = lastStepSize * currentdf;
                    currentf = currentf + 1 / 6.0 * (k1 + 2 * k2 + 2 * k3 + k4);
                    //*/

                    //currentf = currentf + lastStepSize * df(currentx);
                    currentx = x;
                }

                //and return value, store it for future use
                return currentf;
            };
        }

        /// <summary>
        /// Numerical antiderivation (integral computation)
        /// <para/><see cref="http://en.wikipedia.org/wiki/Antiderivative"/> 
        /// <para/><see cref="https://www.khanacademy.org/math/integral-calculus/indefinite-definite-integrals/indefinite_integrals/v/antiderivatives-and-indefinite-integrals"/>
        /// <para/><see cref="http://www-math.mit.edu/~djk/calculus_beginners/chapter16/section01.html"/>
        /// </summary>
        /// <example>
        /// <code>Func&lt;double, double&gt; sin = Antiderivative(cos, 0, 0, 0.001);</code>
        /// </example>
        /// <param name="df">derivation</param>
        /// <param name="x0">start point</param>
        /// <param name="f0">start value f0 = f(x0)</param>
        /// <param name="stepSize">step size</param>
        /// <returns>integral of input function</returns>
        public static Func<double, double> AntiderivativePure(this Func<double, double> df, double x0, double f0, double stepSize)
        {

            //create function
            return (x) =>
            {
                //not implemented for left values, can be done in future
                if (x < x0)
                    throw new ArgumentException("Cannot compute");

                double currentx = x0;
                double currentf = f0;
                double currentdf = df(x0);

                double newLimit = x - stepSize;
                //make loop till the distance is less than deltax
                while (currentx < newLimit)
                {
                    //*
                    double k1 = stepSize * currentdf; //it is same as k1 = deltax * df(currentx);
                    double k2 = stepSize * df(currentx + 0.5 * stepSize);
                    double k3 = k2; //it is same as k3 = deltax * df(currentx + 0.5 * deltax);
                    currentdf = df(currentx + stepSize);
                    double k4 = stepSize * currentdf;
                    currentf = currentf + 1 / 6.0 * (k1 + 2 * k2 + 2 * k3 + k4);
                    //*/

                    currentx = currentx + stepSize;
                }

                //now compute last step, if it is needed
                double lastStepSize = x - currentx;
                if (lastStepSize > eps)
                {
                    //*
                    double k1 = lastStepSize * currentdf; //it is same as k1 = deltax * df(currentx);
                    double k2 = lastStepSize * df(currentx + 0.5 * lastStepSize);
                    double k3 = k2; //it is same as k3 = deltax * df(currentx + 0.5 * deltax);
                    currentdf = df(currentx + lastStepSize);
                    double k4 = lastStepSize * currentdf;
                    currentf = currentf + 1 / 6.0 * (k1 + 2 * k2 + 2 * k3 + k4);
                    //*/

                    //currentf = currentf + lastStepSize * df(currentx);
                    currentx = x;
                }

                //and return value, store it for future use
                return currentf;
            };
        }

        /// <summary>
        /// Numerical antiderivation (integral computation)
        /// <para/><see cref="http://en.wikipedia.org/wiki/Antiderivative"/> 
        /// <para/><see cref="https://www.khanacademy.org/math/integral-calculus/indefinite-definite-integrals/indefinite_integrals/v/antiderivatives-and-indefinite-integrals"/>
        /// <para/><see cref="http://www-math.mit.edu/~djk/calculus_beginners/chapter16/section01.html"/>
        /// </summary>
        /// <example>
        /// <code>Func&lt;double, double&gt; sin = Antiderivative(cos, 0, 0, 0.001);</code>
        /// </example>
        /// <param name="df">derivation</param>
        /// <param name="x0">start point</param>
        /// <param name="f0">start value f0 = f(x0)</param>
        /// <param name="stepSize">step size</param>
        /// <returns>integral of input function</returns>
        public static Func<double, double> AntiderivativeRecursive(this Func<double, double> df, double x0, double f0, double stepSize)
        {

            //create function
            return (x) =>
            {
                //not implemented for left values, can be done in future
                if (x < x0)
                    throw new ArgumentException("Cannot compute");

                if (x - x0 < stepSize)
                {
                    double cstep = x - x0;
                    double k1 = cstep * df(x0); 
                    double k2 = cstep * df(x0 + 0.5 * cstep);
                    double k3 = k2; //it is same as k3 = cstep * df(x0 + 0.5 * cstep);
                    double k4 = cstep * df(x0 + cstep);
                    double currentf = f0 + 1 / 6.0 * (k1 + 2 * k2 + 2 * k3 + k4);

                    return currentf;
                }
                else
                {
                    double k1 = stepSize * df(x - stepSize); 
                    double k2 = stepSize * df(x - 0.5 * stepSize);
                    double k3 = k2; //it is same as k3 = stepSize * df(x - 0.5 * stepSize);
                    double k4 = stepSize * df(x);
                    double delta = 1 / 6.0 * (k1 + 2 * k2 + 2 * k3 + k4);

                    return df.AntiderivativeRecursive(x0, f0, stepSize)(x - stepSize) + delta;
                }

            };
        }

        /// <summary>
        /// Numerical antiderivation (integral computation)
        /// <para/><see cref="http://en.wikipedia.org/wiki/Antiderivative"/> 
        /// <para/><see cref="https://www.khanacademy.org/math/integral-calculus/indefinite-definite-integrals/indefinite_integrals/v/antiderivatives-and-indefinite-integrals"/>
        /// <para/><see cref="http://www-math.mit.edu/~djk/calculus_beginners/chapter16/section01.html"/>
        /// </summary>
        /// <example>
        /// <code>Func&lt;double, double&gt; sin = Antiderivative(cos, 0, 0, 0.001);</code>
        /// </example>
        /// <param name="df">derivation</param>
        /// <param name="x0">start point</param>
        /// <param name="f0">start value f0 = f(x0)</param>
        /// <param name="stepSize">step size</param>
        /// <returns>integral of input function</returns>
        public static Func<double, double> AntiderivativeEx(this Func<double, double> df, double x0, double f0, double stepSize)
        {
            return (x) => df.AntiderivativeExH(x0, f0, stepSize)(x, df(x));
        }

        private static Func<double, double, double> AntiderivativeExH(this Func<double, double> df, double x0, double f0, double stepSize)
        {
            //create function
            return (x, dfx) =>
            {
                //not implemented for left values, can be done in future
                if (x < x0)
                    throw new ArgumentException("Cannot compute");

                if (x - x0 < stepSize)
                {
                    double cstep = x - x0;
                    double k1 = cstep * df(x0);
                    double k2 = cstep * df(x0 + 0.5 * cstep);
                    double k3 = k2; //it is same as k3 = cstep * df(x0 + 0.5 * cstep);
                    double k4 = cstep * dfx; //it is same as k4 = cstep * df(x0 + cstep);
                    double currentf = f0 + 1 / 6.0 * (k1 + 2 * k2 + 2 * k3 + k4);

                    return currentf;
                }
                else
                {
                    double dfxmh = df(x - stepSize);
                    double k1 = stepSize * dfxmh;
                    double k2 = stepSize * df(x - 0.5 * stepSize);
                    double k3 = k2; //it is same as k3 = stepSize * df(x - 0.5 * stepSize);
                    double k4 = stepSize * dfx;
                    double delta = 1 / 6.0 * (k1 + 2 * k2 + 2 * k3 + k4);

                    return df.AntiderivativeExH(x0, f0, stepSize)(x - stepSize, dfxmh) + delta;
                }

            };
        }

        /// <summary>
        /// Takes function and evals f(to) - f(from)
        /// </summary>
        /// <param name="f"></param>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public static double IntervalEval(this Func<double, double> f, double from, double to)
        {
            double l = f(from);
            double h = f(to);
            return h - l;
            //abit better than
            //return f(to) - f(from);
            //especially in usage with Antiderivative function
        }

        /// <summary>
        /// Calculates integral of f over interval where a is low bound and b is high bound
        /// <see cref="http://en.wikipedia.org/wiki/Integral"/>
        /// </summary>
        /// <param name="f">function to integrate</param>
        /// <param name="a">low bound</param>
        /// <param name="b">high bound</param>
        /// <param name="deltax">step size</param>
        /// <returns>Integral value</returns>
        public static double Integral(this Func<double, double> f, double a, double b, double deltax)
        {
            return f.Antiderivative(a, 0, deltax).IntervalEval(a, b);
        }

        /// <summary>
        /// Calculates 2D integral of f over shape defined by lowx, highx, lowy(x) and highy(x) bounds.
        /// </summary>
        /// <param name="fxy">function to integrate</param>
        /// <param name="lowX">x low bound</param>
        /// <param name="highX">x high bound</param>
        /// <param name="lowY">y(x) func low bound</param>
        /// <param name="highY">y(x) func high bound</param>
        /// <param name="stepSize">step size</param>
        /// <returns>Integral value</returns>
        public static double Integral(this Func<double, double, double> fxy, double lowX, double highX, Func<double, double> lowY, Func<double, double> highY, double stepSize)
        {
            Func<double, double> helperFunc = (cx) => fxy.FixateX(cx).Integral(lowY(cx), highY(cx), stepSize);
            return helperFunc.Integral(lowX, highX, stepSize);
        }

        /// <summary>
        /// Calculates 3D integral of f over volume defined by lowx, highx, lowy(x), highy(x), lowz(x, y) and highz(x, y) bounds.
        /// </summary>
        /// <param name="fxyz">function to integrate</param>
        /// <param name="lowX">x low bound</param>
        /// <param name="highX">x high bound</param>
        /// <param name="lowY">y(x) func low bound</param>
        /// <param name="highY">y(x) func high bound</param>
        /// <param name="lowZ">z(x, y) func low bound</param>
        /// <param name="highZ">z(x, y) func high bound</param>
        /// <param name="stepSize">step size</param>
        /// <returns>Integral value</returns>
        public static double Integral(this Func<double, double, double, double> fxyz,
            double lowX, double highX, Func<double, double> lowY, Func<double, double> highY, Func<double, double, double> lowZ, Func<double, double, double> highZ, double stepSize)
        {
            Func<double, double> helperFunc = (cx) => fxyz.FixateX(cx).Integral(lowY(cx), highY(cx), lowZ.FixateX(cx), highZ.FixateX(cx), stepSize);
            return helperFunc.Integral(lowX, highX, stepSize);
        }

        /// <summary>
        /// Create function f(y) from f(x, y) in form f(const x, y)
        /// </summary>
        /// <param name="fxy">function f(x, y)</param>
        /// <param name="x">const x</param>
        /// <returns>function f(y)</returns>
        public static Func<double, double> FixateX(this Func<double, double, double> fxy, double x)
        {
            return (y) => fxy(x, y);
        }

        /// <summary>
        /// Create function f(y, z) from f(x, y, z) in form f(const x, y, z)
        /// </summary>
        /// <param name="fxy">function f(x, y, z)</param>
        /// <param name="x">const x</param>
        /// <returns>function f(y, z)</returns>
        public static Func<double, double, double> FixateX(this Func<double, double, double, double> fxyz, double x)
        {
            return (y, z) => fxyz(x, y, z);
        }

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Czech Republic Czech Republic
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions