public static double TgammaImp(double a, double x) { const Routine routine = Routine.Upper; // special values if (x == 0) { return(Math2.Tgamma(a)); } if (double.IsInfinity(x)) { return(0.0); } // Γ(0,x) = E_1(x) if (a == 0) { return(Math2.Expint(1, x)); } if (a == 1.0) { return(Math.Exp(-x)); } // Process small a, large x with asymptotic series, which is faster than CF if (x >= Asym_MinLargeZ(a)) { return(Asym_SeriesLargeZ(a, x) * Prefix(a, x) / x); } if (a == 0.5) { return(Constants.SqrtPI * Math.Exp(-x) * Math2.Erfcx(Math.Sqrt(x))); } // is a small if ((a < 30) && (x >= a + 1) && (x < DoubleLimits.MaxLogValue)) { double frac = a - Math.Floor(a); if (frac == 0) { return(IntegerA(routine, a, x)); } else if (frac == 0.5) { return(HalfIntegerA(routine, a, x)); } } // Use the series if (x < (a + 1)) { if (a < 1) { return(SmallA(routine, a, x)); } return(TgammaMinusLowerSeries(a, x)); } // Use CF for x > a+1 return(Prefix(a, x, UpperFraction(a, x))); }
public static double ImpQ(double z, double p, double q) { Debug.Assert(q <= 0.5); if (q == 0) { return(DoubleLimits.MinNormalValue); } #if EXTRA_DEBUG var gs = new Stack <double>(); #endif // Set outside limits // Since Q(a, z) > 1/2 for a >= z+1, so maxA < z+1 double minA = DoubleLimits.MinNormalValue; double maxA = z + 1; if (maxA == z) { maxA = Math2.FloatNext(z); } double step = 0.5; double aGuess = 0; // We can use the relationship between the incomplete // gamma function and the poisson distribution to // calculate an approximate inverse. // Mostly it is pretty accurate, except when a is small or q is tiny. // Case 1: a <= 1 : Inverse Cornish Fisher is unreliable in this area var e = Math.Exp(-z); if (q <= e) { if (q == e) { return(1); } maxA = 1; // If a <= 1, P(a, x) = 1 - Q(a, x) >= (1-e^-x)^a var den = (z > 0.5) ? Math2.Log1p(-Math.Exp(-z)) : Math.Log(-Math2.Expm1(-z)); var limit = Math2.Log1p(-q) / den; if (limit < 1) { minA = Math.Max(minA, limit); } step = (1 - minA) * 0.125; aGuess = minA * (1 + step); if (minA < 0.20) { // Use a first order approximation Q(a, z) at a=0 // Mathematica: Assuming[ a >= 0 && z > 0, FunctionExpand[Normal[Series[GammaRegularized[a, z], {a, 0, 1}]]]] // q = -a * ExpIntegralEi[-z] // The smaller a is the better the guess // as z->0 term2/term1->1/2*Log[z], which is adequate for double precision aGuess = -q / Math2.Expint(-z); if (aGuess <= DoubleLimits.MachineEpsilon) { ExtraDebugLog(z, p, q, aGuess, aGuess, step, minA, maxA, 0, ""); return(aGuess); } step = aGuess * 0.125; } else if (minA >= 0.40) { // Use a first order approximation Q(a, z) at a=1 // Mathematica: Assuming[ a >= 0 && z > 0, FunctionExpand[Normal[Series[GammaRegularized[a, z], {a, 1, 1}]]]] var d = Math.Exp(-z) * (Math.Log(z) + Constants.EulerMascheroni) + Math2.Expint(1, z); aGuess = (q - Math.Exp(-z)) / d + 1; } aGuess = Math.Max(aGuess, minA); aGuess = Math.Min(aGuess, maxA); var r = SolveA(z, p, q, aGuess, step, minA, maxA); ExtraDebugLog(z, p, q, r.Result, aGuess, step, minA, maxA, r.Iterations, ""); return(r.Result); } // Case 2: a > 1 : Inverse Poisson Corning Fisher approximation improves as a->Infinity minA = 1; aGuess = 0.5 + InversePoissonCornishFisher(z, q, p); if (aGuess > 100) { step = 0.01; } else if (aGuess > 10) { step = 0.01; } else { // our poisson approximation is weak. step = 0.05; } #if EXTRA_DEBUG double og = aGuess; #endif aGuess = Math.Max(aGuess, minA); aGuess = Math.Min(aGuess, maxA); #if EXTRA_DEBUG if (og != aGuess) { gs.Push(og); } #endif // For small values of q, our Inverse Poisson Cornish Fisher guess can be way off // Try to come up with a better guess if (q < DoubleLimits.MachineEpsilon) { step *= 10; if (Math.Abs((aGuess - 1) / z) < 1.0 / 8) { #if EXTRA_DEBUG double old = aGuess; #endif (aGuess, _) = RefineQ_SmallA_LargeZ(z, p, q, aGuess); #if EXTRA_DEBUG if (old != aGuess) { gs.Push(old); } #endif } } var rr = SolveA(z, p, q, aGuess, step, minA, maxA); #if EXTRA_DEBUG if (rr.Iterations > 6) { string guesses = (gs.Count == 0) ? string.Empty : ", og: (" + string.Join(", ", gs) + ")"; ExtraDebugLog(z, p, q, rr.Result, aGuess, step, minA, maxA, rr.Iterations, guesses); } #endif return(rr.Result); }