Skip to content

Commit 9f89516

Browse files
A polynomial over a power of one linear with a symbol in it is written in powers of the linear (#1724)
t^9/(a + b t)^8 is a polynomial and eight powers of 1/(a + b t). Divided out and decomposed with the symbols in it, it went round the substitution search a dozen levels deep and took forty-six seconds, where with numbers in place of a and b it took sixty milliseconds. Partial fractions asks the reduction at the linear's root first now, for a power of one linear with a symbol in it and nothing else below the bar, and each piece is the table's: 27 ms. Part of #718. Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 098cb2b commit 9f89516

3 files changed

Lines changed: 89 additions & 1 deletion

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,20 @@ power up each step, and what reaches the first power is integrated once over the
309309
| `"1/(a + b*x^2 + c*x^4)^3".ToEntity().Integrate("x")` | `integral(...)` | the same |
310310
| `"x/(a + b*x^3 + c*x^6)^2".ToEntity().Integrate("x")` | `integral(...)` | a polynomial over `a + b x^3 + c x^6`, and the integral of a polynomial over it |
311311

312+
### A polynomial over a power of one linear with a symbol in it is written in powers of the linear
313+
314+
**Answers where there were none.** `t^9/(a + b t)^8` is a polynomial and eight powers of `1/(a + b t)`,
315+
and was declined by 2.5.0. On the unreleased master it was answered after forty-six seconds: divided
316+
out and decomposed with the symbols in it, it went round the substitution search a dozen levels deep.
317+
With nothing else below the bar and a symbol in the linear, the polynomial is written in powers of
318+
the linear at its root now, and each piece is the table's
319+
([#718](https://github.com/asc-community/AngouriMath/issues/718)).
320+
321+
| Input | Was (2.5.0) | Now |
322+
|---|---|---|
323+
| `"t^9/(a + b*t)^8".ToEntity().Integrate("t")` | `integral(...)` | powers of `a/b + t` and a logarithm, in 0.03 s |
324+
| `"x^4/(a + b*sqrt(x))^8".ToEntity().Integrate("x")` | `integral(...)` | the same in `sqrt(x)`, in 0.5 s; a minute on the unreleased master |
325+
312326
### A polynomial over a power of a quadratic with a sum of symbols in it is reduced
313327

314328
**Answers where there were none.** The reduction of `N(x)/Q(x)^n` divides `N` by the quadratic a

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,11 @@ Entity Back(Entity mapped)
644644
return SolveByPartialFractions(reducedTop / reducedBottom, x, integrateByParts)
645645
?? Integration.ComputeIndefiniteIntegral(reducedTop / reducedBottom, x, integrateByParts);
646646

647+
// A polynomial over a power of one linear with a symbol in it, in powers of the linear at
648+
// its root, before the division: see SolveByReducingThePolynomialOverALinearFactor.
649+
if (SolveByReducingThePolynomialOverALinearFactor(expr, x, integrateByParts, alone: true) is { } overThePowersOfTheLinear)
650+
return overThePowersOfTheLinear;
651+
647652
// The helper answers null for a fraction that is already proper, so this cannot
648653
// fire on one and recurse into the problem it started from. The check on the
649654
// quotient is the second half of that guarantee: a division that came back with
@@ -2207,6 +2212,18 @@ over is Entity.Powf(var @base, var power) ?
22072212
/// https://github.com/asc-community/AngouriMath/issues/718
22082213
/// </remarks>
22092214
internal static Entity? SolveByReducingThePolynomialOverALinearFactor(Entity expr, Entity.Variable x, bool integrateByParts)
2215+
=> SolveByReducingThePolynomialOverALinearFactor(expr, x, integrateByParts, alone: false);
2216+
2217+
/// <summary>
2218+
/// <see cref="SolveByReducingThePolynomialOverALinearFactor(Entity, Entity.Variable, bool)"/>,
2219+
/// or with <paramref name="alone"/> a polynomial over a power of one linear and nothing else,
2220+
/// that linear with a symbol in it: <c>t^9/(a + b t)^8</c> is a polynomial and eight powers of
2221+
/// <c>1/(a + b t)</c>, each the table's. <see cref="SolveByPartialFractions"/> asks it so before
2222+
/// it divides: divided out and decomposed with the symbols in it, that one went round the
2223+
/// substitution search a dozen levels deep and took forty-six seconds, and with numbers in
2224+
/// place of <c>a</c> and <c>b</c> it took sixty milliseconds.
2225+
/// </summary>
2226+
private static Entity? SolveByReducingThePolynomialOverALinearFactor(Entity expr, Entity.Variable x, bool integrateByParts, bool alone)
22102227
{
22112228
if (!TryReadAsQuotient(expr, out var numerator, out var denominator))
22122229
return null;
@@ -2241,11 +2258,13 @@ over is Entity.Powf(var @base, var power) ?
22412258
somethingElse = true;
22422259
rest *= factor;
22432260
}
2244-
if (linear is null || !somethingElse)
2261+
if (linear is null || (alone ? somethingElse || rest.ContainsNode(x) || multiplicity < 2 : !somethingElse))
22452262
return null;
22462263

22472264
if (!TreeAnalyzer.TryGetPolynomial(linear, x, out var line))
22482265
return null;
2266+
if (alone && !line.Values.Any(coefficient => coefficient.Vars.Any()))
2267+
return null;
22492268
var beta = line[EInteger.One];
22502269
var alpha = line.TryGetValue(EInteger.Zero, out var constantTerm) ? constantTerm : Number.Integer.Zero;
22512270
var root = Functions.PartialFractions.Bare((-alpha / beta).InnerSimplified);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
/// A polynomial over a power of one linear with a symbol in it, written in powers of the
16+
/// linear at its root: <c>t^9/(a + b t)^8</c> is a polynomial and eight powers of
17+
/// <c>1/(a + b t)</c>. Divided out and decomposed with the symbols in it, that one took
18+
/// forty-six seconds, and <c>x^4/(a + b sqrt(x))^8</c>, which is it under <c>x = t^2</c>,
19+
/// a minute; the sizes here are the ones that were slow, so that a regression shows as a
20+
/// slow suite rather than as a clock in a test.
21+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
22+
/// </summary>
23+
[Trait("Area", "Calculus")]
24+
public sealed class PolynomialOverAPowerOfASymbolicLinearTest
25+
{
26+
[Theory]
27+
[InlineData("x^9/(a + b*x)^8")]
28+
[InlineData("x^6/(a + b*x)^5")]
29+
[InlineData("x^9/(a + x)^8")]
30+
[InlineData("(c + d*x)^3/(a + b*x)^5")]
31+
[InlineData("x^4/(a + b*sqrt(x))^8")]
32+
public void InPowersOfTheLinear(string integrand)
33+
{
34+
var integral = integrand.ToEntity().Integrate("x");
35+
Assert.DoesNotContain("integral(", integral.Stringize());
36+
Assert.DoesNotContain("NaN", integral.Stringize());
37+
Entity Pinned(Entity e) => e.Substitute("a", 2.3).Substitute("b", 0.7).Substitute("c", 1.3).Substitute("d", 1.7);
38+
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
39+
var original = Pinned(integrand.ToEntity());
40+
var compared = 0;
41+
foreach (var at in new[] { -1.7, -0.9, 0.3, 0.8, 1.6, 2.9 })
42+
{
43+
var want = original.Substitute("x", at).EvalNumerical();
44+
if (want.IsNaN || Math.Abs((double)want.ImaginaryPart) > 1e-12)
45+
continue;
46+
compared++;
47+
var got = derivative.Substitute("x", at).EvalNumerical();
48+
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
49+
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
50+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
51+
}
52+
Assert.True(compared >= 3, $"only {compared} points could be compared for {integrand}");
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)