/// <summary> /// Modified Bessel function of the first kind /// </summary> /// <param name="a">Order parameter. Any real number except a negative integer.</param> /// <param name="x">Argument of the Bessel function. Non-negative real number.</param> /// <remarks> /// Reference: /// "A short note on parameter approximation for von Mises-Fisher distributions, And a fast implementation of Is(x)" /// Suvrit Sra /// Computational Statistics, 2011 /// http://people.kyb.tuebingen.mpg.de/suvrit/papers/vmfFinal.pdf /// </remarks> /// <returns>BesselI(a,x)</returns> public static double BesselI(double a, double x) { if (x < 0) { throw new ArgumentException("x (" + x + ") cannot be negative"); } if (a < 0 && a == Math.Floor(a)) { throw new ArgumentException("Order parameter a=" + a + " cannot be a negative integer"); } // http://functions.wolfram.com/Bessel-TypeFunctions/BesselI/02/ double xh = 0.5 * x; double term = Math.Pow(xh, a) / MMath.Gamma(a + 1); double xh2 = xh * xh; double sum = 0; for (int k = 0; k < 1000; k++) { double oldsum = sum; sum += term; term *= xh2 / ((k + 1 + a) * (k + 1)); if (AreEqual(oldsum, sum)) { break; } } return(sum); }
/// <summary> /// Computes <c>1/Gamma(x+1) - 1</c> to high accuracy /// </summary> /// <param name="x">A real number >= 0</param> /// <returns></returns> public static double ReciprocalFactorialMinus1(double x) { if (x > 0.3) { return(1 / MMath.Gamma(x + 1) - 1); } double sum = 0; double term = x; for (int i = 0; i < reciprocalFactorialMinus1Coeffs.Length; i++) { sum += reciprocalFactorialMinus1Coeffs[i] * term; term *= x; } return(sum); }
/// <summary> /// Compute the regularized upper incomplete Gamma function by a series expansion /// </summary> /// <param name="a">The shape parameter, > 0</param> /// <param name="x">The lower bound of the integral, >= 0</param> /// <returns></returns> private static double GammaUpperSeries(double a, double x) { // this series should only be applied when x is small // the series is: 1 - x^a sum_{k=0}^inf (-x)^k /(k! Gamma(a+k+1)) // = (1 - 1/Gamma(a+1)) + (1 - x^a)/Gamma(a+1) - x^a sum_{k=1}^inf (-x)^k/(k! Gamma(a+k+1)) double xaMinus1 = MMath.ExpMinus1(a * Math.Log(x)); double aReciprocalFactorial, aReciprocalFactorialMinus1; if (a > 0.3) { aReciprocalFactorial = 1 / MMath.Gamma(a + 1); aReciprocalFactorialMinus1 = aReciprocalFactorial - 1; } else { aReciprocalFactorialMinus1 = ReciprocalFactorialMinus1(a); aReciprocalFactorial = 1 + aReciprocalFactorialMinus1; } // offset = 1 - x^a/Gamma(a+1) double offset = -xaMinus1 * aReciprocalFactorial - aReciprocalFactorialMinus1; double scale = 1 - offset; double term = x / (a + 1) * a; double sum = term; for (int i = 1; i < 1000; i++) { term *= -(a + i) * x / ((a + i + 1) * (i + 1)); double sumOld = sum; sum += term; //Console.WriteLine("{0}: {1}", i, sum); if (AreEqual(sum, sumOld)) { return(scale * sum + offset); } } throw new Exception(string.Format("GammaUpperSeries not converging for a={0} x={1}", a, x)); }