Skip to content

Commit a09cbf0

Browse files
What a reduction leaves over its block is integrated in named coefficients (#1844)
* What a reduction leaves over its block is integrated in named coefficients Part of #718. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Read the remainder by key and value, for netstandard2.0 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * The entry gives the answers' sizes, measured against master and 2.5.0 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent d675d8e commit a09cbf0

3 files changed

Lines changed: 137 additions & 9 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,30 @@ then cancels before the division of the improper fraction
253253
| `"(a + b*x)^6/(a*c + (b*c + a*d)*x + b*d*x^2)^2".ToEntity().Integrate("x")` | past a minute | 3,592 characters |
254254
| `"csc(e + f*x)^3/(a + b*tan(e + f*x)^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 7,992 characters |
255255

256+
### What a reduction leaves over its block is integrated in named coefficients
257+
258+
**Answers where there were none, and sooner.** `(c + d x^3 + g x^6 + f x^9)/(x^4 (a + b x^3)^3)` took 42 s on the
259+
unreleased master, and `x^5`, `x^7` and `x^8` in place of `x^4` took 35 to 50 s, Rubi's 1.1.3.8. Four polynomials
260+
over `(a + b x^2 + c x^4)^2` and `^3` in 1.2.2.5 ran past two minutes, and
261+
`(d + f x + g x^2 + h x^3 + k x^4)/(a + b x^2 + c x^4)^3` took 31 s. Two reductions -- the split at a power of x beside
262+
a block, and the one that takes a polynomial over a power of a trinomial in `x^n` down to its first power --
263+
integrated what was left over the block with the coefficients they had computed, such as
264+
`(a^2 g - 3 a b d + 6 b^2 c)/a^2`, and the substitution search spent up to a minute simplifying quotients in them
265+
before another rule answered. Each such coefficient is a fresh symbol while the rest is integrated now, and is put
266+
back in the answer. The answers over a power of the trinomial are large, 22,000 to 98,000 characters, and right.
267+
One answer grew: `1/(x^7 (a + b x^3)(c + d x^3))` took 52 s on the unreleased master for 14,565 characters, and
268+
takes 6 s for 265,499 now ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
269+
270+
| Input | Was (2.5.0) | Now |
271+
|---|---|---|
272+
| `"(c + d*x^3 + g*x^6 + f*x^9)/(x^4*(a + b*x^3)^3)".ToEntity().Integrate("x")` | `integral(...)` | 547 characters |
273+
| `"(c + d*x^3 + g*x^6 + f*x^9)/(x^8*(a + b*x^3)^3)".ToEntity().Integrate("x")` | `integral(...)` | 1,859 characters |
274+
| `"1/(x^7*(a + b*x^3)*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 265,499 characters |
275+
| `"(d + f*x + g*x^2 + h*x^3 + k*x^4)/(a + b*x^2 + c*x^4)^3".ToEntity().Integrate("x")` | `integral(...)` | 31,531 characters |
276+
| `"(d + f*x + g*x^2 + h*x^3 + k*x^4 + m*x^5 + n*x^6 + p*x^7)/(a + b*x^2 + c*x^4)^2".ToEntity().Integrate("x")` | `integral(...)` | 21,719 characters |
277+
| `"(d + f*x + g*x^2 + h*x^3 + k*x^4 + m*x^5 + n*x^8 + p*x^11)/(a + b*x^2 + c*x^4)^3".ToEntity().Integrate("x")` | `integral(...)` | 77,389 characters |
278+
| `"csc(c + d*x)^2/(a - b*sin(c + d*x)^4)^3".ToEntity().Integrate("x")` | `integral(...)` | 157,787 characters |
279+
256280
### One linear twice is not two linear powers
257281

258282
**Answers that had no value.** `(a c + b c x)^(-3 - 2 p) (f + g x) (a^2 + 2 a b x + b^2 x^2)^p` was answered

‎Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs‎

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,15 +1361,15 @@ Entity Coefficient(Dictionary<EInteger, Entity> polynomial, int power)
13611361
}
13621362

13631363
// R = (N - P B)/x^k, whose coefficient of x^(m - k) is that of x^m in N - P B.
1364-
Entity remainder = Number.Integer.Zero;
1364+
var remainder = new Dictionary<int, Entity>();
13651365
for (var m = k; m <= System.Math.Max(degreeAbove, degreeBelow + k - 1); m++)
13661366
{
13671367
var coefficient = Coefficient(above, m);
13681368
for (var j = System.Math.Max(0, m - degreeBelow); j <= System.Math.Min(k - 1, m); j++)
13691369
coefficient -= series[j] * Coefficient(below, m - j);
13701370
coefficient = Functions.PartialFractions.InLowestTermsOverTheSymbols(coefficient);
13711371
if (!VanishesIdentically(coefficient))
1372-
remainder += m == k ? coefficient : coefficient * MathS.Pow(x, m - k);
1372+
remainder[m - k] = coefficient;
13731373
}
13741374

13751375
// The part over x^k term by term, and the rest over the block as it is written.
@@ -1379,7 +1379,7 @@ Entity Coefficient(Dictionary<EInteger, Entity> polynomial, int power)
13791379
overX += j == k - 1
13801380
? series[j] * IntegralPatterns.AntiderivativeLog(x)
13811381
: series[j] * MathS.Pow(x, j - k + 1) / (j - k + 1);
1382-
if (remainder == Number.Integer.Zero)
1382+
if (remainder.Count == 0)
13831383
return overX;
13841384
return OverTheRestOfTheBlock(remainder, block, x, integrateByParts) is { } overTheBlock
13851385
? overX + overTheBlock
@@ -1398,10 +1398,69 @@ Entity Coefficient(Dictionary<EInteger, Entity> polynomial, int power)
13981398
/// https://github.com/asc-community/AngouriMath/issues/718
13991399
/// </remarks>
14001400
private static Entity? OverTheRestOfTheBlock(Entity remainder, Entity block, Entity.Variable x, bool integrateByParts)
1401+
=> AtAnotherLinearOfTheBlock(remainder, block, x, integrateByParts)
1402+
?? Integration.ComputeIndefiniteIntegral(remainder / block, x, integrateByParts);
1403+
1404+
/// <summary>
1405+
/// <see cref="OverTheRestOfTheBlock(Entity, Entity, Entity.Variable, bool)"/> for a rest
1406+
/// given by its coefficients, integrated a level down in named coefficients.
1407+
/// </summary>
1408+
private static Entity? OverTheRestOfTheBlock(Dictionary<int, Entity> rest, Entity block, Entity.Variable x, bool integrateByParts)
1409+
{
1410+
Entity remainder = Number.Integer.Zero;
1411+
foreach (var pair in rest.OrderBy(pair => pair.Key))
1412+
remainder += pair.Key == 0 ? pair.Value : pair.Value * (pair.Key == 1 ? x : MathS.Pow(x, pair.Key));
1413+
return AtAnotherLinearOfTheBlock(remainder, block, x, integrateByParts)
1414+
?? IntegratedInNamedCoefficients(rest, block, x, integrateByParts);
1415+
}
1416+
1417+
/// <summary>
1418+
/// <c>R/B</c> split at a linear the block still holds, where that answer differentiates back.
1419+
/// </summary>
1420+
private static Entity? AtAnotherLinearOfTheBlock(Entity remainder, Entity block, Entity.Variable x, bool integrateByParts)
14011421
=> IntegrateOverAPowerOfALinearBesideABlock(remainder, block, x, integrateByParts) is { } atAnotherRoot
14021422
&& Functions.PartialFractions.DerivativeHoldsAtSampledPoints(atAnotherRoot, remainder / block, x)
14031423
? atAnotherRoot
1404-
: Integration.ComputeIndefiniteIntegral(remainder / block, x, integrateByParts);
1424+
: null;
1425+
1426+
/// <summary>
1427+
/// The integral of <c>R/B</c>, for a polynomial <c>R</c> in x given by its coefficients, with each coefficient of
1428+
/// <c>R</c> that is more than a symbol or a number a fresh symbol while it is integrated,
1429+
/// and put back in the answer.
1430+
/// </summary>
1431+
/// <remarks>
1432+
/// What a reduction leaves over its block has coefficients the reduction computed, such as
1433+
/// <c>(a^2 g - 3 a b d + 6 b^2 c)/a^2</c>, and the substitution search simplifies each
1434+
/// quotient it tries in them. Over <c>(a/b + x^3)^3</c> that took 40 s to decline where three
1435+
/// symbols take it 45 ms, and over <c>(a + b x^2 + c x^4)</c> past a minute.
1436+
/// https://github.com/asc-community/AngouriMath/issues/718
1437+
/// </remarks>
1438+
private static Entity? IntegratedInNamedCoefficients(Dictionary<int, Entity> rest, Entity block, Entity.Variable x, bool integrateByParts)
1439+
{
1440+
var named = new List<(Variable Name, Entity Value)>();
1441+
Entity everything = block;
1442+
foreach (var value in rest.Values)
1443+
everything += value;
1444+
Entity inNames = Number.Integer.Zero;
1445+
foreach (var pair in rest.OrderBy(pair => pair.Key))
1446+
{
1447+
var power = pair.Key;
1448+
var coefficient = pair.Value;
1449+
if (coefficient is not (Variable or Number))
1450+
{
1451+
var name = Variable.CreateUnique(everything, "k_r");
1452+
everything += name;
1453+
named.Add((name, coefficient));
1454+
coefficient = name;
1455+
}
1456+
inNames += power == 0 ? coefficient : coefficient * (power == 1 ? x : MathS.Pow(x, power));
1457+
}
1458+
if (Integration.ComputeIndefiniteIntegral(inNames / block, x, integrateByParts) is not { } answer)
1459+
return null;
1460+
foreach (var (name, value) in named)
1461+
answer = answer.Substitute(name, value);
1462+
return answer;
1463+
}
14051464

14061465
/// <summary>
14071466
/// <c>N/(L^k B)</c> for a linear <c>L = g + h x</c> other than x, beside a block <c>B</c>
@@ -1897,13 +1956,13 @@ Entity InU(Dictionary<EInteger, Entity> polynomial)
18971956
Entity answer = Number.Integer.Zero;
18981957
foreach (var pair in overPowers.OrderByDescending(pair => pair.Key))
18991958
answer = answer + pair.Value / (pair.Key == 1 ? trinomial : MathS.Pow(trinomial, pair.Key));
1900-
Entity rest = Number.Integer.Zero;
1901-
foreach (var pair in atThisPower.OrderBy(pair => pair.Key))
1959+
var rest = new Dictionary<int, Entity>();
1960+
foreach (var pair in atThisPower)
19021961
if (!VanishesIdentically(pair.Value))
1903-
rest = rest + pair.Value * (pair.Key == 0 ? Number.Integer.One : pair.Key == 1 ? x : MathS.Pow(x, pair.Key));
1904-
if (rest != Number.Integer.Zero)
1962+
rest[pair.Key] = pair.Value;
1963+
if (rest.Count > 0)
19051964
{
1906-
if (Integration.ComputeIndefiniteIntegral(rest / trinomial, x, integrateByParts) is not { } overTheTrinomial)
1965+
if (IntegratedInNamedCoefficients(rest, trinomial, x, integrateByParts) is not { } overTheTrinomial)
19071966
return null;
19081967
answer = answer + overTheTrinomial;
19091968
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// Copyright (c) 2019-2026 Angouri.
3+
// AngouriMath is licensed under MIT.
4+
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
5+
// Website: https://am.angouri.org.
6+
//
7+
8+
using System;
9+
using AngouriMath.Extensions;
10+
using Xunit;
11+
12+
namespace AngouriMath.Tests.Calculus
13+
{
14+
/// <summary>
15+
/// What a reduction leaves over its block is integrated with its compound coefficients named:
16+
/// with them as written, the substitution search spent up to a minute declining it, and these
17+
/// did not finish. Rubi's 1.1.3.8 and 1.2.2.5.
18+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
19+
/// </summary>
20+
[Trait("Area", "Calculus")]
21+
public sealed class TheRestOverABlockInNamedCoefficientsIntegralTest
22+
{
23+
[Theory]
24+
[InlineData("(c + d*x^3 + g*x^6 + f*x^9)/(x^4*(a + b*x^3)^3)")]
25+
[InlineData("(c + d*x^3 + g*x^6 + f*x^9)/(x^8*(a + b*x^3)^3)")]
26+
[InlineData("(d*x + g*x^3)/(a + b*x^2 + c*x^4)^3")]
27+
public void DifferentiatesBack(string integrand)
28+
{
29+
var integral = integrand.ToEntity().Integrate("x");
30+
Assert.DoesNotContain("integral(", integral.Stringize());
31+
Entity Pinned(Entity e) => e.Substitute("a", 2).Substitute("b", 3).Substitute("c", 5)
32+
.Substitute("d", -1).Substitute("g", 7).Substitute("f", 1);
33+
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
34+
var original = Pinned(integrand.ToEntity());
35+
foreach (var at in new[] { -1.7, -0.6, 0.45, 1.3 })
36+
{
37+
var want = original.Substitute("x", at).EvalNumerical();
38+
var got = derivative.Substitute("x", at).EvalNumerical();
39+
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
40+
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
41+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
42+
}
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)