Search code examples
c#math

implement the Gaussian Hypergeometric Function 2F1(a, b, c, z) in C#, with the ability on handling negative and large z values


How can I implement the Gaussian Hypergeometric Function 2F1(a, b, c, z) in C#, with the ability on handling negative and large z values?

2F1 is part of the integral of a certain equation resulting in the following implementation:

internal static double HCWR(double distance, double n)
{
    double geometricResult = 2F1(1, 1 / n, 1 + 1 / n, -Math.Pow(distance, -n));
    return (Math.Pow(distance, -n) * geometricResult);
}

n can be any number bigger or equal to 2 and distance can be any number in the interval (0,1].

Values like a = 1, b = 0.5, c = 1.5 and z = -25.0, which should be 0.27468, aren't handled well in every implementation. CenterSpace refuses every |z| > 1 and MathNet.Numerics returns -infinity, while I prefer an implementation that can handle non-integer values for z.


Solution

  • Credit to Alander for the comment.

    Since in my example the domain n≥2 and distance∈(0,1] is applied, it follows that for

    z=-(distance^(-n)) we have z≤−1.

    Thus, 0<z/(z−1)<1 holds. We can apply the transformation

    2F1(a,b,c,z) = (1-z)^(-a) * 2F1(a, c-b, c, z/(z-1))

    and implement the function in this domain as follows:

    internal static double HCWR(double distance, double n)
    {
        double z = -Math.Pow(distance, -n);
        double c = 1 + 1 / n;
        double b = 1 / n;
        double a = 1;
    
        double factorTransformed = Math.Pow(1 - z, -a);
        double bTransformed = c - b;
        double zTransformed = z / (z - 1);
    
        double geometricResult = factorTransformed * CenterSpace.NMath.Core.SpecialFunctions.Hypergeometric2F1(a, bTransformed, c, zTransformed);
    
        return (Math.Pow(distance, -n) * geometricResult);
    }
    

    resulting in the following final implementation:

    internal static double HCWR(double distance, double n)
    {
        int a = 1;
        int b = 1;
        double c = 1 + 1 / n;
        double z = 1 / (1 + Math.Pow(distance, n));
        return z * CenterSpace.NMath.Core.SpecialFunctions.Hypergeometric2F1(a, b, c, z);
    }