Repository navigation
A repeated quadratic denominator is integrated (#180) - #1155
Merged
Merged
Conversation
`1/(x^2 + 1)^2` had no antiderivative, while `1/(x^2 - 1)^2` and
`1/(x^2 + 2x + 1)^2` both did: a denominator with real roots comes apart into
linear factors and never reached the gap. What was missing was the irreducible
case.
Writing Q for the quadratic, u for its derivative 2ax + b and D for 4ac - b^2,
the identity u^2 = 4aQ - D turns the derivative of u/Q^(m-1) into an equation
for the integral of 1/Q^m:
J_m = (u / Q^(m-1) + 2a(2m - 3) J_(m-1)) / ((m - 1) D)
unrolled down to J_1, which the table already answered. It is an identity in a,
b and c rather than a fact about signs, so one line serves both signs of the
discriminant; D = 0 is the sole exclusion, and the division by D is why. The two
shapes the reduction cannot speak for get their own arms -- a zero leading
coefficient leaves a linear power, a zero discriminant leaves a power of u --
which is what lets a symbolic quadratic be answered as a piecewise instead of
carrying a division by zero.
A numerator of higher degree is divided by the quadratic first: N = qQ + r takes
one power off the denominator and two degrees off the numerator, and ends. An
improper fraction is declined rather than divided out here, since the recursion
bottoms out at a single power with a numerator of degree two or more -- exactly
the case SolveByPartialFractions already opens with a long division.
The remainder is computed as N - qQ rather than read from the division, which
reports it already divided by the divisor: x^2 over x^2 + 2 answers
(1, -2/(x^2 + 2)), and taking that as a polynomial gives a numerator that is not
one, so every polynomial-numerator case declined.
A numerator already along the denominator's derivative leaves no second integral
and the term is dropped rather than multiplied by zero. That is not tidiness:
0 * (2x/(x^2 + 1)) is `0 provided not 1 + x^2 = 0` and not 0, so multiplying
would attach a condition to x/(x^2 + 1)^2, which had none and is owed none.
A denominator with real roots now takes this route rather than partial
fractions. The value is unchanged and the form is shorter -- one logarithm where
there were two -- which is why the metric that ranks candidates selects it.
Measured, both arms on one machine, the same way. Where the integrand is
rational this is 26 to 165 times faster, because TryStandardIntegrals runs
before every search and a shape answered outright never enters the candidate
exploration: 1/(x^2 + 1)^2 goes from 2879 ms unevaluated to 111 ms answered, and
x^2/(x^2 + 2)^2 from 92,990 ms unevaluated to 565 ms answered. Where the
integrand is a transcendental function over a repeated quadratic, none of which
has an elementary antiderivative, the same search takes 3.4 to 10.8 times longer
to conclude nothing -- e^x/(x^2 + 1)^2 from 7944 ms to 85,683 ms. That is growth
in integration by parts, which those integrands reach and this rule never
matches; it is filed separately rather than bounded here, since by-parts is
machinery every integral goes through.
Full suite 9515 passed, 0 failed. 26 new tests, each answer differentiated back
and compared with the integrand.
Part of #180.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1/(x^2 + 1)^2had no antiderivative.1/(x^2 - 1)^2and1/(x^2 + 2x + 1)^2both did — adenominator with real roots comes apart into linear factors and never reached the gap — so what was
missing was exactly the irreducible case.
The reduction is an identity, not a case analysis
Write
Qfor the quadratic,ufor its derivative2ax + b,Dfor4ac - b^2. Thenu^2is4aQ - D, so differentiatingu/Q^(m-1)gives2a(3 - 2m)/Q^(m-1) + (m-1)D/Q^m, and reading thatas an equation for the integral of
1/Q^mleavesunrolled down to
J_1, which the table already answered. One line serves both signs of thediscriminant, since it is an identity in
a,bandcrather than a fact about signs.D = 0is the sole exclusion and the division by
Dis exactly why.The two shapes the reduction cannot speak for get their own arms — a zero leading coefficient leaves
a linear power, a zero discriminant leaves a power of
u. Both are already answered for a numericquadratic by machinery that exists; the arms are there so a symbolic one, whose discriminant sign
cannot be decided and whose arms therefore cannot be dropped, is answered as a piecewise instead of
carrying a division by zero into the result.
A numerator of any degree
N = qQ + rtakes one power off the denominator and two degrees off the numerator, and ends.Improper fractions are declined rather than divided out here. The recursion bottoms out at a
single power with a numerator still of degree two or more, which is precisely the case
SolveByPartialFractionsalready opens withPolynomialLongDivision. Declining hands it thererather than answering the same thing twice, in two shapes, from two places.
Two things measurement caught that reading would not have
PolynomialLongDivisionreturns the remainder already divided by the divisor.x^2overx^2 + 2answers(1, -2/(x^2 + 2)), not(1, -2). I had assumed the latter, fed it back as anumerator, and it is not a polynomial — so the next division declined and every polynomial-numerator
case silently fell through. The remainder is now computed as
N - qQ, which does not depend on theshape the division chooses to report. Found by instrumenting, after reasoning about the guard chain
gave the wrong answer twice.
A term dropped rather than multiplied by zero. A numerator already along the denominator's
derivative leaves no second integral. Multiplying it out is not harmless:
0 * (2x/(x^2 + 1))is0 provided not 1 + x^2 = 0, correctly, since the factor has no value there — and that conditionwould then be attached to the answer for
x/(x^2 + 1)^2, which had none before and is owed none.What changes that already worked
A denominator with real roots now takes this route rather than partial fractions. Same value,
one logarithm rather than two, which is why the node-count metric that ranks candidates selects it.
No test pinned the old spelling; I found it by probing, and it is in
BREAKING-CHANGES.mdwith bothstrings measured on a build.
I considered guarding the rule off a decidably-negative discriminant to leave those to partial
fractions, and rejected it: it would make the concrete and the symbolic path disagree about which
machinery answers the same shape, which is the kind of split that hides defects.
The cost, stated rather than buried
Both arms on one machine, master and branch, measured the same way.
1/(x^2 + 1)^2x^2/(x^2 + 2)^21/(x^4 + 1)^2ln(x)/(x^2 + 1)^2sin(x)/(x^2 + 1)^2e^x/(x^2 + 1)^2Where the integrand is rational this is 26 to 165 times faster, and the reason is structural rather
than lucky:
TryStandardIntegralsruns before every search, so a shape answered outright neverenters the candidate exploration where this family's cost has always been.
Where it is a transcendental function over a repeated quadratic — none of which has an elementary
antiderivative — the search takes 3.4 to 10.8 times longer to conclude nothing. The sub-integral it
used to fail on immediately now succeeds, and integration by parts carries on from a larger
expression. Those integrands never match this rule; the growth is in by-parts, which every integral
goes through, so bounding it is its own change with its own measurement and I have filed it as
#1156 rather than bundling it here. A reviewer may reasonably want that bound first — the
absolute numbers on that group are bad on master too, and this makes them worse.
Verification
Full suite 9515 passed, 0 failed, 14 skipped. 26 new tests.
Every answer is differentiated back and compared with the integrand. The helper's guard on
integral(is load-bearing rather than decorative: differentiating an unevaluatedintegral(f, x)gives back
f, so without it every declined case would "verify". One case is checked exactly, andthe residual comes back as
0under a condition rather than bare0— right, rather than ashortfall, since both the integrand and its antiderivative are undefined where
1 + x^2 = 0, and anunconditional zero would claim agreement where neither has a value.
Part of #180.