Skip to content

The special functions integrate by parts - #1631

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
special-functions-by-parts
Sep 30, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
special-functions-by-parts

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

None of the nine special functions was a factor that integration by parts would differentiate, so a power of x beside one had no route. x erf(b x), x^2 Ei(b x) and x Si(b x) were all left unevaluated. So were Ei, li, Si, Ci, Shi and Chi of a linear argument on their own.

  • Against a power of x, the function is differentiated. Each has an elementary derivative, which is why the logarithm and the inverse trigonometric functions come first in LIATE, and these functions now stand beside them. What is left is the power times e^(-u^2), e^u/u, sin(u)/u and the like, which the rules answer.
  • Alone, each is integrated against 1, as erf already was. With u linear and each divided by the rate:
    • int Ei(u) = u Ei(u) - e^u
    • int li(u) = u li(u) - Ei(2 ln u)
    • int Si(u) = u Si(u) + cos(u)
    • int Ci(u) = u Ci(u) - sin(u)
    • int Shi(u) = u Shi(u) - cosh(u)
    • int Chi(u) = u Chi(u) - sinh(u)
  • One more step of parts after Si and Ci. Their remainder, x^m sin(b x), needs parts again. It has more nodes than x^m Si(b x) and no factor left to differentiate, so the measure that bounds the descent declined it. Against a polynomial, the next steps descend on its degree, so parts are allowed on that remainder there. Allowed against anything, they took erf(b x)^2 from a decline in a third of a second to a timeout.

What is still out of reach in family 8: the squares F(a + b x)^2, and e^(c - b^2 x^2) erf(b x)^n, need a derivative-divides rule, int F'(u) F(u)^n = F(u)^(n+1)/(n+1), which is the next PR. F(d (a + b ln(c x^n))) beside a power needs a substitution.

Measured

  • Rubi's family 8, whose statable files are Special functions, first tranche: the error functions and the exponential, logarithmic, trigonometric and hyperbolic integrals #1501's tranche, on master (371b38e) and on this branch: 8.1 from 9 to 105 of 177, 8.3 from 2 to 34 of 47, 8.4 from 0 to 44 of 98, and 8.5 from 18 to 75 of 98. In all 29 becomes 258 of 420, with 0 wrong and 0 timeouts on both.
  • Families 0 to 7, side by side with master. Family 0 is 1753 of 1814 on both, row for row. The families sample goes from 810 to 811 of 912, with 0 wrong on both. The one move is 3.1.5:213, (a + b ln(c x^n))/(x^2 (d + e ln(f x^m))): run alone, master declines it in 5.0 s and this branch answers it in 0.9 s.
  • SpecialFunctionsByPartsTest has 24 rows from Rubi's 8.1, 8.3, 8.4 and 8.5. Each is differentiated back with its parameters pinned.
  • The library builds for every target with no warnings, and the unit tests pass: 14,177, none failed (net10.0).
  • The performance gate passes on 8991dbc6, whose code is this head's (only BREAKING-CHANGES.md differs). Allocation matches the baseline on all 19 gated benchmarks.

Part of #1501.

🤖 Generated with Claude Code

None of the nine special functions was a factor integration by parts would differentiate, so a
power of x beside one had no route: x erf(b x), x^2 Ei(b x) and x Si(b x) were all left
unevaluated, and so were Ei, li, Si, Ci, Shi and Chi of a linear argument on their own. Each has
an elementary derivative, which is what puts the logarithm and the inverse trigonometric functions
first in LIATE, and they stand beside them now: against a power of x the function is
differentiated, and what is left is the power times e^(-u^2), e^u/u, sin(u)/u and the like, which
the rules answer. Alone, each is integrated against 1, as erf already was: int Ei(u) = u Ei(u) -
e^u, int li(u) = u li(u) - Ei(2 ln u), int Si(u) = u Si(u) + cos(u), int Ci(u) = u Ci(u) - sin(u),
int Shi(u) = u Shi(u) - cosh(u) and int Chi(u) = u Chi(u) - sinh(u), each over the rate.

After Si or Ci the remainder x^m sin(b x) needs parts again, and with more nodes than x^m Si(b x)
and no factor left to differentiate, the measure that bounds the descent declined it. Against a
polynomial the next steps descend on its degree, so parts are allowed on that remainder there.
Allowed against anything, they took erf(b x)^2 from a decline in a third of a second to a timeout.

Rubi's family 8, whose statable files are #1501's tranche: 8.1 from 9 to 105 of 177, 8.3 from
2 to 34 of 47, 8.4 from 0 to 44 of 98 and 8.5 from 18 to 75 of 98, 29 to 258 of 420 in all, with
0 wrong and 0 timeouts on master (371b38e) and here.

Families 0 to 7, side by side with master: family 0 is 1753 of 1814 on both, row for row, and
the families sample goes from 810 to 811 of 912, 0 wrong on both. The move is 3.1.5:213,
(a + b ln(c x^n))/(x^2 (d + e ln(f x^m))), which master declines in 5.0 s alone and this answers
in 0.9 s.

SpecialFunctionsByPartsTest has 24 rows from Rubi's 8.1, 8.3, 8.4 and 8.5, each differentiated
back with its parameters pinned.

Part of #1501.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Rafael-SOWNet Rafael-SOWNet added this to the 2.6.0 milestone Sep 30, 2026
@Rafael-SOWNet
Rafael-SOWNet merged commit fac6ce1 into master Sep 30, 2026
31 checks passed
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