lean4-htt/tests/elab/grind_mon_order.lean
Leonardo de Moura 0cd6dbaad2
feat: add Sym.Arith infrastructure for arithmetic normalization (#13289)
This PR adds the shared infrastructure for arithmetic normalization in
`Sym.Arith/`,
laying the groundwork for both `Sym.simp`'s arith pre-simproc and the
eventual
unification of grind's `CommRing` module.

The key components:

- **`Types.lean`**: Classification structures (`Semiring`, `Ring`,
`CommRing`,
`CommSemiring`) stored in a `SymExtension`. These are the
classification-only
subset of grind's ring types — no solver state. Includes
`withExpThreshold` for
  controlling exponent evaluation limits.

- **`EvalNum.lean`**: `evalNat?`/`evalInt?` for evaluating ground
Nat/Int
expressions in type classes (e.g., `IsCharP`), adapted from grind to
`SymM`.

- **`Classify.lean`**: Algebraic structure detection (CommRing > Ring >
CommSemiring > Semiring) with a single `typeClassify : PHashMap ExprPtr
ClassifyResult` cache.
  Detects `IsCharP`, `NoNatZeroDivisors`, and `Field` instances.

- **Type classes**: `MonadCanon`, `MonadRing`/`MonadCommRing`,
`MonadSemiring`/`MonadCommSemiring`, `MonadGetVar`/`MonadMkVar` —
abstract over
the monad so the same code works in both `SymM` and grind's
`RingM`/`SemiringM`.
Grind's `MonadCanon` is deleted; grind's monads inherit it from `SymM`
via
  `MonadLift`.

- **`Functions.lean`**: Cached function getters (`getAddFn`, `getMulFn`,
etc.)
generic over the type classes. Synthesizes instances, validates via
`isDefEqI`,
  canonicalizes via `canonExpr`.

- **`Reify.lean`**: Converts Lean expressions into
`RingExpr`/`SemiringExpr` for
reflection-based normalization. Variable creation abstracted via
`MonadMkVar`.

- **`DenoteExpr.lean`**: Converts reified expressions back to Lean
`Expr`s.
Roundtrip tests confirm reify→denote produces definitionally equal
results.

- **`ToExpr.lean`**, **`VarRename.lean`**, **`Poly.lean`**: Moved from
`Grind.Arith.CommRing/` — pure utilities on `Grind.CommRing` types with
no
  solver dependencies.

- **Tests**: Unit tests for classification (`Int` → commRing, `Nat` →
commSemiring,
`Rat` → commRing), `evalNat?`/`evalInt?`, exp threshold, and
reify-denote roundtrips.
  
  
**TODO**: use abstractions to implement `grind` ring module, and delete
code duplication.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 05:21:09 +00:00

126 lines
4.9 KiB
Text

module
public import Init.Grind.Ring.CommSolver
public import Lean.Meta.Sym.Arith.Poly
open Lean.Grind.CommRing
def w : Var := 0
def x : Var := 1
def y : Var := 2
def z : Var := 3
macro:100 (priority:=high) a:ident "^" k:num : term => `(Power.mk $a $k)
infixr:70 (priority:=high) "." => Mon.mult
instance : Coe Power Mon where
coe p := Mon.mult p .unit
instance : Coe Var Power where
coe x := x^1
example : { x := x, k := 2 : Power} = x^2 := rfl
def check_revlex (m₁ m₂ : Mon) : Bool :=
m₁.revlex m₁ == .eq &&
m₂.revlex m₂ == .eq &&
m₁.revlex m₂ == .lt &&
m₂.revlex m₁ == .gt
example : check_revlex z y := rfl
example : check_revlex z x := rfl
example : check_revlex z w := rfl
example : check_revlex y x := rfl
example : check_revlex y w := rfl
example : check_revlex x w := rfl
example : check_revlex (x^2) x := rfl
example : check_revlex (x^100) x := rfl
example : check_revlex (x^100) (x^88) := rfl
example : check_revlex (z^100) z := rfl
example : check_revlex (w . x^2) (w^2 . x) := rfl
example : check_revlex (w . x^2 . y) (w^2 . x . y) := rfl
example : check_revlex (w^2 . x . z^2) (w^2 . x . y) := rfl
example : check_revlex (x^3 . y^2 . z) (w^1 . x^4 . y^1 . z) := rfl
example : check_revlex (x^3 . y^2 . z) (w^1 . x^4 . y^1) := rfl
example : check_revlex (x^3 . y^2 . z) (w^1 . x^4 . y^1) := rfl
example : check_revlex (w^4 . z) (w^3 . x^2) := rfl
example : check_revlex (w^3 . y) (w^2 . x^2) := rfl
example : check_revlex (w . x^2 . y) (w^4) := rfl
example : check_revlex (w^2 . y^2 . z) (w^2 . y^3) := rfl
example : check_revlex (w^2 . x . z^2) (w^3 . x . z) := rfl
example : Mon.revlex (w^2 . x . z^3) (w^2 . x . z^3) = .eq := rfl
example : check_revlex (w . x^2 . z^5) (w . x^2 . z^2) := rfl
example : check_revlex (w . z^2) (w^10 . x^5 . y^3 . z) := rfl
example : check_revlex (w^2 . x^2 . y) (w^2 . x^2) := rfl
example : check_revlex (w^2 . x^2 . y) (w^2) := rfl
example : check_revlex (w^2 . x^2 . y) (x^2) := rfl
example : check_revlex (z) (w^8) := rfl
example : check_revlex (z) (x^100) := rfl
example : check_revlex (z^100) (z) := rfl
example : check_revlex (x^2 . y^2 . z^5) (x^2 . y^3 . z^4) := rfl
example : Mon.div (w^2 . y^2 . z) (w^2 . y) = y . z := rfl
example : Mon.div (w^2 . y^2 . z) y = w^2 . y . z := rfl
example : Mon.div (w^2 . y^2 . z) (y^2) = w^2 . z := rfl
example : Mon.div (w^2 . y^4) .unit = w^2 . y^4 := rfl
example : Mon.div (w^2 . y^4) (w^2 . y^4) = .unit := rfl
example : Mon.div (w^2 . y^4) (w^2 . y^5) = .unit := rfl
example : Mon.div (w^5) w = w^4 := rfl
example : Mon.div (w^5 . x^3 . y^2) (w^2 . x) = w^3 . x^2 . y^2 := rfl
example : Mon.div (y^2 . z^3) (y . z) = y . z^2 := rfl
example : Mon.div (x . y) (x . y) = .unit := rfl
example : Mon.div (w . x^2 . y) (w . x . y) = x := rfl
example : Mon.divides (x^2) (w^5) = false := rfl
def check_divides (m₁ m₂ : Mon) :=
m₂.divides m₁ && (m₁ == m₂ || !m₁.divides m₂)
example : check_divides (w^5) w := rfl
example : check_divides (w^2 . y^2 . z) (w^2 . y) := rfl
example : check_divides (w^2 . y^2 . z) y := rfl
example : check_divides (w^2 . y^2 . z) (y^2) := rfl
example : check_divides (w^2 . y^4) .unit := rfl
example : check_divides (w^2 . y^4) (w^2 . y^4) := rfl
example : check_divides (w^5) w := rfl
example : check_divides (w^5 . x^3 . y^2) (w^2 . x) := rfl
example : check_divides (x . y) (x . y) := rfl
#eval Mon.lcm Mon.unit (w^3 . y^2)
def check_lcm (m₁ m₂ r : Mon) :=
m₁.lcm m₁ == m₁ &&
m₂.lcm m₂ == m₂ &&
m₁.lcm m₂ == r &&
m₂.lcm m₁ == r
example : check_lcm (.unit) (w^3 . y^2) (w^3 . y^2) := by native_decide
example : check_lcm (w^3 . y^2) Mon.unit (w^3 . y^2) := by native_decide
example : check_lcm (w^2) (w^5) (w^5) := by native_decide
example : check_lcm x y (x . y) := by native_decide
example : check_lcm y z (y . z) := by native_decide
example : check_lcm (w^2 . x^3) (w^5 . x . y^2) (w^5 . x^3 . y^2) := by native_decide
example : check_lcm (w . x . y) z (w . x . y . z) := by native_decide
example : check_lcm (x^2 . y^3) (x^2 . y^5) (x^2 . y^5) := by native_decide
example : check_lcm (w^100 . x^2) (x^50 . y) (w^100 . x^50 . y) := by native_decide
def a : Var := 0
def b : Var := 1
def c : Var := 2
example : check_revlex (c) (a) := rfl
example : check_revlex (c) (a . b) := rfl
example : check_revlex (a . b . c) (c) := rfl
example : check_revlex (c) (b) := rfl
example : check_revlex (b) (a) := rfl
example : check_revlex (c^2) (c) := rfl
example : check_revlex (b . c) (a . c) := rfl
example : check_revlex (b . c) (c) := rfl
example : check_revlex (b^2 . c) (c) := rfl
example : check_revlex (a . c) (c) := rfl
example : check_revlex (a . b . c) (c) := rfl
example : check_revlex (a . b . c) (b . c) := rfl
example : check_revlex (a . b . c) (a . c) := rfl
example : check_revlex (a . b . c) (a . b) := rfl
example : check_revlex (a^2 . b . c) (a . b . c) := rfl
example : check_revlex (b . c) (b) := rfl
example : check_revlex (a . c) (a) := rfl
example : check_revlex (b) (a) := rfl