From 70c0a2f0270a4238daf8e6e84d5475b9d03ff4d4 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Fri, 23 Oct 2026 10:56:24 +0000 Subject: [PATCH] Read the incomplete gamma function in the precise evaluation The routes that check their answer at sampled points compare an antiderivative's derivative with the integrand in intervals, doubles first and then forty and eighty digits. Neither tier read gamma(a, z). A point where either side holds one has no value and is skipped, and with fewer than two points left the check fails. So every such route turned away every answer holding a gamma function. The inverse hyperbolic substitution answered x arsinh(a x)^n/sqrt(1 + a^2 x^2) as (gamma(n + 1, u) - u^(n + 1) (-u)^(-(n + 1)) gamma(n + 1, -u))/(2 a^2) with u = arsinh(a x), and then declined its own answer. The forty- and eighty-digit tier now reads it, as it reads the other special functions: the value at the middle of the argument's rectangle, widened by the derivative in z, -z^(a - 1) e^(-z), over the rectangle. Its cut is the negative real axis, except at a whole order a >= 1. The power is the principal one, as that tier's powers are. An order known only to its last place, as 1/3 is, is taken at both ends, and the value is the hull of the two. On Rubi's 7.1.4 and 7.2.4 that is the files' last 27 problems, all at a symbolic power: 610 of 637 solved before, 637 now, 0 wrong. Tests: the precise interval holds gamma(a, z)'s value, a derivative keeping one is checked, and three of the integrals differentiate back. Part of #2075. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../AngouriMath/Numerics/PreciseEvaluation.cs | 28 +++++++++++++++++++ ...BesideAnExponentialOfAPowerIntegralTest.cs | 11 ++++++++ .../UnitTests/Core/IntervalEvaluationTest.cs | 10 ++++++- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Sources/AngouriMath/Numerics/PreciseEvaluation.cs b/Sources/AngouriMath/Numerics/PreciseEvaluation.cs index 2eb168b50..1674c0bc0 100644 --- a/Sources/AngouriMath/Numerics/PreciseEvaluation.cs +++ b/Sources/AngouriMath/Numerics/PreciseEvaluation.cs @@ -545,6 +545,34 @@ private PreciseComplexInterval EvaluateOtherThanArithmetic(Entity expr) return BySlope(Evaluate(argument), Number.Shi, z => Divide(Half(Subtract(Exp(z), Exp(Negate(z)))), z), cutEnd: null); case Chif(var argument): return BySlope(Evaluate(argument), Number.Chi, z => Divide(Half(Add(Exp(z), Exp(Negate(z)))), z), cutEnd: EDecimal.Zero); + // The upper incomplete gamma function of a constant order, through its derivative in z, + // -z^(a - 1) e^(-z), jumping across the negative real axis but at a whole order a >= 1. The + // power is the principal one, as this evaluation's powers are. An order known only to its + // last place, as 1/3 is, is taken at both ends, and the value is the hull of the two: across + // an interval that narrow the function is linear in its order. + // https://github.com/asc-community/AngouriMath/issues/2075 + case IncompleteGammaf(var parameter, var lowerLimit): + { + var order = Evaluate(parameter); + if (!order.IsFinite) + return undefined; + var z = Evaluate(lowerLimit); + var entire = order.IsReal && order.Re.Low.CompareTo(order.Re.High) == 0 && order.Re.Low.IsInteger() && order.Re.Low.Sign > 0; + PreciseComplexInterval AtTheOrder(EDecimal re, EDecimal im) + { + var a = Number.Complex.Create(re, im); + var aLessOne = new PreciseComplexInterval(PreciseInterval.Exactly(re.Subtract(EDecimal.One)), PreciseInterval.Exactly(im)); + return BySlope(z, w => Number.IncompleteGamma(a, w), w => Negate(Multiply(Pow(w, aLessOne), Exp(Negate(w)))), cutEnd: entire ? null : EDecimal.Zero); + } + var low = AtTheOrder(order.Re.Low, order.Im.Low); + if (order.Re.Low.CompareTo(order.Re.High) == 0 && order.Im.Low.CompareTo(order.Im.High) == 0 || !low.IsFinite) + return low; + var high = AtTheOrder(order.Re.High, order.Im.High); + if (!high.IsFinite) + return undefined; + return new(new(EDecimal.Min(low.Re.Low, high.Re.Low), EDecimal.Max(low.Re.High, high.Re.High)), + new(EDecimal.Min(low.Im.Low, high.Im.Low), EDecimal.Max(low.Im.High, high.Im.High))); + } // A sum over the roots of a polynomial, the form Rothstein–Trager answers in: each // root enclosed, the summand worked out on its rectangle, and the terms added. // https://github.com/asc-community/AngouriMath/issues/1285 diff --git a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs index ef6a87725..d913ebd4d 100644 --- a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs @@ -136,6 +136,17 @@ public void APositiveRateIsTakenOutOfThePower() [InlineData("(a + b*x)^2*e^(-c*(d + f*x)^3)")] public void APolynomialBesideAFunctionOfAShiftedLinear(string integrand) => DifferentiatesBack(integrand); + /// + /// A symbolic power of an inverse hyperbolic function, which its substitution writes as powers of u beside + /// exponentials of it, each a gamma function. The route had these answers and turned them away: its check at + /// sampled points could not evaluate a gamma function, and so had no point to compare at. Rubi's 7.1.4 and 7.2.4. + /// + [Theory] + [InlineData("x*asinh(c*x)^n/sqrt(1 + c^2*x^2)")] + [InlineData("(d + f*asinh(c*x))^n*sqrt(1 + c^2*x^2)")] + [InlineData("x*(d + f*acosh(f*x))^n/(sqrt(f*x - 1)*sqrt(f*x + 1))")] + public void ASymbolicPowerOfAnInverseHyperbolicFunction(string integrand) => DifferentiatesBack(integrand); + /// /// Not where the integral is elementary, an exponential integral or an error function: those keep the form /// they had. diff --git a/Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs b/Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs index e7c27a0be..a8425030a 100644 --- a/Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs +++ b/Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs @@ -200,6 +200,10 @@ public void TheSizeOfALargeComplexValueIsFinite() [InlineData("Shi(3/2) + Chi(-1) + Chi(1 + 2i)")] [InlineData("e^(-2) * Ei(sqrt(3)) + Si(pi/4)^2")] [InlineData("iverson(pi > 3) * sqrt(2) + iverson(e < 2)")] + [InlineData("gamma(5/2, 3/2) + gamma(-3, 7/10)")] + [InlineData("gamma(1/2 + i, 2 - i)")] + [InlineData("gamma(3/2, -2) + gamma(4, -1)")] + [InlineData("gamma(1/3, 2) + gamma(2/7 + i/3, 1/2 + i)")] public void ThePreciseIntervalHoldsTheValue(string expression) { var expr = expression.ToEntity(); @@ -247,7 +251,9 @@ public void AnExactZeroFactorIsZero(string expression) /// x erf(x) differentiates to erf(x) and more -- is checked, where reading /// none of them left no point to compare at and the answer was turned away. On the real /// line left of 0 the values are the ones on the cut: Chi(-0.61) is - /// Chi(0.61) + i pi, and li(-0.61) is complex. + /// Chi(0.61) + i pi, and li(-0.61) is complex. The incomplete gamma function was + /// not read, and every answer holding one was turned away by the routes that check theirs: + /// Rubi's x arsinh(a x)^n/sqrt(1 + a^2 x^2) among them. /// [Theory] [InlineData("x * erf(x) + e^(-x^2)/sqrt(pi)", "erf(x)")] @@ -257,6 +263,8 @@ public void AnExactZeroFactorIsZero(string expression) [InlineData("x * Ei(x) - e^x", "Ei(x)")] [InlineData("x * Chi(x) - sinh(x)", "Chi(x)")] [InlineData("x * li(x) - Ei(2 * ln(x))", "li(x)")] + [InlineData("x * gamma(5/2, x) - gamma(7/2, x)", "gamma(5/2, x)")] + [InlineData("x * gamma(n, x) - gamma(n + 1, x)", "gamma(n, x)")] public void ADerivativeKeepingASpecialFunctionIsChecked(string antiderivative, string integrand) { var x = MathS.Var("x");