Skip to content

Read the quotient in the limit instead of gathering it in the simplifier (#802) - #805

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
fix/802-quotient-branch
Aug 8, 2026
Merged

Read the quotient in the limit instead of gathering it in the simplifier (#802)#805
Rafael-SOWNet merged 1 commit into
masterfrom
fix/802-quotient-branch

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Fixes #802 — the quotient twin of #801, and without the cost I said it would have.

a^n / b^n was gathered into (a/b)^n unconditionally, which is false across the branch cuts: sqrt(2)/sqrt(-3) is -0.8165i where (2/-3)^(1/2) is +0.8165i.

I left this out of #801 because guarding it removed limit answers — (x^2 + 1)^x / (x^2)^x stopped having a limit at all, which is the whole of #739 and #740. That framing was wrong, and this is the correction.

The gathering was never the point

#740 wanted it so the limit machinery could read a 1^oo out of a quotient — its own test comment says exactly that. So the rewrite belongs in the limit reader, not the simplifier, because that is the only place it can be justified:

  • a limit needs the identity to hold only in a neighbourhood of the destination, so requiring both bases to be eventually positive there is enough — and there is a destination to check that against;
  • in the simplifier there is no destination and nothing to check, which is precisely why the rule was unconditional.

ApplySecondRemarkable now recognises a^n / b^n itself, and the simplifier's rule takes the same guard the rest of its family carries.

Every limit survives

(x^2 + 1)^x / (x^2)^x 1 unchanged
(x^3 + 1)^x / (x^3)^x 1 unchanged
(x - 5)^x / x^x e^(-5) unchanged
(sqrt(x) + 1)^x / sqrt(x)^x unchanged
sqrt(x) / sqrt(y) was sqrt(x/y) unchanged, and correct at x=2, y=-3

Eleven tests moved from the mechanism to the outcome

PowerQuotientGatheringTest asserted that Simplify prints a single power. It no longer does, and should not — those assertions were pinning something unsound. They now assert that the limit is answered, which is what the mechanism existed for. The four cases with symbolic bases no longer gather at all, and assert instead that simplifying does not change their value, since nothing can say a symbolic quotient stays on the principal branch.

The test that recorded #802 as open is flipped to assert it stays fixed — which is what it was written to do.

Harnesses

  • unit 5543 pass, 0 fail; F# 130/130
  • casbench 113/117, 0 wrong; rootcheck 596/596; simpsweep 10463/10463; propcheck 0 failures

🤖 Generated with Claude Code

…ier (#802)

`a^n / b^n` was gathered into `(a/b)^n` unconditionally, and that is false
across the branch cuts: sqrt(2)/sqrt(-3) is -0.8165i where (2/-3)^(1/2) is
+0.8165i. It is the quotient twin of #801 and was left out of that fix, because
guarding it cost *answers*: `(x^2 + 1)^x / (x^2)^x` stopped having a limit at
all, which is the whole of #739 and #740.

That framing was wrong, and this is the correction. The gathering was never the
point -- #740 wanted it so the limit machinery could read a 1^oo out of a
quotient, and its own test comment says so. The right place for the rewrite is
therefore the limit reader, not the simplifier, because that is the only place
where it can be justified: a limit needs the identity to hold in a neighbourhood
of the destination, so requiring both bases to be eventually positive there is
enough, and there is a destination to check that against. In the simplifier
there is no destination and nothing to check, which is why it was unconditional.

So ApplySecondRemarkable now recognises `a^n / b^n` itself, and the simplifier's
rule takes the same guard its whole family carries. Every limit survives:

    (x^2 + 1)^x / (x^2)^x      1        unchanged
    (x^3 + 1)^x / (x^3)^x      1        unchanged
    (x - 5)^x / x^x            e^(-5)   unchanged
    (sqrt(x) + 1)^x / sqrt(x)^x         unchanged
    sqrt(x) / sqrt(y)          sqrt(x/y) -> unchanged, and correct at x=2, y=-3

Eleven tests in PowerQuotientGatheringTest moved from asserting the *mechanism*
-- a single power in Simplify's output -- to asserting the *outcome* it existed
for, which is that the limit is answered. That distinction is the whole content
of this change, and the tests that pinned the mechanism were pinning something
unsound. The four with symbolic bases no longer gather at all and now assert
that simplifying does not change their value, since nothing can say a symbolic
quotient stays on the principal branch.

The test recording #802 as open is flipped to assert it stays fixed, which is
what it was written to do.

Unit 5543 pass 0 fail; F# 130/130; casbench 113/117 0 wrong; rootcheck 596/596;
simpsweep 10463/10463; propcheck 0 failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Rafael-SOWNet
Rafael-SOWNet merged commit a42e72b into master Aug 8, 2026
25 checks passed
@Rafael-SOWNet
Rafael-SOWNet deleted the fix/802-quotient-branch branch August 8, 2026 00:21
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.

a^n / b^n is gathered into (a/b)^n unconditionally, which is wrong for bases of opposite sign

1 participant