public static double SolveB(double a, double x, double p, double q, double guess, double step, double lower, double upper) { Func <double, double> f; if (p <= q) { f = b => Math2.Ibeta(a, b, x) - p; } else { f = b => Math2.Ibetac(a, b, x) - q; } #if EXTRA_DEBUG Debug.WriteLine("IBetaInvB(a={0}, guess?={1}, x={2}) == p({3}), q({4}); range = [{5}, {6}]", a, guess, x, p, q, lower, upper); #endif RootResults r = RootFinder.Toms748Bracket(f, guess, step, FunctionShape.Unknown, lower, upper, _tol, Policies.MaxRootIterations); if (r == null) { Policies.ReportRootNotFoundError("Invalid parameter in root solver"); return(double.NaN); } if (!r.Success) { Policies.ReportRootNotFoundError("Root not found after {0} iterations", r.Iterations); return(double.NaN); } #if EXTRA_DEBUG Debug.WriteLine("Result = {0}; Toms748 iterations: {1}", r.SolutionX, r.Iterations); #endif return(r.SolutionX); }
/// <summary> /// Use the root finder to solve Q(a,x) == q, for x /// </summary> /// <param name="a"></param> /// <param name="q"></param> /// <param name="guess"></param> /// <param name="min"></param> /// <param name="max"></param> /// <returns></returns> public static double SolveGivenAQ(double a, double q, double guess, double min, double max) { Func <double, ValueTuple <double, double, double> > halleyF = (double x) => { // Calculate Q(x) - q and the first two derivatives double f = Math2.GammaQ(a, x) - q; double f1 = -Math2.GammaPDerivative(a, x); double f2 = -f1 * (1.0 + (1.0 - a) / x); return(f, f1, f2); }; const double relTolerance = RootFinder.DefaultRelTolerance; const double absTolerance = 0; RootResults rr = RootFinder.Halley(halleyF, guess, min, max, relTolerance, absTolerance, Policies.MaxRootIterations); if (rr == null) { Policies.ReportRootNotFoundError("Invalid parameter in root solver"); return(double.NaN); } #if EXTRA_DEBUG Debug.WriteLine("Halley iterations: {0}", rr.Iterations); #endif if (!rr.Success) { Policies.ReportRootNotFoundError("Root not found after {0} iterations", rr.Iterations); return(double.NaN); } return(rr.SolutionX); }
public static (double Result, int Iterations) SolveA(double z, double p, double q, double aGuess, double step, double min, double max) { Debug.Assert(aGuess >= min && aGuess <= max, "Guess out of range"); Func <double, double> f; if (p < q) { f = a => Math2.GammaP(a, z) - p; } else { f = a => Math2.GammaQ(a, z) - q; } // // Use our generic derivative-free root finding procedure. // We could use Newton steps here, taking the PDF of the // Poisson distribution as our derivative, but that's // even worse performance-wise than the generic method :-( // // dQ/da is increasing, dP/da is decreasing FunctionShape fShape = (p < q) ? FunctionShape.Decreasing : FunctionShape.Increasing; RootResults rr = RootFinder.Toms748Bracket(f, aGuess, step, fShape, min, max); if (rr == null) { Policies.ReportRootNotFoundError($"Invalid Parameter: PQInvA(z: {z}, p: {p}, q: {q}) [{min}, {max}] g: {aGuess}"); return(double.NaN, 0); } if (!rr.Success) { Policies.ReportRootNotFoundError("Root not found after {0} iterations", rr.Iterations); return(double.NaN, rr.Iterations); } return(rr.SolutionX, rr.Iterations); }