Collapse a three-term sum that is a square with a radical (#176, #203) - #800
Merged
Merged
Conversation
`1 + sqrt(2x) + x/2` was left as written, where it is `(1 + sqrt(x/2))^2`. u + 2*sqrt(u)*sqrt(v) + v collapses to (sqrt(u) + sqrt(v))^2. The identity is unconditional. sqrt(u)^2 is u for every complex u -- it is the square of a principal root, not the root of a square, and sqrt(u^2) = u is the false one that holds only for a non-negative u (#752). What could not be trusted is the test for whether the cross term matches. Deciding it needs Simplify, and the simplifier equates sqrt(x)*sqrt(y) with sqrt(x*y), which is false across the branch cuts: at x = y = -1, `x + 2*sqrt(x*y) + y` is 0 while `(sqrt(x) + sqrt(y))^2` is -4. Asked symbolically, this rule fired on that sum and gave a wrong answer -- the first version of it did exactly that, and the test that caught it was written before the rule. So the symbolic match only proposes and a numeric check disposes. The sample points include negative values, which is where a branch-cut error shows and nowhere else, and each free variable is offset from the last so that x and y cannot coincide into a case that happens to hold. The rule withdraws unless every sampled point agrees. It is its own pass, run before FactorizeRules rather than as an arm of it. `Replace` walks bottom-up, so the common-factor rule reaches the inner `4 + 4*sqrt(x)` of `4 + 4*sqrt(x) + x` first and rewrites it to `4 * (1 + sqrt(x))` -- by then the three terms are no longer there. Ordering the arms within the switch does not help, since the two rules look at different nodes. 1 + sqrt(2 * x) + x / 2 -> (1 + sqrt(x / 2)) ^ 2 1 + 2 * sqrt(x) + x -> (1 + sqrt(x)) ^ 2 4 + 4 * sqrt(x) + x -> (2 + sqrt(x)) ^ 2 1 + 3 * sqrt(x) + x unchanged -- not a square, and not rounded into one x + 2 * sqrt(x * y) + y unchanged -- not a square over the complex plane x ^ 2 + 2 * x + 1 unchanged here; at x = -3 it is 4 and (sqrt(x^2) + 1)^2 is 16 Unit 5536 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>
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.
Closes #176. Addresses #203, which names #176/#177/#178 as its content — the other two are already closed.
1 + sqrt(2x) + x/2was left as written, where it is(1 + sqrt(x/2))^2. A three-term sumu + 2*sqrt(u)*sqrt(v) + vcollapses to(sqrt(u) + sqrt(v))^2.The identity is unconditional; the test for it is not
sqrt(u)^2isufor every complexu— it is the square of a principal root, not the root of a square.sqrt(u^2) = uis the false one, and holds only for a non-negativeu(#752).What cannot be trusted is deciding whether the cross term matches. That needs
Simplify, and the simplifier equatessqrt(x)*sqrt(y)withsqrt(x*y), which is false across the branch cuts:Asked symbolically, this rule fired on that sum and produced a wrong answer. The first version of it did exactly that, and the test that caught it had been written before the rule.
So the symbolic match only proposes and a numeric check disposes. The sample points include negative values — where a branch-cut error shows and nowhere else — and each free variable is offset from the last so
xandycannot coincide into a case that happens to hold. The rule withdraws unless every sampled point agrees.It is its own pass, not an arm of
FactorizeRulesReplacewalks bottom-up, so the common-factor rule reaches the inner4 + 4*sqrt(x)of4 + 4*sqrt(x) + xfirst and rewrites it to4 * (1 + sqrt(x))— by then the three terms are gone. Ordering the arms within the switch does not help, because the two rules are looking at different nodes.Measured
1 + sqrt(2 * x) + x / 2(1 + sqrt(x / 2)) ^ 21 + 2 * sqrt(x) + x(1 + sqrt(x)) ^ 24 + 4 * sqrt(x) + x(2 + sqrt(x)) ^ 21 + 3 * sqrt(x) + xx + 2 * sqrt(x * y) + yx ^ 2 + 2 * x + 1x = -3it is 4 and(sqrt(x^2) + 1)^2is 16Every positive case is checked by sampling the collapsed form against the original, not by comparing text —
sqrt(x/2)andsqrt(2x)/2are one number and two trees.Harnesses
casbench113/117, 0 wrong;rootcheck596/596;simpsweep10463/10463;propcheck0 failures🤖 Generated with Claude Code