Skip to content

The perfect-square collapse asks the rewrite graph before it asks Simplify - #1201

Merged
Rafael-SOWNet merged 2 commits into
masterfrom
square-proves-equal
Sep 7, 2026
Merged

Rafael-SOWNet merged 2 commits into
masterfrom
square-proves-equal

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

#746 tier 2's last open item is a production caller for the rewrite graph, with the standing
performance condition measured. #1198 measured the graph on the corpus and found it cheap and never
ahead of Simplify on extraction — so the caller worth having is one that needs the graph's
proof, not its form. The library has exactly one place that decides an equality by subtracting
and simplifying: the perfect-square collapse, whose remark calls that test "the whole cost of this
rule".

What changes

After the numeric pre-filter passes, the collapse asks Saturation.ProvesEqual(crossTerm, w)
first, at the safe ceiling, under 2,000 steps / 50 ms, and falls through to the three Simplify
calls only where the graph does not prove. A false from the graph is "not proved", never
"unequal", so nothing the site reached before is lost, and the numeric disposer at the end treats
both routes the same. Saturation.SafeRules is the rule list built once, lazily; the catalogue's
private copy reads it.

Measured, per stage

cross term vs candidate graph symbolic test it replaces whole collapse whole Simplify
2·√2·√3 vs 2·√6 proved, 0.55 ms 1.67 ms 3.1 ms 1.3 ms
2·√a·√b vs itself proved, 0 ms 3.29 ms 163 ms 48 ms
2·√x·√y vs 2·√(xy) not proved, 0.24 ms — correctly: the branch-cut identity 2.85 ms 19 ms 37 ms
2·√1·√(x/2) vs √(2x) not proved, 0.24 ms 5.14 ms 50 ms 59 ms

So: where the graph proves it is 3–6× faster than what it replaces; where it doesn't it costs
0.24 ms; and at the level of a whole Simplify the change is inside the noise (before/after
medians over 15 runs: 52.4 → 50.5 ms, 60.4 → 60.2, 46.9 → 46.3), because the collapse's cost is
in the candidate's own Simplify and the numeric checks, not in the equality test. This is a
caller that meets the condition — it runs only after the numeric pre-filter, bounded — and not a
speed-up to advertise. What made the proved case reachable at all is #1198's constant fold
(2·3 → 6 inside the graph).

PerfectSquareProofTest pins the two facts the caller rests on — the graph proves the surd
product; it refuses √x·√y = √(xy), so a loosened guard fails there before it reaches the rule
— and that the collapse answers exactly what it answered.

Part of #746. With this, tier 2's list from the session stands at: items 1–4 done, item 5 has its
first caller; what remains is the e-matcher over the 35 two-sided rules (EMatching.md), which
changes speed rather than reach.

Checks

Full suite 9610 passed, 0 failed, 14 skipped — 9 of them new.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

Rafael-SOWNet and others added 2 commits September 7, 2026 07:23
…plify

#746 tier 2's last open item is a production caller for the rewrite graph, with the
standing performance condition measured. The one place the library decides an
equality by subtracting and simplifying is the perfect-square collapse, which the
remark on it calls "the whole cost of this rule": after the numeric pre-filter passes,
it ran Simplify three times over radicals to find whether the cross term matches.

It now asks Saturation.ProvesEqual first, at the ceiling of rules that never enlarge,
under a budget of two thousand steps and fifty milliseconds, and falls through to the
symbolic test where the graph does not prove -- a false from the graph is "not proved",
never "unequal", so nothing the site reached before is lost, and the numeric check at
the end disposes of both routes the same way.

Measured per stage on the inputs the rule fires on. Where the graph proves it is
faster than what it replaces -- 0.55 ms against 1.7 to 5.1 ms for 2 * sqrt(2) * sqrt(3)
against 2 * sqrt(6), and nothing where the two are already one tree, which #1198's
constant fold is what made reachable. Where it does not prove it spends 0.24 ms, and
it is right not to: 2 * sqrt(x) * sqrt(y) against 2 * sqrt(x * y) is the branch-cut
identity the site's own remarks call false, and the guarded rule declines it. At the
level of a whole Simplify the change is inside the noise, because the collapse's cost
is elsewhere -- 3 to 163 ms per call, in the candidate's own Simplify and the numeric
checks. So this is a caller that meets the condition, running only after the numeric
pre-filter and bounded, and not a speed-up to advertise.

Saturation.SafeRules is the rule list built once and lazily, so a caller inside the
simplifier does not rebuild it per call and the registry is not read at type
initialisation; the catalogue's private copy now reads it. PerfectSquareProofTest
holds the two facts the caller rests on -- the graph proves the surd product, and it
refuses the branch-cut identity -- and that the collapse answers what it answered.

Part of #746.

Full suite 9610 passed, 0 failed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Every CI leg failed one test: TheSafeCeilingProvesTheCrossTerm, three seconds into a
cold process, where the caller's fifty-millisecond wall went on JIT time and the surd
product came back unproved. The production budget stays as it is -- a cold first
call falls through to Simplify, which is the right answer and merely the slower one,
once -- but a test must not carry a wall clock: steps are what bound a proof, and a
wall in a test measures the runner. The test's budget keeps the caller's two thousand
steps and a thirty-second wall that cannot fire.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
@Rafael-SOWNet
Rafael-SOWNet merged commit 0285c71 into master Sep 7, 2026
31 checks passed
Rafael-SOWNet added a commit that referenced this pull request Sep 7, 2026
…s measured (#1203)

EMatching.md deferred whether Expands rules ever join SafeRules to "a separate document's
question, once one exists", and Transformations.md's list of what is left said the graph
had no production caller. Both were true when written and are not now: #1193 and #1194
measured that the growth ceiling is the scheduling policy and that Expands rules do not
join the safe set; #1198 and #1202 found what the graph did need -- a rational folded on
insertion and an extraction that is a fixed point from the leaves up -- by running the
safe ceiling over the corpus; and #1201 gave the graph its first production caller. The
documents point at those rather than at a question nobody is going to open.

Part of #746.


Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.

1 participant