Repository navigation
Extraction is a fixed point from the leaves up, and a budget is honoured to the millisecond - #1202
Merged
Merged
Conversation
…red to the millisecond #1199 recorded Saturation overshooting a two-second wall by up to thirty-five times. Instrumenting the loop on the worst input put 99% of the time in e-match attempts, one of which ran 1.9 s on a thirteen-member class; timing the steps of that one attempt's first binding put the whole 1.9 s in a single graph.Extract of one bound class -- on a graph of 253 e-nodes. The walk was top-down with a visiting-set cycle guard and a memo that fills only on completion, and a graph full of union-made cycles defeats that: any class on the stack blocks memoisation of everything beneath it, so sibling paths walk the same subgraph again and again. That, not the ledger, was the overshoot. Extraction is now a fixed point from the leaves up: a round recomputes every class from its children's current best, in class order and member order so ties fall the same way they did, and the rounds stop when one changes nothing. No recursion, so no depth guard and no stack to exhaust -- a chain three hundred deep extracts as itself where it used to come back null. The result is kept for as long as the graph does not change and the cost is the same delegate, and only a union can change what an existing class extracts as, so Union is where it is dropped. And the e-matcher charges the ledger once per binding it tries after the first, so an attempt over a class with a thousand members is a thousand steps and not one. Measured on the inputs of #1199, same budget of 20,000 steps and 2 s: total 2000 ms and 2000 ms where it was 2435 and 2595, the longest single attempt 4 ms where it was 1918, the longest extraction 3 ms. The Transformations tests hold every pin they held, the corpus figures included. One of my own claims does not survive this and is corrected where it was made. With the walk no longer the cost, the inputs #1200 called a stall are two things: x ^ 2 / x, x / x * -x and x / x * x * 1/2 are genuine runaways, ten to twelve thousand e-nodes in thirty seconds, and 2 * x * 1/2 and its relatives plateau at some 560 e-nodes and stop only when 200,000 steps run out. "Bounded at 135-583 e-nodes after thirty seconds" was the slow extraction, not the graph. ConstantFoldTest says so now, and its pin is renamed to what it pins: bounded by the budget, and extracting right. Part of #746. Fixes #1199. Full suite 9610 passed, 0 failed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
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>
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.
#1199 recorded
Saturationovershooting a 2 s wall by up to 35×. Now that the perfect-squarecollapse (#1201) relies on a 50 ms budget inside
Simplify, a budget that can be overshot is themost important thing the graph work left open. This closes it, with the cause measured rather
than the one I guessed in the issue.
Where the time went
Instrumenting the loop on the worst input, three layers down:
TryEMatchApply;Extract,Rebuild,Apply,Add≈ 1% togethergraph.Extractof one bound class, on a 253-node graphThe walk was top-down with a
visitingcycle guard and a memo that fills only on completion. Agraph full of union-made cycles defeats that: any class on the stack blocks memoisation of
everything beneath it, so sibling paths walk the same subgraph again and again — exponential in
practice. My guess in #1199 (the ledger not consulted often enough) was wrong:
Spend()readsthe clock every call; the problem was a single un-spendable 1.9 s step.
The fix
children's current best, in class order and member order so ties fall as they did, until a
round changes nothing. No recursion, so no depth guard and no stack to exhaust — a chain 300
deep extracts as itself where it came back
null(the test that pinned thenullis rewrittento say so). The result is kept while the graph is unchanged and the cost is the same delegate;
only a union can change what an existing class extracts as, so
Uniondrops it.thousand-member class is a thousand steps, not one.
Measured
Same inputs, same budget (20,000 steps / 2 s):
x / x * x * 1/2totalx / x * -xtotalEvery Transformations pin holds — the corpus figures, the safe-ceiling 2-of-5, the ablation,
the breadth, the canonical-extraction ties.
A correction of my own
With extraction no longer the cost, the inputs #1200 called a stall are two things:
x ^ 2 / x,x / x * -xandx / x * x * 1/2are genuine runaways (10–12k e-nodes in 30 s,~400 a second), and
2 * x * 1/2and its relatives plateau at ~560 e-nodes and stop only when200,000 steps run out. "Bounded at 135–583 e-nodes after thirty seconds" was the slow extraction,
not the graph.
ConstantFoldTestsays so now and its pin is renamed to what it pins — bounded bythe budget, extracting right. #1200 gets the corrected figures.
Changed answers
None, measured on a build of master (
0285c71a) and of this branch through the publicTransformation.CanonicalizationOverGraphandSaturation.ProvesEqual: the 300-deep chain is300 + xon both (the rule pass on either side of the graph folds it before the graph sees it, sothe old
nullnever reached a caller),x / x * -xis-xandx ^ 2 / xisxon both, andthe two perfect-square proofs succeed on both at 50 ms. What changed is the clock:
x ^ 2 / xagainst a 2 s budget took 2,216 ms on master and 2,006 ms here. No
BREAKING-CHANGES.mdrow,since no value moved.
Part of #746. Fixes #1199.
Checks
Full suite 9610 passed, 0 failed, 14 skipped.
🤖 Generated with Claude Code
https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura