Skip to content

A rule's declared growth is checked against the corpus (#825) - #1158

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
rule-growth
Sep 4, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
rule-growth

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

RewriteRuleGrowth is a claim about every expression a rule fires on: Collects says the
replacement is smaller, Rearranges the same size, Expands larger. For the 35 rules with a
pattern on both sides it is counted from the two patterns. For the rest it is declared by hand, and
nothing checked it.

Running those declarations against the generated corpus finds one that is wrong.

The one it caught

a-common-factor-is-collected-out-of-a-whole-sum declared Collects, and the reason was in the
code:

// Declared, because the replacement is code and nothing counts it: the whole point of it is to be smaller.
growth: RewriteRuleGrowth.Collects));

Over fourteen hundred expressions it fires 42 times, never shrinks, and grows by as much as eight
nodes
:

-x + x + -1/2   ->   x * (-1 + 1) + -1/2         seven nodes for seven

Which of the four is true, checked rather than chosen: Collects wants every firing smaller and a
delta of 0 refutes it; Rearranges wants every one the same size and +8 refutes it; Expands wants
every one bigger and 0 refutes it. Unknown is the only one left, and that is what it now says,
with the counterexample recorded next to it.

Why this is not documentation

Saturation.RulesUpTo selects by growth. A rule saying Collects or Rearranges is one equality
saturation fires; one left at Unknown is one it does not. So that rule has been in SafeRules on
a claim nobody tested, and a declaration wrong in the shrinking direction tells the saturation a
rewrite is safe to run when nobody established it.

That also means the 262 undeclared rules are not a documentation backlog. Each declaration is a
behavioural change, which is why this PR adds the check and no new declarations.

What is deliberately not done

The rule is not made to collect for real. Declining where it does not shrink would make
Collects true, but that changes what the rule does rather than what it says about itself — and
x * (-1 + 1) folding to zero looks like how -x + x reaches 0, so it would have to be
established that the step is not load-bearing first. Separate change, separate measurement.

What the corpus can and cannot say

It can only refute. Firing on fourteen hundred expressions without contradicting a claim is
evidence for it, not proof, since the claim is about every expression there is. So the test fails on
a contradiction and says nothing about a rule it could not make fire — and the doc now says the same
to anyone declaring one.

84 rules reach the corpus, 18 of them carrying a declaration, and the test asserts both counts so
that it cannot pass by never running.

The other 66

They are the measured evidence for declaring more, and — the useful part — the measurement separates
the ones that can be declared from the ones that cannot:

observed rule
+2 on all 84 firings a-negative-factor-in-a-denominator-comes-out
-2 on all firings two-powers-of-one-base-multiply-by-adding-exponents
0 on all 84 firings a-thing-times-a-quotient-keeps-the-divisor-outermost
-2 to 0 a-term-added-to-itself-doubles — honestly Unknown
0 to +4 a-sum-chain-is-sorted-and-grouped — honestly Unknown

A declaration still has to be arguable from the code; the corpus is what stops one being a guess,
and what refutes it when it is.

Part of #825.

`RewriteRuleGrowth` is a claim about every expression a rule fires on -- Collects
says the replacement is smaller, Rearranges the same size, Expands larger -- and
for the thirty-five rules with a pattern on both sides it is counted from the two
patterns. For the rest it is declared by hand and nothing checked it.

It is worth checking because growth is load-bearing rather than documentation.
Saturation.RulesUpTo selects by it, so a rule saying Collects or Rearranges is one
equality saturation fires and a rule left at Unknown is one it does not. A
declaration wrong in the shrinking direction tells the saturation a rewrite is
safe to run when nobody established that.

Running the declarations against the generated corpus finds one that is wrong.
`a-common-factor-is-collected-out-of-a-whole-sum` said Collects, on the stated
grounds that "the whole point of it is to be smaller". Over fourteen hundred
expressions it fires forty-two times, never shrinks, and grows by as much as eight
nodes: `-x + x + -1/2` becomes `x * (-1 + 1) + -1/2`, seven nodes for seven.
Collects wants every firing smaller, Rearranges every one the same size, Expands
every one bigger, and the measurements refute all three, so Unknown is the only
one of the four that is true and that is what it now says.

Deliberately not fixed by making the rule decline where it does not shrink. That
changes what the rule does rather than what it says about itself, and the folding
that turns `x * (-1 + 1)` into zero would have to be checked first -- `-x + x` may
be reaching zero through exactly this step.

The corpus can only refute. Firing on fourteen hundred expressions without
contradicting a claim is evidence for it and not proof, since the claim is about
every expression there is; the test fails on a contradiction and says nothing
about a rule it could not make fire. Eighty-four rules reach it, eighteen of them
carrying a declaration.

The other sixty-six are the measured evidence for declaring more of them, and it
distinguishes the ones that can be from the ones that cannot:
`a-negative-factor-in-a-denominator-comes-out` is +2 across all eighty-four of its
firings, `two-powers-of-one-base-multiply-by-adding-exponents` is -2 across all of
its, while `a-term-added-to-itself-doubles` ranges -2 to 0 and is honestly
Unknown.

Full suite 9552 passed, 0 failed.

Part of #825.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
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