Skip to content

Promote two agreeing one-sided limits over the reals (#719, step 2; #596) - #756

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
feat/promote-agreeing-one-sided-limits
Aug 6, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
feat/promote-agreeing-one-sided-limits

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

Step 2 of #719, on top of #755. Closes #596's reported expression.

What was wrong

A two-sided limit is the two one-sided ones agreeing, and AngouriMath could not say so. The branch that compares them compares two ComputeLimitDivideEtImpera results — the bare descent — where a caller who names a side gets that descent and everything behind it: SolveAsIndeterminatePower, l'Hôpital's rule, and the substitution that moves a finite destination out to infinity.

The promotion could not be made before, and #719 says exactly why: the sides did not stay inside the reals. Over the complex plane lim x->0- x^x answers 1 from the continuation, so promoting agreement would give lim x->0 x^x the value 1 — and x^x is not real to the left of 0 at all. LimitTest.TestNoLimit pins it as non-existent.

#755 gave limits a codomain to read. This uses it.

What it does

Only under Domain.Real, and that is exactly what makes it sound: there the offending side has no value to agree with, so the case this could get wrong is the case that no longer arises.

The expression #596 reported, at 0, over the reals:

1 / ln(x + sqrt(x^2 + 1)) - 1 / ln(x + 1)

  left    -1/2          already
  right   -1/2          already
  both    unevaluated  ->  -1/2

What must not be promoted, and is not:

why
x * ln(x), x ^ x, sqrt(x), ln(x) real on one side only — nothing to agree with
1/x, abs(x)/x, e^(1/x), arctan(1/x) both sides real and they disagree — still NaN

A second change, which this exposed

A two-sided limit is now withdrawn if either approach leaves the reals, where the check added in #755 asked about one direction only. lim x->0 sqrt(x) answered 0 while its own left-hand limit had been withdrawn — the two halves disagreeing about the same reading. It belongs in this PR rather than #755 because promoting from the sides is what made the inconsistency visible.

Cost

Asked only where the answer would otherwise be NaN or nothing, so no limit that already answers pays for it. Bounded at a depth of two: each side may itself reach a two-sided limit of a subexpression, and without a bound the two questions branch into four and so on down the tree.

Measured

  • Suite 5135 → 5147 passed / 0 failed, F# 130/130.
  • Corpus 112/117, 0 wrong / 0 error / 0 timeout, every verdict and answer byte-identical.
  • work/rootcheck 596/596 clean, work/propcheck 0 failures.

Nothing changes by default. The complex reading answers as it always has — pinned by tests, including that x^x and x*ln(x) at 0 are still the definite NaN they were.

🤖 Generated with Claude Code

)

A two-sided limit is the two one-sided ones agreeing, and AngouriMath could not
say so. The branch that compares them compares two `ComputeLimitDivideEtImpera`
results -- the bare descent -- where a caller who names a side gets that descent
*and* everything behind it: SolveAsIndeterminatePower, l'Hopital's rule, and the
substitution that moves a finite destination out to infinity.

The promotion could not be made before because the sides did not stay inside the
reals. Over the complex plane `lim x->0- x^x` answers 1 from the continuation,
so promoting agreement would give `lim x->0 x^x` the value 1 -- and `x^x` is not
real to the left of 0 at all. #755 gave limits a codomain to read; this uses it,
and **only under `Domain.Real`**, which is exactly what makes it sound: there
that side has no value to agree with, so the case this could get wrong is the
case that no longer arises.

The expression #596 reported, at 0, over the reals:

    1/ln(x + sqrt(x^2+1)) - 1/ln(x+1)

    left   -1/2      already
    right  -1/2      already
    both   unevaluated  ->  -1/2

What must not be promoted, and is not:

    x * ln(x), x^x, sqrt(x), ln(x)   real on one side only, so unevaluated
    1/x, abs(x)/x, e^(1/x), arctan(1/x)   sides disagree, so still NaN

A second change came with it, and it belongs here rather than in #755 because
this is what exposed it. A **two-sided** limit is now withdrawn if *either*
approach leaves the reals, where the check asked about one direction only.
`lim x->0 sqrt(x)` answered 0 while its own left-hand limit had been withdrawn,
which is the two halves disagreeing about the same reading.

Asked only where the answer would otherwise be NaN or nothing, so no limit that
already answers pays for it, and bounded by a depth of two: each side may reach
a two-sided limit of a subexpression, and without a bound the two questions
branch into four and so on down the tree.

Nothing changes by default. The complex reading answers as it always has, which
is what keeps `LimitTest.TestNoLimit` meaning what it meant, and that is pinned.

Suite 5135 -> 5147 passed / 0 failed, F# 130/130, corpus 112/117 with every
verdict and answer byte-identical, rootcheck 596/596, propcheck 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Rafael-SOWNet
Rafael-SOWNet merged commit e38da2b into master Aug 6, 2026
24 checks passed
@Rafael-SOWNet
Rafael-SOWNet deleted the feat/promote-agreeing-one-sided-limits branch August 6, 2026 00:03
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.

Unexpected behavior of limits

1 participant