Skip to content

An exponential or a hyperbolic function over several linears is split into partial fractions - #1640

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
an-exponential-over-several-linears
Oct 1, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
an-exponential-over-several-linears

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

e^x/(x (x + 1)) was left unevaluated, where e^x/x and e^x/(x + 1) were each answered with the exponential integral.

  • Several linears below the bar. The rule for an exponential of a linear over a power of a linear read one linear below the bar. The rule for sinh and cosh, which arrive as sums of exponentials, did the same. The rule for a sine or a cosine already split a quotient over several linears into partial fractions, each term the one-linear question. The exponential and hyperbolic ones do too now, through PartialFractions.TrySplitOverWrittenFactors. It reads symbolic coefficients and checks the split at pinned points before it is used. e^x/(x (x + 1)) is Ei(x) - Ei(x + 1)/e. It is also what by parts leaves of Ei(a + b x)/x^2: e^(a + b x)/((a + b x) x).
  • A proper fraction is not divided. The polynomial part comes off by long division only where the fraction is not proper already. The rule for a sine or a cosine divided every time. x over (c + d x)(x - 2) then came back as a quotient and a remainder, 2 provided not c + d x = 0, which nothing splits. So x sin(x)/((c + d x)(x - 2)) was declined, where x sin(x)/((x + 3)(x - 2)) was answered. Both rules divide only an improper fraction now.

Measured

Input Was (2.5.0) Now
"e^x/(x*(x+1))".Integrate("x") integral(e ^ x / (x * (x + 1)), x) Ei(x) + -1 / e * Ei(x + 1) + C
"sinh(x)/(x*(x+1))".Integrate("x") integral((e ^ x - e ^ (-x)) / 2 / (x * (x + 1)), x) 1/2 * (2 * Shi(x) + ((-1) / e + e) * Chi(x + 1) + ((-1) / e - e) * Shi(x + 1)) + C
"x^3*e^(2*x)/((x+1)*(x-2))".Integrate("x") integral(x ^ 3 * e ^ (2 * x) / ((x + 1) * (x - 2)), x) e ^ (2 * x) * (-1/4 + 1/2 * x) + e ^ (2 * x) / 2 + 1/3 * e ^ (-2) * Ei(2 * (x + 1)) + 8/3 * e ^ 4 * Ei(2 * (x - 2)) + C
"x*sin(x)/((c+d*x)*(x-2))".Integrate("x") integral(x * sin(x) / ((c + d * x) * (x - 2)), x) in Si and Ci of (c + d x)/d and x - 2
  • Rubi, master at 9193d973 and this change on it, run side by side, 0 wrong everywhere:
    • Family 8: 296 → 302 of 420, the six being Ei, Shi and Chi of a + b x over x^2 and over x^3.
    • Family 6 at twenty a file: 412 → 414 of 472, cosh(c + d x)/(x (a + b x)) and the same over x^3 (a + b x)^3.
    • Family 2: 543 of 650 on both.
    • The independent suites: 1756 of 1814 on both.
    • Families 1, 3, 4, 5 and 7 at five a file: 682 of 768 on both.
  • Run alone, the eight gains are 0 of 8 on master and 8 of 8 here.
  • The unit tests pass: 14,240, none failed (net10.0), also with the GC heap held to 4 GB (DOTNET_GCHeapHardLimit). The suite's host peaked between 4.4 and 9.1 GB over four unconstrained runs, where master's peaked between 3.1 and 5.8. Under the cap, both suites pass in full, so that is the collector growing the heap while memory is free, not memory the tests need.
  • On master with A special function beside an elementary factor is integrated by parts, and so is its square #1639, the six test classes both changes touch pass: 199 tests. The native AOT publish of the C++ wrapper builds with no trim or AOT warnings.
  • The performance gate passes on 3b94625d, which is this change on 9193d973. Allocation matches the baseline on all 19 gated benchmarks.

ExponentialIntegralIntegrationTest and HyperbolicIntegralIntegrationTest each have a row set over several linears, and TrigonometricIntegralIntegrationTest has two rows over symbolic ones. Each is differentiated back with its parameters pinned.

Part of #1501.

🤖 Generated with Claude Code

… into partial fractions

e^x/(x (x + 1)) was left unevaluated, where e^x/x and e^x/(x + 1) were each answered with the
exponential integral. The rule for an exponential of a linear over a power of a linear read one
linear below the bar, and the rule for sinh and cosh, which arrive as sums of exponentials, did
the same. The rule for a sine or a cosine already split a quotient over several linears into
partial fractions, each term the one-linear question; the exponential and hyperbolic ones do too
now, through PartialFractions.TrySplitOverWrittenFactors, which reads symbolic coefficients and
checks the split at pinned points before it is used. e^x/(x (x + 1)) is Ei(x) - Ei(x + 1)/e.
It is also what by parts leaves of Ei(a + b x)/x^2: e^(a + b x)/((a + b x) x).

The polynomial part comes off by long division only where the fraction is not proper already. The
rule for a sine or a cosine divided every time, and x over (c + d x)(x - 2) came back as a quotient
and a remainder 2 provided not c + d x = 0, which nothing splits: x sin(x)/((c + d x)(x - 2)) was
declined where x sin(x)/((x + 3)(x - 2)) was answered. Both rules divide only an improper fraction
now.

Measured on the Rubi corpus, master at 9193d97 and this change on it, run side by side: family 8
296 -> 302 of 420 (Ei, Shi and Chi of a + b x over x^2 and over x^3), family 6 at twenty a file
412 -> 414 of 472 (cosh(c + d x)/(x (a + b x)) and over x^3 (a + b x)^3), family 2 543 of 650 on
both, the independent suites 1756 of 1814 and families 1, 3, 4, 5 and 7 at five a file 682 of 768
on both. 0 wrong everywhere. Run alone, the eight are 0 of 8 on master and 8 of 8 here.

The unit tests pass, 14,240, also with the GC heap held to 4 GB, and the performance gate passes on
3b94625d, which is this change on 9193d97: allocation is what the baseline says on all 19 gated
benchmarks.

ExponentialIntegralIntegrationTest and HyperbolicIntegralIntegrationTest have a row set each over
several linears, and TrigonometricIntegralIntegrationTest two rows over symbolic ones, each
differentiated back with its parameters pinned.

Part of #1501.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant