Skip to content

Commit 1d971c8

Browse files
authored
feat: Rabinowitsch transformation in grind (#8789)
This PR implements the Rabinowitsch transformation for `Field` disequalities in `grind`. For example, this transformation is necessary for solving: ```lean example [Field α] (a : α) : a^2 = 0 → a = 0 := by grind ```
1 parent 82c2c4c commit 1d971c8

18 files changed

+152
-66
lines changed

src/Init/Grind/CommRing/Poly.lean

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,7 @@ open Classical in
12491249
theorem inv_split {α} [Field α] (a : α) : if a = 0 then a⁻¹ = 0 else a * a⁻¹ = 1 := by
12501250
split
12511251
next h => simp [h, Field.inv_zero]
1252-
next h => rw [CommRing.mul_comm, Field.inv_mul_cancel h]
1252+
next h => rw [Field.mul_inv_cancel h]
12531253

12541254
def one_eq_zero_unsat_cert (p : Poly) :=
12551255
p == .num 1 || p == .num (-1)
@@ -1259,6 +1259,16 @@ theorem one_eq_zero_unsat {α} [Field α] (ctx : Context α) (p : Poly) : one_eq
12591259
next => rw [Eq.comm]; apply Field.zero_ne_one
12601260
next => rw [← neg_eq_zero, neg_neg, Eq.comm]; apply Field.zero_ne_one
12611261

1262+
theorem diseq_to_eq {α} [Field α] (a b : α) : a ≠ b → (a - b)*(a - b)⁻¹ = 1 := by
1263+
intro h
1264+
have : a - b ≠ 0 := by
1265+
intro h'; rw [Ring.sub_eq_zero_iff.mp h'] at h
1266+
contradiction
1267+
exact Field.mul_inv_cancel this
1268+
1269+
theorem diseq0_to_eq {α} [Field α] (a : α) : a ≠ 0 → a*a⁻¹ = 1 := by
1270+
exact Field.mul_inv_cancel
1271+
12621272
end CommRing
12631273

12641274
end Lean.Grind

src/Lean/Meta/Tactic/Grind/Arith/CommRing.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,6 @@ builtin_initialize registerTraceClass `grind.debug.ring.check
3939
builtin_initialize registerTraceClass `grind.debug.ring.impEq
4040
builtin_initialize registerTraceClass `grind.debug.ring.simpBasis
4141
builtin_initialize registerTraceClass `grind.debug.ring.basis
42+
builtin_initialize registerTraceClass `grind.debug.ring.rabinowitsch
4243

4344
end Lean

src/Lean/Meta/Tactic/Grind/Arith/CommRing/DenoteExpr.lean

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
66
prelude
77
import Lean.Meta.Tactic.Grind.Arith.CommRing.Util
88
import Lean.Meta.Tactic.Grind.Arith.CommRing.Var
9+
import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
910

1011
namespace Lean.Meta.Grind.Arith.CommRing
1112
/-!
@@ -16,13 +17,7 @@ variable [Monad M] [MonadGetRing M]
1617

1718
def denoteNum (k : Int) : M Expr := do
1819
let ring ← getRing
19-
let n := mkRawNatLit k.natAbs
20-
let ofNatInst := mkApp3 (mkConst ``Grind.Semiring.ofNat [ring.u]) ring.type ring.semiringInst n
21-
let n := mkApp3 (mkConst ``OfNat.ofNat [ring.u]) ring.type n ofNatInst
22-
if k < 0 then
23-
return mkApp ring.negFn n
24-
else
25-
return n
20+
return denoteNumCore ring.u ring.type ring.semiringInst ring.negFn k
2621

2722
def _root_.Lean.Grind.CommRing.Power.denoteExpr (pw : Power) : M Expr := do
2823
let x := (← getRing).vars[pw.x]!

src/Lean/Meta/Tactic/Grind/Arith/CommRing/EqCnstr.lean

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Leonardo de Moura
55
-/
66
prelude
7+
import Lean.Meta.Tactic.Grind.ProveEq
78
import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
89
import Lean.Meta.Tactic.Grind.Arith.CommRing.Proof
910
import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
@@ -282,6 +283,36 @@ def processNewEqImpl (a b : Expr) : GoalM Unit := do
282283
let p ← (ra.sub rb).toPolyM
283284
addNewEq (← mkEqCnstr p (.core a b ra rb))
284285

286+
private def pre (e : Expr) : GoalM Expr := do
287+
-- We must canonicalize because the instances generated by this module may not match
288+
-- the ones selected by the canonicalizer
289+
shareCommon (← canon e)
290+
291+
private def diseqToEq (a b : Expr) : RingM Unit := do
292+
-- Rabinowitsch transformation
293+
let gen := max (← getGeneration a) (← getGeneration b)
294+
let ring ← getRing
295+
let some fieldInst := ring.fieldInst? | unreachable!
296+
let e ← pre <| mkApp2 ring.subFn a b
297+
modifyRing fun s => { s with invSet := s.invSet.insert e }
298+
let eInv ← pre <| mkApp (← getRing).invFn?.get! e
299+
let lhs ← pre <| mkApp2 ring.mulFn e eInv
300+
internalize lhs gen none
301+
trace[grind.debug.ring.rabinowitsch] "{lhs}"
302+
pushEq lhs ring.one <| mkApp5 (mkConst ``Grind.CommRing.diseq_to_eq [ring.u]) ring.type fieldInst a b (← mkDiseqProof a b)
303+
304+
private def diseqZeroToEq (a b : Expr) : RingM Unit := do
305+
-- Rabinowitsch transformation for `b = 0` case
306+
let gen ← getGeneration a
307+
let ring ← getRing
308+
let some fieldInst := ring.fieldInst? | unreachable!
309+
modifyRing fun s => { s with invSet := s.invSet.insert a }
310+
let aInv ← pre <| mkApp (← getRing).invFn?.get! a
311+
let lhs ← pre <| mkApp2 ring.mulFn a aInv
312+
internalize lhs gen none
313+
trace[grind.debug.ring.rabinowitsch] "{lhs}"
314+
pushEq lhs ring.one <| mkApp4 (mkConst ``Grind.CommRing.diseq0_to_eq [ring.u]) ring.type fieldInst a (← mkDiseqProof a b)
315+
285316
@[export lean_process_ring_diseq]
286317
def processNewDiseqImpl (a b : Expr) : GoalM Unit := do
287318
let some ringId ← inSameRing? a b | return ()
@@ -290,6 +321,13 @@ def processNewDiseqImpl (a b : Expr) : GoalM Unit := do
290321
let some ra ← toRingExpr? a | return ()
291322
let some rb ← toRingExpr? b | return ()
292323
let p ← (ra.sub rb).toPolyM
324+
if (← isField) then
325+
unless p matches .num _ do
326+
if rb matches .num 0 then
327+
diseqZeroToEq a b
328+
else
329+
diseqToEq a b
330+
return ()
293331
addNewDiseq {
294332
lhs := a, rhs := b
295333
rlhs := ra, rrhs := rb

src/Lean/Meta/Tactic/Grind/Arith/CommRing/Internalize.lean

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ private def isForbiddenParent (parent? : Option Expr) : Bool :=
3535
-- Remark: `HDiv` should appear in `getType?` as soon as we add support for `Field`,
3636
-- `LE.le` linear combinations
3737
match_expr parent with
38+
| LT.lt _ _ _ _ => true
3839
| LE.le _ _ _ _ => true
3940
| HDiv.hDiv _ _ _ _ _ _ => true
4041
| HMod.hMod _ _ _ _ _ _ => true
4142
| _ => false
4243
else
43-
true
44+
false
4445

4546
private partial def toInt? (e : Expr) : RingM (Option Int) := do
4647
match_expr e with

src/Lean/Meta/Tactic/Grind/Arith/CommRing/RingId.lean

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ import Lean.Meta.Tactic.Grind.Arith.CommRing.Util
1010

1111
namespace Lean.Meta.Grind.Arith.CommRing
1212

13+
def denoteNumCore (u : Level) (type : Expr) (semiringInst : Expr) (negFn : Expr) (k : Int) : Expr :=
14+
let n := mkRawNatLit k.natAbs
15+
let ofNatInst := mkApp3 (mkConst ``Grind.Semiring.ofNat [u]) type semiringInst n
16+
let n := mkApp3 (mkConst ``OfNat.ofNat [u]) type n ofNatInst
17+
if k < 0 then
18+
mkApp negFn n
19+
else
20+
n
21+
1322
private def internalizeFn (fn : Expr) : GoalM Expr := do
1423
shareCommon (← canon fn)
1524

@@ -103,6 +112,10 @@ def getRingId? (type : Expr) : GoalM (Option Nat) := do
103112
else
104113
let id? ← go?
105114
modify' fun s => { s with typeIdOf := s.typeIdOf.insert { expr := type } id? }
115+
if let some id := id? then
116+
-- Internalize helper constants
117+
let ring := (← get').rings[id]!
118+
internalize ring.one 0
106119
return id?
107120
where
108121
go? : GoalM (Option Nat) := do
@@ -142,14 +155,15 @@ where
142155
let powFn ← getPowFn type u semiringInst
143156
let intCastFn ← getIntCastFn type u ringInst
144157
let natCastFn ← getNatCastFn type u semiringInst
145-
let (invFn?, divFn?)if fieldInst?.isSome then
146-
pure (some (← getInvFn type u), some (← getDivFn type u))
158+
let invFn? ← if fieldInst?.isSome then
159+
pure (some (← getInvFn type u))
147160
else
148-
pure (none, none)
161+
pure none
162+
let one ← shareCommon <| (← canon <| denoteNumCore u type semiringInst negFn 1)
149163
let id := (← get').rings.size
150164
let ring : Ring := {
151165
id, type, u, semiringInst, ringInst, commSemiringInst, commRingInst, charInst?, noZeroDivInst?, fieldInst?,
152-
addFn, mulFn, subFn, negFn, powFn, intCastFn, natCastFn, invFn?, divFn? }
166+
addFn, mulFn, subFn, negFn, powFn, intCastFn, natCastFn, invFn?, one }
153167
modify' fun s => { s with rings := s.rings.push ring }
154168
return some id
155169

src/Lean/Meta/Tactic/Grind/Arith/CommRing/Types.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,7 @@ structure Ring where
153153
natCastFn : Expr
154154
/-- Inverse if `fieldInst?` is `some inst` -/
155155
invFn? : Option Expr
156-
/-- Division if `fieldInst?` is `some inst` -/
157-
divFn? : Option Expr
156+
one : Expr
158157
/--
159158
Mapping from variables to their denotations.
160159
Remark each variable can be in only one ring.

tests/lean/grind/algebra/field_normalization.lean

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,6 @@ open Lean.Grind
22

33
variable (R : Type u) [Field R]
44

5-
example (a : R) : (1 / 2) * a = a / 2 := by grind
6-
example (a : R) : 2⁻¹ * a = a / 2 := by grind
7-
example (a : R) : a⁻¹⁻¹ = a := by grind
8-
95
example (a : R) : (2 * a)⁻¹ = a⁻¹ / 2 := by grind
106

11-
example [IsCharP R 0] (a : R) : a / 2 + a / 3 = 5 * a / 6 := by grind
12-
13-
example (_ : x ≠ 0) (_ : z ≠ 0) : w / x + y / z = (w * z + y * x) / (x * z) := by grind
14-
example (_ : x * z ≠ 0) : w / x + y / z = (w * z + y * x) / (x * z) := by grind
15-
16-
example {x y z w : R} (h : x / y = z / w) (hy : y ≠ 0) (hw : w ≠ 0) : x * w = z * y := by
17-
grind
18-
19-
example (a : R) (_ : 2 * a ≠ 0) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
20-
example [IsCharP R 0] (a : R) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
21-
example [NoNatZeroDivisors R] (a : R) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
227
example (a : R) (_ : (2 : R) ≠ 0) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
23-
24-
example (a b : R) (_ : a ≠ 0) (_ : b ≠ 0) : a / (a / b) = b := by grind
25-
example (a b : R) (_ : a ≠ 0) : a / (a / b) = b := by grind
26-
27-
-- TODO: create a mock implementation of `ℚ` for testing purposes.
28-
variable (ℚ : Type) [Field ℚ] [IsCharP ℚ 0]
29-
30-
example (x : ℚ) (h₀ : x ≠ 0) :
31-
(4 / x)⁻¹ * ((3 * x^3) / x)^2 * ((1 / (2 * x))⁻¹)^3 = 18 * x^8 := by grind

tests/lean/grind/ring_regressions.lean

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,3 @@ example (n : Nat) :
66
example (a b : Nat) : 3 * a * b = a * b * 3 := by grind
77

88
example (k z : Nat) : k * (z * 2 * (z * 2 + 1)) = z * (k * (2 * (z * 2 + 1))) := by grind
9-
10-
open Lean.Grind
11-
12-
example [Field R] (x : R) (cos : R → R) :
13-
(cos x ^ 2 + (2 * cos x ^ 2 - 1) ^ 2 + (4 * cos x ^ 3 - 3 * cos x) ^ 2 - 1) / 4 =
14-
cos x * (cos x ^ 2 - 1 / 2) * (4 * cos x ^ 3 - 3 * cos x) := by
15-
grind
16-
17-
example [Field R] {sqrtTwo a b c : R} :
18-
sqrtTwo / 32 * ((a - b) ^ 2 + (b - c) ^ 2 + (c - a) ^ 2 + (-(a + b + c)) ^ 2) ^ 2 =
19-
9 * sqrtTwo / 32 * (a ^ 2 + b ^ 2 + c ^ 2) ^ 2 := by
20-
grind
21-
22-
example [Field R] [LinearOrder R] [Ring.IsOrdered R] (x y z : R) (hx : x > 0) (hy : y > 0) (hz : z > 0) (h : x * y * z ≥ 1) :
23-
(x ^ 2 - y * z) / (x ^ 2 + y ^ 2 + z ^ 2) + (y ^ 2 - z * x) / (y ^ 2 + z ^ 2 + x ^ 2) +
24-
(z ^ 2 - x * y) / (z ^ 2 + x ^ 2 + y ^ 2) =
25-
1 / 2 * ((x - y) ^ 2 + (y - z) ^ 2 + (z - x) ^ 2) / (x ^ 2 + y ^ 2 + z ^ 2) := by grind

tests/lean/run/grind_const_pattern.lean

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ h : ¬a = 10
1818
[eqc] False propositions
1919
[prop] a = 10
2020
[cutsat] Assignment satisfying linear constraints
21-
[assign] a := 1
21+
[assign] a := 2
2222
-/
2323
#guard_msgs (error) in
2424
example : a = 5 + 5 := by
@@ -49,8 +49,8 @@ h : ¬f a = 11
4949
[eqc] False propositions
5050
[prop] f a = 11
5151
[cutsat] Assignment satisfying linear constraints
52-
[assign] a := 2
53-
[assign] f a := 1
52+
[assign] a := 3
53+
[assign] f a := 2
5454
-/
5555
#guard_msgs (error) in
5656
example : f a = 10 + 1 := by
@@ -75,9 +75,9 @@ h : ¬f x = 11
7575
[ematch] E-matching patterns
7676
[thm] fa: [f `[a]]
7777
[cutsat] Assignment satisfying linear constraints
78-
[assign] x := 3
79-
[assign] a := 2
80-
[assign] f x := 1
78+
[assign] x := 4
79+
[assign] a := 3
80+
[assign] f x := 2
8181
-/
8282
#guard_msgs (error) in
8383
example : f x = 10 + 1 := by

0 commit comments

Comments
 (0)