Repository navigation
Compute limits at infinity by Gruntz's algorithm (#231, #353) - #694
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #694 +/- ##
==========================================
+ Coverage 80.99% 81.54% +0.54%
==========================================
Files 155 159 +4
Lines 13687 13749 +62
Branches 1957 2318 +361
==========================================
+ Hits 11086 11211 +125
+ Misses 1990 1883 -107
- Partials 611 655 +44 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
After D. Gruntz, "On Computing Limits in a Symbolic Manipulation System",
ETH 1996. What it adds are the limits nothing here had any reading of, above all
the ones whose parts cancel to every order:
lim x -> +oo e^(x + e^(-x)) - e^x = 1
lim x -> +oo sqrt(x^2 + 3x) - sqrt(x^2+1) = 3/2
lim x -> +oo x^20 / e^x = 0 (was unevaluated)
lim x -> +oo x / sqrt(x^2 + 1) = 1 (was unevaluated)
The first of those is the standard demonstration: expanding the two exponentials
separately gives two divergent series whose difference cancels entirely, so no
amount of differentiating both halves arrives anywhere. Rewriting the whole
expression in w = e^(-x) gives (e^w - 1)/w, whose leading term is 1.
Two pieces. AsymptoticSeries is a power series in w about 0 with real exponents
and symbolic coefficients, which is not a Taylor series and could not be one: the
coefficients are expressions in x, and the exponents may be negative and
fractional. It takes the value of log(w) from outside, since w is an exponential
and its logarithm is an ordinary expression -- without it the constant term of
log(c w^e) cannot be told from zero. How far to carry it is not known in advance,
so the caller raises the order until a leading term survives the cancellation.
Gruntz itself finds the subexpressions in the fastest comparability class, writes
every one of them as a power of one of their number, and reads the answer off the
leading term. Three things the thesis is emphatic about are honoured: the
exponential exp(s - c t) is never split into a product, which would put back a
function of a faster class; the member everything is written in terms of is one
with no other member inside it; and the leading coefficient is checked to be free
of w before its exponent is trusted.
Scope is the exp-log functions, which is what the algorithm is stated for,
because those are eventually monotone and so comparable at all. A sine has no
limit at infinity and no comparability class, so this declines rather than
guesses, and that is pinned. So is the factorial, which needs Stirling and a
tractable-function mechanism this does not have.
Only for a destination at infinity. A limit at a finite point reaches infinity
too by substituting for x, but there this would be replacing answers the existing
rules already give rather than adding ones they do not -- lim x -> 0 x^x comes out
as 1, against an existing test that pins it as having no limit, and that is a
change to argue on its own evidence rather than to make as a side effect.
21 tests. 4028 in all, 127 F#, corpus 104/117 with nothing wrong, in error or
timing out, and 1320 property checks.
2c0c279 to
90a42d2
Compare
|
Two notes after integrating this with the other open branches. It answers a form #682 pins as unanswerable. That PR adds Neither branch answers those alone; only the two together do. Whichever of the two lands second should turn that test from a non-answer into the answer, and I will push that as part of this PR if #682 goes in first. Flagging it rather than leaving it to surface as a red build. Two small things fixed on this branch since it was opened (force-pushed, no review had happened yet):
|
Two of the forms master pins as unsettleable are settled by the series, and the resolution is to move them rather than to keep asserting they cannot be: x^20 / e^x and x^(3/2)*sqrt(1 + 1/x^2)/x^2 join CompetingGrowthAtInfinity. Both were listed as beyond l'Hopital's rule, which is still true of the rule and no longer true of the library; nothing about its bound on steps changed. sqrt(x^2 + 3x) - sqrt(x^2 + 1), which #682 asserts comes back unevaluated, is 3/2. That assertion was right while the conjugate was the only reading of it. Flagged on both PRs when they were opened, since whichever landed second had to flip it. The oscillating forms stay unsettleable: they have no comparability class, so the series has nothing to say about them either. Gruntz runs last of the readings at infinity, after the cheaper rewrite pass master added, which is the order its own comment asks for.
Implements what #353 proposes and links the paper for, which #231 also asks for.
The first is the standard demonstration. Expanding the two exponentials separately gives two divergent series whose difference cancels entirely — so no amount of differentiating both halves arrives anywhere. Rewriting the whole expression in
w = e^(-x)gives(e^w - 1)/w, whose leading term is 1.Two pieces
AsymptoticSeries— a power series inwabout 0 with real exponents and symbolic coefficients. It could not be a Taylor series: the coefficients are expressions inxand the exponents may be negative and fractional. It takes the value oflog(w)from outside, sincewis an exponential whose logarithm is an ordinary expression — without it the constant term oflog(c·w^e)cannot be told from zero. How far to carry it is not known in advance, so the caller raises the order until a leading term survives the cancellation.Gruntz— finds the subexpressions in the fastest comparability class, writes each as a power of one of their number, and reads the answer off the leading term.Three things the thesis is emphatic about, all of which cause silent wrong answers if missed, are honoured and commented as such:
exp(s - c·t)is never split into a product of exponentials, which would put back a function of a faster class. (This is the bug that makes SymPy 1.14 answerlimit((x!/x^x)^(1/x), x, oo)with 0 instead of1/e— I verified that against a local install.)wbefore its exponent is trusted.Scope, and what it declines
The exp-log functions — what is built from
xand the rationals with the four operations,expandlog— which is what the algorithm is stated for, because those are eventually monotone and so comparable at all. A sine has no limit at infinity and no comparability class either, so this declines rather than guesses, and that is pinned. So is the factorial, which needs Stirling and a tractable-function mechanism this does not have.Only for a destination at infinity. A limit at a finite point reaches infinity too, by substituting for
x, but there this would be replacing answers the existing rules already give rather than adding ones they do not. Concretely:lim x → 0 x^xcomes out as 1, against an existing test (TestNoLimit) that pins it as having no limit. Over ℂ, 1 is defensible; over ℝ the two-sided limit does not exist. That is a change to argue on its own evidence, not to make as a side effect of this, so it is out of scope here.Verification
21 new tests: six that nothing before could reach, twelve ordinary competing growths that must come out unchanged, and three oscillations pinned as declined-and-terminating.
4028 unit tests and 127 F# tests on both target frameworks, none failing. 117-problem self-verifying corpus with nothing wrong, in error or timing out; 1320 property checks over 151 expressions, none failing.
GRUNTZ_DEBUG=1prints the recursion tree — the algorithm calls itself by four different routes and any failure surfaces as a plain decline, so there is no following it otherwise.