Skip to content

Show the rewrites worth reading, not all 270 of them (#28) - #978

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
feat/derivation-steps
Aug 17, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
feat/derivation-steps

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

Closes #28.

RewriteRecording collects every rewrite that fires. On the expression #28 was filed about, that is 270 of them, cycling, almost all the canonical-order sort:

Input: x^(-1)/(y/z)
Output[0]: 1 * x ^ (-1)                                 // Mulf or Divf -> SortAndGroup(...)
Output[1]: y * z ^ (-1)                                 // Mulf or Divf -> SortAndGroup(...)
Output[2]: 1 * x ^ (-1) * y ^ (-1) * (z ^ (-1)) ^ (-1)  // Mulf or Divf -> SortAndGroup(...)
Output[3]: 1 * x ^ (-1)                                 // ... and it repeats
total steps: 270

The mechanism was there; the answer was not readable. Derivation is the same list with two things taken out — rewrites from sets that only put an expression into a canonical shape (251 of the 270 here), and rewrites already shown, since the simplifier explores several candidate forms and rewrites the same subexpression the same way in each.

270 becomes 5, and they are what the reporter asked for:

Input: x^(-1)/(y/z)      (270 recorded, 5 in the derivation)
  Output[0]: 1 * 1 / (x * y)      // Mulf(Divf(a1,a2), Divf(a3,a4)) -> a1*a3/(a2*a4)
  Output[1]: 1 * z / 1            // Divf(var any1, Divf(var any2, var any3)) -> any1 * any3 / any2
  ...
  final: z / (x * y)

Output[1] is the rule the reporter wrote out by hand — any1 / (any2 / any3) -> any1 * any3 / any2 — named, with the subexpression it fired on. x + x and sin(x)^2 + cos(x)^2 come back as one step each.

Normalisation is declared, not detected

RewriteRuleSet.IsNormalization, true for the three sorts and nothing else. Reordering y + x to x + y and collapsing x + x to 2 * x are both equivalences that change the tree; which one was meant as tidying is a fact about intent that no amount of looking at the rewrite settles. So the set says.

What this is not, and the docs say so

It is a set of rewrites, not a path. Simplify searches candidates and keeps the best, so some entries belong to branches that lost — visible on (x + 1) * (x - 1), whose derivation contains a difference-of-squares step to (x - sqrt(1)) * (x + sqrt(1)) that is not on the route to x ^ 2 - 1. And a step's Before is a subexpression, not the whole expression at that moment.

A single path with whole-expression stages needs Simplificator.Alternate to record which candidate each rewrite belonged to. That is not here.

The design I tried first, and why it is not this

I assumed the fix was recording whole-expression transitions rather than node-level ones, and measured before building it: 509 rule-set applications, 214 of them changing the expression, against 270 node-level steps. Recording more faithfully is worse. The problem is not granularity, it is that most of what happens is not worth reading — so the answer had to be selection.

Tests and checks

Four tests added, including one asserting the raw recording does contain normalisation, so the filter test cannot pass vacuously.

Public API is exactly two additive members:

+ RewriteRecording.Derivation { } : IReadOnlyList<RewriteStep>
+ RewriteRuleSet.IsNormalization { } : Boolean

Suite 7317 passed, 0 failed, 14 skipped.

One thing worth stating rather than hiding: an earlier run of this suite had OneSidedLimitTest.ADifferenceOfReciprocalLogarithms(Left) fail, taking 1 m against 11 s in isolation. It passes in isolation, it passed on the re-run, and master ran clean — and this change is data-only, so nothing in the library reads either new member. I believe it is a flake in a test this repo has recorded as timing-sensitive before, but I would rather say so than report a first-time-green suite.

RewriteRecording collects every rewrite that fires. On the expression #28 was
filed about, `x^(-1)/(y/z)`, that is 270 of them, cycling, almost all the
canonical-order sort. The mechanism was there and the answer was not readable.

Derivation is the same list with two things taken out:

- rewrites from sets that only put an expression into a canonical shape, which
  is 251 of the 270 here;
- rewrites already shown, since the simplifier explores several candidate forms
  and rewrites the same subexpression the same way in each.

270 becomes 6, and the six are what the reporter asked for. Their Output[0] was
`any1 / (any2 / any3) -> any1 * any3 / any2` written out by hand; that rule is
in the derivation, named, with the subexpression it fired on. `x + x` and
`sin(x)^2 + cos(x)^2` come back as one step each.

Normalisation is *declared* by the rule set rather than detected. Reordering
`y + x` to `x + y` and collapsing `x + x` to `2 * x` are both equivalences that
change the tree; which one was meant as tidying is a fact about intent that no
amount of looking at the rewrite settles. Hence RewriteRuleSet.IsNormalization,
true for the three sorts and nothing else.

**What this is not, and the docs say so.** It is a set of rewrites, not a path.
Simplify searches candidates and keeps the best, so some entries belong to
branches that lost -- visible on `(x + 1) * (x - 1)`, whose derivation contains
a difference-of-squares step to `(x - sqrt(1)) * (x + sqrt(1))` that is not on
the route to `x ^ 2 - 1`. And a step's Before is a subexpression, not the whole
expression at that moment. A single path with whole-expression stages needs
Simplificator.Alternate to record which candidate each rewrite belonged to,
which it does not do.

Recording whole-expression transitions instead was tried first and measured
worse: 509 rule-set applications, 214 of them changing, against 270 node
rewrites. Volume is not what stands between the recording and an explanation;
selection is.

Public API: two additive members. Suite 7317 passed, 0 failed, 14 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Rafael-SOWNet
Rafael-SOWNet merged commit 6cd78b9 into master Aug 17, 2026
25 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.

Add a way to collect intermediate pattern replacements when simplifying

1 participant