Skip to content

refactor: replace grind canonicalizer with type-directed normalizer#13166

Merged
leodemoura merged 10 commits intomasterfrom
lean-canon-types
Mar 28, 2026
Merged

refactor: replace grind canonicalizer with type-directed normalizer#13166
leodemoura merged 10 commits intomasterfrom
lean-canon-types

Conversation

@leodemoura
Copy link
Copy Markdown
Member

@leodemoura leodemoura commented Mar 28, 2026

This PR replaces the grind canonicalizer with a new type-directed normalizer (Sym.canon) that goes inside binders and applies targeted reductions in type positions, eliminating the O(n^2) isDefEq-based approach.

The old canonicalizer maintained a map from (function, argument_position) to previously seen arguments, iterating the list and calling isDefEq for each new argument. This produced performance problems in some goal. For example, for a goal containing n numeric literals, it would produce O(n^2) isDefEq comparisons.

The new canonicalizer normalizes types directly:

  • Instances: re-synthesized via synthInstance with the type normalized first, so OfNat (Fin (2+1)) 0 and OfNat (Fin 3) 0 produce the same instance.
  • Types: normalized with targeted reductions — eta, projection, match/ite/cond, and Nat arithmetic (n.succ + 1n + 2, 2 + 13).
  • Values: traversed but not reduced, preserving lambdas for grind's beta module.

The canonicalizer enters binders (the old one did not), using separate caches for type-level and value-level contexts. Propositions are not normalized to avoid interfering with grind's proposition handling.

Move SynthInstance from Grind to Sym since the canonicalizer now lives in Sym and needs instance synthesis. The Grind namespace re-exports the key functions.

Add no_index annotations to val_addNat and val_castAdd patterns in Fin/Lemmas.lean — arithmetic in type positions is now normalized, so patterns must not rely on the un-normalized form for e-matching indexing.

leodemoura and others added 6 commits March 27, 2026 14:39
Add `Sym/Canon.lean` with a type-directed canonicalizer that normalizes
expressions in type positions. Performs eta reduction, projection
reduction, match/ite reduction, and basic Nat normalization. Instances
are canonicalized via `synthInstance`.

This is the foundation for replacing `isDefEq`-based type canonicalization
in grind's `canonImpl` with direct normalization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expand the module docstring with sections on reductions, instance
canonicalization, and caching strategy. Add docstrings to `reduceProjFn?`,
`checkDefEqInst`, and the public `canon` entry point. Rename
`isToNormArithDecl` to `isNatArithApp` for clarity. Fix typo in
`canonInsideType'` docstring.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire `Sym.Canon.canon` into `grind`'s `canonImpl`, replacing the old
`isDefEq`-based type canonicalization with direct normalization.

The new canonicalizer goes inside binders and normalizes types via eta
reduction, projection reduction, match/ite reduction, and Nat arithmetic
normalization. Instances are re-synthesized via `synthInstance` with the
type normalized first, ensuring `OfNat (Fin (2+1)) 0` and
`OfNat (Fin 3) 0` produce the same canonical instance.

Add `no_index` annotations to `val_addNat` and `val_castAdd` patterns
in `Fin/Lemmas.lean` — arithmetic in type positions is now normalized,
so patterns must not rely on the un-normalized form for indexing.

Update test expected outputs for `grind_12140` (cleaner diagnostics),
`grind_9572` (eta-reduced output), `grind_array_attach` (disable
out-of-scope theorems), `grind_indexmap_trace` (hash changes).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
and delete old canonicalizer.
@leodemoura leodemoura requested a review from kim-em as a code owner March 28, 2026 01:55
@leodemoura leodemoura added the changelog-tactics User facing tactics label Mar 28, 2026
@leodemoura leodemoura enabled auto-merge March 28, 2026 01:58
@leodemoura leodemoura disabled auto-merge March 28, 2026 02:00
@leodemoura leodemoura added this pull request to the merge queue Mar 28, 2026
Merged via the queue into master with commit 6871aba Mar 28, 2026
17 checks passed
@github-actions github-actions bot added the toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN label Mar 28, 2026
@mathlib-lean-pr-testing
Copy link
Copy Markdown

Mathlib CI status (docs):

  • ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the nightly-with-mathlib branch. Try git rebase d0d135dbe2c7869a8705e98991a455611f49e310 --onto 4786e082dca22873d14d2a5b9b7c8843380c6e78. You can force Mathlib CI using the force-mathlib-ci label. (2026-03-28 03:23:33)

@leanprover-bot
Copy link
Copy Markdown
Collaborator

Reference manual CI status:

  • ❗ Reference manual CI will not be attempted unless your PR branches off the nightly-with-manual branch. Try git rebase d0d135dbe2c7869a8705e98991a455611f49e310 --onto cfa8c5a036d6990635c6ec50b02d0e806995cec3. You can force reference manual CI using the force-manual-ci label. (2026-03-28 03:23:35)

kim-em added a commit to kim-em/lean4 that referenced this pull request Mar 31, 2026
After leanprover#13166, the canonicalizer normalizes Nat arithmetic in type positions
(e.g. `n + 1 + 1` → `n + 2` in `Fin`) but leaves value-level expressions
unchanged. When `toPoly` processes `↑n + 1 + 1` (in Int), the middle `1`
appears as a numeral in a non-tail position and `addMonomial` would fall
back to treating it as a variable.

Fix: use `Poly.addConst` (which already exists in `Init.Data.Int.Linear`)
to fold the numeral into the polynomial's constant term, regardless of
position.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kim-em added a commit to leanprover-community/mathlib4-nightly-testing that referenced this pull request Mar 31, 2026
After leanprover/lean4#13166, grind can no longer see through the chain
of semireducible defs connecting `J.toCoverage ≤ K.toCoverage` with
`∀ ⦃X⦄, ∀ S ∈ J.coverings X, Sieve.generate S ∈ K X`. The new lemma
states this defeq explicitly so it can be passed as a grind hint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kim-em added a commit to leanprover-community/mathlib4-nightly-testing that referenced this pull request Mar 31, 2026
The grind calls in fix_aux were introduced in leanprover-community#35682 and broke after
leanprover/lean4#13166. test/grind_mwe.lean contains a confirmed regression
MWE (passes nightly-2026-03-25, fails nightly-2026-03-30).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
volodeyka pushed a commit that referenced this pull request Apr 16, 2026
…13166)

This PR replaces the `grind` canonicalizer with a new type-directed
normalizer (`Sym.canon`) that goes inside binders and applies targeted
reductions in type positions, eliminating the O(n^2) `isDefEq`-based
approach.

The old canonicalizer maintained a map from `(function,
argument_position)` to previously seen arguments, iterating the list and
calling `isDefEq` for each new argument. This produced performance
problems in some goal. For example, for a goal containing `n` numeric
literals, it would produce O(n^2) `isDefEq` comparisons.

The new canonicalizer normalizes types directly:
- **Instances**: re-synthesized via `synthInstance` with the type
normalized first, so `OfNat (Fin (2+1)) 0` and `OfNat (Fin 3) 0` produce
the same instance.
- **Types**: normalized with targeted reductions — eta, projection,
match/ite/cond, and Nat arithmetic (`n.succ + 1` → `n + 2`, `2 + 1` →
`3`).
- **Values**: traversed but not reduced, preserving lambdas for grind's
beta module.

The canonicalizer enters binders (the old one did not), using separate
caches for type-level and value-level contexts. Propositions are not
normalized to avoid interfering with grind's proposition handling.

Move `SynthInstance` from `Grind` to `Sym` since the canonicalizer now
lives in `Sym` and needs instance synthesis. The `Grind` namespace
re-exports the key functions.

Add `no_index` annotations to `val_addNat` and `val_castAdd` patterns in
`Fin/Lemmas.lean` — arithmetic in type positions is now normalized, so
patterns must not rely on the un-normalized form for e-matching
indexing.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog-tactics User facing tactics toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants