From dc108fe4d15d1b7e43d2049f913b93c5a0007a2a Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Thu, 1 Oct 2026 05:50:41 +0000 Subject: [PATCH] A square of a special function of a shifted argument is asked term by term #1639 asked the remainder of a special function's square term by term only for an argument b x. For a + b x, the remainder divided by the linear, and its terms were the exponentials a hyperbolic function is written as, each a harder question than the whole. With the constant of integration matched to the linear (#1642), the linear cancels and the terms are the case without the offset, so the asking is offered for any linear argument now. The exception is an error function beside anything but itself. Its derivative is a Gaussian of the shifted argument rather than a quotient by it, so a polynomial beside it stays in every term: asking them took the decline of (c + d x) erf(a + b x)^2 from six seconds to twenty-four, and answered nothing. Measured on the Rubi corpus, master at 16203aa6 and this change on it, run side by side: family 8 380 -> 394 of 420, 0 timeout (erf, erfc and erfi of a + b x squared; Si and Ci of a + b x squared beside x and x^2; Ei, Shi and Chi of a + b x squared beside x^2; Shi(a + b x)^2 and Si(a + b x)^2; x^2 Ci(a + b x) cos(a + b x) and x^2 cos(a + b x) Si(a + b x)); family 2 544 of 650, the independent suites 1756 of 1814 and families 1 and 3 to 7 at five a file 800 of 897, on both. 0 wrong everywhere. Run alone, the fourteen are 0 of 14 on master and 14 of 14 here. Measured the same way with the error functions not excepted, family 8 was the same 394 with six timeouts more, and families 0 to 7 moved nowhere. The unit tests pass, 14,265, and the performance gate passes on ec42a120: allocation is what the baseline says on all 19 gated benchmarks. SpecialFunctionsByPartsTest has four more rows with a shifted argument, each differentiated back with its parameters pinned. Part of #1501. Co-Authored-By: Claude Opus 5.5 --- BREAKING-CHANGES.md | 18 +++++++++ .../Integration/IndefiniteIntegralSolver.cs | 37 +++++++++++++------ .../Calculus/SpecialFunctionsByPartsTest.cs | 4 ++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 5e39bfc78..90c8c587b 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -2335,6 +2335,24 @@ beside a power of `x`, the answer is written with the same constant: `x Ei(a + b | `"x*Shi(a+b*x)^2".Integrate("x")` | `a ^ 2 * Shi * x ^ 2 / 2 + a * b * 2 * Shi * x ^ 3 / 3 + b ^ 2 * Shi * x ^ 4 / 4 + C`, with `Shi` a variable | an antiderivative in `Shi(a + b x)` and `Ei(2 (a + b x))`, `Ei(-2 (a + b x))` | | `"x*Ei(a+b*x)^2".Integrate("x")` | `a ^ 2 * Ei * x ^ 2 / 2 + a * b * 2 * Ei * x ^ 3 / 3 + b ^ 2 * Ei * x ^ 4 / 4 + C`, with `Ei` a variable | an antiderivative in `Ei(a + b x)` and `Ei(2 (a + b x))` | +### A square of a special function of a shifted argument is integrated by parts + +The remainder of a special function's square was asked term by term only for an argument `b x`. +For `a + b x`, the remainder divided by the linear until the constant of integration was matched to +it; with that matched, the terms are the case without the offset, and the asking is offered for any +linear argument ([#1501](https://github.com/asc-community/AngouriMath/issues/1501)). The exception +is an error function beside anything but itself, whose derivative is a Gaussian of the shifted +argument, so a polynomial beside it stays in every term: `(c + d x) erf(a + b x)^2` is still +declined. An integrand holding one of these functions had no reading in 2.5.0, which the entries for +the functions themselves record, and no integrand without one changes: Rubi's independent suites and +a sample of its families 1 to 7 are answered alone as they were. + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"Shi(a+b*x)^2".Integrate("x")` | `Shi * (a + b * x) ^ 3 / 3 / b + C`, with `Shi` a variable | an antiderivative in `Shi(a + b x)`, `Ei(2 (a + b x))` and `Ei(-2 (a + b x))` | +| `"x^2*Ei(a+b*x)^2".Integrate("x")` | `a ^ 2 * Ei * x ^ 3 / 3 + a * b * 2 * Ei * x ^ 4 / 4 + b ^ 2 * Ei * x ^ 5 / 5 + C`, with `Ei` a variable | an antiderivative in `Ei(a + b x)` and `Ei(2 (a + b x))` | +| `"erf(a+b*x)^2".Integrate("x")` | `UnrecognizedFunctionParseException`: there is no function `erf` | an antiderivative in `erf` | + ### An exponential or a hyperbolic function over several linears is split into partial fractions over them `e^x/(x (x + 1))` was left unevaluated, where `e^x/x` and `e^x/(x + 1)` were each answered with diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 8694cc5d6..c4354b9a5 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -1842,12 +1842,10 @@ static Entity WithTheConstantMatchedTo(Entity antiderivative, Entity derivativeO // So its terms are asked, each as a question of its own. From the top only, // which is where that asking reaches, and once: a term asked so is at the top // itself, and its own remainder asked the same way would lift the search again, - // each level with the depth reset. And of a multiple of x only: of `a + b x`, the - // remainder is over the linear, and its terms are the exponentials a hyperbolic - // function is written as, each a harder question than the whole -- the decline of - // `x Shi(a + b x)^2` went from a second to past twenty asking them. + // each level with the depth reset. And not for every argument: see + // TheRemainderIsAskedTermByTerm. // https://github.com/asc-community/AngouriMath/issues/1501 - if (remainingIntegral is null && besideASpecialFunction && OfAMultipleOfTheVariable(v, x) + if (remainingIntegral is null && besideASpecialFunction && TheRemainderIsAskedTermByTerm(v, u, x) && Integration.AnsweringTheQuestionAsked && !askingTheTermsOfARemainder) { var terms = remaining is Divf(var remainingAbove, var remainingBelow) @@ -1872,7 +1870,7 @@ static Entity WithTheConstantMatchedTo(Entity antiderivative, Entity derivativeO askingTheTermsOfARemainder = false; } } - else if (remainingIntegral is null && besideASpecialFunction && OfAMultipleOfTheVariable(v, x) + else if (remainingIntegral is null && besideASpecialFunction && TheRemainderIsAskedTermByTerm(v, u, x) && Integration.AnsweringTheQuestionAsked) Integration.DeclinedForItsScope(); if (remainingIntegral is null) return null; @@ -20345,12 +20343,29 @@ private static bool IsASpecialFunctionOfALinearOrAPowerOfOne(Entity factor, Vari || factor is Powf(var @base, Number.Integer power) && power.EInteger.Sign > 0 && IsASpecialFunctionOfALinear(@base, x); /// - /// Whether the special function in , or in the base of its power, - /// is of a multiple of , b x, with no offset. + /// Whether the remainder after differentiating , a special function of + /// a linear argument or a power of one, against is asked term by term + /// when nothing answers it whole. Of a multiple of , always. Of + /// a + b x, where the function's derivative divides by its argument -- Ei, + /// Si, Ci, Shi, Chi -- since the constant of the factor + /// integrated beside it is then matched to the linear and the terms are the case without + /// the offset; and where the function is integrated against itself, the square alone. + /// Not an error function of a + b x beside anything else: its derivative is a + /// Gaussian of the shifted argument rather than a quotient by it, a polynomial beside it + /// stays in every term, and asking them took the decline of (c + d x) erf(a + b x)^2 + /// from six seconds to twenty-four, answering nothing. + /// https://github.com/asc-community/AngouriMath/issues/1501 /// - private static bool OfAMultipleOfTheVariable(Entity factor, Variable x) - => (factor is Powf(var @base, _) ? @base : factor).DirectChildren.FirstOrDefault() is { } argument - && TreeAnalyzer.TryGetPolyLinear(argument, x, out _, out var offset) && TreeAnalyzer.IsZero(offset); + private static bool TheRemainderIsAskedTermByTerm(Entity v, Entity u, Variable x) + { + var special = v is Powf(var @base, _) ? @base : v; + if (special.DirectChildren.FirstOrDefault() is not { } argument + || !TreeAnalyzer.TryGetPolyLinear(argument, x, out _, out var offset)) + return false; + return TreeAnalyzer.IsZero(offset) + || special is not (Entity.Erff or Entity.Erfcf or Entity.Erfif) + || u == special; + } /// /// The factors of that are polynomials in of diff --git a/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs b/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs index 6df826679..c83e8806d 100644 --- a/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs +++ b/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs @@ -142,6 +142,10 @@ public void ASquareIsTwoRoundsOfParts(string integrand) [Theory] [InlineData("x*Shi(a + b*x)^2")] [InlineData("x*Ei(a + b*x)^2")] + [InlineData("Shi(a + b*x)^2")] + [InlineData("x*Si(a + b*x)^2")] + [InlineData("x^2*Ei(a + b*x)^2")] + [InlineData("erf(a + b*x)^2")] public void ASquareOfAShiftedArgumentIsTwoRoundsOfParts(string integrand) => DifferentiatesBack(integrand, Parameters); }