Skip to content

Two rule sets run on the matcher rather than on their switch - #1052

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
feat/two-rule-sets-run-on-the-matcher
Aug 24, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
feat/two-rule-sets-run-on-the-matcher

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

Part of #746 tier 1 (#248). Depends on #1050, which is merged.

The gap this closes

#746's tier 1 row records pattern matching as data as delivered, and then says what is wrong with
that:

Pattern matching as data (#248) was recorded as done and is overstated: the engine exists, with
commutative and n-ary matching, and nothing runs on it — every type in
Core/Transformations/Matching is internal, five rule sets are expressed as data, and 0 of the
30 registered sets use the matcher at runtime. The one exchange that was made was measured at +5%
of Simplify and reverted.

The blocker was that number, and it was a fair one: 5% for one of thirty sets does not scale to
thirty. #1050 changed it, by settling a pattern's determinism once rather than on every attempt.

Re-measured against Simplify, not against a microbenchmark

A microbenchmark ratio is how the 5% became a surprise the first time, so this is measured against
Simplify itself — both arms in one process, interleaved, with a third arm that is the switch
again
as a control, because comparing two runs on this machine measures the machine:

switch   median 539 ms over 40 reps  (min 533, max 679)
data     median 536 ms over 40 reps  (min 534, max 716)     data    / switch = -0.6%
control  median 533 ms over 40 reps  (min 532, max 628)     control / switch = -1.1%

The control differs from its own source code by more than the change does. Allocation is
38,181,736 B against 38,196,896 B, +0.04%. Both arms were checked to give the same answer on
every input before either was timed.

What is exchanged, and why only these two

DivisionPreparing and CollapseMultipleFractions are the two sets whose data form is proven to
agree with the switch it mirrors, over generated expressions, with a minimum firing count so the
agreement cannot be vacuous — MatchedRulesAgreeWithTheSwitchTest. That proof is the precondition
for running one in place of the other, and the other three data sets do not have it: PowerOfPower
is checked against a switch that does more than it, and PythagoreanIdentity has nothing to agree
with by design.

The switch stays, and is not dead code

RewriteRule carries a PatternSource and a SourceLine, which RuleRegistryGenerator reads off
the arms of a switch. It has no way yet to read a rule written as data, so the addressable rules of
these two sets still come from Patterns — and the arms they describe are exactly the ones the
agreement test holds the matcher to, rather than code nothing runs. Teaching the generator to read
MatchedRules is what would let the switch go, and is #825.

This is stated in the doc comment rather than left for a reader to discover, because "the registry
describes a switch that no longer executes" is the kind of thing that is true and invisible.

Evidence

  • Suite 8553 passed, 0 failed, 14 skipped — the same as master.
  • rulecheck, canoncheck, casbench and simpsweep re-run against this branch are
    byte-identical to master's committed reports, ignoring the commit line and timings.
    rulecheck reports 0 value changes across 1005 applications of 30 sets, and it is the harness
    that attributes a change to a named set — so a difference in either of these two would have been
    named rather than averaged away.

After this, #746 tier 1's "0 of the 30 registered sets use the matcher at runtime" reads 2 of 30.

#746 tier 1 records pattern matching as data (#248) as delivered and then says
what is wrong with that: the engine exists and **nothing runs on it** -- every
type in `Core/Transformations/Matching` is internal, five sets are expressed as
data, and 0 of the 30 registered sets used the matcher at run time. The reason
was a number. Swapping `DivisionPreparing` for its data form had been measured
at about 5% of `Simplify`, and 5% for one of thirty sets is not affordable.

#1050 changed that number by settling a pattern's determinism once instead of on
every attempt. Re-measured against `Simplify` itself -- both arms in one process,
plus a third arm that is the switch again as a control, because comparing two
runs on this machine measures the machine:

    switch   median 539 ms
    data     median 536 ms      -0.6%
    control  median 533 ms      -1.1%   <- the switch against itself

The change is smaller than this machine's disagreement with itself. Allocation
is 38,181,736 B against 38,196,896 B, +0.04%.

So `DivisionPreparing` and `CollapseMultipleFractions` now execute as data. They
are the two whose data form is proven to agree with the switch it mirrors over
generated expressions, with a minimum firing count so agreement cannot be
vacuous -- which is the precondition for running one in place of the other.

The switch stays, and is not dead code. `RewriteRule` carries a `PatternSource`
and a `SourceLine` that `RuleRegistryGenerator` reads off a switch's arms, and it
cannot yet read a rule written as data, so the addressable rules of these two
sets still come from `Patterns` and describe arms the agreement test holds the
matcher to. Teaching the generator to read `MatchedRules` is what would let the
switch go, and is #825.

Measured, not assumed: suite 8553/0, and rulecheck, canoncheck, casbench and
simpsweep are byte-identical to master's committed reports -- rulecheck reporting
0 value changes across 1005 applications of 30 sets, which is the harness that
attributes a change to a named set.

Part of #746 tier 1 (#248).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bjumi5K7fg8yx6UK1mZTQd
@Rafael-SOWNet
Rafael-SOWNet merged commit 96f04b8 into master Aug 24, 2026
31 checks passed
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