From 9e190e5b8af9f14727d4eb8e6c033dd910666c1a Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sat, 3 Oct 2026 16:27:12 +0000 Subject: [PATCH 1/2] The scaling rule does not simplify an integrand with the imaginary unit in it The rule that scales the variable by the integrand's one symbol simplified the scaled integrand three times to separate the whole power of the scale it is homogeneous of. With the imaginary unit beside the symbol that search ran past any budget: ((1 + i a x)/(1 - i a x))^(-5/4)/x^2, which is declined, took more than two minutes to be. Inner simplification separates what a whole power of the scale does, and where the parts do not separate so the rule declines; it is declined in 3 s. Part of #718. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Integration/IndefiniteIntegralSolver.cs | 12 +++++++++--- .../ComplexCoefficientRationalIntegralTest.cs | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 60538086d..573b826f2 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -18462,7 +18462,13 @@ bool IsAConstantTimesAPowerOfOneMinusUSquared(Entity polynomial, out Entity cons // dx = c dt. var t = Variable.CreateUnique(expr, "u_scale"); - var scaled = (expr.Substitute(x, scale * t) * scale).Simplify(); + // With the imaginary unit in the integrand the simplifier's search beside the symbol + // does not end within any budget, and inner simplification separates what a whole + // power of the scale does: `((1 + i a x)/(1 - i a x))^(-5/4)/x^2` under its radical's + // substitution was minutes here before it was declined. Where the parts do not + // separate so, the check below declines it. + Entity Simplified(Entity e) => HoldsTheImaginaryUnit(expr) ? Functions.PartialFractions.Bare(e.InnerSimplified) : e.Simplify(); + var scaled = Simplified(expr.Substitute(x, scale * t) * scale); if (scaled.ContainsNode(x)) return null; @@ -18471,11 +18477,11 @@ bool IsAConstantTimesAPowerOfOneMinusUSquared(Entity polynomial, out Entity cons // The integrand with the scale set to one is the candidate h(t), and what is left // over when the scaled integrand is divided by it is the candidate factor. Read off // rather than searched for: if the two do separate, the quotient *is* the factor. - var withoutScale = scaled.Substitute(scale, Number.Integer.One).Simplify(); + var withoutScale = Simplified(scaled.Substitute(scale, Number.Integer.One)); if (withoutScale.ContainsNode(scale) || withoutScale == Number.Integer.Zero) return null; - var factor = (scaled / withoutScale).Simplify(); + var factor = Simplified(scaled / withoutScale); // Collapsing the quotient attaches the condition that the denominator it cleared is // non-zero. That denominator is the integrand's own, so the condition says where the // integrand is defined and nothing about this rewrite; it is dropped for the same diff --git a/Sources/Tests/UnitTests/Calculus/ComplexCoefficientRationalIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ComplexCoefficientRationalIntegralTest.cs index 53e961c15..37f56139a 100644 --- a/Sources/Tests/UnitTests/Calculus/ComplexCoefficientRationalIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/ComplexCoefficientRationalIntegralTest.cs @@ -79,5 +79,20 @@ private static void DifferentiatesBack(string integrand) [InlineData("(A + B*tan(x))/(a + i*a*tan(x))^2")] [InlineData("1/(a + i*a*tan(c + d*x))^3")] public void APowerOfAPlusIATangent(string integrand) => DifferentiatesBack(integrand); + + /// + /// A power of a quotient of linears with the imaginary unit in it, beside a power of x the + /// substitution for it does not take down to something answered: declined, and within a + /// minute. The scaling of the variable simplified it with the symbol beside the imaginary + /// unit, and that ran past two minutes before the decline. + /// + [Theory] + [InlineData("((1 + i*a*x)/(1 - i*a*x))^(-5/4)/x^2")] + [InlineData("((1 + i*a*x)/(1 - i*a*x))^(-5/4)/x^3")] + public void AnImaginaryMobiusPowerIsDeclinedWithinAMinute(string integrand) + { + var integrating = System.Threading.Tasks.Task.Run(() => integrand.ToEntity().Integrate("x")); + Assert.True(integrating.Wait(TimeSpan.FromSeconds(60)), $"{integrand} was not settled within a minute"); + } } } From 4132047badd9262914bacddff7e1df14537704d1 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sat, 3 Oct 2026 19:37:16 +0000 Subject: [PATCH 2/2] The scaling rule's change in BREAKING-CHANGES.md Measured on the corpus, the integrands the scaling rule held up are answered by the rules after it now: sec(c + d x)^3/(a + i a tan(c + d x)), five powers of x beside e^(k i arctan(a + b x)), 1/(a + b csc(c + d x)^2)^4 among them, and 2.5.0 declined eight of nine. Part of #718. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index cdc3c4a0a..fbd1bab80 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -450,6 +450,24 @@ interval between the poles of the tangent, as the substitution's are | `"(A + B*tan(x))/(a + i*a*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)` | the same | | `"1/(a + i*a*tan(c + d*x))^3".ToEntity().Integrate("x")` | `integral(...)` | the same in `tan(c + d x)` | +### An integrand with the imaginary unit in it is not simplified by the rule that scales the variable + +**Answers where there were none.** The rule that scales the variable by the integrand's one symbol +simplified the scaled integrand three times, to separate the whole power of the scale it is +homogeneous of. With the imaginary unit beside the symbol that search ran past any budget: on the +unreleased master `((1 + i a x)/(1 - i a x))^(-5/4)/x^2` took more than two minutes to be declined, +and the rules after this one never had their turn on integrands that it held up. Such an integrand is +inner-simplified now, and declined in seconds where the parts do not separate +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"sec(c + d*x)^3/(a + i*a*tan(c + d*x))".ToEntity().Integrate("x")` | `integral(...)` | logarithms of `tan((c + d x)/2) ± 1` and a quotient | +| `"tan(c + d*x)^(8/3)/(a + i*a*tan(c + d*x))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative in `tan(c + d x)^(1/3)` | +| `"1/(a + b*csc(c + d*x)^2)^4".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative, by the signs of `a` and `b` | +| `"x^4*e^(2*i*atan(a + b*x))".ToEntity().Integrate("x")` | `integral(...)` | a polynomial, logarithms and arctangents of `a + b x` | +| `"sec(c + d*x)^2/(a + i*a*tan(c + d*x))".ToEntity().Integrate("x")` | `ln(i a d tan(c + d x) + a d)/(i a d)` | `ln(i tan(c + d x) + 1)/(i a d)`, the same up to a constant | + ### `NaN` was returned as the antiderivative of something that has one **A wrong answer, not a missing one.** `1/(a*x^2)` came back as `NaN + C`, and `NaN` is this