Skip to content

Commit 958aa71

Browse files
kim-emclaude
andauthored
fix: rename ring variable indices in grind cancel_var proofs (#11410)
This PR fixes a kernel type mismatch error in grind's denominator cleanup feature. When generating proofs involving inverse numerals (like `2⁻¹`), the proof context is compacted to only include variables actually used. This involves renaming variable indices - e.g., if original indices were `{0: r, 1: 2⁻¹}` and only `2⁻¹` is used, it gets renamed to index 0. The bug was that polynomials were correctly renamed via `varRename`, but the variable index `x` stored in `cancelDen` constraints was passed directly to the proof without renaming, causing a mismatch between the polynomial's variable references and the theorem's variable argument. Added `ringVarDecls` to track ring variable indices that need renaming, similar to how `ringPolyDecls` tracks polynomials. The `mkRingContext` function now also renames these variable indices. See zulip discussion at [#nightly-testing > Mathlib status updates @ 💬](https://leanprover.zulipchat.com/#narrow/channel/428973-nightly-testing/topic/Mathlib.20status.20updates/near/560575295). 🤖 Prepared with Claude Code Co-authored-by: Claude <[email protected]>
1 parent fc36b1b commit 958aa71

File tree

2 files changed

+39
-4
lines changed

2 files changed

+39
-4
lines changed

src/Lean/Meta/Tactic/Grind/Arith/Linear/Proof.lean

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ structure ProofM.State where
4040
exprDecls : Std.HashMap LinExpr Expr := {}
4141
ringPolyDecls : Std.HashMap CommRing.Poly Expr := {}
4242
ringExprDecls : Std.HashMap RingExpr Expr := {}
43+
ringVarDecls : Std.HashMap Var Expr := {}
4344

4445
structure ProofM.Context where
4546
ctx : Expr
@@ -90,6 +91,9 @@ def mkRingPolyDecl (p : CommRing.Poly) : ProofM Expr := do
9091
def mkRingExprDecl (e : RingExpr) : ProofM Expr := do
9192
declare! ringExprDecls e
9293

94+
def mkRingVarDecl (x : Var) : ProofM Expr := do
95+
declare! ringVarDecls x
96+
9397
private def mkContext (h : Expr) : ProofM Expr := do
9498
let varDecls := (← get).varDecls
9599
let polyDecls := (← get).polyDecls
@@ -120,12 +124,17 @@ private def mkRingContext (h : Expr) : ProofM Expr := do
120124
unless (← isCommRing) do return h
121125
let ring ← withRingM do CommRing.getRing
122126
let vars := ring.vars
123-
let usedVars := collectMapVars (← get).ringPolyDecls (·.collectVars) >> collectMapVars (← get).ringExprDecls (·.collectVars) <| {}
127+
let ringVarDecls := (← get).ringVarDecls
128+
let usedVars := collectMapVars (← get).ringPolyDecls (·.collectVars) >> collectMapVars (← get).ringExprDecls (·.collectVars) >> collectMapVars ringVarDecls collectVar <| {}
124129
let vars' := usedVars.toArray
125130
let varRename := mkVarRename vars'
126131
let vars := vars'.map fun x => vars[x]!
127132
let h := mkLetOfMap (← get).ringExprDecls h `re (mkConst ``Grind.CommRing.Expr) fun e => toExpr <| e.renameVars varRename
128133
let h := mkLetOfMap (← get).ringPolyDecls h `rp (mkConst ``Grind.CommRing.Poly) fun p => toExpr <| p.renameVars varRename
134+
-- Replace ring variable FVars with their renamed indices
135+
let varFVars := ringVarDecls.toArray.map (·.2)
136+
let varIdsAsExpr := ringVarDecls.toArray.map fun (v, _) => toExpr (varRename v)
137+
let h := h.replaceFVars varFVars varIdsAsExpr
129138
let h := h.abstract #[(← read).ringCtx]
130139
if h.hasLooseBVars then
131140
let struct ← getStruct
@@ -281,7 +290,7 @@ partial def RingIneqCnstr.toExprProof (c' : RingIneqCnstr) : ProofM Expr := do
281290
mkCommRingLTThmPrefix ``Grind.CommRing.lt_cancel_var
282291
else
283292
mkCommRingLEThmPrefix ``Grind.CommRing.le_cancel_var
284-
return mkApp7 h' (toExpr val) (toExpr x) p₁ (← mkRingPolyDecl c'.p) eagerReflBoolTrue h_eq_one h
293+
return mkApp7 h' (toExpr val) (← mkRingVarDecl x) p₁ (← mkRingPolyDecl c'.p) eagerReflBoolTrue h_eq_one h
285294

286295
partial def RingEqCnstr.toExprProof (c' : RingEqCnstr) : ProofM Expr := do
287296
match c'.h with
@@ -299,7 +308,7 @@ partial def RingEqCnstr.toExprProof (c' : RingEqCnstr) : ProofM Expr := do
299308
let h := mkApp5 h (← mkRingPolyDecl c.p) (toExpr (val^n)) p₁ eagerReflBoolTrue (← c.toExprProof)
300309
let h_eq_one := mkApp2 (← mkFieldChar0ThmPrefix ``Grind.CommRing.inv_int_eq') (toExpr val) eagerReflBoolTrue
301310
let h' ← mkCommRingThmPrefix ``Grind.CommRing.eq_cancel_var
302-
return mkApp7 h' (toExpr val) (toExpr x) p₁ (← mkRingPolyDecl c'.p) eagerReflBoolTrue h_eq_one h
311+
return mkApp7 h' (toExpr val) (← mkRingVarDecl x) p₁ (← mkRingPolyDecl c'.p) eagerReflBoolTrue h_eq_one h
303312

304313
partial def RingDiseqCnstr.toExprProof (c' : RingDiseqCnstr) : ProofM Expr := do
305314
match c'.h with
@@ -313,7 +322,7 @@ partial def RingDiseqCnstr.toExprProof (c' : RingDiseqCnstr) : ProofM Expr := do
313322
let h := mkApp5 h (← mkRingPolyDecl c.p) (toExpr (val^n)) p₁ eagerReflBoolTrue (← c.toExprProof)
314323
let h_eq_one := mkApp2 (← mkFieldChar0ThmPrefix ``Grind.CommRing.inv_int_eq') (toExpr val) eagerReflBoolTrue
315324
let h' ← mkCommRingThmPrefix ``Grind.CommRing.diseq_cancel_var
316-
return mkApp7 h' (toExpr val) (toExpr x) p₁ (← mkRingPolyDecl c'.p) eagerReflBoolTrue h_eq_one h
325+
return mkApp7 h' (toExpr val) (← mkRingVarDecl x) p₁ (← mkRingPolyDecl c'.p) eagerReflBoolTrue h_eq_one h
317326

318327
mutual
319328
partial def IneqCnstr.toExprProof (c' : IneqCnstr) : ProofM Expr := caching c' do
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-- Test for fix: grind diseq_cancel_var was using wrong variable index after renaming
2+
-- Previously this would produce a kernel type mismatch error
3+
-- The unused variable `hr` is intentional - it triggers the variable renaming that exposed the bug
4+
set_option linter.unusedVariables false in
5+
example (r : Rat) (hr : r ≤ r) : 2⁻¹ * 2 = (1 : Rat) := by grind
6+
7+
-- Leo's test cases for diseq_cancel_var (from Zulip)
8+
-- These should still work after the fix
9+
open Std Lean.Grind
10+
11+
variable {α : Type} [Field α] [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α] [OrderedRing α] [NoNatZeroDivisors α]
12+
example (a b : α) (h : a = b / 2) : a + a ≤ b := by grind
13+
example (a : α) (h : a ≠ 1/2) : 2*a > 12*a < 1 := by grind
14+
example (a : α) (h : a ≠ (1/2)^3) : 8*a > 18*a < 1 := by grind
15+
example (a : α) (h : a ≠ (1/2)^3) : 8*a > 18*a < 1 := by grind
16+
example (a : α) (h : a ≠ (1/3)*(1/2)^3) : 24*a > 124*a < 1 := by grind
17+
example (a : α) (h : a ≠ b*(2⁻¹)^3) : 8*a > b ∨ 8*a < b := by grind
18+
example (a : α) (h : a ≠ (2⁻¹)^3*b) : 8*a > b ∨ 8*a < b := by grind
19+
example (a : α) (h : 5*(2⁻¹)^3*b ≠ a) : 8*a > 5*b ∨ 8*a < 5*b := by grind
20+
example (a : α) (h : 5*(2⁻¹)*(2⁻¹)^3*b + (3/2)*c ≠ a) : 16*a > 5*b + 24*c ∨ 16*a < 5*b + 24*c := by grind
21+
example (x : α) : x ≥ 1/3 → x ≥ 0 := by grind
22+
example (a : α) (h : a ≠ 1/2 + 1/3) : 6*a > 56*a < 5 := by grind
23+
example (a : α) (h : 1/2 + 1/3 ≠ a) : 6*a > 56*a < 5 := by grind
24+
example (h : (1/4:α) ≠ (1/2)*(1/2)) : False := by grind
25+
example (h : (1/4:α) + 1/4 ≠ (1/2)) : False := by grind
26+
example (h : (1/2:α) + 1/3 ≠ (5/6)) : False := by grind

0 commit comments

Comments
 (0)