ZAIDX11 commited on
Commit
2a81ac9
·
verified ·
1 Parent(s): c0f01c8

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +8 -0
  2. backend/core/leanprover--lean4---v4.22.0/lib/lean/Lean/Meta/Tactic/Grind/Arith/Linear/PropagateEq.olean +3 -0
  3. backend/core/leanprover--lean4---v4.22.0/lib/lean/Lean/Meta/Tactic/Grind/Arith/Linear/Reify.olean +3 -0
  4. backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/Iterators/Combinators/Monadic/TakeWhile.olean +3 -0
  5. backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/Iterators/Lemmas/Producers/Monadic/List.olean +3 -0
  6. backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/Triple/Basic.olean +3 -0
  7. backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Impl/Substructure.olean +3 -0
  8. backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Lemmas/Var.olean +3 -0
  9. backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Time/Date/Basic.olean +3 -0
  10. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing.lean +43 -0
  11. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/DenoteExpr.lean +92 -0
  12. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/EqCnstr.lean +517 -0
  13. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Internalize.lean +130 -0
  14. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Inv.lean +57 -0
  15. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/PP.lean +55 -0
  16. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Poly.lean +236 -0
  17. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Proof.lean +586 -0
  18. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Reify.lean +170 -0
  19. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/RingId.lean +197 -0
  20. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/SafePoly.lean +114 -0
  21. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/ToExpr.lean +79 -0
  22. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Types.lean +279 -0
  23. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Util.lean +184 -0
  24. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Var.lean +38 -0
  25. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat.lean +44 -0
  26. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/CommRing.lean +58 -0
  27. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/DvdCnstr.lean +130 -0
  28. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/EqCnstr.lean +542 -0
  29. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Inv.lean +119 -0
  30. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/LeCnstr.lean +212 -0
  31. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/MBTC.lean +45 -0
  32. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Model.lean +77 -0
  33. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Nat.lean +183 -0
  34. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Norm.lean +49 -0
  35. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Proof.lean +520 -0
  36. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/ReorderVars.lean +166 -0
  37. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Search.lean +588 -0
  38. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/SearchM.lean +79 -0
  39. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/ToInt.lean +378 -0
  40. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/ToIntInfo.lean +85 -0
  41. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Types.lean +346 -0
  42. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Internalize.lean +20 -0
  43. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Inv.lean +18 -0
  44. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Linear.lean +42 -0
  45. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Main.lean +61 -0
  46. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Model.lean +8 -0
  47. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/ModelUtil.lean +123 -0
  48. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Offset.lean +27 -0
  49. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/ProofUtil.lean +24 -0
  50. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Simproc.lean +53 -0
.gitattributes CHANGED
@@ -4358,3 +4358,11 @@ backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/B
4358
  backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/WP/Monad.olean filter=lfs diff=lfs merge=lfs -text
4359
  backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/Do/Syntax.olean filter=lfs diff=lfs merge=lfs -text
4360
  backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Time/DateTime.olean filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
4358
  backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/WP/Monad.olean filter=lfs diff=lfs merge=lfs -text
4359
  backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/Do/Syntax.olean filter=lfs diff=lfs merge=lfs -text
4360
  backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Time/DateTime.olean filter=lfs diff=lfs merge=lfs -text
4361
+ backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Impl/Substructure.olean filter=lfs diff=lfs merge=lfs -text
4362
+ backend/core/leanprover--lean4---v4.22.0/lib/lean/Lean/Meta/Tactic/Grind/Arith/Linear/PropagateEq.olean filter=lfs diff=lfs merge=lfs -text
4363
+ backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/Iterators/Lemmas/Producers/Monadic/List.olean filter=lfs diff=lfs merge=lfs -text
4364
+ backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/Triple/Basic.olean filter=lfs diff=lfs merge=lfs -text
4365
+ backend/core/leanprover--lean4---v4.22.0/lib/lean/Lean/Meta/Tactic/Grind/Arith/Linear/Reify.olean filter=lfs diff=lfs merge=lfs -text
4366
+ backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/Iterators/Combinators/Monadic/TakeWhile.olean filter=lfs diff=lfs merge=lfs -text
4367
+ backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Lemmas/Var.olean filter=lfs diff=lfs merge=lfs -text
4368
+ backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Time/Date/Basic.olean filter=lfs diff=lfs merge=lfs -text
backend/core/leanprover--lean4---v4.22.0/lib/lean/Lean/Meta/Tactic/Grind/Arith/Linear/PropagateEq.olean ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2de3cb458ceff785a54415409cba6d5db42cf4e365612d41343cab9fad51fc77
3
+ size 1545272
backend/core/leanprover--lean4---v4.22.0/lib/lean/Lean/Meta/Tactic/Grind/Arith/Linear/Reify.olean ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7b047e7935aa1986e41f765d7d0ac1b1c9469a769340130298e25f626df32152
3
+ size 432920
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/Iterators/Combinators/Monadic/TakeWhile.olean ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:88c64d21f17c53d5b885dea928cd0a2f57fdb7803d29b313bff051fc4afc3145
3
+ size 358176
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/Iterators/Lemmas/Producers/Monadic/List.olean ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a7796ccf1c2ade3c400ec8e94d460559cb27bd6947e523d1911fc12b2cd27638
3
+ size 491680
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/Triple/Basic.olean ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:56cc768c2c230024859db7d718f81796782627d78c51fc3b836e48fd6dbaf3eb
3
+ size 120040
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Impl/Substructure.olean ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4104065cda956d977c3175423a07767b2dc70f248d994517e12157bb68a5aade
3
+ size 352432
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Lemmas/Var.olean ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2ea84d8f6f9579e71bad5ce4724459329c472627fb8f44f52fe59e62302632b4
3
+ size 353848
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Time/Date/Basic.olean ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:695cad08d65230aa524b0b86ae256c50aff3a43a3babf3a2218ab52b7bd17af6
3
+ size 326864
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing.lean ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Util.Trace
8
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Poly
9
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Types
10
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
11
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Internalize
12
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.ToExpr
13
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Var
14
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Reify
15
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.EqCnstr
16
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Proof
17
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
18
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Inv
19
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.PP
20
+
21
+ namespace Lean
22
+
23
+ builtin_initialize registerTraceClass `grind.ring
24
+ builtin_initialize registerTraceClass `grind.ring.internalize
25
+ builtin_initialize registerTraceClass `grind.ring.assert
26
+ builtin_initialize registerTraceClass `grind.ring.assert.unsat (inherited := true)
27
+ builtin_initialize registerTraceClass `grind.ring.assert.trivial (inherited := true)
28
+ builtin_initialize registerTraceClass `grind.ring.assert.queue (inherited := true)
29
+ builtin_initialize registerTraceClass `grind.ring.assert.basis (inherited := true)
30
+ builtin_initialize registerTraceClass `grind.ring.assert.store (inherited := true)
31
+ builtin_initialize registerTraceClass `grind.ring.simp
32
+ builtin_initialize registerTraceClass `grind.ring.superpose
33
+ builtin_initialize registerTraceClass `grind.ring.impEq
34
+
35
+ builtin_initialize registerTraceClass `grind.debug.ring.simp
36
+ builtin_initialize registerTraceClass `grind.debug.ring.proof
37
+ builtin_initialize registerTraceClass `grind.debug.ring.check
38
+ builtin_initialize registerTraceClass `grind.debug.ring.impEq
39
+ builtin_initialize registerTraceClass `grind.debug.ring.simpBasis
40
+ builtin_initialize registerTraceClass `grind.debug.ring.basis
41
+ builtin_initialize registerTraceClass `grind.debug.ring.rabinowitsch
42
+
43
+ end Lean
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/DenoteExpr.lean ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.Ring.OfSemiring
8
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Util
9
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Var
10
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
11
+
12
+ namespace Lean.Meta.Grind.Arith.CommRing
13
+ /-!
14
+ Helper functions for converting reified terms back into their denotations.
15
+ -/
16
+
17
+ variable [Monad M] [MonadGetRing M]
18
+
19
+ def denoteNum (k : Int) : M Expr := do
20
+ let ring ← getRing
21
+ return denoteNumCore ring.u ring.type ring.semiringInst ring.negFn k
22
+
23
+ def _root_.Lean.Grind.CommRing.Power.denoteExpr (pw : Power) : M Expr := do
24
+ let x := (← getRing).vars[pw.x]!
25
+ if pw.k == 1 then
26
+ return x
27
+ else
28
+ return mkApp2 (← getRing).powFn x (toExpr pw.k)
29
+
30
+ def _root_.Lean.Grind.CommRing.Mon.denoteExpr (m : Mon) : M Expr := do
31
+ match m with
32
+ | .unit => denoteNum 1
33
+ | .mult pw m => go m (← pw.denoteExpr)
34
+ where
35
+ go (m : Mon) (acc : Expr) : M Expr := do
36
+ match m with
37
+ | .unit => return acc
38
+ | .mult pw m => go m (mkApp2 (← getRing).mulFn acc (← pw.denoteExpr))
39
+
40
+ def _root_.Lean.Grind.CommRing.Poly.denoteExpr (p : Poly) : M Expr := do
41
+ match p with
42
+ | .num k => denoteNum k
43
+ | .add k m p => go p (← denoteTerm k m)
44
+ where
45
+ denoteTerm (k : Int) (m : Mon) : M Expr := do
46
+ if k == 1 then
47
+ m.denoteExpr
48
+ else
49
+ return mkApp2 (← getRing).mulFn (← denoteNum k) (← m.denoteExpr)
50
+
51
+ go (p : Poly) (acc : Expr) : M Expr := do
52
+ match p with
53
+ | .num 0 => return acc
54
+ | .num k => return mkApp2 (← getRing).addFn acc (← denoteNum k)
55
+ | .add k m p => go p (mkApp2 (← getRing).addFn acc (← denoteTerm k m))
56
+
57
+ def _root_.Lean.Grind.CommRing.Expr.denoteExpr (e : RingExpr) : M Expr := do
58
+ go e
59
+ where
60
+ go : RingExpr → M Expr
61
+ | .num k => denoteNum k
62
+ | .var x => return (← getRing).vars[x]!
63
+ | .add a b => return mkApp2 (← getRing).addFn (← go a) (← go b)
64
+ | .sub a b => return mkApp2 (← getRing).subFn (← go a) (← go b)
65
+ | .mul a b => return mkApp2 (← getRing).mulFn (← go a) (← go b)
66
+ | .pow a k => return mkApp2 (← getRing).powFn (← go a) (toExpr k)
67
+ | .neg a => return mkApp (← getRing).negFn (← go a)
68
+
69
+ private def mkEq (a b : Expr) : M Expr := do
70
+ let r ← getRing
71
+ return mkApp3 (mkConst ``Eq [r.u.succ]) r.type a b
72
+
73
+ def EqCnstr.denoteExpr (c : EqCnstr) : M Expr := do
74
+ mkEq (← c.p.denoteExpr) (← denoteNum 0)
75
+
76
+ def PolyDerivation.denoteExpr (d : PolyDerivation) : M Expr := do
77
+ d.p.denoteExpr
78
+
79
+ def DiseqCnstr.denoteExpr (c : DiseqCnstr) : M Expr := do
80
+ return mkNot (← mkEq (← c.d.denoteExpr) (← denoteNum 0))
81
+
82
+ def _root_.Lean.Grind.Ring.OfSemiring.Expr.denoteAsRingExpr (e : SemiringExpr) : SemiringM Expr := do
83
+ shareCommon (← go e)
84
+ where
85
+ go : SemiringExpr → SemiringM Expr
86
+ | .num k => denoteNum k
87
+ | .var x => return mkApp (← getSemiring).toQFn (← getSemiring).vars[x]!
88
+ | .add a b => return mkApp2 (← getRing).addFn (← go a) (← go b)
89
+ | .mul a b => return mkApp2 (← getRing).mulFn (← go a) (← go b)
90
+ | .pow a k => return mkApp2 (← getRing).powFn (← go a) (toExpr k)
91
+
92
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/EqCnstr.lean ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.ProveEq
8
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
9
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Proof
10
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
11
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Inv
12
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Reify
13
+
14
+ namespace Lean.Meta.Grind.Arith.CommRing
15
+ /-- Returns `some ringId` if `a` and `b` are elements of the same ring. -/
16
+ private def inSameRing? (a b : Expr) : GoalM (Option Nat) := do
17
+ let some ringId ← getTermRingId? a | return none
18
+ let some ringId' ← getTermRingId? b | return none
19
+ unless ringId == ringId' do return none -- This can happen when we have heterogeneous equalities
20
+ return ringId
21
+
22
+ /-- Returns `some semiringId` if `a` and `b` are elements of the same semiring. -/
23
+ private def inSameSemiring? (a b : Expr) : GoalM (Option Nat) := do
24
+ let some semiringId ← getTermSemiringId? a | return none
25
+ let some semiringId' ← getTermSemiringId? b | return none
26
+ unless semiringId == semiringId' do return none -- This can happen when we have heterogeneous equalities
27
+ return semiringId
28
+
29
+ def mkEqCnstr (p : Poly) (h : EqCnstrProof) : RingM EqCnstr := do
30
+ let id := (← getRing).nextId
31
+ let sugar := p.degree
32
+ modifyRing fun s => { s with nextId := s.nextId + 1 }
33
+ return { sugar, p, h, id }
34
+
35
+ /--
36
+ Returns the ring expression denoting the given Lean expression.
37
+ Recall that we compute the ring expressions during internalization.
38
+ -/
39
+ private def toRingExpr? (e : Expr) : RingM (Option RingExpr) := do
40
+ let ring ← getRing
41
+ if let some re := ring.denote.find? { expr := e } then
42
+ return some re
43
+ else if let some x := ring.varMap.find? { expr := e } then
44
+ return some (.var x)
45
+ else
46
+ reportIssue! "failed to convert to ring expression{indentExpr e}"
47
+ return none
48
+
49
+ /--
50
+ Returns the semiring expression denoting the given Lean expression.
51
+ Recall that we compute the semiring expressions during internalization.
52
+ -/
53
+ private def toSemiringExpr? (e : Expr) : SemiringM (Option SemiringExpr) := do
54
+ let semiring ← getSemiring
55
+ if let some re := semiring.denote.find? { expr := e } then
56
+ return some re
57
+ else if let some x := semiring.varMap.find? { expr := e } then
58
+ return some (.var x)
59
+ else
60
+ reportIssue! "failed to convert to semiring expression{indentExpr e}"
61
+ return none
62
+
63
+ /--
64
+ Returns `some c`, where `c` is an equation from the basis whose leading monomial divides `m`.
65
+ Remark: if the current ring does not satisfy the property
66
+ ```
67
+ ∀ (k : Nat) (a : α), k ≠ 0 → OfNat.ofNat (α := α) k * a = 0 → a = 0
68
+ ```
69
+ then the leading coefficient of the equation must also divide `k`
70
+ -/
71
+ def _root_.Lean.Grind.CommRing.Mon.findSimp? (k : Int) (m : Mon) : RingM (Option EqCnstr) := do
72
+ let checkCoeff ← checkCoeffDvd
73
+ let noZeroDiv ← noZeroDivisors
74
+ for c in (← getRing).basis do
75
+ if !checkCoeff || noZeroDiv || (c.p.lc ∣ k) then
76
+ if c.p.divides m then
77
+ return some c
78
+ return none
79
+
80
+ /--
81
+ Returns `some c`, where `c` is an equation from the basis whose leading monomial divides some
82
+ monomial in `p`.
83
+ -/
84
+ def _root_.Lean.Grind.CommRing.Poly.findSimp? (p : Poly) : RingM (Option EqCnstr) := do
85
+ match p with
86
+ | .num _ => return none
87
+ | .add k m p =>
88
+ match (← m.findSimp? k) with
89
+ | some c => return some c
90
+ | none => p.findSimp?
91
+
92
+ /-- Simplifies `d.p` using `c`, and returns an extended polynomial derivation. -/
93
+ def PolyDerivation.simplifyWith (d : PolyDerivation) (c : EqCnstr) : RingM PolyDerivation := do
94
+ let some r := d.p.simp? c.p (← nonzeroChar?) | return d
95
+ incSteps
96
+ trace_goal[grind.ring.simp] "{← r.p.denoteExpr}"
97
+ return .step r.p r.k₁ d r.k₂ r.m₂ c
98
+
99
+ def PolyDerivation.simplifyNumEq0 (d : PolyDerivation) : RingM PolyDerivation := do
100
+ let some numEq0 := (← getRing).numEq0? | return d
101
+ let .num k := numEq0.p | return d
102
+ return .normEq0 (d.p.normEq0 k.natAbs) d numEq0
103
+
104
+ /-- Simplified `d.p` using the current basis, and returns the extended polynomial derivation. -/
105
+ def PolyDerivation.simplify (d : PolyDerivation) : RingM PolyDerivation := do
106
+ let mut d := d
107
+ repeat
108
+ d ← d.simplifyNumEq0
109
+ if (← checkMaxSteps) then return d
110
+ let some c ← d.p.findSimp? |
111
+ trace_goal[grind.debug.ring.simp] "simplified{indentD (← d.denoteExpr)}"
112
+ return d
113
+ d ← d.simplifyWith c
114
+ return d
115
+
116
+ /-- Simplifies `c₁` using `c₂`. -/
117
+ def EqCnstr.simplifyWithCore (c₁ c₂ : EqCnstr) : RingM (Option EqCnstr) := do
118
+ let some r := c₁.p.simp? c₂.p (← nonzeroChar?) | return none
119
+ let c := { c₁ with
120
+ p := r.p
121
+ h := .simp r.k₁ c₁ r.k₂ r.m₂ c₂
122
+ }
123
+ incSteps
124
+ trace_goal[grind.ring.simp] "{← c.p.denoteExpr}"
125
+ return some c
126
+
127
+ /-- Simplifies `c₁` using `c₂`. -/
128
+ def EqCnstr.simplifyWith (c₁ c₂ : EqCnstr) : RingM EqCnstr := do
129
+ let some c ← c₁.simplifyWithCore c₂ | return c₁
130
+ return c
131
+
132
+ /-- Simplifies `c₁` using `c₂` exhaustively. -/
133
+ partial def EqCnstr.simplifyWithExhaustively (c₁ c₂ : EqCnstr) : RingM EqCnstr := do
134
+ let some c ← c₁.simplifyWithCore c₂ | return c₁
135
+ c.simplifyWithExhaustively c₂
136
+
137
+ def EqCnstr.simplifyUsingNumEq0 (c : EqCnstr) : RingM EqCnstr := do
138
+ let some c' := (← getRing).numEq0? | return c
139
+ let .num k := c'.p | return c
140
+ return { c with p := c.p.normEq0 k.natAbs, h := .numEq0 k.natAbs c' c }
141
+
142
+ /-- Simplify the given equation constraint using the current basis. -/
143
+ def EqCnstr.simplify (c : EqCnstr) : RingM EqCnstr := do
144
+ let mut c := c
145
+ repeat
146
+ c ← simplifyUsingNumEq0 c
147
+ if (← checkMaxSteps) then return c
148
+ let some c' ← c.p.findSimp? |
149
+ trace_goal[grind.debug.ring.simp] "simplified{indentD (← c.denoteExpr)}"
150
+ return c
151
+ c ← c.simplifyWith c'
152
+ return c
153
+
154
+ /-- Returns `true` if `c.p` is the constant polynomial. -/
155
+ def EqCnstr.checkConstant (c : EqCnstr) : RingM Bool := do
156
+ let .num n := c.p | return false
157
+ if n == 0 then
158
+ trace_goal[grind.ring.assert.trivial] "{← c.denoteExpr}"
159
+ else if (← hasChar) then
160
+ c.setUnsat
161
+ else if (← pure (n.natAbs == 1) <&&> isField) then
162
+ c.setUnsat
163
+ else
164
+ let mut c := c
165
+ let mut n := n
166
+ if n < 0 then
167
+ n := -n
168
+ c := { c with p := .num n, h := .mul (-1) c }
169
+ if let some c' := (← getRing).numEq0? then
170
+ let .num m := c'.p | unreachable!
171
+ let (g, a, b) := gcdExt n m
172
+ c := { c with p := .num g, h := .gcd a b c c' }
173
+ modifyRing fun s => { s with numEq0? := some c, numEq0Updated := true }
174
+ trace_goal[grind.ring.assert.store] "{← c.denoteExpr}"
175
+ return true
176
+
177
+ /--
178
+ Simplifies and checks whether the resulting constraint is trivial (i.e., `0 = 0`),
179
+ or inconsistent (i.e., `k = 0` where `k % c != 0` for a comm-ring with characteristic `c`),
180
+ and returns `none`. Otherwise, returns the simplified constraint.
181
+ -/
182
+ def EqCnstr.simplifyAndCheck (c : EqCnstr) : RingM (Option EqCnstr) := do
183
+ let c ← c.simplify
184
+ if (← c.checkConstant) then
185
+ return none
186
+ else
187
+ return some c
188
+
189
+ private def addSorted (c : EqCnstr) : List EqCnstr → List EqCnstr
190
+ | [] => [c]
191
+ | c' :: cs =>
192
+ if c.p.lm.grevlex c'.p.lm == .gt then
193
+ c :: c' :: cs
194
+ else
195
+ c' :: addSorted c cs
196
+
197
+ def addToBasisCore (c : EqCnstr) : RingM Unit := do
198
+ trace[grind.debug.ring.basis] "{← c.denoteExpr}"
199
+ modifyRing fun s => { s with
200
+ basis := addSorted c s.basis
201
+ recheck := true
202
+ }
203
+
204
+ def EqCnstr.addToQueue (c : EqCnstr) : RingM Unit := do
205
+ if (← checkMaxSteps) then return ()
206
+ trace_goal[grind.ring.assert.queue] "{← c.denoteExpr}"
207
+ modifyRing fun s => { s with queue := s.queue.insert c }
208
+
209
+ def EqCnstr.superposeWith (c : EqCnstr) : RingM Unit := do
210
+ if (← checkMaxSteps) then return ()
211
+ let .add _ m _ := c.p | return ()
212
+ for c' in (← getRing).basis do
213
+ let .add _ m' _ := c'.p | pure ()
214
+ if m.sharesVar m' then
215
+ let r ← c.p.spolM c'.p
216
+ trace_goal[grind.ring.superpose] "{← c.denoteExpr}\nwith: {← c'.denoteExpr}\nresult: {← r.spol.denoteExpr} = 0"
217
+ addToQueue (← mkEqCnstr r.spol <| .superpose r.k₁ r.m₁ c r.k₂ r.m₂ c')
218
+
219
+ /--
220
+ Tries to convert the leading monomial into a monic one.
221
+
222
+ It exploits the fact that given a polynomial with leading coefficient `k`,
223
+ if the ring has a nonzero characteristic `p` and `gcd k p = 1`, then
224
+ `k` has an inverse.
225
+
226
+ It also handles the easy case where `k` is `-1`.
227
+
228
+ Remark: if the ring implements the class `NoNatZeroDivisors`, then
229
+ the coefficients are divided by the gcd of all coefficients.
230
+ -/
231
+ def EqCnstr.toMonic (c : EqCnstr) : RingM EqCnstr := do
232
+ let k := c.p.lc
233
+ if k == 1 then return c
234
+ if let some p ← nonzeroChar? then
235
+ let (g, α, _β) := gcdExt k p
236
+ if g == 1 then
237
+ -- `α*k + β*p = 1`
238
+ -- `α*k = 1 (mod p)`
239
+ let α := if α < 0 then α % p else α
240
+ return { c with p := c.p.mulConstC α p, h := .mul α c }
241
+ if (← noZeroDivisors) then
242
+ let g : Int := c.p.gcdCoeffs
243
+ if g != 1 then
244
+ let g := if k < 0 then -g else g
245
+ return { c with p := c.p.divConst g, h := .div g c }
246
+ if k < 0 then
247
+ return { c with p := c.p.mulConst (-1), h := .mul (-1) c }
248
+ return c
249
+
250
+ def EqCnstr.simplifyBasis (c : EqCnstr) : RingM Unit := do
251
+ trace[grind.debug.ring.simpBasis] "using: {← c.denoteExpr}"
252
+ let .add _ m _ := c.p | return ()
253
+ let rec go (basis : List EqCnstr) (acc : List EqCnstr) : RingM (List EqCnstr) := do
254
+ match basis with
255
+ | [] => return acc.reverse
256
+ | c' :: basis =>
257
+ match c'.p with
258
+ | .add _ m' _ =>
259
+ if m.divides m' then
260
+ let c'' ← c'.simplifyWithExhaustively c
261
+ trace[grind.debug.ring.simpBasis] "simplified: {← c''.denoteExpr}"
262
+ unless (← checkConstant c'') do
263
+ addToQueue c''
264
+ go basis acc
265
+ else
266
+ go basis (c' :: acc)
267
+ | _ => go basis (c' :: acc)
268
+ let basis ← go (← getRing).basis []
269
+ modifyRing fun s => { s with basis }
270
+
271
+ def EqCnstr.addToBasisAfterSimp (c : EqCnstr) : RingM Unit := do
272
+ let c ← c.toMonic
273
+ c.simplifyBasis
274
+ c.superposeWith
275
+ trace_goal[grind.ring.assert.basis] "{← c.denoteExpr}"
276
+ addToBasisCore c
277
+
278
+ private def checkNumEq0Updated : RingM Unit := do
279
+ if (← getRing).numEq0Updated then
280
+ -- `numEq0?` was updated, then we must move the basis back to the queue to be simplified.
281
+ let basis := (← getRing).basis
282
+ modifyRing fun s => { s with numEq0Updated := false, basis := {} }
283
+ for c in basis do
284
+ c.addToQueue
285
+
286
+ abbrev withCheckingNumEq0 (k : RingM Unit) : RingM Unit := do
287
+ try
288
+ k
289
+ finally
290
+ checkNumEq0Updated
291
+
292
+ def EqCnstr.addToBasis (c : EqCnstr) : RingM Unit := do
293
+ withCheckingNumEq0 do
294
+ let some c ← c.simplifyAndCheck | return ()
295
+ c.addToBasisAfterSimp
296
+
297
+ def addNewEq (c : EqCnstr) : RingM Unit := do
298
+ withCheckingNumEq0 do
299
+ let some c ← c.simplifyAndCheck | return ()
300
+ if c.p.degree == 1 then
301
+ c.addToBasisAfterSimp
302
+ else
303
+ c.addToQueue
304
+
305
+ /-- Returns `true` if `c.d.p` is the constant polynomial. -/
306
+ def DiseqCnstr.checkConstant (c : DiseqCnstr) : RingM Bool := do
307
+ let .num k := c.d.p | return false
308
+ if k == 0 then
309
+ c.setUnsat
310
+ else if (← hasChar) then
311
+ trace_goal[grind.ring.assert.trivial] "{← c.denoteExpr}"
312
+ return true
313
+
314
+ def DiseqCnstr.simplify (c : DiseqCnstr) : RingM DiseqCnstr :=
315
+ withCheckCoeffDvd do
316
+ -- We must enable `checkCoeffDvd := true`. See comments at `PolyDerivation`.
317
+ return { c with d := (← c.d.simplify) }
318
+
319
+ def saveDiseq (c : DiseqCnstr) : RingM Unit := do
320
+ trace_goal[grind.ring.assert.store] "{← c.denoteExpr}"
321
+ modifyRing fun s => { s with diseqs := s.diseqs.push c }
322
+
323
+ def addNewDiseq (c : DiseqCnstr) : RingM Unit := do
324
+ let c ← c.simplify
325
+ if (← c.checkConstant) then
326
+ return ()
327
+ trace[grind.ring.assert.store] "{← c.denoteExpr}"
328
+ saveDiseq c
329
+
330
+ @[export lean_process_ring_eq]
331
+ def processNewEqImpl (a b : Expr) : GoalM Unit := do
332
+ if isSameExpr a b then return () -- TODO: check why this is needed
333
+ if let some ringId ← inSameRing? a b then RingM.run ringId do
334
+ trace_goal[grind.ring.assert] "{← mkEq a b}"
335
+ let some ra ← toRingExpr? a | return ()
336
+ let some rb ← toRingExpr? b | return ()
337
+ let p ← (ra.sub rb).toPolyM
338
+ addNewEq (← mkEqCnstr p (.core a b ra rb))
339
+ else if let some semiringId ← inSameSemiring? a b then SemiringM.run semiringId do
340
+ if (← getConfig).ringNull then return () -- TODO: remove after we add Nullstellensatz certificates for semiring adapter
341
+ trace_goal[grind.ring.assert] "{← mkEq a b}"
342
+ let some sa ← toSemiringExpr? a | return ()
343
+ let some sb ← toSemiringExpr? b | return ()
344
+ let lhs ← sa.denoteAsRingExpr
345
+ let rhs ← sb.denoteAsRingExpr
346
+ RingM.run (← getSemiring).ringId do
347
+ let some ra ← reify? lhs (skipVar := false) (gen := (← getGeneration a)) | return ()
348
+ let some rb ← reify? rhs (skipVar := false) (gen := (← getGeneration b)) | return ()
349
+ let p ← (ra.sub rb).toPolyM
350
+ addNewEq (← mkEqCnstr p (.coreS a b sa sb ra rb))
351
+
352
+ private def pre (e : Expr) : GoalM Expr := do
353
+ -- We must canonicalize because the instances generated by this module may not match
354
+ -- the ones selected by the canonicalizer
355
+ shareCommon (← canon e)
356
+
357
+ private def diseqToEq (a b : Expr) : RingM Unit := do
358
+ -- Rabinowitsch transformation
359
+ let gen := max (← getGeneration a) (← getGeneration b)
360
+ let ring ← getRing
361
+ let some fieldInst := ring.fieldInst? | unreachable!
362
+ let e ← pre <| mkApp2 ring.subFn a b
363
+ modifyRing fun s => { s with invSet := s.invSet.insert e }
364
+ let eInv ← pre <| mkApp (← getRing).invFn?.get! e
365
+ let lhs ← pre <| mkApp2 ring.mulFn e eInv
366
+ internalize lhs gen none
367
+ trace[grind.debug.ring.rabinowitsch] "{lhs}"
368
+ pushEq lhs ring.one <| mkApp5 (mkConst ``Grind.CommRing.diseq_to_eq [ring.u]) ring.type fieldInst a b (← mkDiseqProof a b)
369
+
370
+ private def diseqZeroToEq (a b : Expr) : RingM Unit := do
371
+ -- Rabinowitsch transformation for `b = 0` case
372
+ let gen ← getGeneration a
373
+ let ring ← getRing
374
+ let some fieldInst := ring.fieldInst? | unreachable!
375
+ modifyRing fun s => { s with invSet := s.invSet.insert a }
376
+ let aInv ← pre <| mkApp (← getRing).invFn?.get! a
377
+ let lhs ← pre <| mkApp2 ring.mulFn a aInv
378
+ internalize lhs gen none
379
+ trace[grind.debug.ring.rabinowitsch] "{lhs}"
380
+ pushEq lhs ring.one <| mkApp4 (mkConst ``Grind.CommRing.diseq0_to_eq [ring.u]) ring.type fieldInst a (← mkDiseqProof a b)
381
+
382
+ @[export lean_process_ring_diseq]
383
+ def processNewDiseqImpl (a b : Expr) : GoalM Unit := do
384
+ if let some ringId ← inSameRing? a b then RingM.run ringId do
385
+ trace_goal[grind.ring.assert] "{mkNot (← mkEq a b)}"
386
+ let some ra ← toRingExpr? a | return ()
387
+ let some rb ← toRingExpr? b | return ()
388
+ let p ← (ra.sub rb).toPolyM
389
+ if (← isField) then
390
+ if !(p matches .num _) || !(← hasChar) then
391
+ if rb matches .num 0 then
392
+ diseqZeroToEq a b
393
+ else
394
+ diseqToEq a b
395
+ return ()
396
+ addNewDiseq {
397
+ lhs := a, rhs := b
398
+ rlhs := ra, rrhs := rb
399
+ d := .input p
400
+ ofSemiring? := none
401
+ }
402
+ else if let some semiringId ← inSameSemiring? a b then SemiringM.run semiringId do
403
+ if (← getSemiring).addRightCancelInst?.isSome then
404
+ if (← getConfig).ringNull then return () -- TODO: remove after we add Nullstellensatz certificates for semiring adapter
405
+ trace_goal[grind.ring.assert] "{mkNot (← mkEq a b)}"
406
+ let some sa ← toSemiringExpr? a | return ()
407
+ let some sb ← toSemiringExpr? b | return ()
408
+ let lhs ← sa.denoteAsRingExpr
409
+ let rhs ← sb.denoteAsRingExpr
410
+ RingM.run (← getSemiring).ringId do
411
+ let some ra ← reify? lhs (skipVar := false) (gen := (← getGeneration a)) | return ()
412
+ let some rb ← reify? rhs (skipVar := false) (gen := (← getGeneration b)) | return ()
413
+ let p ← (ra.sub rb).toPolyM
414
+ addNewDiseq {
415
+ lhs := a, rhs := b
416
+ rlhs := ra, rrhs := rb
417
+ d := .input p
418
+ ofSemiring? := some (sa, sb)
419
+ }
420
+ else
421
+ -- If semiring does not have `AddRightCancel`,
422
+ -- we may still normalize and check whether lhs and rhs are equal
423
+ let some sa ← toSemiringExpr? a | return ()
424
+ let some sb ← toSemiringExpr? b | return ()
425
+ if sa.toPoly == sb.toPoly then
426
+ setSemiringDiseqUnsat a b sa sb
427
+
428
+ /--
429
+ Returns `true` if the todo queue is not empty or the `recheck` flag is set to `true`
430
+ -/
431
+ private def needCheck : RingM Bool := do
432
+ unless (← isQueueEmpty) do return true
433
+ return (← getRing).recheck
434
+
435
+ private def checkDiseqs : RingM Unit := do
436
+ let diseqs := (← getRing).diseqs
437
+ modifyRing fun s => { s with diseqs := {} }
438
+ -- No indexing simple
439
+ for diseq in diseqs do
440
+ addNewDiseq diseq
441
+ if (← isInconsistent) then return
442
+
443
+ abbrev PropagateEqMap := Std.HashMap (Int × Poly) (Expr × RingExpr)
444
+
445
+ /--
446
+ Propagates implied equalities.
447
+ -/
448
+ private def propagateEqs : RingM Unit := do
449
+ if (← isInconsistent) then return ()
450
+ /-
451
+ This is a very simple procedure that does not use any indexing data-structure.
452
+ We don't even cache the simplified polynomials.
453
+ TODO: optimize
454
+ TODO: support for semiring
455
+ -/
456
+ let mut map : PropagateEqMap := {}
457
+ for a in (← getRing).vars do
458
+ if (← checkMaxSteps) then return ()
459
+ let some ra ← toRingExpr? a | unreachable!
460
+ map ← process map a ra
461
+ for (a, ra) in (← getRing).denote do
462
+ if (← checkMaxSteps) then return ()
463
+ map ← process map a.expr ra
464
+ where
465
+ process (map : PropagateEqMap) (a : Expr) (ra : RingExpr) : RingM PropagateEqMap := do
466
+ let d : PolyDerivation := .input (← ra.toPolyM)
467
+ let d ← d.simplify
468
+ let k := d.getMultiplier
469
+ trace_goal[grind.debug.ring.impEq] "{a}, {k}, {← d.p.denoteExpr}"
470
+ if let some (b, rb) := map[(k, d.p)]? then
471
+ -- TODO: use `isEqv` more effectively
472
+ unless (← isEqv a b) do
473
+ let p ← (ra.sub rb).toPolyM
474
+ let d : PolyDerivation := .input p
475
+ let d ← d.simplify
476
+ if d.getMultiplier != 1 then
477
+ unless (← noZeroDivisors) do
478
+ -- Given the multiplier `k' = d.getMultiplier`, we have that `k*(a - b) = 0`,
479
+ -- but we cannot eliminate the `k` because we don't have `noZeroDivisors`.
480
+ trace_goal[grind.ring.impEq] "skip: {← mkEq a b}, k: {k}, noZeroDivisors: false"
481
+ return map.insert (k, d.p) (a, ra)
482
+ trace_goal[grind.ring.impEq] "{← mkEq a b}, {k}, {← p.denoteExpr}"
483
+ propagateEq a b ra rb d
484
+ return map
485
+ else
486
+ return map.insert (k, d.p) (a, ra)
487
+
488
+ def checkRing : RingM Bool := do
489
+ unless (← needCheck) do return false
490
+ trace_goal[grind.debug.ring.check] "{(← getRing).type}"
491
+ repeat
492
+ checkSystem "ring"
493
+ let some c ← getNext? | break
494
+ trace_goal[grind.debug.ring.check] "{← c.denoteExpr}"
495
+ c.addToBasis
496
+ if (← isInconsistent) then return true
497
+ if (← checkMaxSteps) then return true
498
+ checkDiseqs
499
+ propagateEqs
500
+ modifyRing fun s => { s with recheck := false }
501
+ return true
502
+
503
+ def check : GoalM Bool := do
504
+ if (← checkMaxSteps) then return false
505
+ let mut progress := false
506
+ checkInvariants
507
+ try
508
+ for ringId in [:(← get').rings.size] do
509
+ let r ← RingM.run ringId checkRing
510
+ progress := progress || r
511
+ if (← isInconsistent) then
512
+ return true
513
+ return progress
514
+ finally
515
+ checkInvariants
516
+
517
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Internalize.lean ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Simp
8
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
9
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Reify
10
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
11
+
12
+ namespace Lean.Meta.Grind.Arith.CommRing
13
+
14
+ /-- If `e` is a function application supported by the `CommRing` module, return its type. -/
15
+ private def getType? (e : Expr) : Option Expr :=
16
+ match_expr e with
17
+ | HAdd.hAdd α _ _ _ _ _ => some α
18
+ | HSub.hSub α _ _ _ _ _ => some α
19
+ | HMul.hMul α _ _ _ _ _ => some α
20
+ | HPow.hPow α β _ _ _ _ =>
21
+ let_expr Nat := β | none
22
+ some α
23
+ | Neg.neg α _ _ => some α
24
+ | OfNat.ofNat α _ _ => some α
25
+ | NatCast.natCast α _ _ => some α
26
+ | IntCast.intCast α _ _ => some α
27
+ | _ => none
28
+
29
+ private def isForbiddenParent (parent? : Option Expr) : Bool :=
30
+ if let some parent := parent? then
31
+ if getType? parent |>.isSome then
32
+ true
33
+ else
34
+ -- We also ignore the following parents.
35
+ -- Remark: `HDiv` should appear in `getType?` as soon as we add support for `Field`,
36
+ -- `LE.le` linear combinations
37
+ match_expr parent with
38
+ | LT.lt _ _ _ _ => true
39
+ | LE.le _ _ _ _ => true
40
+ | HDiv.hDiv _ _ _ _ _ _ => true
41
+ | HMod.hMod _ _ _ _ _ _ => true
42
+ | _ => false
43
+ else
44
+ false
45
+
46
+ private partial def toInt? (e : Expr) : RingM (Option Int) := do
47
+ match_expr e with
48
+ | Neg.neg _ i a =>
49
+ if isNegInst (← getRing) i then return (- .) <$> (← toInt? a) else return none
50
+ | IntCast.intCast _ i a =>
51
+ if isIntCastInst (← getRing) i then getIntValue? a else return none
52
+ | NatCast.natCast _ i a =>
53
+ if isNatCastInst (← getRing) i then
54
+ let some v ← getNatValue? a | return none
55
+ return some (Int.ofNat v)
56
+ else
57
+ return none
58
+ | OfNat.ofNat _ n _ =>
59
+ let some v ← getNatValue? n | return none
60
+ return some (Int.ofNat v)
61
+ | _ => return none
62
+
63
+ private def isInvInst (inst : Expr) : RingM Bool := do
64
+ let some fn := (← getRing).invFn? | return false
65
+ return isSameExpr fn.appArg! inst
66
+
67
+ /--
68
+ Given `e` of the form `@Inv.inv _ inst a`,
69
+ asserts `a * a⁻¹ = 1` if `a` is a numeral.
70
+ Otherwise, asserts `if a = 0 then a⁻¹ = 0 else a * a⁻¹ = 1`
71
+ -/
72
+ private def processInv (e inst a : Expr) : RingM Unit := do
73
+ unless (← isInvInst inst) do return ()
74
+ let ring ← getRing
75
+ let some fieldInst := ring.fieldInst? | return ()
76
+ if (← getRing).invSet.contains a then return ()
77
+ modifyRing fun s => { s with invSet := s.invSet.insert a }
78
+ if let some k ← toInt? a then
79
+ assert! k != 0 -- We have the normalization rule `Field.inv_zero`
80
+ if (← hasChar) then
81
+ let (charInst, c) ← getCharInst
82
+ if c == 0 then
83
+ let expected ← mkEq (mkApp2 ring.mulFn a e) (← denoteNum 1)
84
+ pushNewFact <| mkExpectedPropHint
85
+ (mkApp5 (mkConst ``Grind.CommRing.inv_int_eq [ring.u]) ring.type fieldInst charInst (mkIntLit k) reflBoolTrue)
86
+ expected
87
+ else if k % c == 0 then
88
+ let expected ← mkEq e (← denoteNum 0)
89
+ pushNewFact <| mkExpectedPropHint
90
+ (mkApp6 (mkConst ``Grind.CommRing.inv_zero_eqC [ring.u]) ring.type (mkNatLit c) fieldInst charInst (mkIntLit k) reflBoolTrue)
91
+ expected
92
+ else
93
+ let expected ← mkEq (mkApp2 ring.mulFn a e) (← denoteNum 1)
94
+ pushNewFact <| mkExpectedPropHint
95
+ (mkApp6 (mkConst ``Grind.CommRing.inv_int_eqC [ring.u]) ring.type (mkNatLit c) fieldInst charInst (mkIntLit k) reflBoolTrue)
96
+ expected
97
+ return ()
98
+ pushNewFact <| mkApp3 (mkConst ``Grind.CommRing.inv_split [ring.u]) ring.type fieldInst a
99
+
100
+ /-- Returns `true` if `e` is a term `a⁻¹`. -/
101
+ private def internalizeInv (e : Expr) : GoalM Bool := do
102
+ match_expr e with
103
+ | Inv.inv α inst a =>
104
+ let some ringId ← getRingId? α | return true
105
+ RingM.run ringId do processInv e inst a
106
+ return true
107
+ | _ => return false
108
+
109
+ def internalize (e : Expr) (parent? : Option Expr) : GoalM Unit := do
110
+ if !(← getConfig).ring then return ()
111
+ if isIntModuleVirtualParent parent? then
112
+ -- `e` is an auxiliary term used to convert `CommRing` to `IntModule`
113
+ return ()
114
+ if (← internalizeInv e) then return ()
115
+ let some type := getType? e | return ()
116
+ if isForbiddenParent parent? then return ()
117
+ if let some ringId ← getRingId? type then RingM.run ringId do
118
+ let some re ← reify? e | return ()
119
+ trace_goal[grind.ring.internalize] "[{ringId}]: {e}"
120
+ setTermRingId e
121
+ markAsCommRingTerm e
122
+ modifyRing fun s => { s with denote := s.denote.insert { expr := e } re }
123
+ else if let some semiringId ← getSemiringId? type then SemiringM.run semiringId do
124
+ let some re ← sreify? e | return ()
125
+ trace_goal[grind.ring.internalize] "semiring [{semiringId}]: {e}"
126
+ setTermSemiringId e
127
+ markAsCommRingTerm e
128
+ modifySemiring fun s => { s with denote := s.denote.insert { expr := e } re }
129
+
130
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Inv.lean ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Util
8
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Poly
9
+
10
+ namespace Lean.Meta.Grind.Arith.CommRing
11
+
12
+ private def checkVars : RingM Unit := do
13
+ let s ← getRing
14
+ let mut num := 0
15
+ for ({ expr }, var) in s.varMap do
16
+ if h : var < s.vars.size then
17
+ let expr' := s.vars[var]
18
+ assert! isSameExpr expr expr'
19
+ else
20
+ unreachable!
21
+ num := num + 1
22
+ assert! s.vars.size == num
23
+
24
+ private def checkPoly (p : Poly) : RingM Unit := do
25
+ assert! p.isSorted
26
+ assert! p.checkCoeffs
27
+ assert! p.checkNoUnitMon
28
+ assert! !(p matches .num _)
29
+
30
+ private def checkBasis : RingM Unit := do
31
+ let mut x := 0
32
+ for c in (← getRing).basis do
33
+ checkPoly c.p
34
+ x := x + 1
35
+
36
+ private def checkQueue : RingM Unit := do
37
+ for c in (← getRing).queue do
38
+ checkPoly c.p
39
+
40
+ private def checkDiseqs : RingM Unit := do
41
+ for c in (← getRing).diseqs do
42
+ checkPoly c.d.p
43
+
44
+ private def checkRingInvs : RingM Unit := do
45
+ checkVars
46
+ checkBasis
47
+ checkQueue
48
+ checkDiseqs
49
+
50
+ def checkInvariants : GoalM Unit := do
51
+ unless grind.debug.get (← getOptions) do return ()
52
+ for ringId in [: (← get').rings.size] do
53
+ RingM.run ringId do
54
+ assert! (← getRingId) == ringId
55
+ checkRingInvs
56
+
57
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/PP.lean ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
8
+
9
+ namespace Lean.Meta.Grind.Arith.CommRing
10
+
11
+ instance : MonadGetRing (ReaderT Ring MetaM) where
12
+ getRing := read
13
+
14
+ private def M := ReaderT Goal (StateT (Array MessageData) MetaM)
15
+
16
+ private def toOption (cls : Name) (header : Thunk MessageData) (msgs : Array MessageData) : Option MessageData :=
17
+ if msgs.isEmpty then
18
+ none
19
+ else
20
+ some (.trace {cls} header.get msgs)
21
+
22
+ private def push (msgs : Array MessageData) (msg? : Option MessageData) : Array MessageData :=
23
+ if let some msg := msg? then msgs.push msg else msgs
24
+
25
+ def ppBasis? : ReaderT Ring MetaM (Option MessageData) := do
26
+ let mut basis := #[]
27
+ for c in (← getRing).basis do
28
+ basis := basis.push (toTraceElem (← c.denoteExpr))
29
+ return toOption `basis "Basis" basis
30
+
31
+ def ppDiseqs? : ReaderT Ring MetaM (Option MessageData) := do
32
+ let mut diseqs := #[]
33
+ for d in (← getRing).diseqs do
34
+ diseqs := diseqs.push (toTraceElem (← d.denoteExpr))
35
+ return toOption `diseqs "Disequalities" diseqs
36
+
37
+ def ppRing? : ReaderT Ring MetaM (Option MessageData) := do
38
+ let msgs := #[]
39
+ let msgs := push msgs (← ppBasis?)
40
+ let msgs := push msgs (← ppDiseqs?)
41
+ return toOption `ring m!"Ring `{(← getRing).type}`" msgs
42
+
43
+ def pp? (goal : Goal) : MetaM (Option MessageData) := do
44
+ let mut msgs := #[]
45
+ for ring in goal.arith.ring.rings do
46
+ let some msg ← ppRing? ring | pure ()
47
+ msgs := msgs.push msg
48
+ if msgs.isEmpty then
49
+ return none
50
+ else if h : msgs.size = 1 then
51
+ return some msgs[0]
52
+ else
53
+ return some (.trace { cls := `ring } "Rings" msgs)
54
+
55
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Poly.lean ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.Ring.Poly
8
+ namespace Lean.Grind.CommRing
9
+
10
+ /-- `sharesVar m₁ m₂` returns `true` if `m₁` and `m₂` shares at least one variable. -/
11
+ def Mon.sharesVar : Mon → Mon → Bool
12
+ | .unit, _ => false
13
+ | _, .unit => false
14
+ | .mult pw₁ m₁, .mult pw₂ m₂ =>
15
+ match compare pw₁.x pw₂.x with
16
+ | .eq => true
17
+ | .lt => sharesVar m₁ (.mult pw₂ m₂)
18
+ | .gt => sharesVar (.mult pw₁ m₁) m₂
19
+
20
+ /-- `lcm m₁ m₂` returns the least common multiple of the given monomials. -/
21
+ def Mon.lcm : Mon → Mon → Mon
22
+ | .unit, m₂ => m₂
23
+ | m₁, .unit => m₁
24
+ | .mult pw₁ m₁, .mult pw₂ m₂ =>
25
+ match compare pw₁.x pw₂.x with
26
+ | .eq => .mult { x := pw₁.x, k := max pw₁.k pw₂.k } (lcm m₁ m₂)
27
+ | .lt => .mult pw₁ (lcm m₁ (.mult pw₂ m₂))
28
+ | .gt => .mult pw₂ (lcm (.mult pw₁ m₁) m₂)
29
+
30
+ /--
31
+ `divides m₁ m₂` returns `true` if monomial `m₁` divides `m₂`
32
+ Example: `x^2.z` divides `w.x^3.y^2.z`
33
+ -/
34
+ def Mon.divides : Mon → Mon → Bool
35
+ | .unit, _ => true
36
+ | _, .unit => false
37
+ | .mult pw₁ m₁, .mult pw₂ m₂ =>
38
+ match compare pw₁.x pw₂.x with
39
+ | .eq => pw₁.k ≤ pw₂.k && divides m₁ m₂
40
+ | .lt => false
41
+ | .gt => divides (.mult pw₁ m₁) m₂
42
+
43
+ /--
44
+ Given `m₁` and `m₂` such that `m₂.divides m₁`, then
45
+ `m₁.div m₂` returns the result.
46
+ Example `w.x^3.y^2.z` div `x^2.z` returns `w.x.y^2`
47
+ -/
48
+ def Mon.div : Mon → Mon → Mon
49
+ | m₁, .unit => m₁
50
+ | .unit, _ => .unit -- reachable only if pre-condition does not hold
51
+ | .mult pw₁ m₁, .mult pw₂ m₂ =>
52
+ match compare pw₁.x pw₂.x with
53
+ | .eq =>
54
+ let k := pw₁.k - pw₂.k
55
+ if k == 0 then
56
+ div m₁ m₂
57
+ else
58
+ .mult { x := pw₁.x, k } (div m₁ m₂)
59
+ | .lt => .mult pw₁ (div m₁ (.mult pw₂ m₂))
60
+ | .gt => .unit -- reachable only if pre-condition does not hold
61
+
62
+ /--
63
+ `coprime m₁ m₂` returns `true` if the given monomials
64
+ do not have any variable in common.
65
+ -/
66
+ def Mon.coprime : Mon → Mon → Bool
67
+ | .unit, _ => true
68
+ | _, .unit => true
69
+ | .mult pw₁ m₁, .mult pw₂ m₂ =>
70
+ match compare pw₁.x pw₂.x with
71
+ | .eq => false
72
+ | .lt => coprime m₁ (.mult pw₂ m₂)
73
+ | .gt => coprime (.mult pw₁ m₁) m₂
74
+
75
+ /--
76
+ Contains the S-polynomial resulting from superposing two polynomials `p₁` and `p₂`,
77
+ along with coefficients and monomials used in their construction.
78
+ -/
79
+ structure SPolResult where
80
+ /-- The computed S-polynomial. -/
81
+ spol : Poly := .num 0
82
+ /-- Coefficient applied to polynomial `p₁`. -/
83
+ k₁ : Int := 0
84
+ /-- Monomial factor applied to polynomial `p₁`. -/
85
+ m₁ : Mon := .unit
86
+ /-- Coefficient applied to polynomial `p₂`. -/
87
+ k₂ : Int := 0
88
+ /-- Monomial factor applied to polynomial `p₂`. -/
89
+ m₂ : Mon := .unit
90
+
91
+ def Poly.mulConst' (p : Poly) (k : Int) (char? : Option Nat := none) : Poly :=
92
+ if let some char := char? then p.mulConstC k char else p.mulConst k
93
+
94
+ def Poly.mulMon' (p : Poly) (k : Int) (m : Mon) (char? : Option Nat := none) : Poly :=
95
+ if let some char := char? then p.mulMonC k m char else p.mulMon k m
96
+
97
+ def Poly.combine' (p₁ p₂ : Poly) (char? : Option Nat := none) : Poly :=
98
+ if let some char := char? then p₁.combineC p₂ char else p₁.combine p₂
99
+
100
+ /--
101
+ Returns the S-polynomial of polynomials `p₁` and `p₂`, and coefficients&terms used to construct it.
102
+ Given polynomials with leading terms `k₁*m₁` and `k₂*m₂`, the S-polynomial is defined as:
103
+ ```
104
+ S(p₁, p₂) = (k₂/gcd(k₁, k₂)) * (lcm(m₁, m₂)/m₁) * p₁ - (k₁/gcd(k₁, k₂)) * (lcm(m₁, m₂)/m₂) * p₂
105
+ ```
106
+ Remark: if `char? = some c`, then `c` is the characteristic of the ring.
107
+ -/
108
+ def Poly.spol (p₁ p₂ : Poly) (char? : Option Nat := none) : SPolResult :=
109
+ match p₁, p₂ with
110
+ | .add k₁ m₁ p₁, .add k₂ m₂ p₂ =>
111
+ let m := m₁.lcm m₂
112
+ let m₁ := m.div m₁
113
+ let m₂ := m.div m₂
114
+ let g := Nat.gcd k₁.natAbs k₂.natAbs
115
+ let c₁ := k₂/g
116
+ let c₂ := -k₁/g
117
+ let p₁ := p₁.mulMon' c₁ m₁ char?
118
+ let p₂ := p₂.mulMon' c₂ m₂ char?
119
+ let spol := p₁.combine' p₂ char?
120
+ { spol, m₁, m₂, k₁ := c₁, k₂ := c₂ }
121
+ | _, _ => {}
122
+
123
+ /--
124
+ Result of simplifying a polynomial `p₁` using a polynomial `p₂`.
125
+
126
+ The simplification rewrites the first monomial of `p₁` that can be divided
127
+ by the leading monomial of `p₂`.
128
+ -/
129
+ structure SimpResult where
130
+ /-- The resulting simplified polynomial after rewriting. -/
131
+ p : Poly := .num 0
132
+ /-- The integer coefficient multiplied with polynomial `p₁` in the rewriting step. -/
133
+ k₁ : Int := 0
134
+ /-- The integer coefficient multiplied with polynomial `p₂` during rewriting. -/
135
+ k₂ : Int := 0
136
+ /-- The monomial factor applied to polynomial `p₂`. -/
137
+ m₂ : Mon := .unit
138
+
139
+ /--
140
+ Simplifies polynomial `p₁` using polynomial `p₂` by rewriting.
141
+
142
+ This function attempts to rewrite `p₁` by eliminating the first occurrence of
143
+ the leading monomial of `p₂`.
144
+
145
+ Remark: if `char? = some c`, then `c` is the characteristic of the ring.
146
+ -/
147
+ def Poly.simp? (p₁ p₂ : Poly) (char? : Option Nat := none) : Option SimpResult :=
148
+ match p₂ with
149
+ | .add k₂' m₂ p₂ =>
150
+ let rec go? (p₁ : Poly) : Option SimpResult :=
151
+ match p₁ with
152
+ | .add k₁' m₁ p₁ =>
153
+ if m₂.divides m₁ then
154
+ let m₂ := m₁.div m₂
155
+ let g := Nat.gcd k₁'.natAbs k₂'.natAbs
156
+ let k₁ := k₂'/g
157
+ let k₂ := -k₁'/g
158
+ let p := (p₂.mulMon' k₂ m₂ char?).combine' (p₁.mulConst' k₁ char?) char?
159
+ some { p, k₁, k₂, m₂ }
160
+ else if let some r := go? p₁ then
161
+ if let some char := char? then
162
+ let k := (k₁'*r.k₁) % char
163
+ if k == 0 then
164
+ some r
165
+ else
166
+ some { r with p := .add k m₁ r.p }
167
+ else
168
+ some { r with p := .add (k₁'*r.k₁) m₁ r.p }
169
+ else
170
+ none
171
+ | .num _ => none
172
+ go? p₁
173
+ | _ => none
174
+
175
+ def Poly.degree : Poly → Nat
176
+ | .num _ => 0
177
+ | .add _ m _ => m.degree
178
+
179
+ /-- Returns `true` if the leading monomial of `p` divides `m`. -/
180
+ def Poly.divides (p : Poly) (m : Mon) : Bool :=
181
+ match p with
182
+ | .num _ => true -- should be unreachable
183
+ | .add _ m' _ => m'.divides m
184
+
185
+ /-- Returns the leading coefficient of the given polynomial -/
186
+ def Poly.lc : Poly → Int
187
+ | .num k => k
188
+ | .add k _ _ => k
189
+
190
+ /-- Returns the leading monomial of the given polynomial. -/
191
+ def Poly.lm : Poly → Mon
192
+ | .num _ => .unit
193
+ | .add _ m _ => m
194
+
195
+ def Poly.isZero : Poly → Bool
196
+ | .num 0 => true
197
+ | _ => false
198
+
199
+ def Poly.checkCoeffs : Poly → Bool
200
+ | .num _ => true
201
+ | .add k _ p => k != 0 && checkCoeffs p
202
+
203
+ def Poly.checkNoUnitMon : Poly → Bool
204
+ | .num _ => true
205
+ | .add _ .unit _ => false
206
+ | .add _ _ p => p.checkNoUnitMon
207
+
208
+ def Poly.gcdCoeffs : Poly → Nat
209
+ | .num k => k.natAbs
210
+ | .add k _ p => go p k.natAbs
211
+ where
212
+ go (p : Poly) (acc : Nat) : Nat :=
213
+ if acc == 1 then
214
+ acc
215
+ else match p with
216
+ | .num k => Nat.gcd acc k.natAbs
217
+ | .add k _ p => go p (Nat.gcd acc k.natAbs)
218
+
219
+ def Poly.divConst (p : Poly) (a : Int) : Poly :=
220
+ match p with
221
+ | .num k => .num (k / a)
222
+ | .add k m p => .add (k / a) m (divConst p a)
223
+
224
+ def Mon.size : Mon → Nat
225
+ | .unit => 0
226
+ | .mult _ m => m.size + 1
227
+
228
+ def Poly.size : Poly → Nat
229
+ | .num _ => 1
230
+ | .add _ m p => m.size + 1 + p.size
231
+
232
+ def Poly.length : Poly → Nat
233
+ | .num _ => 0
234
+ | .add _ _ p => 1 + p.length
235
+
236
+ end Lean.Grind.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Proof.lean ADDED
@@ -0,0 +1,586 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.Ring.OfSemiring
8
+ import Lean.Meta.Tactic.Grind.Diseq
9
+ import Lean.Meta.Tactic.Grind.Arith.ProofUtil
10
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
11
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
12
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.SafePoly
13
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.ToExpr
14
+
15
+ namespace Lean.Meta.Grind.Arith.CommRing
16
+
17
+ /--
18
+ Returns a context of type `RArray α` containing the variables of the given ring.
19
+ `α` is the type of the ring.
20
+ -/
21
+ def toContextExpr : RingM Expr := do
22
+ let ring ← getRing
23
+ if h : 0 < ring.vars.size then
24
+ RArray.toExpr ring.type id (RArray.ofFn (ring.vars[·]) h)
25
+ else
26
+ RArray.toExpr ring.type id (RArray.leaf (mkApp ring.natCastFn (toExpr 0)))
27
+
28
+ private def toSContextExpr' : SemiringM Expr := do
29
+ let semiring ← getSemiring
30
+ if h : 0 < semiring.vars.size then
31
+ RArray.toExpr semiring.type id (RArray.ofFn (semiring.vars[·]) h)
32
+ else
33
+ RArray.toExpr semiring.type id (RArray.leaf (mkApp semiring.natCastFn (toExpr 0)))
34
+
35
+ /-- Similar to `toContextExpr`, but for semirings. -/
36
+ private def toSContextExpr (semiringId : Nat) : RingM Expr := do
37
+ SemiringM.run semiringId do toSContextExpr'
38
+
39
+ def throwNoNatZeroDivisors : RingM α := do
40
+ throwError "`grind` internal error, `NoNatZeroDivisors` instance is needed, but it is not available for{indentExpr (← getRing).type}"
41
+
42
+ def getPolyConst (p : Poly) : RingM Int := do
43
+ let .num k := p
44
+ | throwError "`grind` internal error, constant polynomial expected {indentExpr (← p.denoteExpr)}"
45
+ return k
46
+
47
+ namespace Null
48
+ /-!
49
+ Proof term for a Nullstellensatz certificate.
50
+ -/
51
+
52
+ /--
53
+ A "pre" Nullstellensatz certificate.
54
+ Recall that, given the hypotheses `h₁ : lhs₁ = rhs₁` ... `hₙ : lhsₙ = rhsₙ`,
55
+ a Nullstellensatz certificate is of the form
56
+ ```
57
+ q₁*(lhs₁ - rhs₁) + ... + qₙ*(lhsₙ - rhsₙ)
58
+ ```
59
+ Each hypothesis is an `EqCnstr` justified by a `.core ..` `EqnCnstrProof`.
60
+ We dynamically associate them with unique indices based on the order we find them
61
+ during traversal.
62
+ For the other `EqCnstr`s we compute a "pre" certificate as a dense array
63
+ containing `q₁` ... `qₙ` needed to create the `EqCnstr`.
64
+
65
+ We are assuming the number of hypotheses used to derive a conclusion is small
66
+ and a dense array is a reasonable representation.
67
+ -/
68
+ structure PreNullCert where
69
+ qs : Array Poly
70
+ /--
71
+ We don't use rational coefficients in the polynomials.
72
+ Thus, we need to track a denominator to justify the proof step `div`.
73
+ -/
74
+ d : Int := 1
75
+ deriving Inhabited
76
+
77
+ def PreNullCert.zero : PreNullCert :=
78
+ { qs := #[] }
79
+
80
+ def PreNullCert.unit (i : Nat) (n : Nat) : PreNullCert :=
81
+ let qs := Array.replicate n (.num 0)
82
+ let qs := qs.set! i (.num 1)
83
+ { qs }
84
+
85
+ def PreNullCert.div (c : PreNullCert) (k : Int) : RingM PreNullCert := do
86
+ return { c with d := c.d * k }
87
+
88
+ def PreNullCert.mul (c : PreNullCert) (k : Int) : RingM PreNullCert := do
89
+ if k == 1 then
90
+ return c
91
+ else
92
+ let g := Int.gcd k c.d
93
+ let k := k / g
94
+ let d := c.d / g
95
+ if k == 1 then
96
+ return { c with d }
97
+ else
98
+ let qs ← c.qs.mapM fun q => q.mulConstM k
99
+ return { qs, d }
100
+
101
+ def PreNullCert.combine (k₁ : Int) (m₁ : Mon) (c₁ : PreNullCert) (k₂ : Int) (m₂ : Mon) (c₂ : PreNullCert) : RingM PreNullCert := do
102
+ let d₁ := c₁.d
103
+ let d₂ := c₂.d
104
+ let k₁_d₂ := k₁*d₂
105
+ let k₂_d₁ := k₂*d₁
106
+ let d₁_d₂ := d₁*d₂
107
+ let g := Int.gcd (Int.gcd k₁_d₂ k₂_d₁) d₁_d₂
108
+ let k₁ := k₁_d₂ / g
109
+ let k₂ := k₂_d₁ / g
110
+ let d := d₁_d₂ / g
111
+ let qs₁ := c₁.qs
112
+ let qs₂ := c₂.qs
113
+ let n := Nat.max qs₁.size qs₂.size
114
+ let mut qs : Vector Poly n := Vector.replicate n (.num 0)
115
+ for h : i in [:n] do
116
+ if h₁ : i < qs₁.size then
117
+ let q₁ ← qs₁[i].mulMonM k₁ m₁
118
+ if h₂ : i < qs₂.size then
119
+ let q₂ ← qs₂[i].mulMonM k₂ m₂
120
+ qs := qs.set i (← q₁.combineM q₂)
121
+ else
122
+ qs := qs.set i q₁
123
+ else
124
+ have : i < n := h.upper
125
+ have : qs₁.size = n ∨ qs₂.size = n := by simp +zetaDelta only [Nat.max_def, right_eq_ite_iff]; split <;> simp [*]
126
+ have : i < qs₂.size := by omega
127
+ let q₂ ← qs₂[i].mulMonM k₂ m₂
128
+ qs := qs.set i q₂
129
+ return { qs := qs.toArray, d }
130
+
131
+ structure NullCertHypothesis where
132
+ h : Expr
133
+ lhs : RingExpr
134
+ rhs : RingExpr
135
+
136
+ structure ProofM.State where
137
+ /-- Mapping from `EqCnstr` to `PreNullCert` -/
138
+ cache : Std.HashMap UInt64 PreNullCert := {}
139
+ hyps : Array NullCertHypothesis := #[]
140
+
141
+ abbrev ProofM := StateRefT ProofM.State RingM
142
+
143
+ private abbrev caching (c : α) (k : ProofM PreNullCert) : ProofM PreNullCert := do
144
+ let addr := unsafe (ptrAddrUnsafe c).toUInt64 >>> 2
145
+ if let some h := (← get).cache[addr]? then
146
+ return h
147
+ else
148
+ let h ← k
149
+ modify fun s => { s with cache := s.cache.insert addr h }
150
+ return h
151
+
152
+ structure NullCertExt where
153
+ d : Int
154
+ qhs : Array (Poly × NullCertHypothesis)
155
+
156
+ end Null
157
+
158
+ section
159
+ open Null
160
+ partial def EqCnstr.toPreNullCert (c : EqCnstr) : ProofM PreNullCert := caching c do
161
+ match c.h with
162
+ | .core a b lhs rhs =>
163
+ let i := (← get).hyps.size
164
+ let h ← mkEqProof a b
165
+ modify fun s => { s with hyps := s.hyps.push { h, lhs, rhs } }
166
+ return PreNullCert.unit i (i+1)
167
+ | .coreS _a _b _sa _sb _ra _rb =>
168
+ throwError "`grind +ringNull` is not supported yet for this goal"
169
+ | .superpose k₁ m₁ c₁ k₂ m₂ c₂ => (← c₁.toPreNullCert).combine k₁ m₁ k₂ m₂ (← c₂.toPreNullCert)
170
+ | .simp k₁ c₁ k₂ m₂ c₂ => (← c₁.toPreNullCert).combine k₁ .unit k₂ m₂ (← c₂.toPreNullCert)
171
+ | .mul k c => (← c.toPreNullCert).mul k
172
+ | .div k c => (← c.toPreNullCert).div k
173
+ | .gcd .. | .numEq0 .. =>
174
+ throwError "`grind +ringNull` is not supported yet for this goal"
175
+
176
+ def PolyDerivation.toPreNullCert (d : PolyDerivation) : ProofM PreNullCert := do
177
+ match d with
178
+ | .input _ => return .zero
179
+ | .step _p k₁ d k₂ m₂ c₂ =>
180
+ -- Recall that _p = k₁*d.getPoly + k₂*m₂*c.p
181
+ trace[grind.debug.ring.proof] ">> k₁: {k₁}, {(← d.toPreNullCert).d}, {(← c₂.toPreNullCert).d}"
182
+ (← d.toPreNullCert).combine k₁ .unit (-k₂) m₂ (← c₂.toPreNullCert)
183
+ | .normEq0 .. =>
184
+ throwError "`grind +ringNull` is not supported yet for this goal"
185
+
186
+ /-- Returns the multiplier `k` for the input polynomial. See comment at `PolyDerivation.step`. -/
187
+ def PolyDerivation.getMultiplier (d : PolyDerivation) : Int :=
188
+ go d 1
189
+ where
190
+ go (d : PolyDerivation) (acc : Int) : Int :=
191
+ match d with
192
+ | .input _ => acc
193
+ | .step _ k₁ d .. => go d (k₁ * acc)
194
+ | .normEq0 _ d .. => go d acc
195
+
196
+ def EqCnstr.mkNullCertExt (c : EqCnstr) : RingM NullCertExt := do
197
+ let (nc, s) ← c.toPreNullCert.run {}
198
+ return { d := nc.d, qhs := nc.qs.zip s.hyps }
199
+
200
+ def PolyDerivation.mkNullCertExt (d : PolyDerivation) : RingM NullCertExt := do
201
+ let (nc, s) ← d.toPreNullCert.run {}
202
+ return { d := nc.d, qhs := nc.qs.zip s.hyps }
203
+
204
+ def DiseqCnstr.mkNullCertExt (c : DiseqCnstr) : RingM NullCertExt :=
205
+ c.d.mkNullCertExt
206
+ end
207
+
208
+ namespace Null
209
+ def NullCertExt.toPoly (nc : NullCertExt) : RingM Poly := do
210
+ let mut p : Poly := .num 0
211
+ for (q, h) in nc.qhs do
212
+ p ← p.combineM (← q.mulM (← (h.lhs.sub h.rhs).toPolyM))
213
+ return p
214
+
215
+ def NullCertExt.check (c : EqCnstr) (nc : NullCertExt) : RingM Bool := do
216
+ let p₁ := c.p.mulConst' nc.d (← nonzeroChar?)
217
+ let p₂ ← nc.toPoly
218
+ return p₁ == p₂
219
+
220
+ def NullCertExt.toNullCert (nc : NullCertExt) : Grind.CommRing.NullCert :=
221
+ go nc.qhs.size .empty (by omega)
222
+ where
223
+ go (i : Nat) (acc : Grind.CommRing.NullCert) (h : i ≤ nc.qhs.size) : Grind.CommRing.NullCert :=
224
+ if h : i > 0 then
225
+ let i := i - 1
226
+ let (p, h) := nc.qhs[i]
227
+ go i (.add p h.lhs h.rhs acc) (by omega)
228
+ else
229
+ acc
230
+
231
+ /--
232
+ Given a `pr`, returns `pr h₁ ... hₙ`, where `n` is size `nc.qhs.size`, and `hᵢ`s
233
+ are the equalities in the certificate.
234
+ -/
235
+ def NullCertExt.applyEqs (pr : Expr) (nc : NullCertExt) : Expr :=
236
+ go 0 pr
237
+ where
238
+ go (i : Nat) (pr : Expr) : Expr :=
239
+ if _ : i < nc.qhs.size then
240
+ let (_, h) := nc.qhs[i]
241
+ go (i + 1) (mkApp pr h.h)
242
+ else
243
+ pr
244
+
245
+ private def getNoZeroDivInstIfNeeded? (k : Int) : RingM (Option Expr) := do
246
+ if k == 1 then
247
+ return none
248
+ else
249
+ let some inst ← noZeroDivisorsInst? | throwNoNatZeroDivisors
250
+ return some inst
251
+
252
+ def setEqUnsat (c : EqCnstr) : RingM Unit := do
253
+ trace_goal[grind.ring.assert.unsat] "{← c.denoteExpr}"
254
+ let k ← getPolyConst c.p
255
+ let ncx ← c.mkNullCertExt
256
+ trace_goal[grind.ring.assert.unsat] "{ncx.d}*({← c.p.denoteExpr}), {← (← ncx.toPoly).denoteExpr}"
257
+ let ring ← getRing
258
+ let some (charInst, char) := ring.charInst?
259
+ | throwError "`grind` internal error, `IsCharP` instance is needed, but it is not available for{indentExpr (← getRing).type}\nconsider not using `+ringNull`"
260
+ let h := if char == 0 then
261
+ mkApp (mkConst ``Grind.CommRing.NullCert.eq_unsat [ring.u]) ring.type
262
+ else
263
+ mkApp2 (mkConst ``Grind.CommRing.NullCert.eq_unsatC [ring.u]) ring.type (toExpr char)
264
+ let ctx ← toContextExpr
265
+ let nc := toExpr (ncx.toNullCert)
266
+ let h := mkApp6 h ring.commRingInst charInst ctx nc (toExpr k) reflBoolTrue
267
+ closeGoal <| ncx.applyEqs h
268
+
269
+ def setDiseqUnsat (c : DiseqCnstr) : RingM Unit := do
270
+ trace_goal[grind.ring.assert.unsat] "{← c.denoteExpr}"
271
+ let ncx ← c.mkNullCertExt
272
+ trace_goal[grind.ring.assert.unsat] "multiplier: {c.d.getMultiplier}, {ncx.d}*({← c.d.p.denoteExpr}), {← (← ncx.toPoly).denoteExpr}"
273
+ let nc := toExpr (ncx.toNullCert)
274
+ let ring ← getRing
275
+ let ctx ← toContextExpr
276
+ let k := c.d.getMultiplier * ncx.d
277
+ let h := match (← nonzeroCharInst?), (← getNoZeroDivInstIfNeeded? k) with
278
+ | some (charInst, char), some nzDivInst =>
279
+ mkApp8 (mkConst ``Grind.CommRing.NullCert.ne_nzdiv_unsatC [ring.u]) ring.type (toExpr char) ring.commRingInst charInst nzDivInst ctx nc (toExpr k)
280
+ | some (charInst, char), none =>
281
+ mkApp6 (mkConst ``Grind.CommRing.NullCert.ne_unsatC [ring.u]) ring.type (toExpr char) ring.commRingInst charInst ctx nc
282
+ | none, some nzDivInst =>
283
+ mkApp6 (mkConst ``Grind.CommRing.NullCert.ne_nzdiv_unsat [ring.u]) ring.type ring.commRingInst nzDivInst ctx nc (toExpr k)
284
+ | none, none =>
285
+ mkApp4 (mkConst ``Grind.CommRing.NullCert.ne_unsat [ring.u]) ring.type ring.commRingInst ctx nc
286
+ let h := mkApp4 h (toExpr c.rlhs) (toExpr c.rrhs) reflBoolTrue (← mkDiseqProof c.lhs c.rhs)
287
+ closeGoal <| ncx.applyEqs h
288
+
289
+ def propagateEq (a b : Expr) (ra rb : RingExpr) (d : PolyDerivation) : RingM Unit := do
290
+ let ncx ← d.mkNullCertExt
291
+ let nc := toExpr (ncx.toNullCert)
292
+ let ring ← getRing
293
+ let ctx ← toContextExpr
294
+ let k := d.getMultiplier * ncx.d
295
+ let h := match (← nonzeroCharInst?), (← getNoZeroDivInstIfNeeded? k) with
296
+ | some (charInst, char), some nzDivInst =>
297
+ mkApp8 (mkConst ``Grind.CommRing.NullCert.eq_nzdivC [ring.u]) ring.type (toExpr char) ring.commRingInst charInst nzDivInst ctx nc (toExpr k)
298
+ | some (charInst, char), none =>
299
+ mkApp6 (mkConst ``Grind.CommRing.NullCert.eqC [ring.u]) ring.type (toExpr char) ring.commRingInst charInst ctx nc
300
+ | none, some nzDivInst =>
301
+ mkApp6 (mkConst ``Grind.CommRing.NullCert.eq_nzdiv [ring.u]) ring.type ring.commRingInst nzDivInst ctx nc (toExpr k)
302
+ | none, none =>
303
+ mkApp4 (mkConst ``Grind.CommRing.NullCert.eq [ring.u]) ring.type ring.commRingInst ctx nc
304
+ let h := mkApp3 h (toExpr ra) (toExpr rb) reflBoolTrue
305
+ trace_goal[grind.debug.ring.impEq] "{a}, {b}"
306
+ let eq := mkApp3 (mkConst ``Eq [.succ ring.u]) ring.type a b
307
+ pushEq a b <| mkExpectedPropHint (ncx.applyEqs h) eq
308
+
309
+ end Null
310
+
311
+ namespace Stepwise
312
+ /-!
313
+ Alternative proof term construction where we generate a sub-term for each step in
314
+ the derivation.
315
+ -/
316
+
317
+ structure ProofM.State where
318
+ cache : Std.HashMap UInt64 Expr := {}
319
+ polyMap : Std.HashMap Poly Expr := {}
320
+ monMap : Std.HashMap Mon Expr := {}
321
+ exprMap : Std.HashMap RingExpr Expr := {}
322
+ sexprMap : Std.HashMap SemiringExpr Expr := {}
323
+
324
+ structure ProofM.Context where
325
+ ctx : Expr
326
+ /-- Context for semiring variables if available -/
327
+ sctx? : Option Expr
328
+
329
+ abbrev ProofM := ReaderT ProofM.Context (StateRefT ProofM.State RingM)
330
+
331
+ /-- Returns a Lean expression representing the variable context used to construct `CommRing` proof steps. -/
332
+ private abbrev getContext : ProofM Expr := do
333
+ return (← read).ctx
334
+
335
+ /--
336
+ Returns a Lean expression representing the semiring variable context
337
+ used to construct `CommRing` proof steps.
338
+ -/
339
+ private abbrev getSContext : ProofM Expr := do
340
+ let some sctx := (← read).sctx?
341
+ | throwError "`grind` internal error, semiring context is not available"
342
+ return sctx
343
+
344
+ private abbrev caching (c : α) (k : ProofM Expr) : ProofM Expr := do
345
+ let addr := unsafe (ptrAddrUnsafe c).toUInt64 >>> 2
346
+ if let some h := (← get).cache[addr]? then
347
+ return h
348
+ else
349
+ let h ← k
350
+ modify fun s => { s with cache := s.cache.insert addr h }
351
+ return h
352
+
353
+ def mkPolyDecl (p : Poly) : ProofM Expr := do
354
+ if let some x := (← get).polyMap[p]? then
355
+ return x
356
+ let x := mkFVar (← mkFreshFVarId)
357
+ modify fun s => { s with polyMap := s.polyMap.insert p x }
358
+ return x
359
+
360
+ def mkExprDecl (e : RingExpr) : ProofM Expr := do
361
+ if let some x := (← get).exprMap[e]? then
362
+ return x
363
+ let x := mkFVar (← mkFreshFVarId)
364
+ modify fun s => { s with exprMap := s.exprMap.insert e x }
365
+ return x
366
+
367
+ def mkSExprDecl (e : SemiringExpr) : ProofM Expr := do
368
+ if let some x := (← get).sexprMap[e]? then
369
+ return x
370
+ let x := mkFVar (← mkFreshFVarId)
371
+ modify fun s => { s with sexprMap := s.sexprMap.insert e x }
372
+ return x
373
+
374
+ def mkMonDecl (m : Mon) : ProofM Expr := do
375
+ if let some x := (← get).monMap[m]? then
376
+ return x
377
+ let x := mkFVar (← mkFreshFVarId)
378
+ modify fun s => { s with monMap := s.monMap.insert m x }
379
+ return x
380
+
381
+ private def mkStepBasicPrefix (declName : Name) : ProofM Expr := do
382
+ let ctx ← getContext
383
+ let ring ← getRing
384
+ return mkApp3 (mkConst declName [ring.u]) ring.type ring.commRingInst ctx
385
+
386
+ private def mkStepPrefix (declName declNameC : Name) : ProofM Expr := do
387
+ if let some (charInst, char) ← nonzeroCharInst? then
388
+ let ctx ← getContext
389
+ let ring ← getRing
390
+ return mkApp5 (mkConst declNameC [ring.u]) ring.type (toExpr char) ring.commRingInst charInst ctx
391
+ else
392
+ mkStepBasicPrefix declName
393
+
394
+ private def getSemiringOf : RingM Semiring := do
395
+ let some semiringId := (← getRing).semiringId? | throwError "`grind` internal error, semiring is not available"
396
+ SemiringM.run semiringId do getSemiring
397
+
398
+ private def mkSemiringPrefix (declName : Name) : ProofM Expr := do
399
+ let sctx ← getSContext
400
+ let semiring ← getSemiringOf
401
+ return mkApp3 (mkConst declName [semiring.u]) semiring.type semiring.semiringInst sctx
402
+
403
+ private def mkSemiringAddRightCancelPrefix (declName : Name) : ProofM Expr := do
404
+ let sctx ← getSContext
405
+ let semiring ← getSemiringOf
406
+ let some addRightCancelInst := semiring.addRightCancelInst?
407
+ | throwError "`grind` internal error, `AddRightCancel` instance is not available"
408
+ return mkApp4 (mkConst declName [semiring.u]) semiring.type semiring.semiringInst addRightCancelInst sctx
409
+
410
+ open Lean.Grind.CommRing in
411
+ partial def _root_.Lean.Meta.Grind.Arith.CommRing.EqCnstr.toExprProof (c : EqCnstr) : ProofM Expr := caching c do
412
+ match c.h with
413
+ | .core a b lhs rhs =>
414
+ let h ← mkStepPrefix ``Stepwise.core ``Stepwise.coreC
415
+ return mkApp5 h (← mkExprDecl lhs) (← mkExprDecl rhs) (← mkPolyDecl c.p) reflBoolTrue (← mkEqProof a b)
416
+ | .coreS a b sa sb ra rb =>
417
+ let h' ← mkSemiringPrefix ``Grind.Ring.OfSemiring.of_eq
418
+ let h' := mkApp3 h' (← mkSExprDecl sa) (← mkSExprDecl sb) (← mkEqProof a b)
419
+ let h ← mkStepPrefix ``Stepwise.core ``Stepwise.coreC
420
+ return mkApp5 h (← mkExprDecl ra) (← mkExprDecl rb) (← mkPolyDecl c.p) reflBoolTrue h'
421
+ | .superpose k₁ m₁ c₁ k₂ m₂ c₂ =>
422
+ let h ← mkStepPrefix ``Stepwise.superpose ``Stepwise.superposeC
423
+ return mkApp10 h
424
+ (toExpr k₁) (← mkMonDecl m₁) (← mkPolyDecl c₁.p)
425
+ (toExpr k₂) (← mkMonDecl m₂) (← mkPolyDecl c₂.p)
426
+ (← mkPolyDecl c.p) reflBoolTrue (← toExprProof c₁) (← toExprProof c₂)
427
+ | .simp k₁ c₁ k₂ m₂ c₂ =>
428
+ let h ← mkStepPrefix ``Stepwise.simp ``Stepwise.simpC
429
+ return mkApp9 h
430
+ (toExpr k₁) (← mkPolyDecl c₁.p)
431
+ (toExpr k₂) (← mkMonDecl m₂) (← mkPolyDecl c₂.p)
432
+ (← mkPolyDecl c.p) reflBoolTrue (← toExprProof c₁) (← toExprProof c₂)
433
+ | .mul k c₁ =>
434
+ let h ← mkStepPrefix ``Stepwise.mul ``Stepwise.mulC
435
+ return mkApp5 h (← mkPolyDecl c₁.p) (toExpr k) (← mkPolyDecl c.p) reflBoolTrue (← toExprProof c₁)
436
+ | .div k c₁ =>
437
+ let h ← mkStepPrefix ``Stepwise.div ``Stepwise.divC
438
+ let some nzInst ← noZeroDivisorsInst?
439
+ | throwNoNatZeroDivisors
440
+ return mkApp6 h nzInst (← mkPolyDecl c₁.p) (toExpr k) (← mkPolyDecl c.p) reflBoolTrue (← toExprProof c₁)
441
+ | .gcd a b c₁ c₂ =>
442
+ let h ← mkStepBasicPrefix ``Grind.CommRing.eq_gcd
443
+ return mkApp8 h (toExpr a) (toExpr b) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c.p)
444
+ reflBoolTrue (← toExprProof c₁) (← toExprProof c₂)
445
+ | .numEq0 k c₁ c₂ =>
446
+ let h ← mkStepBasicPrefix ``Grind.CommRing.eq_normEq0
447
+ return mkApp7 h (toExpr k) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c.p)
448
+ reflBoolTrue (← toExprProof c₁) (← toExprProof c₂)
449
+
450
+ open Lean.Grind.CommRing in
451
+ /--
452
+ Given a polynomial derivation, returns `(k, p₀, h)` where `h` is a proof that
453
+ `k*p₀ = d.p`
454
+ -/
455
+ private def derivToExprProof (d : PolyDerivation) : ProofM (Int × Poly × Expr) := do
456
+ match d with
457
+ | .input p₀ =>
458
+ let h := mkApp (← mkStepBasicPrefix ``Stepwise.d_init) (← mkPolyDecl p₀)
459
+ return (1, p₀, h)
460
+ | .step p k₁ d k₂ m₂ c₂ =>
461
+ let (k, p₀, h₁) ← derivToExprProof d
462
+ let h₂ ← c₂.toExprProof
463
+ let h ← if k₁ == 1 then
464
+ mkStepPrefix ``Stepwise.d_step1 ``Stepwise.d_step1C
465
+ else
466
+ pure <| mkApp (← mkStepPrefix ``Stepwise.d_stepk ``Stepwise.d_stepkC) (toExpr k₁)
467
+ let h := mkApp10 h
468
+ (toExpr k) (← mkPolyDecl p₀) (← mkPolyDecl d.p)
469
+ (toExpr k₂) (← mkMonDecl m₂) (← mkPolyDecl c₂.p) (← mkPolyDecl p)
470
+ reflBoolTrue h₁ h₂
471
+ return (k₁*k, p₀, h)
472
+ | .normEq0 p d c =>
473
+ let (k, p₀, h₁) ← derivToExprProof d
474
+ let h₂ ← c.toExprProof
475
+ let .num a := c.p | unreachable!
476
+ let h ← mkStepBasicPrefix ``Grind.CommRing.d_normEq0
477
+ let h := mkApp9 h
478
+ (toExpr k) (toExpr a.natAbs) (← mkPolyDecl p₀) (← mkPolyDecl d.p)
479
+ (← mkPolyDecl c.p) (← mkPolyDecl p) reflBoolTrue h₁ h₂
480
+ return (k, p₀, h)
481
+
482
+ open Lean.Grind.CommRing in
483
+ /--
484
+ Given a derivation `d` for `k * p = 0` where `lhs - rhs = p`, returns a proof for `lhs = rhs`.
485
+ -/
486
+ private def mkImpEqExprProof (lhs rhs : RingExpr) (d : PolyDerivation) : ProofM Expr := do
487
+ assert! d.p matches .num 0
488
+ let (k, p₀, h₁) ← derivToExprProof d
489
+ let h ← if k == 1 then
490
+ mkStepPrefix ``Stepwise.imp_1eq ``Stepwise.imp_1eqC
491
+ else
492
+ let some nzInst ← noZeroDivisorsInst?
493
+ | throwNoNatZeroDivisors
494
+ pure <| mkApp2 (← mkStepPrefix ``Stepwise.imp_keq ``Stepwise.imp_keqC) nzInst (toExpr k)
495
+ return mkApp6 h (← mkExprDecl lhs) (← mkExprDecl rhs) (← mkPolyDecl p₀) (← mkPolyDecl d.p) reflBoolTrue h₁
496
+
497
+ private abbrev withSemiringContext (k : Option Expr → RingM Expr) : RingM Expr := do
498
+ let some semiringId := (← getRing).semiringId? | k none
499
+ let sctx ← toSContextExpr semiringId
500
+ let semiring ← getSemiringOf
501
+ withLetDecl `sctx (mkApp (mkConst ``RArray [semiring.u]) semiring.type) sctx fun sctx =>
502
+ k (some sctx)
503
+
504
+ private abbrev withProofContext (x : ProofM Expr) : RingM Expr := do
505
+ let ring ← getRing
506
+ withLetDecl `ctx (mkApp (mkConst ``RArray [ring.u]) ring.type) (← toContextExpr) fun ctx =>
507
+ withSemiringContext fun sctx? =>
508
+ go { ctx, sctx? } |>.run' {}
509
+ where
510
+ go : ProofM Expr := do
511
+ let h ← x
512
+ let h ← mkLetOfMap (← get).polyMap h `p (mkConst ``Grind.CommRing.Poly) toExpr
513
+ let h ← mkLetOfMap (← get).monMap h `m (mkConst ``Grind.CommRing.Mon) toExpr
514
+ let h ← mkLetOfMap (← get).exprMap h `e (mkConst ``Grind.CommRing.Expr) toExpr
515
+ let h ← mkLetOfMap (← get).sexprMap h `s (mkConst ``Grind.Ring.OfSemiring.Expr) toExpr
516
+ let h ← if let some sctx := (← read).sctx? then mkLetFVars #[sctx] h else pure h
517
+ mkLetFVars #[(← getContext)] h
518
+
519
+ open Lean.Grind.CommRing in
520
+ def setEqUnsat (c : EqCnstr) : RingM Unit := do
521
+ let h ← withProofContext do
522
+ let ring ← getRing
523
+ if let some (charInst, char) := ring.charInst? then
524
+ let mut h ← mkStepPrefix ``Stepwise.unsat_eq ``Stepwise.unsat_eqC
525
+ if char == 0 then
526
+ h := mkApp h charInst
527
+ let k ← getPolyConst c.p
528
+ return mkApp4 h (← mkPolyDecl c.p) (toExpr k) reflBoolTrue (← c.toExprProof)
529
+ else if let some fieldInst := ring.fieldInst? then
530
+ return mkApp6 (mkConst ``Grind.CommRing.one_eq_zero_unsat [ring.u]) ring.type fieldInst (← getContext)
531
+ (← mkPolyDecl c.p) reflBoolTrue (← c.toExprProof)
532
+ else
533
+ throwError "`grind ring` internal error, unexpected unsat eq proof {← c.denoteExpr}"
534
+ closeGoal h
535
+
536
+ def setDiseqUnsat (c : DiseqCnstr) : RingM Unit := do
537
+ let h ← withProofContext do
538
+ let heq ← mkImpEqExprProof c.rlhs c.rrhs c.d
539
+ let hne ← if let some (sa, sb) := c.ofSemiring? then
540
+ let h ← mkSemiringAddRightCancelPrefix ``Grind.Ring.OfSemiring.of_diseq
541
+ pure <| mkApp3 h (← mkSExprDecl sa) (← mkSExprDecl sb) (← mkDiseqProof c.lhs c.rhs)
542
+ else
543
+ mkDiseqProof c.lhs c.rhs
544
+ return mkApp hne heq
545
+ closeGoal h
546
+
547
+ def propagateEq (a b : Expr) (ra rb : RingExpr) (d : PolyDerivation) : RingM Unit := do
548
+ let heq ← withProofContext do
549
+ mkImpEqExprProof ra rb d
550
+ let ring ← getRing
551
+ let eq := mkApp3 (mkConst ``Eq [.succ ring.u]) ring.type a b
552
+ pushEq a b <| mkExpectedPropHint heq eq
553
+
554
+ end Stepwise
555
+
556
+ def EqCnstr.setUnsat (c : EqCnstr) : RingM Unit := do
557
+ if (← getConfig).ringNull then
558
+ Null.setEqUnsat c
559
+ else
560
+ Stepwise.setEqUnsat c
561
+
562
+ def DiseqCnstr.setUnsat (c : DiseqCnstr) : RingM Unit := do
563
+ if (← getConfig).ringNull then
564
+ Null.setDiseqUnsat c
565
+ else
566
+ Stepwise.setDiseqUnsat c
567
+
568
+ def propagateEq (a b : Expr) (ra rb : RingExpr) (d : PolyDerivation) : RingM Unit := do
569
+ if (← getConfig).ringNull then
570
+ Null.propagateEq a b ra rb d
571
+ else
572
+ Stepwise.propagateEq a b ra rb d
573
+
574
+ /--
575
+ Given `a` and `b`, such that `a ≠ b` in the core and `sa` and `sb` their reified semiring
576
+ terms s.t. `sa.toPoly == sb.toPoly`, close the goal.
577
+ -/
578
+ def setSemiringDiseqUnsat (a b : Expr) (sa sb : SemiringExpr) : SemiringM Unit := do
579
+ let ctx ← toSContextExpr'
580
+ let semiring ← getSemiring
581
+ let hne ← mkDiseqProof a b
582
+ let h := mkApp3 (mkConst ``Grind.Ring.OfSemiring.eq_normS [semiring.u]) semiring.type semiring.commSemiringInst ctx
583
+ let h := mkApp3 h (toExpr sa) (toExpr sb) reflBoolTrue
584
+ closeGoal (mkApp hne h)
585
+
586
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Reify.lean ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Simp
8
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Util
9
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Var
10
+
11
+ namespace Lean.Meta.Grind.Arith.CommRing
12
+
13
+ def isAddInst (ring : Ring) (inst : Expr) : Bool :=
14
+ isSameExpr ring.addFn.appArg! inst
15
+ def isMulInst (ring : Ring) (inst : Expr) : Bool :=
16
+ isSameExpr ring.mulFn.appArg! inst
17
+ def isSubInst (ring : Ring) (inst : Expr) : Bool :=
18
+ isSameExpr ring.subFn.appArg! inst
19
+ def isNegInst (ring : Ring) (inst : Expr) : Bool :=
20
+ isSameExpr ring.negFn.appArg! inst
21
+ def isPowInst (ring : Ring) (inst : Expr) : Bool :=
22
+ isSameExpr ring.powFn.appArg! inst
23
+ def isIntCastInst (ring : Ring) (inst : Expr) : Bool :=
24
+ isSameExpr ring.intCastFn.appArg! inst
25
+ def isNatCastInst (ring : Ring) (inst : Expr) : Bool :=
26
+ isSameExpr ring.natCastFn.appArg! inst
27
+
28
+ private def reportAppIssue (e : Expr) : GoalM Unit := do
29
+ reportIssue! "comm ring term with unexpected instance{indentExpr e}"
30
+
31
+ /--
32
+ Converts a Lean expression `e` in the `CommRing` into a `CommRing.Expr` object.
33
+
34
+ If `skipVar` is `true`, then the result is `none` if `e` is not an interpreted `CommRing` term.
35
+ We use `skipVar := false` when processing inequalities, and `skipVar := true` for equalities and disequalities
36
+ -/
37
+ partial def reify? (e : Expr) (skipVar := true) (gen : Nat := 0) : RingM (Option RingExpr) := do
38
+ let mkVar (e : Expr) : RingM Var := do
39
+ if (← alreadyInternalized e) then
40
+ mkVar e
41
+ else
42
+ internalize e gen
43
+ mkVar e
44
+ let toVar (e : Expr) : RingM RingExpr := do
45
+ return .var (← mkVar e)
46
+ let asVar (e : Expr) : RingM RingExpr := do
47
+ reportAppIssue e
48
+ return .var (← mkVar e)
49
+ let rec go (e : Expr) : RingM RingExpr := do
50
+ match_expr e with
51
+ | HAdd.hAdd _ _ _ i a b =>
52
+ if isAddInst (← getRing) i then return .add (← go a) (← go b) else asVar e
53
+ | HMul.hMul _ _ _ i a b =>
54
+ if isMulInst (← getRing) i then return .mul (← go a) (← go b) else asVar e
55
+ | HSub.hSub _ _ _ i a b =>
56
+ if isSubInst (← getRing) i then return .sub (← go a) (← go b) else asVar e
57
+ | HPow.hPow _ _ _ i a b =>
58
+ let some k ← getNatValue? b | toVar e
59
+ if isPowInst (← getRing) i then return .pow (← go a) k else asVar e
60
+ | Neg.neg _ i a =>
61
+ if isNegInst (← getRing) i then return .neg (← go a) else asVar e
62
+ | IntCast.intCast _ i a =>
63
+ if isIntCastInst (← getRing) i then
64
+ let some k ← getIntValue? a | toVar e
65
+ return .num k
66
+ else
67
+ asVar e
68
+ | NatCast.natCast _ i a =>
69
+ if isNatCastInst (← getRing) i then
70
+ let some k ← getNatValue? a | toVar e
71
+ return .num k
72
+ else
73
+ asVar e
74
+ | OfNat.ofNat _ n _ =>
75
+ let some k ← getNatValue? n | toVar e
76
+ return .num k
77
+ | _ => toVar e
78
+ let toTopVar (e : Expr) : RingM (Option RingExpr) := do
79
+ if skipVar then
80
+ return none
81
+ else
82
+ return some (← toVar e)
83
+ let asTopVar (e : Expr) : RingM (Option RingExpr) := do
84
+ reportAppIssue e
85
+ toTopVar e
86
+ match_expr e with
87
+ | HAdd.hAdd _ _ _ i a b =>
88
+ if isAddInst (← getRing) i then return some (.add (← go a) (← go b)) else asTopVar e
89
+ | HMul.hMul _ _ _ i a b =>
90
+ if isMulInst (← getRing) i then return some (.mul (← go a) (← go b)) else asTopVar e
91
+ | HSub.hSub _ _ _ i a b =>
92
+ if isSubInst (← getRing) i then return some (.sub (← go a) (← go b)) else asTopVar e
93
+ | HPow.hPow _ _ _ i a b =>
94
+ let some k ← getNatValue? b | return none
95
+ if isPowInst (← getRing) i then return some (.pow (← go a) k) else asTopVar e
96
+ | Neg.neg _ i a =>
97
+ if isNegInst (← getRing) i then return some (.neg (← go a)) else asTopVar e
98
+ | IntCast.intCast _ i a =>
99
+ if isIntCastInst (← getRing) i then
100
+ let some k ← getIntValue? a | toTopVar e
101
+ return some (.num k)
102
+ else
103
+ asTopVar e
104
+ | NatCast.natCast _ i a =>
105
+ if isNatCastInst (← getRing) i then
106
+ let some k ← getNatValue? a | toTopVar e
107
+ return some (.num k)
108
+ else
109
+ asTopVar e
110
+ | OfNat.ofNat _ n _ =>
111
+ let some k ← getNatValue? n | asTopVar e
112
+ return some (.num k)
113
+ | _ => toTopVar e
114
+
115
+ private def reportSAppIssue (e : Expr) : GoalM Unit := do
116
+ reportIssue! "comm semiring term with unexpected instance{indentExpr e}"
117
+
118
+ /--
119
+ Similar to `reify?` but for `CommSemiring`
120
+ -/
121
+ partial def sreify? (e : Expr) : SemiringM (Option SemiringExpr) := do
122
+ let toVar (e : Expr) : SemiringM SemiringExpr := do
123
+ return .var (← mkSVar e)
124
+ let asVar (e : Expr) : SemiringM SemiringExpr := do
125
+ reportSAppIssue e
126
+ return .var (← mkSVar e)
127
+ let rec go (e : Expr) : SemiringM SemiringExpr := do
128
+ match_expr e with
129
+ | HAdd.hAdd _ _ _ i a b =>
130
+ if isSameExpr (← getSemiring).addFn.appArg! i then return .add (← go a) (← go b) else asVar e
131
+ | HMul.hMul _ _ _ i a b =>
132
+ if isSameExpr (← getSemiring).mulFn.appArg! i then return .mul (← go a) (← go b) else asVar e
133
+ | HPow.hPow _ _ _ i a b =>
134
+ let some k ← getNatValue? b | toVar e
135
+ if isSameExpr (← getSemiring).powFn.appArg! i then return .pow (← go a) k else asVar e
136
+ | NatCast.natCast _ i a =>
137
+ if isSameExpr (← getSemiring).natCastFn.appArg! i then
138
+ let some k ← getNatValue? a | toVar e
139
+ return .num k
140
+ else
141
+ asVar e
142
+ | OfNat.ofNat _ n _ =>
143
+ let some k ← getNatValue? n | toVar e
144
+ return .num k
145
+ | _ => toVar e
146
+ let toTopVar (e : Expr) : SemiringM (Option SemiringExpr) := do
147
+ return some (← toVar e)
148
+ let asTopVar (e : Expr) : SemiringM (Option SemiringExpr) := do
149
+ reportSAppIssue e
150
+ toTopVar e
151
+ match_expr e with
152
+ | HAdd.hAdd _ _ _ i a b =>
153
+ if isSameExpr (← getSemiring).addFn.appArg! i then return some (.add (← go a) (← go b)) else asTopVar e
154
+ | HMul.hMul _ _ _ i a b =>
155
+ if isSameExpr (← getSemiring).mulFn.appArg! i then return some (.mul (← go a) (← go b)) else asTopVar e
156
+ | HPow.hPow _ _ _ i a b =>
157
+ let some k ← getNatValue? b | return none
158
+ if isSameExpr (← getSemiring).powFn.appArg! i then return some (.pow (← go a) k) else asTopVar e
159
+ | NatCast.natCast _ i a =>
160
+ if isSameExpr (← getSemiring).natCastFn.appArg! i then
161
+ let some k ← getNatValue? a | toTopVar e
162
+ return some (.num k)
163
+ else
164
+ asTopVar e
165
+ | OfNat.ofNat _ n _ =>
166
+ let some k ← getNatValue? n | asTopVar e
167
+ return some (.num k)
168
+ | _ => toTopVar e
169
+
170
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/RingId.lean ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.Ring.Field
8
+ import Init.Grind.Ring.Envelope
9
+ import Lean.Meta.Tactic.Grind.Simp
10
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Util
11
+
12
+ namespace Lean.Meta.Grind.Arith.CommRing
13
+
14
+ def denoteNumCore (u : Level) (type : Expr) (semiringInst : Expr) (negFn : Expr) (k : Int) : Expr :=
15
+ let n := mkRawNatLit k.natAbs
16
+ let ofNatInst := mkApp3 (mkConst ``Grind.Semiring.ofNat [u]) type semiringInst n
17
+ let n := mkApp3 (mkConst ``OfNat.ofNat [u]) type n ofNatInst
18
+ if k < 0 then
19
+ mkApp negFn n
20
+ else
21
+ n
22
+
23
+ private def internalizeFn (fn : Expr) : GoalM Expr := do
24
+ shareCommon (← canon fn)
25
+
26
+ private def getUnaryFn (type : Expr)(u : Level) (instDeclName : Name) (declName : Name) : GoalM Expr := do
27
+ let instType := mkApp (mkConst instDeclName [u]) type
28
+ let .some inst ← trySynthInstance instType
29
+ | throwError "`grind ring` failed to find instance{indentExpr instType}"
30
+ internalizeFn <| mkApp2 (mkConst declName [u]) type inst
31
+
32
+ private def getBinHomoFn (type : Expr)(u : Level) (instDeclName : Name) (declName : Name) : GoalM Expr := do
33
+ let instType := mkApp3 (mkConst instDeclName [u, u, u]) type type type
34
+ let .some inst ← trySynthInstance instType
35
+ | throwError "`grind ring` failed to find instance{indentExpr instType}"
36
+ internalizeFn <| mkApp4 (mkConst declName [u, u, u]) type type type inst
37
+
38
+ -- Remark: we removed consistency checks such as the one that ensures `HAdd` instance matches `Semiring.toAdd`
39
+ -- That is, we are assuming the type classes were properly setup.
40
+
41
+ private def getAddFn (type : Expr) (u : Level) : GoalM Expr := do
42
+ getBinHomoFn type u ``HAdd ``HAdd.hAdd
43
+
44
+ private def getMulFn (type : Expr) (u : Level) : GoalM Expr := do
45
+ getBinHomoFn type u ``HMul ``HMul.hMul
46
+
47
+ private def getSubFn (type : Expr) (u : Level) : GoalM Expr := do
48
+ getBinHomoFn type u ``HSub ``HSub.hSub
49
+
50
+ private def getDivFn (type : Expr) (u : Level) : GoalM Expr := do
51
+ getBinHomoFn type u ``HDiv ``HDiv.hDiv
52
+
53
+ private def getNegFn (type : Expr) (u : Level) : GoalM Expr := do
54
+ getUnaryFn type u ``Neg ``Neg.neg
55
+
56
+ private def getInvFn (type : Expr) (u : Level) : GoalM Expr := do
57
+ getUnaryFn type u ``Inv ``Inv.inv
58
+
59
+ private def getPowFn (type : Expr) (u : Level) (semiringInst : Expr) : GoalM Expr := do
60
+ let instType := mkApp3 (mkConst ``HPow [u, 0, u]) type Nat.mkType type
61
+ let .some inst ← trySynthInstance instType |
62
+ throwError "failed to find instance for ring power operator{indentExpr instType}"
63
+ let inst' := mkApp2 (mkConst ``Grind.Semiring.toHPow [u]) type semiringInst
64
+ unless (← withDefault <| isDefEq inst inst') do
65
+ throwError "instance for power operator{indentExpr inst}\nis not definitionally equal to the `Grind.Semiring` one{indentExpr inst'}"
66
+ internalizeFn <| mkApp4 (mkConst ``HPow.hPow [u, 0, u]) type Nat.mkType type inst
67
+
68
+ private def getIntCastFn (type : Expr) (u : Level) (ringInst : Expr) : GoalM Expr := do
69
+ let inst' := mkApp2 (mkConst ``Grind.Ring.intCast [u]) type ringInst
70
+ let instType := mkApp (mkConst ``IntCast [u]) type
71
+ -- Note that `Ring.intCast` is not registered as a global instance
72
+ -- (to avoid introducing unwanted coercions)
73
+ -- so merely having a `Ring α` instance
74
+ -- does not guarantee that an `IntCast α` will be available.
75
+ -- When both are present we verify that they are defeq,
76
+ -- and otherwise fall back to the field of the `Ring α` instance that we already have.
77
+ let inst ← match (← trySynthInstance instType).toOption with
78
+ | none => pure inst'
79
+ | some inst =>
80
+ unless (← withDefault <| isDefEq inst inst') do
81
+ throwError "instance for intCast{indentExpr inst}\nis not definitionally equal to the `Grind.Ring` one{indentExpr inst'}"
82
+ pure inst
83
+ internalizeFn <| mkApp2 (mkConst ``IntCast.intCast [u]) type inst
84
+
85
+ private def getNatCastFn (type : Expr) (u : Level) (semiringInst : Expr) : GoalM Expr := do
86
+ let inst' := mkApp2 (mkConst ``Grind.Semiring.natCast [u]) type semiringInst
87
+ let instType := mkApp (mkConst ``NatCast [u]) type
88
+ -- Note that `Semiring.natCast` is not registered as a global instance
89
+ -- (to avoid introducing unwanted coercions)
90
+ -- so merely having a `Semiring α` instance
91
+ -- does not guarantee that an `NatCast α` will be available.
92
+ -- When both are present we verify that they are defeq,
93
+ -- and otherwise fall back to the field of the `Semiring α` instance that we already have.
94
+ let inst ← match (← trySynthInstance instType).toOption with
95
+ | none => pure inst'
96
+ | some inst =>
97
+ unless (← withDefault <| isDefEq inst inst') do
98
+ throwError "instance for natCast{indentExpr inst}\nis not definitionally equal to the `Grind.Semiring` one{indentExpr inst'}"
99
+ pure inst
100
+ internalizeFn <| mkApp2 (mkConst ``NatCast.natCast [u]) type inst
101
+
102
+ /--
103
+ Returns the ring id for the given type if there is a `CommRing` instance for it.
104
+
105
+ This function will also perform sanity-checks
106
+ (e.g., the `Add` instance for `type` must be definitionally equal to the `CommRing.toAdd` one.)
107
+
108
+ It also caches the functions representing `+`, `*`, `-`, `^`, and `intCast`.
109
+ -/
110
+ def getRingId? (type : Expr) : GoalM (Option Nat) := do
111
+ if let some id? := (← get').typeIdOf.find? { expr := type } then
112
+ return id?
113
+ else
114
+ let id? ← go?
115
+ modify' fun s => { s with typeIdOf := s.typeIdOf.insert { expr := type } id? }
116
+ if let some id := id? then
117
+ -- Internalize helper constants
118
+ let ring := (← get').rings[id]!
119
+ internalize ring.one 0
120
+ return id?
121
+ where
122
+ go? : GoalM (Option Nat) := do
123
+ let u ← getDecLevel type
124
+ let semiring := mkApp (mkConst ``Grind.Semiring [u]) type
125
+ let .some semiringInst ← trySynthInstance semiring | return none
126
+ let ring := mkApp (mkConst ``Grind.Ring [u]) type
127
+ let .some ringInst ← trySynthInstance ring | return none
128
+ let commSemiring := mkApp (mkConst ``Grind.CommSemiring [u]) type
129
+ let .some commSemiringInst ← trySynthInstance commSemiring | return none
130
+ let commRing := mkApp (mkConst ``Grind.CommRing [u]) type
131
+ let .some commRingInst ← trySynthInstance commRing | return none
132
+ trace_goal[grind.ring] "new ring: {type}"
133
+ let charInst? ← getIsCharInst? u type semiringInst
134
+ let noZeroDivInst? ← getNoZeroDivInst? u type
135
+ trace_goal[grind.ring] "NoNatZeroDivisors available: {noZeroDivInst?.isSome}"
136
+ let field := mkApp (mkConst ``Grind.Field [u]) type
137
+ let fieldInst? : Option Expr ← LOption.toOption <$> trySynthInstance field
138
+ let addFn ← getAddFn type u
139
+ let mulFn ← getMulFn type u
140
+ let subFn ← getSubFn type u
141
+ let negFn ← getNegFn type u
142
+ let powFn ← getPowFn type u semiringInst
143
+ let intCastFn ← getIntCastFn type u ringInst
144
+ let natCastFn ← getNatCastFn type u semiringInst
145
+ let invFn? ← if fieldInst?.isSome then
146
+ pure (some (← getInvFn type u))
147
+ else
148
+ pure none
149
+ let one ← shareCommon <| (← canon <| denoteNumCore u type semiringInst negFn 1)
150
+ let semiringId? := none
151
+ let id := (← get').rings.size
152
+ let ring : Ring := {
153
+ id, semiringId?, type, u, semiringInst, ringInst, commSemiringInst,
154
+ commRingInst, charInst?, noZeroDivInst?, fieldInst?,
155
+ addFn, mulFn, subFn, negFn, powFn, intCastFn, natCastFn, invFn?, one }
156
+ modify' fun s => { s with rings := s.rings.push ring }
157
+ return some id
158
+
159
+ private def setSemiringId (ringId : Nat) (semiringId : Nat) : GoalM Unit := do
160
+ RingM.run ringId do modifyRing fun s => { s with semiringId? := some semiringId }
161
+
162
+ def getSemiringId? (type : Expr) : GoalM (Option Nat) := do
163
+ if let some id? := (← get').stypeIdOf.find? { expr := type } then
164
+ return id?
165
+ else
166
+ let id? ← go?
167
+ modify' fun s => { s with stypeIdOf := s.stypeIdOf.insert { expr := type } id? }
168
+ return id?
169
+ where
170
+ go? : GoalM (Option Nat) := do
171
+ let u ← getDecLevel type
172
+ let semiring := mkApp (mkConst ``Grind.Semiring [u]) type
173
+ let .some semiringInst ← trySynthInstance semiring | return none
174
+ let commSemiring := mkApp (mkConst ``Grind.CommSemiring [u]) type
175
+ let .some commSemiringInst ← trySynthInstance commSemiring | return none
176
+ let toQFn ← internalizeFn <| mkApp2 (mkConst ``Grind.Ring.OfSemiring.toQ [u]) type semiringInst
177
+ let addFn ← getAddFn type u
178
+ let mulFn ← getMulFn type u
179
+ let powFn ← getPowFn type u semiringInst
180
+ let natCastFn ← getNatCastFn type u semiringInst
181
+ let add := mkApp (mkConst ``Add [u]) type
182
+ let .some addInst ← trySynthInstance add | return none
183
+ let addRightCancel := mkApp2 (mkConst ``Grind.AddRightCancel [u]) type addInst
184
+ let addRightCancelInst? ← LOption.toOption <$> trySynthInstance addRightCancel
185
+ let q ← shareCommon (← canon (mkApp2 (mkConst ``Grind.Ring.OfSemiring.Q [u]) type semiringInst))
186
+ let some ringId ← getRingId? q
187
+ | throwError "`grind` unexpected failure, failure to initialize ring{indentExpr q}"
188
+ let id := (← get').semirings.size
189
+ let semiring : Semiring := {
190
+ id, type, ringId, u, semiringInst, commSemiringInst,
191
+ addFn, mulFn, powFn, natCastFn, toQFn, addRightCancelInst?
192
+ }
193
+ modify' fun s => { s with semirings := s.semirings.push semiring }
194
+ setSemiringId ringId id
195
+ return some id
196
+
197
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/SafePoly.lean ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
8
+
9
+ namespace Lean.Meta.Grind.Arith.CommRing
10
+
11
+ /-!
12
+ The polynomial functions at `Poly.lean` are used for constructing proofs-by-reflection,
13
+ but they do not provide mechanisms for aborting expensive computations.
14
+ -/
15
+
16
+ private def applyChar (a : Int) : RingM Int := do
17
+ if let some c ← nonzeroChar? then
18
+ return a % c
19
+ else
20
+ return a
21
+
22
+ private def addConst (p : Poly) (k : Int) : RingM Poly := do
23
+ if let some c ← nonzeroChar? then return .addConstC p k c else return .addConst p k
24
+
25
+ private def mulConst (k : Int) (p : Poly) : RingM Poly := do
26
+ if let some c ← nonzeroChar? then return .mulConstC k p c else return .mulConst k p
27
+
28
+ private def mulMon (k : Int) (m : Mon) (p : Poly) : RingM Poly := do
29
+ if let some c ← nonzeroChar? then return .mulMonC k m p c else return .mulMon k m p
30
+
31
+ private def combine (p₁ p₂ : Poly) : RingM Poly := withIncRecDepth do
32
+ match p₁, p₂ with
33
+ | .num k₁, .num k₂ => return .num (← applyChar (k₁ + k₂))
34
+ | .num k₁, .add k₂ m₂ p₂ => addConst (.add k₂ m₂ p₂) k₁
35
+ | .add k₁ m₁ p₁, .num k₂ => addConst (.add k₁ m₁ p₁) k₂
36
+ | .add k₁ m₁ p₁, .add k₂ m₂ p₂ =>
37
+ match m₁.grevlex m₂ with
38
+ | .eq =>
39
+ let k ← applyChar (k₁ + k₂)
40
+ bif k == 0 then
41
+ combine p₁ p₂
42
+ else
43
+ return .add k m₁ (← combine p₁ p₂)
44
+ | .gt => return .add k₁ m₁ (← combine p₁ (.add k₂ m₂ p₂))
45
+ | .lt => return .add k₂ m₂ (← combine (.add k₁ m₁ p₁) p₂)
46
+
47
+ private def mul (p₁ : Poly) (p₂ : Poly) : RingM Poly :=
48
+ go p₁ (.num 0)
49
+ where
50
+ go (p₁ : Poly) (acc : Poly) : RingM Poly := withIncRecDepth do
51
+ match p₁ with
52
+ | .num k => combine acc (← mulConst k p₂)
53
+ | .add k m p₁ =>
54
+ checkSystem "grind ring"
55
+ go p₁ (← combine acc (← mulMon k m p₂))
56
+
57
+ private def pow (p : Poly) (k : Nat) : RingM Poly := withIncRecDepth do
58
+ match k with
59
+ | 0 => return .num 1
60
+ | 1 => return p
61
+ | 2 => mul p p
62
+ | k+3 => mul p (← pow p (k+2))
63
+
64
+ private def toPoly (e : RingExpr) : RingM Poly := do
65
+ match e with
66
+ | .num n => return .num (← applyChar n)
67
+ | .var x => return .ofVar x
68
+ | .add a b => combine (← toPoly a) (← toPoly b)
69
+ | .mul a b => mul (← toPoly a) (← toPoly b)
70
+ | .neg a => mulConst (-1) (← toPoly a)
71
+ | .sub a b => combine (← toPoly a) (← mulConst (-1) (← toPoly b))
72
+ | .pow a k =>
73
+ if k == 0 then
74
+ return .num 1
75
+ else match a with
76
+ | .num n => return .num (← applyChar (n^k))
77
+ | .var x => return .ofMon (.mult {x, k} .unit)
78
+ | _ => pow (← toPoly a) k
79
+
80
+ /--
81
+ Converts the given ring expression into a multivariate polynomial.
82
+ If the ring has a nonzero characteristic, it is used during normalization.
83
+ -/
84
+ abbrev _root_.Lean.Grind.CommRing.Expr.toPolyM (e : RingExpr) : RingM Poly := do
85
+ toPoly e
86
+
87
+ abbrev _root_.Lean.Grind.CommRing.Poly.mulConstM (p : Poly) (k : Int) : RingM Poly :=
88
+ mulConst k p
89
+
90
+ abbrev _root_.Lean.Grind.CommRing.Poly.mulMonM (p : Poly) (k : Int) (m : Mon) : RingM Poly :=
91
+ mulMon k m p
92
+
93
+ abbrev _root_.Lean.Grind.CommRing.Poly.mulM (p₁ p₂ : Poly) : RingM Poly := do
94
+ mul p₁ p₂
95
+
96
+ abbrev _root_.Lean.Grind.CommRing.Poly.combineM (p₁ p₂ : Poly) : RingM Poly :=
97
+ combine p₁ p₂
98
+
99
+ def _root_.Lean.Grind.CommRing.Poly.spolM (p₁ p₂ : Poly) : RingM Grind.CommRing.SPolResult := do
100
+ match p₁, p₂ with
101
+ | .add k₁ m₁ p₁, .add k₂ m₂ p₂ =>
102
+ let m := m₁.lcm m₂
103
+ let m₁ := m.div m₁
104
+ let m₂ := m.div m₂
105
+ let g := Nat.gcd k₁.natAbs k₂.natAbs
106
+ let c₁ := k₂/g
107
+ let c₂ := -k₁/g
108
+ let p₁ ← mulMon c₁ m₁ p₁
109
+ let p₂ ← mulMon c₂ m₂ p₂
110
+ let spol ← combine p₁ p₂
111
+ return { spol, m₁, m₂, k₁ := c₁, k₂ := c₂ }
112
+ | _, _ => return {}
113
+
114
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/ToExpr.lean ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.Ring.Poly
8
+ import Init.Grind.Ring.OfSemiring
9
+ import Lean.ToExpr
10
+
11
+ namespace Lean.Meta.Grind.Arith.CommRing
12
+ open Grind.CommRing
13
+ /-!
14
+ `ToExpr` instances for `CommRing.Poly` types.
15
+ -/
16
+
17
+ def ofPower (p : Power) : Expr :=
18
+ mkApp2 (mkConst ``Power.mk) (toExpr p.x) (toExpr p.k)
19
+
20
+ instance : ToExpr Power where
21
+ toExpr := ofPower
22
+ toTypeExpr := mkConst ``Power
23
+
24
+ def ofMon (m : Mon) : Expr :=
25
+ match m with
26
+ | .unit => mkConst ``Mon.unit
27
+ | .mult pw m => mkApp2 (mkConst ``Mon.mult) (toExpr pw) (ofMon m)
28
+
29
+ instance : ToExpr Mon where
30
+ toExpr := ofMon
31
+ toTypeExpr := mkConst ``Mon
32
+
33
+ def ofPoly (p : Poly) : Expr :=
34
+ match p with
35
+ | .num k => mkApp (mkConst ``Poly.num) (toExpr k)
36
+ | .add k m p => mkApp3 (mkConst ``Poly.add) (toExpr k) (toExpr m) (ofPoly p)
37
+
38
+ instance : ToExpr Poly where
39
+ toExpr := ofPoly
40
+ toTypeExpr := mkConst ``Poly
41
+
42
+ open Lean.Grind
43
+
44
+ def ofRingExpr (e : CommRing.Expr) : Expr :=
45
+ match e with
46
+ | .num k => mkApp (mkConst ``CommRing.Expr.num) (toExpr k)
47
+ | .var x => mkApp (mkConst ``CommRing.Expr.var) (toExpr x)
48
+ | .add a b => mkApp2 (mkConst ``CommRing.Expr.add) (ofRingExpr a) (ofRingExpr b)
49
+ | .mul a b => mkApp2 (mkConst ``CommRing.Expr.mul) (ofRingExpr a) (ofRingExpr b)
50
+ | .sub a b => mkApp2 (mkConst ``CommRing.Expr.sub) (ofRingExpr a) (ofRingExpr b)
51
+ | .neg a => mkApp (mkConst ``CommRing.Expr.neg) (ofRingExpr a)
52
+ | .pow a k => mkApp2 (mkConst ``CommRing.Expr.pow) (ofRingExpr a) (toExpr k)
53
+
54
+ instance : ToExpr CommRing.Expr where
55
+ toExpr := ofRingExpr
56
+ toTypeExpr := mkConst ``CommRing.Expr
57
+
58
+ def ofNullCert (nc : NullCert) : Expr :=
59
+ match nc with
60
+ | .empty => mkConst ``CommRing.NullCert.empty
61
+ | .add q lhs rhs nc => mkApp4 (mkConst ``CommRing.NullCert.add) (toExpr q) (toExpr lhs) (toExpr rhs) (ofNullCert nc)
62
+
63
+ instance : ToExpr CommRing.NullCert where
64
+ toExpr := ofNullCert
65
+ toTypeExpr := mkConst ``CommRing.NullCert
66
+
67
+ def ofSemiringExpr (e : Ring.OfSemiring.Expr) : Expr :=
68
+ match e with
69
+ | .num k => mkApp (mkConst ``Ring.OfSemiring.Expr.num) (toExpr k)
70
+ | .var x => mkApp (mkConst ``Ring.OfSemiring.Expr.var) (toExpr x)
71
+ | .add a b => mkApp2 (mkConst ``Ring.OfSemiring.Expr.add) (ofSemiringExpr a) (ofSemiringExpr b)
72
+ | .mul a b => mkApp2 (mkConst ``Ring.OfSemiring.Expr.mul) (ofSemiringExpr a) (ofSemiringExpr b)
73
+ | .pow a k => mkApp2 (mkConst ``Ring.OfSemiring.Expr.pow) (ofSemiringExpr a) (toExpr k)
74
+
75
+ instance : ToExpr Ring.OfSemiring.Expr where
76
+ toExpr := ofSemiringExpr
77
+ toTypeExpr := mkConst ``Ring.OfSemiring.Expr
78
+
79
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Types.lean ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.Ring.OfSemiring
8
+ import Lean.Data.PersistentArray
9
+ import Lean.Data.RBTree
10
+ import Lean.Meta.Tactic.Grind.ExprPtr
11
+ import Lean.Meta.Tactic.Grind.Arith.Util
12
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Poly
13
+
14
+ namespace Lean.Meta.Grind.Arith.CommRing
15
+ export Lean.Grind.CommRing (Var Power Mon Poly)
16
+ abbrev RingExpr := Grind.CommRing.Expr
17
+ abbrev SemiringExpr := Grind.Ring.OfSemiring.Expr
18
+
19
+ mutual
20
+ structure EqCnstr where
21
+ p : Poly
22
+ h : EqCnstrProof
23
+ sugar : Nat
24
+ id : Nat
25
+
26
+ inductive EqCnstrProof where
27
+ | core (a b : Expr) (ra rb : RingExpr)
28
+ | coreS (a b : Expr) (sa sb : SemiringExpr) (ra rb : RingExpr)
29
+ | superpose (k₁ : Int) (m₁ : Mon) (c₁ : EqCnstr) (k₂ : Int) (m₂ : Mon) (c₂ : EqCnstr)
30
+ | simp (k₁ : Int) (c₁ : EqCnstr) (k₂ : Int) (m₂ : Mon) (c₂ : EqCnstr)
31
+ | mul (k : Int) (e : EqCnstr)
32
+ | div (k : Int) (e : EqCnstr)
33
+ | gcd (a b : Int) (c₁ c₂ : EqCnstr)
34
+ | numEq0 (k : Nat) (c₁ c₂ : EqCnstr)
35
+ end
36
+
37
+ instance : Inhabited EqCnstrProof where
38
+ default := .core default default default default
39
+
40
+ instance : Inhabited EqCnstr where
41
+ default := { p := default, h := default, sugar := 0, id := 0 }
42
+
43
+ protected def EqCnstr.compare (c₁ c₂ : EqCnstr) : Ordering :=
44
+ (compare c₁.sugar c₂.sugar) |>.then
45
+ (compare c₁.p.degree c₂.p.degree) |>.then
46
+ (compare c₁.id c₂.id)
47
+
48
+ abbrev Queue : Type := RBTree EqCnstr EqCnstr.compare
49
+
50
+ /--
51
+ A polynomial equipped with a chain of rewrite steps that justifies its equality to the original input.
52
+ From an input polynomial `p`, we use equations (i.e., `EqCnstr`) as rewriting rules.
53
+ For example, consider the following sequence of rewrites for the input polynomial `x^2 + x*y`
54
+ using the equations `x - 1 = 0` (`c₁`) and `y - 2 = 0` (`c₂`).
55
+ ```
56
+ 2*x^2 + x*y | s₁ := .input (2*x^2 + x*y)
57
+ = - 2*x*(x - 1)
58
+ (2*x + x*y) | s₂ := .step (2*x + x*y) 1 s₁ (-2) x c₁
59
+ = - 2*1*(x - 1)
60
+ (x*y + 2) | s₃ := .step (x*y + 2) 1 s₂ (-2) 1 c₁
61
+ = - 1*y*(x - 1)
62
+ (y + 2) | s₄ := .step (y+2) 1 s₃ (-1) y c₁
63
+ = - 1*1*(y - 2)
64
+ 4 | s₅ := .step 4 1 s₄ 1 1 c₂
65
+ ```
66
+ From the chain above, we build the certificate
67
+ ```
68
+ (-2*x - y - 2)*(x-1) + (-1)*(y-2)
69
+ ```
70
+ for
71
+ ```
72
+ 4 = (2*x^2 + x*y)
73
+ ```
74
+ because `x-1 = 0` and `y-2=0`
75
+ -/
76
+ inductive PolyDerivation where
77
+ | input (p : Poly)
78
+ | /--
79
+ ```
80
+ p = k₁*d.getPoly + k₂*m₂*c.p
81
+ ```
82
+ The coefficient `k₁` is used because the leading monomial in `c` may not be monic.
83
+ Thus, if we follow the chain back to the input polynomial, we have that
84
+ `p = C * input_p` for a `C` that is equal to the product of all `k₁`s in the chain.
85
+ We have that `C ≠ 1` only if the ring does not implement `NoNatZeroDivisors`.
86
+ Here is a small example where we simplify `x+y` using the equations
87
+ `2*x - 1 = 0` (`c₁`), `3*y - 1 = 0` (`c₂`), and `6*z + 5 = 0` (`c₃`)
88
+ ```
89
+ x + y + z | s₁ := .input (x + y + z)
90
+ *2
91
+ = - 1*1*(2*x - 1)
92
+ 2*y + 2*z + 1 | s₂ := .step (2*y + 2*z + 1) 2 s₁ (-1) 1 c₁
93
+ *3
94
+ = - 2*1*(3*y - 1)
95
+ 6*z + 5 | s₃ := .step (6*z + 5) 3 s₂ (-2) 1 c₂
96
+ = - 1*1*(6*z + 5)
97
+ 0 | s₄ := .step (0) 1 s₃ (-1) 1 c₃
98
+ ```
99
+ For this chain, we build the certificate
100
+ ```
101
+ (-3)*(2*x - 1) + (-2)*(3*y - 1) + (-1)*(6*z + 5)
102
+ ```
103
+ for
104
+ ```
105
+ 0 = 6*(x + y + z)
106
+ ```
107
+ Recall that if the ring implement `NoNatZeroDivisors`, then the following property holds:
108
+ ```
109
+ ∀ (k : Int) (a : α), k ≠ 0 → (intCast k) * a = 0 → a = 0
110
+ ```
111
+ grind can deduce that `x+y+z = 0`
112
+ -/
113
+ step (p : Poly) (k₁ : Int) (d : PolyDerivation) (k₂ : Int) (m₂ : Mon) (c : EqCnstr)
114
+ | /--
115
+ Given `c.p == .num k`
116
+ ```
117
+ p = d.getPoly.normEq0 k
118
+ ```
119
+ -/
120
+ normEq0 (p : Poly) (d : PolyDerivation) (c : EqCnstr)
121
+
122
+ def PolyDerivation.p : PolyDerivation → Poly
123
+ | .input p => p
124
+ | .step p .. => p
125
+ | .normEq0 p .. => p
126
+
127
+ /-- A disequality `lhs ≠ rhs` asserted by the core. -/
128
+ structure DiseqCnstr where
129
+ lhs : Expr
130
+ rhs : Expr
131
+ /-- Reified `lhs` -/
132
+ rlhs : RingExpr
133
+ /-- Reified `rhs` -/
134
+ rrhs : RingExpr
135
+ /-- `lhs - rhs` simplification chain. If it becomes `0` we have an inconsistency. -/
136
+ d : PolyDerivation
137
+ /--
138
+ If `lhs` and `rhs` are semiring expressions that have been adapted as ring ones.
139
+ The respective semiring reified expressions are stored here.
140
+ -/
141
+ ofSemiring? : Option (SemiringExpr × SemiringExpr)
142
+
143
+ /-- State for each `CommRing` processed by this module. -/
144
+ structure Ring where
145
+ id : Nat
146
+ /--
147
+ If this is a `OfSemiring.Q α` ring, this field contain the
148
+ `semiringId` for `α`.
149
+ -/
150
+ semiringId? : Option Nat
151
+ type : Expr
152
+ /-- Cached `getDecLevel type` -/
153
+ u : Level
154
+ /-- `Semiring` instance for `type` -/
155
+ semiringInst : Expr
156
+ /-- `Ring` instance for `type` -/
157
+ ringInst : Expr
158
+ /-- `CommSemiring` instance for `type` -/
159
+ commSemiringInst : Expr
160
+ /-- `CommRing` instance for `type` -/
161
+ commRingInst : Expr
162
+ /-- `IsCharP` instance for `type` if available. -/
163
+ charInst? : Option (Expr × Nat)
164
+ /-- `NoNatZeroDivisors` instance for `type` if available. -/
165
+ noZeroDivInst? : Option Expr
166
+ /-- `Field` instance for `type` if available. -/
167
+ fieldInst? : Option Expr
168
+ addFn : Expr
169
+ mulFn : Expr
170
+ subFn : Expr
171
+ negFn : Expr
172
+ powFn : Expr
173
+ intCastFn : Expr
174
+ natCastFn : Expr
175
+ /-- Inverse if `fieldInst?` is `some inst` -/
176
+ invFn? : Option Expr
177
+ one : Expr
178
+ /--
179
+ Mapping from variables to their denotations.
180
+ Remark each variable can be in only one ring.
181
+ -/
182
+ vars : PArray Expr := {}
183
+ /-- Mapping from `Expr` to a variable representing it. -/
184
+ varMap : PHashMap ExprPtr Var := {}
185
+ /-- Mapping from Lean expressions to their representations as `RingExpr` -/
186
+ denote : PHashMap ExprPtr RingExpr := {}
187
+ /-- Next unique id for `EqCnstr`s. -/
188
+ nextId : Nat := 0
189
+ /-- Number of "steps": simplification and superposition. -/
190
+ steps : Nat := 0
191
+ /-- Equations to process. -/
192
+ queue : Queue := {}
193
+ /--
194
+ The basis is currently just a list. If this is a performance bottleneck, we should use
195
+ a better data-structure. For examples, we could use a simple indexing for the linear case
196
+ where we map variable in the leading monomial to `EqCnstr`.
197
+ -/
198
+ basis : List EqCnstr := {}
199
+ /-- Disequalities. -/
200
+ -- TODO: add indexing
201
+ diseqs : PArray DiseqCnstr := {}
202
+ /--
203
+ If `recheck` is `true`, then new equalities have been added to the basis since we checked
204
+ disequalities and implied equalities.
205
+ -/
206
+ recheck : Bool := false
207
+ /-- Inverse theorems that have been already asserted. -/
208
+ invSet : PHashSet Expr := {}
209
+ /--
210
+ An equality of the form `c = 0`. It is used to simplify polynomial coefficients.
211
+ -/
212
+ numEq0? : Option EqCnstr := none
213
+ /-- Flag indicating whether `numEq0?` has been updated. -/
214
+ numEq0Updated : Bool := false
215
+ deriving Inhabited
216
+
217
+ /--
218
+ State for each `CommSemiring` processed by this module.
219
+ Recall that `CommSemiring` are processed using the envelop `OfCommSemiring.Q`
220
+ -/
221
+ structure Semiring where
222
+ id : Nat
223
+ /-- Id for `OfCommSemiring.Q` -/
224
+ ringId : Nat
225
+ type : Expr
226
+ /-- Cached `getDecLevel type` -/
227
+ u : Level
228
+ /-- `Semiring` instance for `type` -/
229
+ semiringInst : Expr
230
+ /-- `CommSemiring` instance for `type` -/
231
+ commSemiringInst : Expr
232
+ /-- `AddRightCancel` instance for `type` if available. -/
233
+ addRightCancelInst? : Option Expr
234
+ toQFn : Expr
235
+ addFn : Expr
236
+ mulFn : Expr
237
+ powFn : Expr
238
+ natCastFn : Expr
239
+ /-- Mapping from Lean expressions to their representations as `SemiringExpr` -/
240
+ denote : PHashMap ExprPtr SemiringExpr := {}
241
+ /--
242
+ Mapping from variables to their denotations.
243
+ Remark each variable can be in only one ring.
244
+ -/
245
+ vars : PArray Expr := {}
246
+ /-- Mapping from `Expr` to a variable representing it. -/
247
+ varMap : PHashMap ExprPtr Var := {}
248
+ deriving Inhabited
249
+
250
+ /-- State for all `CommRing` types detected by `grind`. -/
251
+ structure State where
252
+ /--
253
+ Commutative rings.
254
+ We expect to find a small number of rings in a given goal. Thus, using `Array` is fine here.
255
+ -/
256
+ rings : Array Ring := {}
257
+ /--
258
+ Mapping from types to its "ring id". We cache failures using `none`.
259
+ `typeIdOf[type]` is `some id`, then `id < rings.size`. -/
260
+ typeIdOf : PHashMap ExprPtr (Option Nat) := {}
261
+ /- Mapping from expressions/terms to their ring ids. -/
262
+ exprToRingId : PHashMap ExprPtr Nat := {}
263
+ /-- Commutative semirings. We support them using the envelope `OfCommRing.Q` -/
264
+ semirings : Array Semiring := {}
265
+ /--
266
+ Mapping from types to its "semiring id". We cache failures using `none`.
267
+ `stypeIdOf[type]` is `some id`, then `id < semirings.size`.
268
+ If a type is in this map, it is not in `typeIdOf`.
269
+ -/
270
+ stypeIdOf : PHashMap ExprPtr (Option Nat) := {}
271
+ /-
272
+ Mapping from expressions/terms to their semiring ids.
273
+ If an expression is in this map, it is not in `exprToRingId`.
274
+ -/
275
+ exprToSemiringId : PHashMap ExprPtr Nat := {}
276
+ steps := 0
277
+ deriving Inhabited
278
+
279
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Util.lean ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Types
8
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Poly
9
+
10
+ namespace Lean.Meta.Grind.Arith.CommRing
11
+
12
+ def get' : GoalM State := do
13
+ return (← get).arith.ring
14
+
15
+ @[inline] def modify' (f : State → State) : GoalM Unit := do
16
+ modify fun s => { s with arith.ring := f s.arith.ring }
17
+
18
+ def checkMaxSteps : GoalM Bool := do
19
+ return (← get').steps >= (← getConfig).ringSteps
20
+
21
+ def incSteps : GoalM Unit := do
22
+ modify' fun s => { s with steps := s.steps + 1 }
23
+
24
+ structure RingM.Context where
25
+ ringId : Nat
26
+ /--
27
+ If `checkCoeffDvd` is `true`, then when using a polynomial `k*m - p`
28
+ to simplify `.. + k'*m*m_2 + ...`, the substitution is performed IF
29
+ - `k` divides `k'`, OR
30
+ - Ring implements `NoNatZeroDivisors`.
31
+
32
+ We need this check when simplifying disequalities. In this case, if we perform
33
+ the simplification anyway, we may end up with a proof that `k * q = 0`, but
34
+ we cannot deduce `q = 0` since the ring does not implement `NoNatZeroDivisors`
35
+ See comment at `PolyDerivation`.
36
+ -/
37
+ checkCoeffDvd : Bool := false
38
+
39
+ class MonadGetRing (m : Type → Type) where
40
+ getRing : m Ring
41
+
42
+ export MonadGetRing (getRing)
43
+
44
+ @[always_inline]
45
+ instance (m n) [MonadLift m n] [MonadGetRing m] : MonadGetRing n where
46
+ getRing := liftM (getRing : m Ring)
47
+
48
+ /-- We don't want to keep carrying the `RingId` around. -/
49
+ abbrev RingM := ReaderT RingM.Context GoalM
50
+
51
+ abbrev RingM.run (ringId : Nat) (x : RingM α) : GoalM α :=
52
+ x { ringId }
53
+
54
+ abbrev getRingId : RingM Nat :=
55
+ return (← read).ringId
56
+
57
+ protected def RingM.getRing : RingM Ring := do
58
+ let s ← get'
59
+ let ringId ← getRingId
60
+ if h : ringId < s.rings.size then
61
+ return s.rings[ringId]
62
+ else
63
+ throwError "`grind` internal error, invalid ringId"
64
+
65
+ instance : MonadGetRing RingM where
66
+ getRing := RingM.getRing
67
+
68
+ @[inline] def modifyRing (f : Ring → Ring) : RingM Unit := do
69
+ let ringId ← getRingId
70
+ modify' fun s => { s with rings := s.rings.modify ringId f }
71
+
72
+ structure SemiringM.Context where
73
+ semiringId : Nat
74
+
75
+ abbrev SemiringM := ReaderT SemiringM.Context GoalM
76
+
77
+ abbrev SemiringM.run (semiringId : Nat) (x : SemiringM α) : GoalM α :=
78
+ x { semiringId }
79
+
80
+ abbrev getSemiringId : SemiringM Nat :=
81
+ return (← read).semiringId
82
+
83
+ def getSemiring : SemiringM Semiring := do
84
+ let s ← get'
85
+ let semiringId ← getSemiringId
86
+ if h : semiringId < s.semirings.size then
87
+ return s.semirings[semiringId]
88
+ else
89
+ throwError "`grind` internal error, invalid semiringId"
90
+
91
+ protected def SemiringM.getRing : SemiringM Ring := do
92
+ let s ← get'
93
+ let ringId := (← getSemiring).ringId
94
+ if h : ringId < s.rings.size then
95
+ return s.rings[ringId]
96
+ else
97
+ throwError "`grind` internal error, invalid ringId"
98
+
99
+ instance : MonadGetRing SemiringM where
100
+ getRing := SemiringM.getRing
101
+
102
+ @[inline] def modifySemiring (f : Semiring → Semiring) : SemiringM Unit := do
103
+ let semiringId ← getSemiringId
104
+ modify' fun s => { s with semirings := s.semirings.modify semiringId f }
105
+
106
+ abbrev withCheckCoeffDvd (x : RingM α) : RingM α :=
107
+ withReader (fun ctx => { ctx with checkCoeffDvd := true }) x
108
+
109
+ def checkCoeffDvd : RingM Bool :=
110
+ return (← read).checkCoeffDvd
111
+
112
+ def getTermRingId? (e : Expr) : GoalM (Option Nat) := do
113
+ return (← get').exprToRingId.find? { expr := e }
114
+
115
+ def setTermRingId (e : Expr) : RingM Unit := do
116
+ let ringId ← getRingId
117
+ if let some ringId' ← getTermRingId? e then
118
+ unless ringId' == ringId do
119
+ reportIssue! "expression in two different rings{indentExpr e}"
120
+ return ()
121
+ modify' fun s => { s with exprToRingId := s.exprToRingId.insert { expr := e } ringId }
122
+
123
+ def getTermSemiringId? (e : Expr) : GoalM (Option Nat) := do
124
+ return (← get').exprToSemiringId.find? { expr := e }
125
+
126
+ def setTermSemiringId (e : Expr) : SemiringM Unit := do
127
+ let semiringId ← getSemiringId
128
+ if let some semiringId' ← getTermSemiringId? e then
129
+ unless semiringId' == semiringId do
130
+ reportIssue! "expression in two different semirings{indentExpr e}"
131
+ return ()
132
+ modify' fun s => { s with exprToSemiringId := s.exprToSemiringId.insert { expr := e } semiringId }
133
+
134
+ /-- Returns `some c` if the current ring has a nonzero characteristic `c`. -/
135
+ def nonzeroChar? [Monad m] [MonadGetRing m] : m (Option Nat) := do
136
+ if let some (_, c) := (← getRing).charInst? then
137
+ if c != 0 then
138
+ return some c
139
+ return none
140
+
141
+ /-- Returns `some (charInst, c)` if the current ring has a nonzero characteristic `c`. -/
142
+ def nonzeroCharInst? [Monad m] [MonadGetRing m] : m (Option (Expr × Nat)) := do
143
+ if let some (inst, c) := (← getRing).charInst? then
144
+ if c != 0 then
145
+ return some (inst, c)
146
+ return none
147
+
148
+ def noZeroDivisorsInst? : RingM (Option Expr) := do
149
+ return (← getRing).noZeroDivInst?
150
+
151
+ /--
152
+ Returns `true` if the current ring satisfies the property
153
+ ```
154
+ ∀ (k : Nat) (a : α), k ≠ 0 → OfNat.ofNat (α := α) k * a = 0 → a = 0
155
+ ```
156
+ -/
157
+ def noZeroDivisors : RingM Bool := do
158
+ return (← getRing).noZeroDivInst?.isSome
159
+
160
+ /-- Returns `true` if the current ring has a `IsCharP` instance. -/
161
+ def hasChar : RingM Bool := do
162
+ return (← getRing).charInst?.isSome
163
+
164
+ /--
165
+ Returns the pair `(charInst, c)`. If the ring does not have a `IsCharP` instance, then throws internal error.
166
+ -/
167
+ def getCharInst : RingM (Expr × Nat) := do
168
+ let some c := (← getRing).charInst?
169
+ | throwError "`grind` internal error, ring does not have a characteristic"
170
+ return c
171
+
172
+ def isField : RingM Bool :=
173
+ return (← getRing).fieldInst?.isSome
174
+
175
+ def isQueueEmpty : RingM Bool :=
176
+ return (← getRing).queue.isEmpty
177
+
178
+ def getNext? : RingM (Option EqCnstr) := do
179
+ let some c := (← getRing).queue.min | return none
180
+ modifyRing fun s => { s with queue := s.queue.erase c }
181
+ incSteps
182
+ return some c
183
+
184
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Var.lean ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Util
8
+
9
+ namespace Lean.Meta.Grind.Arith.CommRing
10
+
11
+ def mkVar (e : Expr) : RingM Var := do
12
+ let s ← getRing
13
+ if let some var := s.varMap.find? { expr := e } then
14
+ return var
15
+ let var : Var := s.vars.size
16
+ modifyRing fun s => { s with
17
+ vars := s.vars.push e
18
+ varMap := s.varMap.insert { expr := e } var
19
+ }
20
+ setTermRingId e
21
+ markAsCommRingTerm e
22
+ return var
23
+
24
+ /-- Similar to `mkVar` but for `Semiring`s -/
25
+ def mkSVar (e : Expr) : SemiringM Var := do
26
+ let s ← getSemiring
27
+ if let some var := s.varMap.find? { expr := e } then
28
+ return var
29
+ let var : Var := s.vars.size
30
+ modifySemiring fun s => { s with
31
+ vars := s.vars.push e
32
+ varMap := s.varMap.insert { expr := e } var
33
+ }
34
+ setTermSemiringId e
35
+ markAsCommRingTerm e
36
+ return var
37
+
38
+ end Lean.Meta.Grind.Arith.CommRing
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat.lean ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Util.Trace
8
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.DvdCnstr
9
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.LeCnstr
10
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Search
11
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Inv
12
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Proof
13
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Types
14
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
15
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
16
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
17
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.SearchM
18
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
19
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.MBTC
20
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Nat
21
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.CommRing
22
+
23
+ namespace Lean
24
+
25
+ builtin_initialize registerTraceClass `grind.cutsat
26
+ builtin_initialize registerTraceClass `grind.cutsat.model
27
+ builtin_initialize registerTraceClass `grind.cutsat.assert
28
+ builtin_initialize registerTraceClass `grind.cutsat.assert.trivial
29
+ builtin_initialize registerTraceClass `grind.cutsat.assert.unsat
30
+ builtin_initialize registerTraceClass `grind.cutsat.assert.store
31
+ builtin_initialize registerTraceClass `grind.cutsat.assert.nonlinear
32
+
33
+ builtin_initialize registerTraceClass `grind.debug.cutsat.subst
34
+ builtin_initialize registerTraceClass `grind.debug.cutsat.search
35
+ builtin_initialize registerTraceClass `grind.debug.cutsat.search.split (inherited := true)
36
+ builtin_initialize registerTraceClass `grind.debug.cutsat.search.assign (inherited := true)
37
+ builtin_initialize registerTraceClass `grind.debug.cutsat.search.conflict (inherited := true)
38
+ builtin_initialize registerTraceClass `grind.debug.cutsat.search.backtrack (inherited := true)
39
+ builtin_initialize registerTraceClass `grind.debug.cutsat.internalize
40
+ builtin_initialize registerTraceClass `grind.debug.cutsat.toInt
41
+ builtin_initialize registerTraceClass `grind.debug.cutsat.search.cnstrs
42
+ builtin_initialize registerTraceClass `grind.debug.cutsat.search.reorder
43
+
44
+ end Lean
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/CommRing.lean ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.ProveEq
8
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
9
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Reify
10
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
11
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
12
+
13
+ /-!
14
+ CommRing interface for cutsat. We use it to normalize nonlinear polynomials.
15
+ -/
16
+
17
+ namespace Lean.Meta.Grind.Arith.Cutsat
18
+
19
+ /-- Returns `true` if `p` contains a nonlinear monomial. -/
20
+ def _root_.Int.Linear.Poly.isNonlinear (p : Poly) : GoalM Bool := do
21
+ let .add _ x p := p | return false
22
+ if (← getVar x).isAppOf ``HMul.hMul then return true
23
+ p.isNonlinear
24
+
25
+ def _root_.Int.Linear.Poly.getGeneration (p : Poly) : GoalM Nat := do
26
+ go p 0
27
+ where
28
+ go : Poly → Nat → GoalM Nat
29
+ | .num _, gen => return gen
30
+ | .add _ x p, gen => do go p (max (← Grind.getGeneration (← getVar x)) gen)
31
+
32
+ def getIntRingId? : GoalM (Option Nat) := do
33
+ CommRing.getRingId? (← getIntExpr)
34
+
35
+ /-- Normalize the polynomial using `CommRing`-/
36
+ def _root_.Int.Linear.Poly.normCommRing? (p : Poly) : GoalM (Option (CommRing.RingExpr × CommRing.Poly × Poly)) := do
37
+ unless (← p.isNonlinear) do return none
38
+ let some ringId ← getIntRingId? | return none
39
+ CommRing.RingM.run ringId do
40
+ let e ← p.denoteExpr'
41
+ -- TODO: we can avoid the following statement if we construct the `Int` denotation using
42
+ -- Internalized operators instead of `mkIntMul` and `mkIntAdd`
43
+ let e ← shareCommon (← canon e)
44
+ let gen ← p.getGeneration
45
+ let some re ← CommRing.reify? e (gen := gen) | return none
46
+ let p' := re.toPoly
47
+ let e' ← p'.denoteExpr
48
+ let e' ← preprocessLight e'
49
+ -- Remark: we are reusing the `IntModule` virtual parent.
50
+ -- TODO: Investigate whether we should have a custom virtual parent for cutsat
51
+ internalize e' gen (some getIntModuleVirtualParent)
52
+ let p'' ← toPoly e'
53
+ if p == p'' then return none
54
+ modify' fun s => { s with usedCommRing := true }
55
+ trace[grind.cutsat.assert.nonlinear] "{← p.pp} ===> {← p''.pp}"
56
+ return some (re, p', p'')
57
+
58
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/DvdCnstr.lean ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Simp.Arith.Int
8
+ import Lean.Meta.Tactic.Grind.PropagatorAttr
9
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
10
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
11
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Proof
12
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Norm
13
+
14
+ namespace Lean.Meta.Grind.Arith.Cutsat
15
+
16
+ def DvdCnstr.norm (c : DvdCnstr) : DvdCnstr :=
17
+ let c := if c.p.isSorted then
18
+ c
19
+ else
20
+ { d := c.d, p := c.p.norm, h :=.norm c }
21
+ let g := c.p.gcdCoeffs c.d
22
+ let g := if c.d < 0 then -g else g
23
+ if c.p.getConst % g == 0 && g != 1 then
24
+ { d := c.d/g, p := c.p.div g, h := .divCoeffs c }
25
+ else
26
+ c
27
+
28
+ /--
29
+ Given an equation `c₁` containing the monomial `a*x`, and a divisibility constraint `c₂`
30
+ containing the monomial `b*x`, eliminate `x` by applying substitution.
31
+ -/
32
+ def DvdCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : DvdCnstr) : GoalM DvdCnstr := do
33
+ let p := c₁.p
34
+ let q := c₂.p
35
+ let d := Int.ofNat (a * c₂.d).natAbs
36
+ let p := (q.mul a |>.combine (p.mul (-b)))
37
+ trace[grind.debug.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
38
+ return { d, p, h := .subst x c₁ c₂ }
39
+
40
+ partial def DvdCnstr.applySubsts (c : DvdCnstr) : GoalM DvdCnstr := withIncRecDepth do
41
+ let some (b, x, c₁) ← c.p.findVarToSubst | return c
42
+ let a := c₁.p.coeff x
43
+ let c ← c.applyEq a x c₁ b
44
+ applySubsts c
45
+
46
+ /-- Asserts divisibility constraint. -/
47
+ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
48
+ if (← inconsistent) then return ()
49
+ trace[grind.cutsat.assert] "{← c.pp}"
50
+ let c ← c.norm.applySubsts
51
+ if c.isUnsat then
52
+ trace[grind.cutsat.assert.unsat] "{← c.pp}"
53
+ setInconsistent (.dvd c)
54
+ return ()
55
+ if c.isTrivial then
56
+ trace[grind.cutsat.assert.trivial] "{← c.pp}"
57
+ return ()
58
+ let d₁ := c.d
59
+ let .add a₁ x p₁ := c.p | c.throwUnexpected
60
+ if (← c.satisfied) == .false then
61
+ resetAssignmentFrom x
62
+ if let some c' := (← get').dvds[x]! then
63
+ let d₂ := c'.d
64
+ let .add a₂ _ p₂ := c'.p | c'.throwUnexpected
65
+ let (d, α, β) := gcdExt (a₁*d₂) (a₂*d₁)
66
+ /-
67
+ We have that
68
+ `d = α*a₁*d₂ + β*a₂*d₁`
69
+ `d = gcd (a₁*d₂) (a₂*d₁)`
70
+ and two implied divisibility constraints:
71
+ - `d₁*d₂ ∣ d*x + α*d₂*p₁ + β*d₁*p₂`
72
+ - `d ∣ a₂*p₁ - a₁*p₂`
73
+ -/
74
+ let α_d₂_p₁ := p₁.mul (α*d₂)
75
+ let β_d₁_p₂ := p₂.mul (β*d₁)
76
+ let combine := { d := d₁*d₂, p := .add d x (α_d₂_p₁.combine β_d₁_p₂), h := .solveCombine c c' : DvdCnstr }
77
+ modify' fun s => { s with dvds := s.dvds.set x none}
78
+ combine.assert
79
+ let a₂_p₁ := p₁.mul a₂
80
+ let a₁_p₂ := p₂.mul (-a₁)
81
+ let elim := { d, p := a₂_p₁.combine a₁_p₂, h := .solveElim c c' : DvdCnstr }
82
+ elim.assert
83
+ else
84
+ trace[grind.cutsat.assert.store] "{← c.pp}"
85
+ c.p.updateOccs
86
+ modify' fun s => { s with dvds := s.dvds.set x (some c) }
87
+
88
+ /-- Asserts a constraint coming from the core. -/
89
+ private def DvdCnstr.assertCore (c : DvdCnstr) : GoalM Unit := do
90
+ if let some (re, rp, p) ← c.p.normCommRing? then
91
+ let c := { c with p, h := .commRingNorm c re rp : DvdCnstr}
92
+ c.assert
93
+ else
94
+ c.assert
95
+
96
+ def propagateIntDvd (e : Expr) : GoalM Unit := do
97
+ let_expr Dvd.dvd _ inst a b ← e | return ()
98
+ unless (← isInstDvdInt inst) do return ()
99
+ let some d ← getIntValue? a
100
+ | reportIssue! "non-linear divisibility constraint found{indentExpr e}"
101
+ return ()
102
+ if (← isEqTrue e) then
103
+ let p ← toPoly b
104
+ let c := { d, p, h := .core e : DvdCnstr }
105
+ c.assertCore
106
+ else if (← isEqFalse e) then
107
+ pushNewFact <| mkApp4 (mkConst ``Int.Linear.of_not_dvd) a b reflBoolTrue (mkOfEqFalseCore e (← mkEqFalseProof e))
108
+
109
+ def propagateNatDvd (e : Expr) : GoalM Unit := do
110
+ let some (d, b) ← Int.OfNat.toIntDvd? e | return ()
111
+ let gen ← getGeneration e
112
+ let ctx ← getNatVars
113
+ let b' ← toLinearExpr (← b.denoteAsIntExpr ctx) gen
114
+ let p := b'.norm
115
+ if (← isEqTrue e) then
116
+ let c := { d, p, h := .coreNat e d b b' : DvdCnstr }
117
+ c.assertCore
118
+ else if (← isEqFalse e) then
119
+ let_expr Dvd.dvd _ _ a b ← e | return ()
120
+ pushNewFact <| mkApp3 (mkConst ``Nat.emod_pos_of_not_dvd) a b (mkOfEqFalseCore e (← mkEqFalseProof e))
121
+
122
+ builtin_grind_propagator propagateDvd ↓Dvd.dvd := fun e => do
123
+ unless (← getConfig).cutsat do return ()
124
+ let_expr Dvd.dvd α _ _ _ ← e | return ()
125
+ if α.isConstOf ``Nat then
126
+ propagateNatDvd e
127
+ else
128
+ propagateIntDvd e
129
+
130
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/EqCnstr.lean ADDED
@@ -0,0 +1,542 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Simp
8
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
9
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.DvdCnstr
10
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.LeCnstr
11
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.ToInt
12
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.CommRing
13
+
14
+ namespace Lean.Meta.Grind.Arith.Cutsat
15
+
16
+ private def _root_.Int.Linear.Poly.substVar (p : Poly) : GoalM (Option (Var × EqCnstr × Poly)) := do
17
+ let some (a, x, c) ← p.findVarToSubst | return none
18
+ let b := c.p.coeff x
19
+ let p' := p.mul (-b) |>.combine (c.p.mul a)
20
+ trace[grind.debug.cutsat.subst] "{← p.pp}, {a}, {← getVar x}, {← c.pp}, {b}, {← p'.pp}"
21
+ return some (x, c, p')
22
+
23
+ def EqCnstr.norm (c : EqCnstr) : EqCnstr :=
24
+ if c.p.isSorted then
25
+ c
26
+ else
27
+ { p := c.p.norm, h := .norm c }
28
+
29
+ def DiseqCnstr.norm (c : DiseqCnstr) : DiseqCnstr :=
30
+ if c.p.isSorted then
31
+ c
32
+ else
33
+ { p := c.p.norm, h := .norm c }
34
+
35
+ /--
36
+ Given an equation `c₁` containing the monomial `a*x`, and a disequality constraint `c₂`
37
+ containing the monomial `b*x`, eliminate `x` by applying substitution.
38
+ -/
39
+ def DiseqCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : DiseqCnstr) : GoalM DiseqCnstr := do
40
+ let p := c₁.p
41
+ let q := c₂.p
42
+ let p := p.mul b |>.combine (q.mul (-a))
43
+ trace[grind.debug.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
44
+ return { p, h := .subst x c₁ c₂ }
45
+
46
+ partial def DiseqCnstr.applySubsts (c : DiseqCnstr) : GoalM DiseqCnstr := withIncRecDepth do
47
+ let some (x, c₁, p) ← c.p.substVar | return c
48
+ trace[grind.debug.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
49
+ applySubsts { p, h := .subst x c₁ c }
50
+
51
+ /--
52
+ Given a disequality `c`, tries to find an inequality to be refined using
53
+ `p ≤ 0 → p ≠ 0 → p + 1 ≤ 0`
54
+ -/
55
+ private def DiseqCnstr.findLe (c : DiseqCnstr) : GoalM Bool := do
56
+ let .add _ x _ := c.p | c.throwUnexpected
57
+ let s ← get'
58
+ let go (atLower : Bool) : GoalM Bool := do
59
+ let cs' := if atLower then s.lowers[x]! else s.uppers[x]!
60
+ for c' in cs' do
61
+ if c.p == c'.p || c.p.isNegEq c'.p then
62
+ c'.erase
63
+ { p := c'.p.addConst 1, h := .ofLeDiseq c' c : LeCnstr }.assert
64
+ return true
65
+ return false
66
+ go true <||> go false
67
+
68
+ def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
69
+ if (← inconsistent) then return ()
70
+ trace[grind.cutsat.assert] "{← c.pp}"
71
+ let c ← c.norm.applySubsts
72
+ if c.p.isUnsatDiseq then
73
+ trace[grind.cutsat.assert.unsat] "{← c.pp}"
74
+ setInconsistent (.diseq c)
75
+ return ()
76
+ if c.isTrivial then
77
+ trace[grind.cutsat.assert.trivial] "{← c.pp}"
78
+ return ()
79
+ let k := c.p.gcdCoeffs c.p.getConst
80
+ let c := if k == 1 then
81
+ c
82
+ else
83
+ { p := c.p.div k, h := .divCoeffs c }
84
+ if (← c.findLe) then
85
+ return ()
86
+ let .add _ x _ := c.p | c.throwUnexpected
87
+ c.p.updateOccs
88
+ trace[grind.cutsat.assert.store] "{← c.pp}"
89
+ modify' fun s => { s with diseqs := s.diseqs.modify x (·.push c) }
90
+ if (← c.satisfied) == .false then
91
+ resetAssignmentFrom x
92
+
93
+ /--
94
+ Selects the variable in the given linear polynomial whose coefficient has the smallest absolute value.
95
+ -/
96
+ def _root_.Int.Linear.Poly.pickVarToElim? (p : Poly) : Option (Int × Var) :=
97
+ match p with
98
+ | .num _ => none
99
+ | .add k x p => go k x p
100
+ where
101
+ go (k : Int) (x : Var) (p : Poly) : Int × Var :=
102
+ if k == 1 || k == -1 then
103
+ (k, x)
104
+ else match p with
105
+ | .num _ => (k, x)
106
+ | .add k' x' p =>
107
+ if k'.natAbs < k.natAbs then
108
+ go k' x' p
109
+ else
110
+ go k x p
111
+
112
+ partial def EqCnstr.applySubsts (c : EqCnstr) : GoalM EqCnstr := withIncRecDepth do
113
+ let some (x, c₁, p) ← c.p.substVar | return c
114
+ trace[grind.debug.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
115
+ applySubsts { p, h := .subst x c₁ c : EqCnstr }
116
+
117
+ private def updateDvdCnstr (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
118
+ let some c' := (← get').dvds[y]! | return ()
119
+ let b := c'.p.coeff x
120
+ if b == 0 then return ()
121
+ modify' fun s => { s with dvds := s.dvds.set y none }
122
+ let c' ← c'.applyEq a x c b
123
+ c'.assert
124
+
125
+ private def splitLeCnstrs (x : Var) (cs : PArray LeCnstr) : PArray LeCnstr × Array (Int × LeCnstr) :=
126
+ split cs fun c => c.p.coeff x
127
+
128
+ /--
129
+ Given an equation `c₁` containing `a*x`, eliminate `x` from the inequalities in `todo`.
130
+ `todo` contains pairs of the form `(b, c₂)` where `b` is the coefficient of `x` in `c₂`.
131
+ -/
132
+ private def updateLeCnstrs (a : Int) (x : Var) (c₁ : EqCnstr) (todo : Array (Int × LeCnstr)) : GoalM Unit := do
133
+ for (b, c₂) in todo do
134
+ let c₂ ← c₂.applyEq a x c₁ b
135
+ c₂.assert
136
+ if (← inconsistent) then return ()
137
+
138
+ /--
139
+ Given an equation `c₁` containing `a*x`, eliminate `x` from lower bound inequalities of `y`.
140
+ -/
141
+ private def updateLowers (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
142
+ if (← inconsistent) then return ()
143
+ let (lowers', todo) := splitLeCnstrs x (← get').lowers[y]!
144
+ modify' fun s => { s with lowers := s.lowers.set y lowers' }
145
+ updateLeCnstrs a x c todo
146
+
147
+ /--
148
+ Given an equation `c₁` containing `a*x`, eliminate `x` from upper bound inequalities of `y`.
149
+ -/
150
+ private def updateUppers (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
151
+ if (← inconsistent) then return ()
152
+ let (uppers', todo) := splitLeCnstrs x (← get').uppers[y]!
153
+ modify' fun s => { s with uppers := s.uppers.set y uppers' }
154
+ updateLeCnstrs a x c todo
155
+
156
+ private def splitDiseqs (x : Var) (cs : PArray DiseqCnstr) : PArray DiseqCnstr × Array (Int × DiseqCnstr) :=
157
+ split cs fun c => c.p.coeff x
158
+
159
+ private def updateDiseqs (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
160
+ if (← inconsistent) then return ()
161
+ let (diseqs', todo) := splitDiseqs x (← get').diseqs[y]!
162
+ modify' fun s => { s with diseqs := s.diseqs.set y diseqs' }
163
+ for (b, c₂) in todo do
164
+ let c₂ ← c₂.applyEq a x c b
165
+ c₂.assert
166
+ if (← inconsistent) then return ()
167
+
168
+ private def updateOccsAt (k : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
169
+ updateDvdCnstr k x c y
170
+ updateLowers k x c y
171
+ updateUppers k x c y
172
+ updateDiseqs k x c y
173
+
174
+ private def updateOccs (k : Int) (x : Var) (c : EqCnstr) : GoalM Unit := do
175
+ let ys := (← get').occurs[x]!
176
+ modify' fun s => { s with occurs := s.occurs.set x {} }
177
+ updateOccsAt k x c x
178
+ for y in ys do
179
+ updateOccsAt k x c y
180
+
181
+ @[export lean_grind_cutsat_assert_eq]
182
+ def EqCnstr.assertImpl (c : EqCnstr) : GoalM Unit := do
183
+ if (← inconsistent) then return ()
184
+ trace[grind.cutsat.assert] "{← c.pp}"
185
+ let c ← c.norm.applySubsts
186
+ if c.p.isUnsatEq then
187
+ trace[grind.cutsat.assert.unsat] "{← c.pp}"
188
+ setInconsistent (.eq c)
189
+ return ()
190
+ if c.isTrivial then
191
+ trace[grind.cutsat.assert.trivial] "{← c.pp}"
192
+ return ()
193
+ let k := c.p.gcdCoeffs'
194
+ if c.p.getConst % k > 0 then
195
+ setInconsistent (.eq c)
196
+ return ()
197
+ let c := if k == 1 then
198
+ c
199
+ else
200
+ { p := c.p.div k, h := .divCoeffs c }
201
+ let some (k, x) := c.p.pickVarToElim? | c.throwUnexpected
202
+ trace[grind.debug.cutsat.subst] ">> {← getVar x}, {← c.pp}"
203
+ trace[grind.cutsat.assert.store] "{← c.pp}"
204
+ modify' fun s => { s with
205
+ elimEqs := s.elimEqs.set x (some c)
206
+ elimStack := x :: s.elimStack
207
+ }
208
+ updateOccs k x c
209
+ if (← inconsistent) then return ()
210
+ -- assert a divisibility constraint IF `|k| != 1`
211
+ if k.natAbs != 1 then
212
+ let p := c.p.insert (-k) x
213
+ let d := Int.ofNat k.natAbs
214
+ { d, p, h := .ofEq x c : DvdCnstr }.assert
215
+
216
+ private def exprAsPoly (a : Expr) : GoalM Poly := do
217
+ if let some k ← getIntValue? a then
218
+ return .num k
219
+ else if let some var := (← get').varMap.find? { expr := a } then
220
+ return .add 1 var (.num 0)
221
+ else
222
+ throwError "internal `grind` error, expression is not relevant to cutsat{indentExpr a}"
223
+
224
+ private def processNewIntEq (a b : Expr) : GoalM Unit := do
225
+ let p₁ ← exprAsPoly a
226
+ let p₂ ← exprAsPoly b
227
+ -- Remark: we don't need to use the comm ring normalizer here because `p` is always linear.
228
+ let p := p₁.combine (p₂.mul (-1))
229
+ { p, h := .core a b p₁ p₂ : EqCnstr }.assert
230
+
231
+ /-- Asserts a constraint coming from the core. -/
232
+ private def EqCnstr.assertCore (c : EqCnstr) : GoalM Unit := do
233
+ if let some (re, rp, p) ← c.p.normCommRing? then
234
+ let c := { p, h := .commRingNorm c re rp : EqCnstr}
235
+ c.assert
236
+ else
237
+ c.assert
238
+
239
+ private def processNewNatEq (a b : Expr) : GoalM Unit := do
240
+ let (lhs, rhs) ← Int.OfNat.toIntEq a b
241
+ let gen ← getGeneration a
242
+ let ctx ← getNatVars
243
+ let lhs' ← toLinearExpr (← lhs.denoteAsIntExpr ctx) gen
244
+ let rhs' ← toLinearExpr (← rhs.denoteAsIntExpr ctx) gen
245
+ let p := lhs'.sub rhs' |>.norm
246
+ let c := { p, h := .coreNat a b lhs rhs lhs' rhs' : EqCnstr }
247
+ trace[grind.debug.cutsat.nat] "{← c.pp}"
248
+ c.assertCore
249
+
250
+ private def processNewToIntEq (a b : Expr) : ToIntM Unit := do
251
+ let gen := max (← getGeneration a) (← getGeneration b)
252
+ let (a', h₁) ← toInt a
253
+ let (b', h₂) ← toInt b
254
+ let thm := mkApp6 (← getInfo).ofEq a b a' b' h₁ h₂
255
+ let lhs ← toLinearExpr a' gen
256
+ let rhs ← toLinearExpr b' gen
257
+ let p := lhs.sub rhs |>.norm
258
+ let c := { p, h := .coreToInt a b thm lhs rhs : EqCnstr }
259
+ c.assertCore
260
+
261
+ @[export lean_process_cutsat_eq]
262
+ def processNewEqImpl (a b : Expr) : GoalM Unit := do
263
+ unless (← getConfig).cutsat do return ()
264
+ if (← isNatTerm a <&&> isNatTerm b) then
265
+ processNewNatEq a b
266
+ else if (← isIntTerm a <&&> isIntTerm b) then
267
+ processNewIntEq a b
268
+ else
269
+ let some α ← getToIntTermType? a | return ()
270
+ let some β ← getToIntTermType? b | return ()
271
+ unless isSameExpr α β do return ()
272
+ ToIntM.run α do processNewToIntEq a b
273
+
274
+ private def processNewIntDiseq (a b : Expr) : GoalM Unit := do
275
+ -- Remark: we don't need to use comm ring to normalize these polynomials because they are
276
+ -- always linear.
277
+ let p₁ ← exprAsPoly a
278
+ let c ← if let some 0 ← getIntValue? b then
279
+ pure { p := p₁, h := .core0 a b : DiseqCnstr }
280
+ else
281
+ let p₂ ← exprAsPoly b
282
+ let p := p₁.combine (p₂.mul (-1))
283
+ pure {p, h := .core a b p₁ p₂ : DiseqCnstr }
284
+ c.assert
285
+
286
+ /-- Asserts a constraint coming from the core. -/
287
+ private def DiseqCnstr.assertCore (c : DiseqCnstr) : GoalM Unit := do
288
+ if let some (re, rp, p) ← c.p.normCommRing? then
289
+ let c := { p, h := .commRingNorm c re rp : DiseqCnstr }
290
+ c.assert
291
+ else
292
+ c.assert
293
+
294
+ private def processNewNatDiseq (a b : Expr) : GoalM Unit := do
295
+ let (lhs, rhs) ← Int.OfNat.toIntEq a b
296
+ let gen ← getGeneration a
297
+ let ctx ← getNatVars
298
+ let lhs' ← toLinearExpr (← lhs.denoteAsIntExpr ctx) gen
299
+ let rhs' ← toLinearExpr (← rhs.denoteAsIntExpr ctx) gen
300
+ let p := lhs'.sub rhs' |>.norm
301
+ let c := { p, h := .coreNat a b lhs rhs lhs' rhs' : DiseqCnstr }
302
+ c.assertCore
303
+
304
+ private def processNewToIntDiseq (a b : Expr) : ToIntM Unit := do
305
+ let gen := max (← getGeneration a) (← getGeneration b)
306
+ let (a', h₁) ← toInt a
307
+ let (b', h₂) ← toInt b
308
+ let thm := mkApp6 (← getInfo).ofDiseq a b a' b' h₁ h₂
309
+ let lhs ← toLinearExpr a' gen
310
+ let rhs ← toLinearExpr b' gen
311
+ let p := lhs.sub rhs |>.norm
312
+ let c := { p, h := .coreToInt a b thm lhs rhs : DiseqCnstr }
313
+ c.assertCore
314
+
315
+ @[export lean_process_cutsat_diseq]
316
+ def processNewDiseqImpl (a b : Expr) : GoalM Unit := do
317
+ unless (← getConfig).cutsat do return ()
318
+ if (← isNatTerm a <&&> isNatTerm b) then
319
+ processNewNatDiseq a b
320
+ else if (← isIntTerm a <&&> isIntTerm b) then
321
+ processNewIntDiseq a b
322
+ else
323
+ let some α ← getToIntTermType? a | return ()
324
+ let some β ← getToIntTermType? b | return ()
325
+ unless isSameExpr α β do return ()
326
+ ToIntM.run α do processNewToIntDiseq a b
327
+
328
+ /-- Different kinds of terms internalized by this module. -/
329
+ private inductive SupportedTermKind where
330
+ | add | mul | num | div | mod | sub | pow | natAbs | toNat | natCast | neg
331
+ deriving BEq
332
+
333
+ private def getKindAndType? (e : Expr) : Option (SupportedTermKind × Expr) :=
334
+ match_expr e with
335
+ | HAdd.hAdd α _ _ _ _ _ => some (.add, α)
336
+ | HSub.hSub α _ _ _ _ _ => some (.sub, α)
337
+ | HMul.hMul α _ _ _ _ _ => some (.mul, α)
338
+ | HDiv.hDiv α _ _ _ _ _ => some (.div, α)
339
+ | HMod.hMod α _ _ _ _ _ => some (.mod, α)
340
+ | HPow.hPow α _ _ _ _ _ => some (.pow, α)
341
+ | OfNat.ofNat α _ _ => some (.num, α)
342
+ | Neg.neg α _ a =>
343
+ let_expr OfNat.ofNat _ _ _ := a | some (.neg, α)
344
+ some (.num, α)
345
+ | Int.natAbs _ => some (.natAbs, Nat.mkType)
346
+ | Int.toNat _ => some (.toNat, Nat.mkType)
347
+ | NatCast.natCast α _ _ => some (.natCast, α)
348
+ | _ => none
349
+
350
+ private def isForbiddenParent (parent? : Option Expr) (k : SupportedTermKind) : Bool := Id.run do
351
+ let some parent := parent? | return false
352
+ let .const declName _ := parent.getAppFn | return false
353
+ -- TODO: document `NatCast.natCast` case.
354
+ -- Remark: we added it to prevent natCast_sub from being expanded twice.
355
+ if declName == ``NatCast.natCast then return true
356
+ if k matches .div | .mod | .sub | .pow | .neg | .natAbs | .toNat | .natCast then return false
357
+ if declName == ``HAdd.hAdd || declName == ``LE.le || declName == ``Dvd.dvd then return true
358
+ match k with
359
+ | .add => return false
360
+ | .mul => return declName == ``HMul.hMul
361
+ | .num =>
362
+ -- Recall that we don't want to internalize numerals occurring at terms such as `x^3`.
363
+ return declName == ``HMul.hMul || declName == ``HPow.hPow
364
+ | _ => unreachable!
365
+
366
+ private def internalizeInt (e : Expr) : GoalM Unit := do
367
+ if (← hasVar e) then return ()
368
+ let p ← toPoly e
369
+ trace[grind.debug.cutsat.internalize] "{aquote e}:= {← p.pp}"
370
+ let x ← mkVar e
371
+ if p == .add 1 x (.num 0) then
372
+ -- It is pointless to assert `x = x`
373
+ -- This can happen if `e` is a nonlinear term (e.g., `e` is `a*b`)
374
+ return
375
+ if let some (re, rp, p') ← p.normCommRing? then
376
+ let c := { p := .add (-1) x p', h := .defnCommRing e p re rp p' : EqCnstr }
377
+ c.assert
378
+ else
379
+ let c := { p := .add (-1) x p, h := .defn e p : EqCnstr }
380
+ c.assert
381
+
382
+ private def expandDivMod (a : Expr) (b : Int) : GoalM Unit := do
383
+ if b == 0 || b == 1 || b == -1 then
384
+ throwError "`grind` internal error, found non-normalized div/mod by {b}"
385
+ if (← get').divMod.contains (a, b) then return ()
386
+ modify' fun s => { s with divMod := s.divMod.insert (a, b) }
387
+ let n : Int := 1 - b.natAbs
388
+ let b := mkIntLit b
389
+ pushNewFact <| mkApp2 (mkConst ``Int.Linear.ediv_emod) a b
390
+ pushNewFact <| mkApp3 (mkConst ``Int.Linear.emod_nonneg) a b reflBoolTrue
391
+ pushNewFact <| mkApp4 (mkConst ``Int.Linear.emod_le) a b (toExpr n) reflBoolTrue
392
+
393
+ private def propagateDiv (e : Expr) : GoalM Unit := do
394
+ let_expr HDiv.hDiv _ _ _ inst a b ← e | return ()
395
+ if (← isInstHDivInt inst) then
396
+ let some b ← getIntValue? b | return ()
397
+ -- Remark: we currently do not consider the case where `b` is in the equivalence class of a numeral.
398
+ expandDivMod a b
399
+
400
+ private def propagateMod (e : Expr) : GoalM Unit := do
401
+ let_expr HMod.hMod _ _ _ inst a b ← e | return ()
402
+ if (← isInstHModInt inst) then
403
+ let some b ← getIntValue? b | return ()
404
+ expandDivMod a b
405
+
406
+ private def propagateNatSub (e : Expr) : GoalM Unit := do
407
+ let_expr HSub.hSub _ _ _ inst a b := e | return ()
408
+ unless (← isInstHSubNat inst) do return ()
409
+ discard <| mkNatVar a
410
+ discard <| mkNatVar b
411
+ pushNewFact <| mkApp2 (mkConst ``Int.Linear.natCast_sub) a b
412
+
413
+ private def propagateNatAbs (e : Expr) : GoalM Unit := do
414
+ let_expr Int.natAbs a := e | return ()
415
+ pushNewFact <| mkApp (mkConst ``Lean.Omega.Int.ofNat_natAbs) a
416
+
417
+ private def propagateToNat (e : Expr) : GoalM Unit := do
418
+ let_expr Int.toNat a := e | return ()
419
+ pushNewFact <| mkApp (mkConst ``Int.OfNat.ofNat_toNat) a
420
+
421
+ private def internalizeNat (e : Expr) : GoalM Unit := do
422
+ let e' : Int.OfNat.Expr ← Int.OfNat.toOfNatExpr e
423
+ let gen ← getGeneration e
424
+ let ctx ← getNatVars
425
+ let e'' : Expr ← e'.denoteAsIntExpr ctx
426
+ -- If `e''` is of the form `NatCast.natCast e`, then it is wasteful to
427
+ -- assert an equality
428
+ match_expr e'' with
429
+ | NatCast.natCast _ _ a => if e == a then return ()
430
+ | _ => pure ()
431
+ let e'' : Int.Linear.Expr ← toLinearExpr e'' gen
432
+ let p := e''.norm
433
+ let natCast_e ← shareCommon (mkIntNatCast e)
434
+ internalize natCast_e gen
435
+ trace[grind.debug.cutsat.internalize] "{aquote natCast_e}:= {← p.pp}"
436
+ let x ← mkVar natCast_e
437
+ modify' fun s => { s with natDef := s.natDef.insert { expr := e } x }
438
+ if let some (re, rp, p') ← p.normCommRing? then
439
+ let c := { p := .add (-1) x p', h := .defnNatCommRing e' x e'' p re rp p' : EqCnstr }
440
+ c.assert
441
+ else
442
+ let c := { p := .add (-1) x p, h := .defnNat e' x e'' : EqCnstr }
443
+ c.assert
444
+
445
+ private def isToIntForbiddenParent (parent? : Option Expr) : Bool :=
446
+ if let some parent := parent? then
447
+ getKindAndType? parent |>.isSome
448
+ else
449
+ false
450
+
451
+ private def internalizeIntTerm (e type : Expr) (parent? : Option Expr) (k : SupportedTermKind) : GoalM Unit := do
452
+ if isForbiddenParent parent? k then return ()
453
+ trace[grind.debug.cutsat.internalize] "{e} : {type}"
454
+ match k with
455
+ | .div => propagateDiv e
456
+ | .mod => propagateMod e
457
+ | _ => internalizeInt e
458
+
459
+ private def internalizeNatTerm (e type : Expr) (parent? : Option Expr) (k : SupportedTermKind) : GoalM Unit := do
460
+ if isForbiddenParent parent? k then return ()
461
+ if (← isNatTerm e) then return ()
462
+ trace[grind.debug.cutsat.internalize] "{e} : {type}"
463
+ discard <| mkNatVar e
464
+ match k with
465
+ | .sub => propagateNatSub e
466
+ | .natAbs => propagateNatAbs e
467
+ | .toNat => propagateToNat e
468
+ | _ => internalizeNat e
469
+
470
+ private def internalizeToIntTerm (e type : Expr) : GoalM Unit := do
471
+ if (← isToIntTerm e) then return () -- already internalized
472
+ if let some (eToInt, he) ← toInt? e type then
473
+ trace[grind.debug.cutsat.internalize] "{e} : {type}"
474
+ trace[grind.debug.cutsat.toInt] "{e} ==> {eToInt}"
475
+ let α := type
476
+ modify' fun s => { s with
477
+ toIntTermMap := s.toIntTermMap.insert { expr := e } { eToInt, he, α }
478
+ }
479
+ markAsCutsatTerm e
480
+
481
+ /--
482
+ Internalizes an integer (and `Nat`) expression. Here are the different cases that are handled.
483
+
484
+ - `a + b` when `parent?` is not `+`, `≤`, or `∣`
485
+ - `k * a` when `k` is a numeral and `parent?` is not `+`, `*`, `≤`, `∣`
486
+ - numerals when `parent?` is not `+`, `*`, `≤`, `∣`.
487
+ Recall that we must internalize numerals to make sure we can propagate equalities
488
+ back to the congruence closure module. Example: we have `f 5`, `f x`, `x - y = 3`, `y = 2`.
489
+ -/
490
+ def internalize (e : Expr) (parent? : Option Expr) : GoalM Unit := do
491
+ unless (← getConfig).cutsat do return ()
492
+ if let some (k, type) := getKindAndType? e then
493
+ if type.isConstOf ``Int then
494
+ internalizeIntTerm e type parent? k
495
+ else if type.isConstOf ``Nat then
496
+ internalizeNatTerm e type parent? k
497
+ else
498
+ if isToIntForbiddenParent parent? then return ()
499
+ internalizeToIntTerm e type
500
+ else
501
+ /-
502
+ Remark: types implementing the `ToInt` class have a finite number
503
+ of elements. Thus, we must internalize all of them. Otherwise,
504
+ `grind` would fail to solve
505
+ ```
506
+ example (a : Fin 2) : a ≠ 0 → a ≠ 1 → False := by
507
+ grind
508
+ ```
509
+ It is not sufficient to internalize only the terms occurring in equalities and inequalities.
510
+ Here is an example where we must internalize `a`.
511
+ ```
512
+ example (a : Fin 2) (f : Fin 2 → Nat) : f 0 = 1 → f 1 = 1 → f a = 1 → False := by
513
+ grind
514
+ ```
515
+ Note that is not sufficient to internalize only the local declarations (e.g., `a`).
516
+ ```
517
+ example (g : Nat → Fin 2) (f : Fin 2 → Nat) : f 0 = 1 → f 1 = 1 → f (g 1) = 1 → False := by
518
+ grind
519
+ ```
520
+ That said, we currently do **not** support model-based theory combination for `ToInt` types.
521
+ Thus, we consider the extra terms occurring in equalities.
522
+
523
+ Recall that skip internalizing `Int` variables occurring in terms such as
524
+ ```
525
+ a = b
526
+ ```
527
+ is fine, because `Int` has an infinite number of elements, just using
528
+ the information in core, we can always find an assignment for them if even they have
529
+ not been internalized.
530
+
531
+ TODO: infer type and internalize all terms `a : α` s.t. `[ToInt α]` after we add
532
+ model-based theory combination for `ToInt`. One concern is performance, we will have
533
+ to use `inferType` again, and perform some form of canonicalization. Running
534
+ `ToInt` for them may be too expensive because the `ToInt` type class has output parameters.
535
+ Perhaps, we should have a `HasToInt` auxiliary class without output parameters.
536
+ -/
537
+ let_expr Eq α a b := e | return ()
538
+ unless (← getToIntInfo? α).isSome do return ()
539
+ internalizeToIntTerm a α
540
+ internalizeToIntTerm b α
541
+
542
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Inv.lean ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
8
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
9
+
10
+ namespace Int.Linear
11
+ /-- Returns `true` if all coefficients are not `0`. -/
12
+ def Poly.checkCoeffs : Poly → Bool
13
+ | .num _ => true
14
+ | .add k _ p => k != 0 && checkCoeffs p
15
+
16
+ end Int.Linear
17
+
18
+ namespace Lean.Meta.Grind.Arith.Cutsat
19
+
20
+ def _root_.Int.Linear.Poly.checkNoElimVars (p : Poly) : GoalM Unit := do
21
+ let .add _ x p := p | return ()
22
+ assert! !(← eliminated x)
23
+ checkNoElimVars p
24
+
25
+ def _root_.Int.Linear.Poly.checkOccs (p : Poly) : GoalM Unit := do
26
+ let .add _ y p := p | return ()
27
+ let rec go (p : Poly) : GoalM Unit := do
28
+ let .add _ x p := p | return ()
29
+ assert! (← getOccursOf x).contains y
30
+ go p
31
+ go p
32
+
33
+ def _root_.Int.Linear.Poly.checkCnstrOf (p : Poly) (x : Var) : GoalM Unit := do
34
+ assert! p.isSorted
35
+ assert! p.checkCoeffs
36
+ unless (← inconsistent) do
37
+ p.checkNoElimVars
38
+ p.checkOccs
39
+ let .add _ y _ := p | unreachable!
40
+ assert! x == y
41
+
42
+ def checkLeCnstrs (css : PArray (PArray LeCnstr)) (isLower : Bool) : GoalM Unit := do
43
+ let mut x := 0
44
+ for cs in css do
45
+ for c in cs do
46
+ c.p.checkCnstrOf x
47
+ let .add a _ _ := c.p | unreachable!
48
+ assert! isLower == (a < 0)
49
+ x := x + 1
50
+ return ()
51
+
52
+ def checkLowers : GoalM Unit := do
53
+ let s ← get'
54
+ assert! s.lowers.size == s.vars.size
55
+ checkLeCnstrs s.lowers (isLower := true)
56
+
57
+ def checkUppers : GoalM Unit := do
58
+ let s ← get'
59
+ assert! s.uppers.size == s.vars.size
60
+ checkLeCnstrs s.uppers (isLower := false)
61
+
62
+ def checkDvds : GoalM Unit := do
63
+ let s ← get'
64
+ assert! s.vars.size == s.dvds.size
65
+ let mut x := 0
66
+ for c? in s.dvds do
67
+ if let some c := c? then
68
+ c.p.checkCnstrOf x
69
+ assert! c.d > 1
70
+ x := x + 1
71
+
72
+ def checkVars : GoalM Unit := do
73
+ let s ← get'
74
+ let mut num := 0
75
+ for ({ expr }, var) in s.varMap do
76
+ if h : var < s.vars.size then
77
+ let expr' := s.vars[var]
78
+ assert! isSameExpr expr expr'
79
+ else
80
+ unreachable!
81
+ num := num + 1
82
+ assert! s.vars.size == num
83
+
84
+ def checkElimEqs : GoalM Unit := do
85
+ let s ← get'
86
+ assert! s.elimEqs.size == s.vars.size
87
+ let mut x := 0
88
+ for c? in s.elimEqs do
89
+ if let some c := c? then
90
+ assert! c.p.isSorted
91
+ assert! c.p.checkCoeffs
92
+ assert! s.elimStack.contains x
93
+ assert! c.p.coeff x != 0
94
+ x := x + 1
95
+
96
+ def checkElimStack : GoalM Unit := do
97
+ for x in (← get').elimStack do
98
+ assert! (← eliminated x)
99
+
100
+ def checkDiseqCnstrs : GoalM Unit := do
101
+ let s ← get'
102
+ assert! s.vars.size == s.diseqs.size
103
+ let mut x := 0
104
+ for cs in s.diseqs do
105
+ for c in cs do
106
+ c.p.checkCnstrOf x
107
+ x := x + 1
108
+ return ()
109
+
110
+ def checkInvariants : GoalM Unit := do
111
+ checkVars
112
+ checkDvds
113
+ checkLowers
114
+ checkUppers
115
+ checkElimEqs
116
+ checkElimStack
117
+ checkDiseqCnstrs
118
+
119
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/LeCnstr.lean ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Simp.Arith.Int
8
+ import Lean.Meta.Tactic.Grind.PropagatorAttr
9
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
10
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
11
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Proof
12
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Nat
13
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Norm
14
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.ToInt
15
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.CommRing
16
+
17
+ namespace Lean.Meta.Grind.Arith.Cutsat
18
+
19
+ def LeCnstr.norm (c : LeCnstr) : LeCnstr :=
20
+ let c := if c.p.isSorted then
21
+ c
22
+ else
23
+ { p := c.p.norm, h := .norm c }
24
+ let k := c.p.gcdCoeffs'
25
+ if k != 1 then
26
+ { p := c.p.div k, h := .divCoeffs c }
27
+ else
28
+ c
29
+
30
+ /--
31
+ Given an equation `c₁` containing the monomial `a*x`, and an inequality constraint `c₂`
32
+ containing the monomial `b*x`, eliminate `x` by applying substitution.
33
+ -/
34
+ def LeCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : LeCnstr) : GoalM LeCnstr := do
35
+ let p := c₁.p
36
+ let q := c₂.p
37
+ let p := if a ≥ 0 then
38
+ q.mul a |>.combine (p.mul (-b))
39
+ else
40
+ p.mul b |>.combine (q.mul (-a))
41
+ trace[grind.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
42
+ return { p, h := .subst x c₁ c₂ }
43
+
44
+ partial def LeCnstr.applySubsts (c : LeCnstr) : GoalM LeCnstr := withIncRecDepth do
45
+ let some (b, x, c₁) ← c.p.findVarToSubst | return c
46
+ let a := c₁.p.coeff x
47
+ let c ← c.applyEq a x c₁ b
48
+ applySubsts c
49
+
50
+ def _root_.Int.Linear.Poly.isNegEq (p₁ p₂ : Poly) : Bool :=
51
+ match p₁, p₂ with
52
+ | .num k₁, .num k₂ => k₁ == -k₂
53
+ | .add a₁ x p₁, .add a₂ y p₂ => a₁ == -a₂ && x == y && isNegEq p₁ p₂
54
+ | _, _ => false
55
+
56
+ def LeCnstr.erase (c : LeCnstr) : GoalM Unit := do
57
+ let .add a x _ := c.p | c.throwUnexpected
58
+ if a < 0 then
59
+ modify' fun s => { s with lowers := s.lowers.modify x fun cs' => cs'.filter fun c' => c'.p != c.p }
60
+ else
61
+ modify' fun s => { s with uppers := s.uppers.modify x fun cs' => cs'.filter fun c' => c'.p != c.p }
62
+
63
+ /--
64
+ Given a lower (upper) bound constraint `c`, tries to find
65
+ an imply equality by searching a upper (lower) bound constraint `c'` such that
66
+ `c.p == -c'.p`
67
+ -/
68
+ private def findEq (c : LeCnstr) : GoalM Bool := do
69
+ let .add a x _ := c.p | c.throwUnexpected
70
+ let s ← get'
71
+ let cs' := if a < 0 then s.uppers[x]! else s.lowers[x]!
72
+ for c' in cs' do
73
+ if c.p.isNegEq c'.p then
74
+ c'.erase
75
+ let eq := { p := c.p, h := .ofLeGe c c' : EqCnstr }
76
+ trace[grind.debug.cutsat.eq] "new eq: {← eq.pp}"
77
+ eq.assert
78
+ return true
79
+ return false
80
+
81
+ /--
82
+ Applies `p ≤ 0 → p ≠ 0 → p + 1 ≤ 0`
83
+ -/
84
+ private def refineWithDiseq (c : LeCnstr) : GoalM LeCnstr := do
85
+ let .add _ x _ := c.p | c.throwUnexpected
86
+ let mut c := c
87
+ repeat
88
+ let some c' ← refineWithDiseqStep? x c | return c
89
+ c := c'
90
+ return c
91
+ where
92
+ refineWithDiseqStep? (x : Var) (c : LeCnstr) : GoalM (Option LeCnstr) := do
93
+ let s ← get'
94
+ let cs' := s.diseqs[x]!
95
+ for c' in cs' do
96
+ if c.p == c'.p || c.p.isNegEq c'.p then
97
+ -- Remove `c'`
98
+ modify' fun s => { s with diseqs := s.diseqs.modify x fun cs' => cs'.filter fun c => c.p != c'.p }
99
+ return some { p := c.p.addConst 1, h := .ofLeDiseq c c' }
100
+ return none
101
+
102
+ @[export lean_grind_cutsat_assert_le]
103
+ def LeCnstr.assertImpl (c : LeCnstr) : GoalM Unit := do
104
+ if (← inconsistent) then return ()
105
+ trace[grind.cutsat.assert] "{← c.pp}"
106
+ let c ← c.norm.applySubsts
107
+ if c.isUnsat then
108
+ trace[grind.cutsat.assert.unsat] "{← c.pp}"
109
+ setInconsistent (.le c)
110
+ return ()
111
+ if c.isTrivial then
112
+ trace[grind.cutsat.assert.trivial] "{← c.pp}"
113
+ return ()
114
+ let .add a x _ := c.p | c.throwUnexpected
115
+ if (← findEq c) then
116
+ return ()
117
+ let c ← refineWithDiseq c
118
+ trace[grind.cutsat.assert.store] "{← c.pp}"
119
+ c.p.updateOccs
120
+ if a < 0 then
121
+ modify' fun s => { s with lowers := s.lowers.modify x (·.push c) }
122
+ else
123
+ modify' fun s => { s with uppers := s.uppers.modify x (·.push c) }
124
+ if (← c.satisfied) == .false then
125
+ resetAssignmentFrom x
126
+
127
+ private def reportNonNormalized (e : Expr) : GoalM Unit := do
128
+ reportIssue! "unexpected non normalized inequality constraint found{indentExpr e}"
129
+
130
+ private def toPolyLe? (e : Expr) : GoalM (Option Poly) := do
131
+ let_expr LE.le _ inst a b ← e | return none
132
+ unless (← isInstLEInt inst) do return none
133
+ let some k ← getIntValue? b
134
+ | reportNonNormalized e; return none
135
+ unless k == 0 do
136
+ reportNonNormalized e; return none
137
+ return some (← toPoly a)
138
+
139
+ /-- Asserts a constraint coming from the core. -/
140
+ private def LeCnstr.assertCore (c : LeCnstr) : GoalM Unit := do
141
+ if let some (re, rp, p) ← c.p.normCommRing? then
142
+ let c := { p, h := .commRingNorm c re rp : LeCnstr}
143
+ c.assert
144
+ else
145
+ c.assert
146
+
147
+ /--
148
+ Given an expression `e` that is in `True` (or `False` equivalence class), if `e` is an
149
+ integer inequality, asserts it to the cutsat state.
150
+ -/
151
+ def propagateIntLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
152
+ let some p ← toPolyLe? e | return ()
153
+ let c ← if eqTrue then
154
+ pure { p, h := .core e : LeCnstr }
155
+ else
156
+ pure { p := p.mul (-1) |>.addConst 1, h := .coreNeg e p : LeCnstr }
157
+ c.assertCore
158
+
159
+ def propagateNatLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
160
+ let some (lhs, rhs) ← Int.OfNat.toIntLe? e | return ()
161
+ let gen ← getGeneration e
162
+ let ctx ← getNatVars
163
+ let lhs' ← toLinearExpr (← lhs.denoteAsIntExpr ctx) gen
164
+ let rhs' ← toLinearExpr (← rhs.denoteAsIntExpr ctx) gen
165
+ let p := lhs'.sub rhs' |>.norm
166
+ let c ← if eqTrue then
167
+ pure { p, h := .coreNat e lhs rhs lhs' rhs' : LeCnstr }
168
+ else
169
+ pure { p := p.mul (-1) |>.addConst 1, h := .coreNatNeg e lhs rhs lhs' rhs' : LeCnstr }
170
+ c.assertCore
171
+
172
+ def propagateToIntLe (e : Expr) (eqTrue : Bool) : ToIntM Unit := do
173
+ let some thm ← if eqTrue then pure (← getInfo).ofLE? else pure (← getInfo).ofNotLE? | return ()
174
+ let_expr LE.le _ _ a b := e | return ()
175
+ let gen ← getGeneration e
176
+ let (a', h₁) ← toInt a
177
+ let (b', h₂) ← toInt b
178
+ let thm := mkApp6 thm a b a' b' h₁ h₂
179
+ let (a', b') := if eqTrue then (a', b') else (mkIntAdd b' (mkIntLit 1), a')
180
+ let lhs ← toLinearExpr a' gen
181
+ let rhs ← toLinearExpr b' gen
182
+ let p := lhs.sub rhs |>.norm
183
+ let c := { p, h := .coreToInt e eqTrue thm lhs rhs : LeCnstr }
184
+ c.assertCore
185
+
186
+ def propagateLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
187
+ unless (← getConfig).cutsat do return ()
188
+ let_expr LE.le α _ _ _ := e | return ()
189
+ if α.isConstOf ``Nat then
190
+ propagateNatLe e eqTrue
191
+ else if α.isConstOf ``Int then
192
+ propagateIntLe e eqTrue
193
+ else ToIntM.run α do
194
+ propagateToIntLe e eqTrue
195
+
196
+ def propagateLt (e : Expr) (eqTrue : Bool) : GoalM Unit := do
197
+ unless (← getConfig).cutsat do return ()
198
+ let_expr LT.lt α _ a b := e | return ()
199
+ ToIntM.run α do
200
+ let some thm ← if eqTrue then pure (← getInfo).ofLT? else pure (← getInfo).ofNotLT? | return ()
201
+ let gen ← getGeneration e
202
+ let (a', h₁) ← toInt a
203
+ let (b', h₂) ← toInt b
204
+ let thm := mkApp6 thm a b a' b' h₁ h₂
205
+ let (a', b') := if eqTrue then (mkIntAdd a' (mkIntLit 1), b') else (b', a')
206
+ let lhs ← toLinearExpr a' gen
207
+ let rhs ← toLinearExpr b' gen
208
+ let p := lhs.sub rhs |>.norm
209
+ let c := { p, h := .coreToInt e eqTrue thm lhs rhs : LeCnstr }
210
+ c.assert
211
+
212
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/MBTC.lean ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Canon
8
+ import Lean.Meta.Tactic.Grind.MBTC
9
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
10
+
11
+ namespace Lean.Meta.Grind.Arith.Cutsat
12
+
13
+ private def getAssignmentExt? (e : Expr) : GoalM (Option Rat) := do
14
+ let val? ← getAssignment? (← get) e
15
+ if val?.isSome then
16
+ return val?
17
+ let type ← inferType e
18
+ if type == Nat.mkType then
19
+ for parent in (← getParents e) do
20
+ let_expr NatCast.natCast _ inst _ := parent | pure ()
21
+ let_expr instNatCastInt := inst | pure ()
22
+ return (← getAssignment? (← get) parent)
23
+ return none
24
+
25
+ private def hasTheoryVar (e : Expr) : GoalM Bool := do
26
+ return (← getAssignmentExt? e).isSome
27
+
28
+ private def isInterpreted (e : Expr) : GoalM Bool := do
29
+ if isInterpretedTerm e then return true
30
+ let f := e.getAppFn
31
+ return f.isConstOf ``LE.le || f.isConstOf ``Dvd.dvd
32
+
33
+ private def eqAssignment (a b : Expr) : GoalM Bool := do
34
+ let some v₁ ← getAssignmentExt? a | return false
35
+ let some v₂ ← getAssignmentExt? b | return false
36
+ return v₁ == v₂
37
+
38
+ def mbtc : GoalM Bool := do
39
+ Grind.mbtc {
40
+ hasTheoryVar := hasTheoryVar
41
+ isInterpreted := isInterpreted
42
+ eqAssignment := eqAssignment
43
+ }
44
+
45
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Model.lean ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.ToInt
8
+ import Lean.Meta.Tactic.Grind.Types
9
+ import Lean.Meta.Tactic.Grind.Arith.ModelUtil
10
+
11
+ namespace Lean.Meta.Grind.Arith.Cutsat
12
+
13
+ private def isIntNatENode (n : ENode) : MetaM Bool :=
14
+ withDefault do
15
+ let type ← inferType n.self
16
+ isDefEq type Int.mkType
17
+ <||>
18
+ isDefEq type Nat.mkType
19
+
20
+ private def getCutsatAssignment? (goal : Goal) (node : ENode) : Option Rat := Id.run do
21
+ assert! isSameExpr node.self node.root
22
+ let some e := node.cutsat? | return none
23
+ let some x := goal.arith.cutsat.varMap.find? { expr := e } | return none
24
+ if h : x < goal.arith.cutsat.assignment.size then
25
+ return goal.arith.cutsat.assignment[x]
26
+ else
27
+ return none
28
+
29
+ private def natCastToInt? (e : Expr) : Option Expr :=
30
+ match_expr e with
31
+ | NatCast.natCast _ inst a =>
32
+ let_expr instNatCastInt := inst | none
33
+ some a
34
+ | Grind.ToInt.toInt _ _ _ a => some a
35
+ | _ => none
36
+
37
+ def getAssignment? (goal : Goal) (e : Expr) : MetaM (Option Rat) := do
38
+ let node ← goal.getENode (← goal.getRoot e)
39
+ if let some v := getCutsatAssignment? goal node then
40
+ return some v
41
+ else if let some v ← getIntValue? node.self then
42
+ return some v
43
+ else if let some v ← getNatValue? node.self then
44
+ return some (Int.ofNat v)
45
+ else
46
+ return none
47
+
48
+ /--
49
+ Construct a model that satisfies all constraints in the cutsat model.
50
+ It also assigns values to integer terms that have not been internalized by the
51
+ cutsat model.
52
+
53
+ Remark: it uses rational numbers because cutsat may have failed to build an
54
+ integer model.
55
+ -/
56
+ def mkModel (goal : Goal) : MetaM (Array (Expr × Rat)) := do
57
+ let mut model := {}
58
+ -- Assign on expressions associated with cutsat terms or interpreted terms
59
+ for e in goal.exprs do
60
+ let node ← goal.getENode e
61
+ if node.isRoot then
62
+ if (← isIntNatENode node) then
63
+ if let some v ← getAssignment? goal node.self then
64
+ model := assignEqc goal node.self v model
65
+ -- Assign natCast and toInt terms
66
+ for e in goal.exprs do
67
+ let node ← goal.getENode e
68
+ let i := node.self
69
+ let some n := natCastToInt? i | pure ()
70
+ if model[n]?.isNone then
71
+ let some v := model[i]? | pure ()
72
+ model := assignEqc goal n v model
73
+ let r ← finalizeModel goal isIntNatENode model
74
+ traceModel `grind.cutsat.model r
75
+ return r
76
+
77
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Nat.lean ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Data.Int.OfNat
8
+ import Lean.Meta.Tactic.Grind.Simp
9
+ import Lean.Meta.Tactic.Simp.Arith.Nat.Basic
10
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Norm
11
+
12
+ namespace Lean.Meta.Grind.Arith.Cutsat
13
+
14
+ def mkNatVar (e : Expr) : GoalM Var := do
15
+ if let some x := (← get').natVarMap.find? { expr := e } then
16
+ return x
17
+ let x := (← get').natVars.size
18
+ modify' fun s => { s with
19
+ natVars := s.natVars.push e
20
+ natVarMap := s.natVarMap.insert { expr := e } x
21
+ }
22
+ markAsCutsatTerm e
23
+ return x
24
+
25
+ def getNatVars : GoalM (PArray Expr) := do
26
+ return (← get').natVars
27
+
28
+ def isNatTerm (e : Expr) : GoalM Bool := do
29
+ return (← get').natVarMap.contains { expr := e }
30
+
31
+ end Lean.Meta.Grind.Arith.Cutsat
32
+
33
+ namespace Int.OfNat
34
+ open Lean
35
+
36
+ protected def toExpr (e : Expr) : Lean.Expr :=
37
+ open Int.OfNat.Expr in
38
+ match e with
39
+ | .num v => mkApp (mkConst ``num) (mkNatLit v)
40
+ | .var i => mkApp (mkConst ``var) (mkNatLit i)
41
+ | .add a b => mkApp2 (mkConst ``add) (OfNat.toExpr a) (OfNat.toExpr b)
42
+ | .mul a b => mkApp2 (mkConst ``mul) (OfNat.toExpr a) (OfNat.toExpr b)
43
+ | .div a b => mkApp2 (mkConst ``div) (OfNat.toExpr a) (OfNat.toExpr b)
44
+ | .mod a b => mkApp2 (mkConst ``mod) (OfNat.toExpr a) (OfNat.toExpr b)
45
+ | .pow a k => mkApp2 (mkConst ``pow) (OfNat.toExpr a) (mkNatLit k)
46
+
47
+ instance : ToExpr OfNat.Expr where
48
+ toExpr a := OfNat.toExpr a
49
+ toTypeExpr := mkConst ``OfNat.Expr
50
+
51
+ open Lean.Meta.Grind
52
+ open Lean.Meta.Grind.Arith.Cutsat
53
+
54
+ open Meta
55
+
56
+ def Expr.denoteAsIntExpr (ctx : PArray Lean.Expr) (e : Expr) : GoalM Lean.Expr :=
57
+ shareCommon (go e)
58
+ where
59
+ go (e : Expr) : Lean.Expr :=
60
+ match e with
61
+ | .num v => mkIntLit v
62
+ | .var i => mkIntNatCast ctx[i]!
63
+ | .add a b => mkIntAdd (go a) (go b)
64
+ | .mul a b => mkIntMul (go a) (go b)
65
+ | .div a b => mkIntDiv (go a) (go b)
66
+ | .mod a b => mkIntMod (go a) (go b)
67
+ | .pow a b => mkIntPowNat (go a) (mkNatLit b)
68
+
69
+ partial def toOfNatExpr (e : Lean.Expr) : GoalM Expr := do
70
+ let mkVar (e : Lean.Expr) : GoalM Expr := do
71
+ let x ← mkNatVar e
72
+ return .var x
73
+ match_expr e with
74
+ | OfNat.ofNat _ _ _ =>
75
+ if let some n ← getNatValue? e then
76
+ return .num n
77
+ else
78
+ mkVar e
79
+ | HAdd.hAdd _ _ _ i a b =>
80
+ if (← isInstHAddNat i) then return .add (← toOfNatExpr a) (← toOfNatExpr b)
81
+ else mkVar e
82
+ | HMul.hMul _ _ _ i a b =>
83
+ if (← isInstHMulNat i) then return .mul (← toOfNatExpr a) (← toOfNatExpr b)
84
+ else mkVar e
85
+ | HDiv.hDiv _ _ _ i a b =>
86
+ if (← isInstHDivNat i) then return .div (← toOfNatExpr a) (← toOfNatExpr b)
87
+ else mkVar e
88
+ | HMod.hMod _ _ _ i a b =>
89
+ if (← isInstHModNat i) then return .mod (← toOfNatExpr a) (← toOfNatExpr b)
90
+ else mkVar e
91
+ | HPow.hPow _ _ _ i a b =>
92
+ let some k ← getNatValue? b | mkVar e
93
+ if (← isInstHPowNat i) then return .pow (← toOfNatExpr a) k
94
+ else mkVar e
95
+ | _ => mkVar e
96
+
97
+ /--
98
+ Given `e` of the form `lhs ≤ rhs` where `lhs` and `rhs` have type `Nat`,
99
+ returns `(lhs, rhs)` where `lhs` and `rhs` are `Int.OfNat.Expr`.
100
+ -/
101
+ def toIntLe? (e : Lean.Expr) : GoalM (Option (Expr × Expr)) := do
102
+ let_expr LE.le _ inst lhs rhs := e | return none
103
+ unless (← isInstLENat inst) do return none
104
+ let lhs ← toOfNatExpr lhs
105
+ let rhs ← toOfNatExpr rhs
106
+ return some (lhs, rhs)
107
+
108
+ def toIntDvd? (e : Lean.Expr) : GoalM (Option (Nat × Expr)) := do
109
+ let_expr Dvd.dvd _ inst a b := e | return none
110
+ unless (← isInstDvdNat inst) do return none
111
+ let some d ← getNatValue? a
112
+ | reportIssue! "non-linear divisibility constraint found{indentExpr e}"
113
+ return none
114
+ let b ← toOfNatExpr b
115
+ return some (d, b)
116
+
117
+ def toIntEq (lhs rhs : Lean.Expr) : GoalM (Expr × Expr) := do
118
+ let lhs ← toOfNatExpr lhs
119
+ let rhs ← toOfNatExpr rhs
120
+ return (lhs, rhs)
121
+
122
+ /--
123
+ Given `e` of type `Int`, tries to compute `a : Int.OfNat.Expr` s.t.
124
+ `a.denoteAsInt ctx` is `e`
125
+ -/
126
+ partial def ofDenoteAsIntExpr? (e : Lean.Expr) : OptionT GoalM Expr := do
127
+ match_expr e with
128
+ | OfNat.ofNat _ _ _ =>
129
+ let some n ← getIntValue? e | failure
130
+ guard (n ≥ 0)
131
+ return .num n.toNat
132
+ | HAdd.hAdd _ _ _ i a b =>
133
+ guard (← isInstHAddInt i)
134
+ return .add (← ofDenoteAsIntExpr? a) (← ofDenoteAsIntExpr? b)
135
+ | HMul.hMul _ _ _ i a b =>
136
+ guard (← isInstHMulInt i)
137
+ return .mul (← ofDenoteAsIntExpr? a) (← ofDenoteAsIntExpr? b)
138
+ | HDiv.hDiv _ _ _ i a b =>
139
+ guard (← isInstHDivInt i)
140
+ return .div (← ofDenoteAsIntExpr? a) (← ofDenoteAsIntExpr? b)
141
+ | HMod.hMod _ _ _ i a b =>
142
+ guard (← isInstHModInt i)
143
+ return .mod (← ofDenoteAsIntExpr? a) (← ofDenoteAsIntExpr? b)
144
+ | _ =>
145
+ let_expr NatCast.natCast _ inst a ← e | failure
146
+ let_expr instNatCastInt := inst | failure
147
+ let x ← mkNatVar a
148
+ return .var x
149
+
150
+ end Int.OfNat
151
+
152
+ namespace Lean.Meta.Grind.Arith.Cutsat
153
+ /--
154
+ If `e` is of the form `a.denoteAsInt ctx` for some `a` and `ctx`,
155
+ assert that `e` is nonnegative.
156
+ -/
157
+ def assertDenoteAsIntNonneg (e : Expr) : GoalM Unit := withIncRecDepth do
158
+ if e.isAppOf ``NatCast.natCast then return ()
159
+ if e.isAppOf ``OfNat.ofNat then return () -- we don't want to propagate constraints such as `2 ≥ 0`
160
+ let some rhs ← Int.OfNat.ofDenoteAsIntExpr? e |>.run | return ()
161
+ let gen ← getGeneration e
162
+ let ctx ← getNatVars
163
+ let lhs' : Int.Linear.Expr := .num 0
164
+ let rhs' ← toLinearExpr (← rhs.denoteAsIntExpr ctx) gen
165
+ let p := lhs'.sub rhs' |>.norm
166
+ let c := { p, h := .denoteAsIntNonneg rhs rhs' : LeCnstr }
167
+ c.assert
168
+
169
+ /--
170
+ Given `x` whose denotation is `e`, if `e` is of the form `NatCast.natCast a`,
171
+ asserts that it is nonnegative.
172
+ -/
173
+ def assertNatCast (e : Expr) (x : Var) : GoalM Unit := do
174
+ let_expr NatCast.natCast _ inst a := e | return ()
175
+ let_expr instNatCastInt := inst | return ()
176
+ if a.isAppOf ``OfNat.ofNat then return () -- we don't want to propagate constraints such as `2 ≥ 0`
177
+ if (← get').natDef.contains { expr := a } then return ()
178
+ let n ← mkNatVar a
179
+ let p := .add (-1) x (.num 0)
180
+ let c := { p, h := .denoteAsIntNonneg (.var n) (.var x) : LeCnstr}
181
+ c.assert
182
+
183
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Norm.lean ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
8
+
9
+ namespace Lean.Meta.Grind.Arith.Cutsat
10
+ /-!
11
+ `Int` expressions are normalized by the `grind` preprocessor, but we
12
+ dynamically convert expressions from other types into `Int` expressions
13
+ and these expressions must be normalized inside of the cutsat module.
14
+ -/
15
+
16
+ /-- Converts the given integer expression into `Int.Linear.Expr` -/
17
+ partial def toLinearExpr (e : Expr) (generation : Nat := 0) : GoalM Int.Linear.Expr := do
18
+ let toVar (e : Expr) := do
19
+ let e ← shareCommon e
20
+ if (← alreadyInternalized e) then
21
+ return .var (← mkVar e)
22
+ else
23
+ internalize e generation
24
+ return .var (← mkVar e)
25
+ let mul (a b : Expr) := do
26
+ match (← getIntValue? a) with
27
+ | some k => return .mulL k (← toLinearExpr b)
28
+ | none => match (← getIntValue? b) with
29
+ | some k => return .mulR (← toLinearExpr a) k
30
+ | none => toVar e
31
+ match_expr e with
32
+ | OfNat.ofNat _ _ _ =>
33
+ if let some n ← getIntValue? e then return .num n
34
+ else toVar e
35
+ | Neg.neg _ i a =>
36
+ if (← isInstNegInt i) then return .neg (← toLinearExpr a)
37
+ else toVar e
38
+ | HAdd.hAdd _ _ _ i a b =>
39
+ if (← isInstHAddInt i) then return .add (← toLinearExpr a) (← toLinearExpr b)
40
+ else toVar e
41
+ | HSub.hSub _ _ _ i a b =>
42
+ if (← isInstHSubInt i) then return .sub (← toLinearExpr a) (← toLinearExpr b)
43
+ else toVar e
44
+ | HMul.hMul _ _ _ i a b =>
45
+ if (← isInstHMulInt i) then mul a b
46
+ else toVar e
47
+ | _ => toVar e
48
+
49
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Proof.lean ADDED
@@ -0,0 +1,520 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.Ring.Poly
8
+ import Lean.Meta.Tactic.Grind.Diseq
9
+ import Lean.Meta.Tactic.Grind.Arith.Util
10
+ import Lean.Meta.Tactic.Grind.Arith.ProofUtil
11
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
12
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Nat
13
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.CommRing
14
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Util
15
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Proof
16
+
17
+ namespace Lean.Meta.Grind.Arith.Cutsat
18
+
19
+ deriving instance Hashable for Int.Linear.Expr
20
+ deriving instance Hashable for Int.OfNat.Expr
21
+
22
+ structure ProofM.State where
23
+ cache : Std.HashMap UInt64 Expr := {}
24
+ polyMap : Std.HashMap Poly Expr := {}
25
+ exprMap : Std.HashMap Int.Linear.Expr Expr := {}
26
+ natExprMap : Std.HashMap Int.OfNat.Expr Expr := {}
27
+ ringPolyMap : Std.HashMap CommRing.Poly Expr := {}
28
+ ringExprMap : Std.HashMap CommRing.RingExpr Expr := {}
29
+
30
+ structure ProofM.Context where
31
+ ctx : Expr
32
+ /-- Variables before reordering -/
33
+ ctx' : Expr
34
+ natCtx : Expr
35
+ ringCtx : Expr
36
+ /--
37
+ `unordered` is `true` if we entered a `.reorder c` justification. The variables in `c` and
38
+ its dependencies are unordered.
39
+ -/
40
+ unordered : Bool := false
41
+
42
+ /-- Auxiliary monad for constructing cutsat proofs. -/
43
+ abbrev ProofM := ReaderT ProofM.Context (StateRefT ProofM.State GoalM)
44
+
45
+ /-- Returns a Lean expression representing the variable context used to construct cutsat proofs. -/
46
+ private abbrev getContext : ProofM Expr := do
47
+ return (← read).ctx
48
+
49
+ private abbrev getNatContext : ProofM Expr := do
50
+ return (← read).natCtx
51
+
52
+ /--
53
+ Execute `k` with `unordered := true`, and the unordered variable context.
54
+ We use this combinator to process `.reorder c` justifications.
55
+ -/
56
+ private abbrev withUnordered (k : ProofM α) : ProofM α := do
57
+ withReader (fun c => { c with ctx := c.ctx', unordered := true }) k
58
+
59
+ private abbrev getVarMap : ProofM (PHashMap ExprPtr Var) := do
60
+ if (← read).unordered then
61
+ return (← get').varMap'
62
+ else
63
+ return (← get').varMap
64
+
65
+ private abbrev caching (c : α) (k : ProofM Expr) : ProofM Expr := do
66
+ let addr := unsafe (ptrAddrUnsafe c).toUInt64 >>> 2
67
+ if let some h := (← get).cache[addr]? then
68
+ return h
69
+ else
70
+ let h ← k
71
+ modify fun s => { s with cache := s.cache.insert addr h }
72
+ return h
73
+
74
+ private def mkPolyDecl (p : Poly) : ProofM Expr := do
75
+ if let some x := (← get).polyMap[p]? then
76
+ return x
77
+ let x := mkFVar (← mkFreshFVarId)
78
+ modify fun s => { s with polyMap := s.polyMap.insert p x }
79
+ return x
80
+
81
+ private def mkExprDecl (e : Int.Linear.Expr) : ProofM Expr := do
82
+ if let some x := (← get).exprMap[e]? then
83
+ return x
84
+ let x := mkFVar (← mkFreshFVarId)
85
+ modify fun s => { s with exprMap := s.exprMap.insert e x }
86
+ return x
87
+
88
+ private def mkNatExprDecl (e : Int.OfNat.Expr) : ProofM Expr := do
89
+ if let some x := (← get).natExprMap[e]? then
90
+ return x
91
+ let x := mkFVar (← mkFreshFVarId)
92
+ modify fun s => { s with natExprMap := s.natExprMap.insert e x }
93
+ return x
94
+
95
+ private def mkRingPolyDecl (p : CommRing.Poly) : ProofM Expr := do
96
+ if let some x := (← get).ringPolyMap[p]? then
97
+ return x
98
+ let x := mkFVar (← mkFreshFVarId)
99
+ modify fun s => { s with ringPolyMap := s.ringPolyMap.insert p x }
100
+ return x
101
+
102
+ private def mkRingExprDecl (e : CommRing.RingExpr) : ProofM Expr := do
103
+ if let some x := (← get).ringExprMap[e]? then
104
+ return x
105
+ let x := mkFVar (← mkFreshFVarId)
106
+ modify fun s => { s with ringExprMap := s.ringExprMap.insert e x }
107
+ return x
108
+
109
+ private def toContextExprCore (vars : PArray Expr) (type : Expr) : MetaM Expr :=
110
+ if h : 0 < vars.size then
111
+ RArray.toExpr type id (RArray.ofFn (vars[·]) h)
112
+ else
113
+ RArray.toExpr type id (RArray.leaf (mkIntLit 0))
114
+
115
+ private def toContextExpr : GoalM Expr := do
116
+ toContextExprCore (← getVars) Int.mkType
117
+
118
+ private def toContextExpr' : GoalM Expr := do
119
+ toContextExprCore (← get').vars' Int.mkType
120
+
121
+ private def toNatContextExpr : GoalM Expr := do
122
+ toContextExprCore (← getNatVars) Nat.mkType
123
+
124
+ private def toRingContextExpr : GoalM Expr := do
125
+ if (← get').usedCommRing then
126
+ if let some ringId ← getIntRingId? then
127
+ return (← CommRing.RingM.run ringId do CommRing.toContextExpr)
128
+ RArray.toExpr Int.mkType id (RArray.leaf (mkIntLit 0))
129
+
130
+ private abbrev withProofContext (x : ProofM Expr) : GoalM Expr := do
131
+ withLetDecl `ctx (mkApp (mkConst ``RArray [levelZero]) Int.mkType) (← toContextExpr) fun ctx => do
132
+ withLetDecl `ctx' (mkApp (mkConst ``RArray [levelZero]) Int.mkType) (← toContextExpr') fun ctx' => do
133
+ withLetDecl `rctx (mkApp (mkConst ``RArray [levelZero]) Int.mkType) (← toRingContextExpr) fun ringCtx => do
134
+ withLetDecl `nctx (mkApp (mkConst ``RArray [levelZero]) Nat.mkType) (← toNatContextExpr) fun natCtx => do
135
+ go { ctx, ctx', ringCtx, natCtx } |>.run' {}
136
+ where
137
+ go : ProofM Expr := do
138
+ let h ← x
139
+ let h ← mkLetOfMap (← get).polyMap h `p (mkConst ``Int.Linear.Poly) toExpr
140
+ let h ← mkLetOfMap (← get).exprMap h `e (mkConst ``Int.Linear.Expr) toExpr
141
+ let h ← mkLetOfMap (← get).natExprMap h `a (mkConst ``Int.OfNat.Expr) toExpr
142
+ let h ← mkLetOfMap (← get).ringPolyMap h `rp (mkConst ``Grind.CommRing.Poly) toExpr
143
+ let h ← mkLetOfMap (← get).ringExprMap h `re (mkConst ``Grind.CommRing.Expr) toExpr
144
+ mkLetFVars #[(← getContext), (← read).ctx', (← read).ringCtx, (← getNatContext)] h
145
+
146
+ /--
147
+ Returns a Lean expression representing the auxiliary `CommRing` variable context needed for normalizing
148
+ nonlinear polynomials.
149
+ -/
150
+ private abbrev getRingContext : ProofM Expr := do
151
+ return (← read).ringCtx
152
+
153
+ private def DvdCnstr.get_d_a (c : DvdCnstr) : GoalM (Int × Int) := do
154
+ let d := c.d
155
+ let .add a _ _ := c.p | c.throwUnexpected
156
+ return (d, a)
157
+
158
+ mutual
159
+ partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := caching c' do
160
+ trace[grind.debug.cutsat.proof] "{← c'.pp}"
161
+ match c'.h with
162
+ | .core0 a zero =>
163
+ mkEqProof a zero
164
+ | .core a b p₁ p₂ =>
165
+ let h ← mkEqProof a b
166
+ return mkApp6 (mkConst ``Int.Linear.eq_of_core) (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (← mkPolyDecl c'.p) reflBoolTrue h
167
+ | .coreNat a b lhs rhs lhs' rhs' =>
168
+ let ctx ← getNatContext
169
+ let h := mkApp4 (mkConst ``Int.OfNat.of_eq) ctx (← mkNatExprDecl lhs) (← mkNatExprDecl rhs) (← mkEqProof a b)
170
+ return mkApp6 (mkConst ``Int.Linear.eq_norm_expr) (← getContext) (← mkExprDecl lhs') (← mkExprDecl rhs') (← mkPolyDecl c'.p) reflBoolTrue h
171
+ | .coreToInt a b toIntThm lhs rhs =>
172
+ let h := mkApp toIntThm (← mkEqProof a b)
173
+ return mkApp6 (mkConst ``Int.Linear.eq_norm_expr) (← getContext) (← mkExprDecl lhs) (← mkExprDecl rhs) (← mkPolyDecl c'.p) reflBoolTrue h
174
+ | .defn e p =>
175
+ let some x := (← getVarMap).find? { expr := e } | throwError "`grind` internal error, missing cutsat variable{indentExpr e}"
176
+ return mkApp6 (mkConst ``Int.Linear.eq_def) (← getContext) (toExpr x) (← mkPolyDecl p) (← mkPolyDecl c'.p) reflBoolTrue (← mkEqRefl e)
177
+ | .defnNat e x e' =>
178
+ let h := mkApp2 (mkConst ``Int.OfNat.Expr.eq_denoteAsInt) (← getNatContext) (← mkNatExprDecl e)
179
+ return mkApp6 (mkConst ``Int.Linear.eq_def') (← getContext) (toExpr x) (← mkExprDecl e') (← mkPolyDecl c'.p) reflBoolTrue h
180
+ | .norm c =>
181
+ return mkApp5 (mkConst ``Int.Linear.eq_norm) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) reflBoolTrue (← c.toExprProof)
182
+ | .divCoeffs c =>
183
+ let k := c.p.gcdCoeffs c.p.getConst
184
+ return mkApp6 (mkConst ``Int.Linear.eq_coeff) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) (toExpr k) reflBoolTrue (← c.toExprProof)
185
+ | .subst x c₁ c₂ =>
186
+ return mkApp8 (mkConst ``Int.Linear.eq_eq_subst)
187
+ (← getContext) (toExpr x) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p)
188
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
189
+ | .ofLeGe c₁ c₂ =>
190
+ return mkApp6 (mkConst ``Int.Linear.eq_of_le_ge)
191
+ (← getContext) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p)
192
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
193
+ | .reorder c => withUnordered <| c.toExprProof
194
+ | .commRingNorm c e p =>
195
+ let h := mkApp4 (mkConst ``Grind.CommRing.norm_int) (← getRingContext) (← mkRingExprDecl e) (← mkRingPolyDecl p) reflBoolTrue
196
+ return mkApp5 (mkConst ``Int.Linear.eq_norm_poly) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) h (← c.toExprProof)
197
+ | .defnCommRing e p re rp p' =>
198
+ let h := mkApp4 (mkConst ``Grind.CommRing.norm_int) (← getRingContext) (← mkRingExprDecl re) (← mkRingPolyDecl rp) reflBoolTrue
199
+ let some x := (← getVarMap).find? { expr := e } | throwError "`grind` internal error, missing cutsat variable{indentExpr e}"
200
+ return mkApp8 (mkConst ``Int.Linear.eq_def_norm) (← getContext)
201
+ (toExpr x) (← mkPolyDecl p) (← mkPolyDecl p') (← mkPolyDecl c'.p)
202
+ reflBoolTrue (← mkEqRefl e) h
203
+ | .defnNatCommRing e x e' p re rp p' =>
204
+ let h₁ := mkApp2 (mkConst ``Int.OfNat.Expr.eq_denoteAsInt) (← getNatContext) (← mkNatExprDecl e)
205
+ let h₂ := mkApp4 (mkConst ``Grind.CommRing.norm_int) (← getRingContext) (← mkRingExprDecl re) (← mkRingPolyDecl rp) reflBoolTrue
206
+ return mkApp9 (mkConst ``Int.Linear.eq_def'_norm) (← getContext) (toExpr x) (← mkExprDecl e')
207
+ (← mkPolyDecl p) (← mkPolyDecl p') (← mkPolyDecl c'.p) reflBoolTrue h₁ h₂
208
+
209
+ partial def DvdCnstr.toExprProof (c' : DvdCnstr) : ProofM Expr := caching c' do
210
+ trace[grind.debug.cutsat.proof] "{← c'.pp}"
211
+ match c'.h with
212
+ | .core e =>
213
+ mkOfEqTrue (← mkEqTrueProof e)
214
+ | .coreNat e d b b' =>
215
+ let ctx ← getNatContext
216
+ let h := mkApp4 (mkConst ``Int.OfNat.of_dvd) ctx (toExpr d) (← mkNatExprDecl b) (mkOfEqTrueCore e (← mkEqTrueProof e))
217
+ return mkApp6 (mkConst ``Int.Linear.dvd_norm_expr) (← getContext) (toExpr (Int.ofNat d)) (← mkExprDecl b') (← mkPolyDecl c'.p) reflBoolTrue h
218
+ | .norm c =>
219
+ return mkApp6 (mkConst ``Int.Linear.dvd_norm) (← getContext) (toExpr c.d) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) reflBoolTrue (← c.toExprProof)
220
+ | .elim c =>
221
+ return mkApp7 (mkConst ``Int.Linear.dvd_elim) (← getContext) (toExpr c.d) (← mkPolyDecl c.p) (toExpr c'.d) (← mkPolyDecl c'.p) reflBoolTrue (← c.toExprProof)
222
+ | .divCoeffs c =>
223
+ let g := c.p.gcdCoeffs c.d
224
+ let g := if c.d < 0 then -g else g
225
+ return mkApp8 (mkConst ``Int.Linear.dvd_coeff) (← getContext) (toExpr c.d) (← mkPolyDecl c.p) (toExpr c'.d) (← mkPolyDecl c'.p) (toExpr g) reflBoolTrue (← c.toExprProof)
226
+ | .solveCombine c₁ c₂ =>
227
+ let (d₁, a₁) ← c₁.get_d_a
228
+ let (d₂, a₂) ← c₂.get_d_a
229
+ let (g, α, β) := gcdExt (a₁*d₂) (a₂*d₁)
230
+ let r := mkApp10 (mkConst ``Int.Linear.dvd_solve_combine)
231
+ (← getContext) (toExpr c₁.d) (← mkPolyDecl c₁.p) (toExpr c₂.d) (← mkPolyDecl c₂.p) (toExpr c'.d) (← mkPolyDecl c'.p)
232
+ (toExpr g) (toExpr α) (toExpr β)
233
+ return mkApp3 r reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
234
+ | .solveElim c₁ c₂ =>
235
+ return mkApp10 (mkConst ``Int.Linear.dvd_solve_elim)
236
+ (← getContext) (toExpr c₁.d) (← mkPolyDecl c₁.p) (toExpr c₂.d) (← mkPolyDecl c₂.p) (toExpr c'.d) (← mkPolyDecl c'.p)
237
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
238
+ | .ofEq x c =>
239
+ return mkApp7 (mkConst ``Int.Linear.dvd_of_eq)
240
+ (← getContext) (toExpr x) (← mkPolyDecl c.p) (toExpr c'.d) (← mkPolyDecl c'.p)
241
+ reflBoolTrue (← c.toExprProof)
242
+ | .subst x c₁ c₂ =>
243
+ return mkApp10 (mkConst ``Int.Linear.eq_dvd_subst)
244
+ (← getContext) (toExpr x) (← mkPolyDecl c₁.p) (toExpr c₂.d) (← mkPolyDecl c₂.p) (toExpr c'.d) (← mkPolyDecl c'.p)
245
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
246
+ | .cooper₁ s =>
247
+ let { c₁, c₂, c₃?, left } := s.pred
248
+ let p₁ := c₁.p
249
+ let p₂ := c₂.p
250
+ match c₃? with
251
+ | none =>
252
+ let thmName := if left then ``Int.Linear.cooper_left_split_dvd else ``Int.Linear.cooper_right_split_dvd
253
+ return mkApp8 (mkConst thmName)
254
+ (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (toExpr s.k) (toExpr c'.d) (← mkPolyDecl c'.p) (← s.toExprProof) reflBoolTrue
255
+ | some c₃ =>
256
+ let thmName := if left then ``Int.Linear.cooper_dvd_left_split_dvd1 else ``Int.Linear.cooper_dvd_right_split_dvd1
257
+ return mkApp10 (mkConst thmName)
258
+ (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (← mkPolyDecl c₃.p) (toExpr c₃.d) (toExpr s.k) (toExpr c'.d) (← mkPolyDecl c'.p)
259
+ (← s.toExprProof) reflBoolTrue
260
+ | .cooper₂ s =>
261
+ let { c₁, c₂, c₃?, left } := s.pred
262
+ let p₁ := c₁.p
263
+ let p₂ := c₂.p
264
+ let some c₃ := c₃? | throwError "`grind` internal error, unexpected `cooper₂` proof"
265
+ let thmName := if left then ``Int.Linear.cooper_dvd_left_split_dvd2 else ``Int.Linear.cooper_dvd_right_split_dvd2
266
+ return mkApp10 (mkConst thmName)
267
+ (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (← mkPolyDecl c₃.p) (toExpr c₃.d) (toExpr s.k) (toExpr c'.d) (← mkPolyDecl c'.p)
268
+ (← s.toExprProof) reflBoolTrue
269
+ | .reorder c => withUnordered <| c.toExprProof
270
+ | .commRingNorm c e p =>
271
+ let h := mkApp4 (mkConst ``Grind.CommRing.norm_int) (← getRingContext) (← mkRingExprDecl e) (← mkRingPolyDecl p) reflBoolTrue
272
+ return mkApp6 (mkConst ``Int.Linear.dvd_norm_poly) (← getContext) (toExpr c.d) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) h (← c.toExprProof)
273
+
274
+ partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := caching c' do
275
+ trace[grind.debug.cutsat.proof] "{← c'.pp}"
276
+ match c'.h with
277
+ | .core e =>
278
+ mkOfEqTrue (← mkEqTrueProof e)
279
+ | .coreNeg e p =>
280
+ let h ← mkOfEqFalse (← mkEqFalseProof e)
281
+ return mkApp5 (mkConst ``Int.Linear.le_neg) (← getContext) (← mkPolyDecl p) (← mkPolyDecl c'.p) reflBoolTrue h
282
+ | .coreNat e lhs rhs lhs' rhs' =>
283
+ let ctx ← getNatContext
284
+ let h := mkApp4 (mkConst ``Int.OfNat.of_le) ctx (← mkNatExprDecl lhs) (← mkNatExprDecl rhs) (mkOfEqTrueCore e (← mkEqTrueProof e))
285
+ return mkApp6 (mkConst ``Int.Linear.le_norm_expr) (← getContext) (← mkExprDecl lhs') (← mkExprDecl rhs') (← mkPolyDecl c'.p) reflBoolTrue h
286
+ | .coreToInt e pos toIntThm lhs rhs =>
287
+ let h ← if pos then pure <| mkOfEqTrueCore e (← mkEqTrueProof e) else pure <| mkOfEqFalseCore e (← mkEqFalseProof e)
288
+ let h := mkApp toIntThm h
289
+ return mkApp6 (mkConst ``Int.Linear.le_norm_expr) (← getContext) (← mkExprDecl lhs) (← mkExprDecl rhs) (← mkPolyDecl c'.p) reflBoolTrue h
290
+ | .denoteAsIntNonneg rhs rhs' =>
291
+ let ctx ← getNatContext
292
+ let h := mkApp2 (mkConst ``Int.OfNat.Expr.denoteAsInt_nonneg) ctx (toExpr rhs)
293
+ return mkApp6 (mkConst ``Int.Linear.le_norm_expr) (← getContext) (← mkExprDecl (.num 0)) (← mkExprDecl rhs') (← mkPolyDecl c'.p) reflBoolTrue h
294
+ | .coreNatNeg e lhs rhs lhs' rhs' =>
295
+ let ctx ← getNatContext
296
+ let h := mkApp4 (mkConst ``Int.OfNat.of_not_le) ctx (← mkNatExprDecl lhs) (← mkNatExprDecl rhs) (mkOfEqFalseCore e (← mkEqFalseProof e))
297
+ return mkApp6 (mkConst ``Int.Linear.not_le_norm_expr) (← getContext) (← mkExprDecl lhs') (← mkExprDecl rhs') (← mkPolyDecl c'.p) reflBoolTrue h
298
+ | .bound h => return h
299
+ | .dec h =>
300
+ return mkFVar h
301
+ | .norm c =>
302
+ return mkApp5 (mkConst ``Int.Linear.le_norm) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) reflBoolTrue (← c.toExprProof)
303
+ | .divCoeffs c =>
304
+ let k := c.p.gcdCoeffs'
305
+ return mkApp6 (mkConst ``Int.Linear.le_coeff) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) (toExpr (Int.ofNat k)) reflBoolTrue (← c.toExprProof)
306
+ | .combine c₁ c₂ =>
307
+ return mkApp7 (mkConst ``Int.Linear.le_combine)
308
+ (← getContext) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p)
309
+ reflBoolTrue
310
+ (← c₁.toExprProof) (← c₂.toExprProof)
311
+ | .combineDivCoeffs c₁ c₂ k =>
312
+ return mkApp8 (mkConst ``Int.Linear.le_combine_coeff)
313
+ (← getContext) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p) (toExpr k)
314
+ reflBoolTrue
315
+ (← c₁.toExprProof) (← c₂.toExprProof)
316
+ | .subst x c₁ c₂ =>
317
+ let a := c₁.p.coeff x
318
+ let thm := if a ≥ 0 then
319
+ mkConst ``Int.Linear.eq_le_subst_nonneg
320
+ else
321
+ mkConst ``Int.Linear.eq_le_subst_nonpos
322
+ return mkApp8 thm
323
+ (← getContext) (toExpr x) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p)
324
+ reflBoolTrue
325
+ (← c₁.toExprProof) (← c₂.toExprProof)
326
+ | .ofLeDiseq c₁ c₂ =>
327
+ return mkApp7 (mkConst ``Int.Linear.le_of_le_diseq)
328
+ (← getContext) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p)
329
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
330
+ | .dvdTight c₁ c₂ =>
331
+ return mkApp8 (mkConst ``Int.Linear.dvd_le_tight)
332
+ (← getContext) (toExpr c₁.d) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p)
333
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
334
+ | .negDvdTight c₁ c₂ =>
335
+ return mkApp8 (mkConst ``Int.Linear.dvd_neg_le_tight)
336
+ (← getContext) (toExpr c₁.d) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p)
337
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
338
+ | .ofDiseqSplit c₁ fvarId h _ =>
339
+ let p₂ := c₁.p.addConst 1
340
+ let hFalse ← h.toExprProofCore
341
+ let hNot := mkLambda `h .default (mkIntLE (← p₂.denoteExpr') (mkIntLit 0)) (hFalse.abstract #[mkFVar fvarId])
342
+ return mkApp7 (mkConst ``Int.Linear.diseq_split_resolve)
343
+ (← getContext) (← mkPolyDecl c₁.p) (← mkPolyDecl p₂) (← mkPolyDecl c'.p) reflBoolTrue (← c₁.toExprProof) hNot
344
+ | .cooper s =>
345
+ let { c₁, c₂, c₃?, left } := s.pred
346
+ let p₁ := c₁.p
347
+ let p₂ := c₂.p
348
+ let coeff := if left then p₂.leadCoeff else p₁.leadCoeff
349
+ match c₃? with
350
+ | none =>
351
+ let thmName := if left then ``Int.Linear.cooper_left_split_ineq else ``Int.Linear.cooper_right_split_ineq
352
+ return mkApp8 (mkConst thmName)
353
+ (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (toExpr s.k) (toExpr coeff) (← mkPolyDecl c'.p) (← s.toExprProof) reflBoolTrue
354
+ | some c₃ =>
355
+ let thmName := if left then ``Int.Linear.cooper_dvd_left_split_ineq else ``Int.Linear.cooper_dvd_right_split_ineq
356
+ return mkApp10 (mkConst thmName)
357
+ (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (← mkPolyDecl c₃.p) (toExpr c₃.d) (toExpr s.k) (toExpr coeff) (← mkPolyDecl c'.p) (← s.toExprProof) reflBoolTrue
358
+ | .reorder c => withUnordered <| c.toExprProof
359
+ | .commRingNorm c e p =>
360
+ let h := mkApp4 (mkConst ``Grind.CommRing.norm_int) (← getRingContext) (← mkRingExprDecl e) (← mkRingPolyDecl p) reflBoolTrue
361
+ return mkApp5 (mkConst ``Int.Linear.le_norm_poly) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) h (← c.toExprProof)
362
+
363
+ partial def DiseqCnstr.toExprProof (c' : DiseqCnstr) : ProofM Expr := caching c' do
364
+ match c'.h with
365
+ | .core0 a zero =>
366
+ mkDiseqProof a zero
367
+ | .core a b p₁ p₂ =>
368
+ let h ← mkDiseqProof a b
369
+ return mkApp6 (mkConst ``Int.Linear.diseq_of_core) (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (← mkPolyDecl c'.p) reflBoolTrue h
370
+ | .coreNat a b lhs rhs lhs' rhs' =>
371
+ let ctx ← getNatContext
372
+ let h := mkApp4 (mkConst ``Int.OfNat.of_not_eq) ctx (← mkNatExprDecl lhs) (← mkNatExprDecl rhs) (← mkDiseqProof a b)
373
+ return mkApp6 (mkConst ``Int.Linear.not_eq_norm_expr) (← getContext) (← mkExprDecl lhs') (← mkExprDecl rhs') (← mkPolyDecl c'.p) reflBoolTrue h
374
+ | .coreToInt a b toIntThm lhs rhs =>
375
+ let h := mkApp toIntThm (← mkDiseqProof a b)
376
+ return mkApp6 (mkConst ``Int.Linear.not_eq_norm_expr) (← getContext) (← mkExprDecl lhs) (← mkExprDecl rhs) (← mkPolyDecl c'.p) reflBoolTrue h
377
+ | .norm c =>
378
+ return mkApp5 (mkConst ``Int.Linear.diseq_norm) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) reflBoolTrue (← c.toExprProof)
379
+ | .divCoeffs c =>
380
+ let k := c.p.gcdCoeffs c.p.getConst
381
+ return mkApp6 (mkConst ``Int.Linear.diseq_coeff) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) (toExpr k) reflBoolTrue (← c.toExprProof)
382
+ | .neg c =>
383
+ return mkApp5 (mkConst ``Int.Linear.diseq_neg) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) reflBoolTrue (← c.toExprProof)
384
+ | .subst x c₁ c₂ =>
385
+ return mkApp8 (mkConst ``Int.Linear.eq_diseq_subst)
386
+ (← getContext) (toExpr x) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p)
387
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
388
+ | .reorder c => withUnordered <| c.toExprProof
389
+ | .commRingNorm c e p =>
390
+ let h := mkApp4 (mkConst ``Grind.CommRing.norm_int) (← getRingContext) (← mkRingExprDecl e) (← mkRingPolyDecl p) reflBoolTrue
391
+ return mkApp5 (mkConst ``Int.Linear.diseq_norm_poly) (← getContext) (← mkPolyDecl c.p) (← mkPolyDecl c'.p) h (← c.toExprProof)
392
+
393
+ partial def CooperSplit.toExprProof (s : CooperSplit) : ProofM Expr := caching s do
394
+ match s.h with
395
+ | .dec h => return mkFVar h
396
+ | .last hs _ =>
397
+ let { c₁, c₂, c₃?, left } := s.pred
398
+ let p₁ := c₁.p
399
+ let p₂ := c₂.p
400
+ let n := s.pred.numCases
401
+ unless hs.size + 1 == n do
402
+ throwError "`grind` internal error, unexpected number of cases at `CopperSplit` hs.size: {hs.size}, n: {n}, left: {left}\nc₁: {← c₁.pp}\nc₂: {← c₂.pp}\nc₃: {← if let some c₃ := c₃? then c₃.pp else pure ""}"
403
+ let (base, pred) ← match c₃? with
404
+ | none =>
405
+ let thmName := if left then ``Int.Linear.cooper_left else ``Int.Linear.cooper_right
406
+ let predName := if left then ``Int.Linear.cooper_left_split else ``Int.Linear.cooper_right_split
407
+ let base := mkApp7 (mkConst thmName) (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (toExpr n)
408
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
409
+ let pred := mkApp3 (mkConst predName) (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂)
410
+ pure (base, pred)
411
+ | some c₃ =>
412
+ let thmName := if left then ``Int.Linear.cooper_dvd_left else ``Int.Linear.cooper_dvd_right
413
+ let predName := if left then ``Int.Linear.cooper_dvd_left_split else ``Int.Linear.cooper_dvd_right_split
414
+ let base := mkApp10 (mkConst thmName) (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (← mkPolyDecl c₃.p) (toExpr c₃.d) (toExpr n)
415
+ reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof) (← c₃.toExprProof)
416
+ let pred := mkApp5 (mkConst predName) (← getContext) (← mkPolyDecl p₁) (← mkPolyDecl p₂) (← mkPolyDecl c₃.p) (toExpr c₃.d)
417
+ pure (base, pred)
418
+ -- `pred` is an expressions of the form `cooper_*_split ...` with type `Nat → Prop`
419
+ let mut k := n
420
+ let mut result := base -- `OrOver k (cooper_*_splti)
421
+ result := mkApp3 (mkConst ``Int.Linear.orOver_cases) (toExpr (n-1)) pred result
422
+ for (fvarId, c) in hs do
423
+ let type := mkApp pred (toExpr (k-1))
424
+ let h ← c.toExprProofCore -- proof of `False`
425
+ -- `hNotCase` is a proof for `¬ pred (k-1)`
426
+ let hNotCase := mkLambda `h .default type (h.abstract #[mkFVar fvarId])
427
+ result := mkApp result hNotCase
428
+ k := k - 1
429
+ -- `result` is now a proof of `p 0`
430
+ return result
431
+
432
+ partial def UnsatProof.toExprProofCore (h : UnsatProof) : ProofM Expr := do
433
+ match h with
434
+ | .le c =>
435
+ return mkApp4 (mkConst ``Int.Linear.le_unsat) (← getContext) (← mkPolyDecl c.p) reflBoolTrue (← c.toExprProof)
436
+ | .dvd c =>
437
+ return mkApp5 (mkConst ``Int.Linear.dvd_unsat) (← getContext) (toExpr c.d) (← mkPolyDecl c.p) reflBoolTrue (← c.toExprProof)
438
+ | .eq c =>
439
+ if c.p.isUnsatEq then
440
+ return mkApp4 (mkConst ``Int.Linear.eq_unsat) (← getContext) (← mkPolyDecl c.p) reflBoolTrue (← c.toExprProof)
441
+ else
442
+ let k := c.p.gcdCoeffs'
443
+ return mkApp5 (mkConst ``Int.Linear.eq_unsat_coeff) (← getContext) (← mkPolyDecl c.p) (toExpr (Int.ofNat k)) reflBoolTrue (← c.toExprProof)
444
+ | .diseq c =>
445
+ return mkApp4 (mkConst ``Int.Linear.diseq_unsat) (← getContext) (← mkPolyDecl c.p) reflBoolTrue (← c.toExprProof)
446
+ | .cooper c₁ c₂ c₃ =>
447
+ let .add c _ _ := c₃.p | c₃.throwUnexpected
448
+ let d := c₃.d
449
+ let (_, α, β) := gcdExt c d
450
+ let h := mkApp7 (mkConst ``Int.Linear.cooper_unsat) (← getContext) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c₃.p) (toExpr c₃.d) (toExpr α) (toExpr β)
451
+ return mkApp4 h reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof) (← c₃.toExprProof)
452
+
453
+ end
454
+
455
+ def UnsatProof.toExprProof (h : UnsatProof) : GoalM Expr := do
456
+ withProofContext do h.toExprProofCore
457
+
458
+ def setInconsistent (h : UnsatProof) : GoalM Unit := do
459
+ if (← get').caseSplits then
460
+ -- Let the search procedure in `SearchM` resolve the conflict.
461
+ modify' fun s => { s with conflict? := some h }
462
+ else
463
+ let h ← h.toExprProof
464
+ closeGoal h
465
+
466
+ /-!
467
+ A cutsat proof may depend on decision variables.
468
+ We collect them and perform non chronological backtracking.
469
+ -/
470
+
471
+ open CollectDecVars
472
+ mutual
473
+ partial def EqCnstr.collectDecVars (c' : EqCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c') do
474
+ match c'.h with
475
+ | .core0 .. | .core .. | .coreNat .. | .defn .. | .defnNat ..
476
+ | .defnCommRing .. | .defnNatCommRing .. | .coreToInt .. => return () -- Equalities coming from the core never contain cutsat decision variables
477
+ | .commRingNorm c .. | .reorder c | .norm c | .divCoeffs c => c.collectDecVars
478
+ | .subst _ c₁ c₂ | .ofLeGe c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
479
+
480
+ partial def CooperSplit.collectDecVars (s : CooperSplit) : CollectDecVarsM Unit := do unless (← alreadyVisited s) do
481
+ s.pred.c₁.collectDecVars
482
+ s.pred.c₂.collectDecVars
483
+ if let some c₃ := s.pred.c₃? then
484
+ c₃.collectDecVars
485
+ match s.h with
486
+ | .dec h => markAsFound h
487
+ | .last (decVars := decVars) .. => decVars.forM markAsFound
488
+
489
+ partial def DvdCnstr.collectDecVars (c' : DvdCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c') do
490
+ match c'.h with
491
+ | .core _ | .coreNat .. => return ()
492
+ | .cooper₁ c | .cooper₂ c
493
+ | .commRingNorm c .. | .reorder c | .norm c | .elim c | .divCoeffs c | .ofEq _ c => c.collectDecVars
494
+ | .solveCombine c₁ c₂ | .solveElim c₁ c₂ | .subst _ c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
495
+
496
+ partial def LeCnstr.collectDecVars (c' : LeCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c') do
497
+ match c'.h with
498
+ | .core .. | .coreNeg .. | .coreNat .. | .coreNatNeg .. | .coreToInt .. | .denoteAsIntNonneg .. | .bound .. => return ()
499
+ | .dec h => markAsFound h
500
+ | .commRingNorm c .. | .reorder c | .cooper c
501
+ | .norm c | .divCoeffs c => c.collectDecVars
502
+ | .dvdTight c₁ c₂ | .negDvdTight c₁ c₂
503
+ | .combine c₁ c₂ | .combineDivCoeffs c₁ c₂ _
504
+ | .subst _ c₁ c₂ | .ofLeDiseq c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
505
+ | .ofDiseqSplit (decVars := decVars) .. => decVars.forM markAsFound
506
+
507
+ partial def DiseqCnstr.collectDecVars (c' : DiseqCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c') do
508
+ match c'.h with
509
+ | .core0 .. | .core .. | .coreNat .. | .coreToInt .. => return () -- Disequalities coming from the core never contain cutsat decision variables
510
+ | .commRingNorm c .. | .reorder c | .norm c | .divCoeffs c | .neg c => c.collectDecVars
511
+ | .subst _ c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
512
+
513
+ end
514
+
515
+ def UnsatProof.collectDecVars (h : UnsatProof) : CollectDecVarsM Unit := do
516
+ match h with
517
+ | .le c | .dvd c | .eq c | .diseq c => c.collectDecVars
518
+ | .cooper c₁ c₂ c₃ => c₁.collectDecVars; c₂.collectDecVars; c₃.collectDecVars
519
+
520
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/ReorderVars.lean ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
8
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Inv
9
+
10
+ namespace Lean.Meta.Grind.Arith.Cutsat
11
+
12
+ /-! Collect variable information -/
13
+
14
+ structure VarInfo where
15
+ maxLowerCoeff : Nat := 0
16
+ maxUpperCoeff : Nat := 0
17
+ maxDvdCoeff : Nat := 0
18
+ deriving Inhabited
19
+
20
+ private abbrev CollectM := StateT (Array VarInfo) GoalM
21
+
22
+ private def updateLower (a : Nat) (x : Var) : CollectM Unit := do
23
+ modify fun infos => infos.modify x fun info => { info with maxLowerCoeff := max a info.maxLowerCoeff }
24
+
25
+ private def updateUpper (a : Nat) (x : Var) : CollectM Unit := do
26
+ modify fun infos => infos.modify x fun info => { info with maxUpperCoeff := max a info.maxUpperCoeff }
27
+
28
+ private def updateVarCoeff (a : Int) (x : Var) : CollectM Unit := do
29
+ if a < 0 then updateLower a.natAbs x else updateUpper a.natAbs x
30
+
31
+ private def updateDvd (a : Nat) (x : Var) : CollectM Unit := do
32
+ modify fun infos => infos.modify x fun info => { info with maxDvdCoeff := max a info.maxDvdCoeff }
33
+
34
+ private def collectVarInfo : GoalM (Array VarInfo) := do
35
+ let (_, info) ← go |>.run (Array.replicate (← get').vars.size {})
36
+ return info
37
+ where
38
+ go : CollectM Unit := do
39
+ let s ← get'
40
+ for x in [: s.vars.size] do
41
+ unless (← eliminated x) do
42
+ for c in s.lowers[x]! do
43
+ visitPoly c.p
44
+ for c in s.uppers[x]! do
45
+ visitPoly c.p
46
+ if let some c := s.dvds[x]! then
47
+ updateDvd c.d.natAbs x
48
+
49
+ visitPoly : Poly → CollectM Unit
50
+ | .num .. => return ()
51
+ | .add a x p => do updateVarCoeff a x; visitPoly p
52
+
53
+ /-!
54
+ We order variables in decreasing order of "cost".
55
+ We use the lexicographical order of two different costs.
56
+ The first one is the `max (min lowerCoeff upperCoeff) dvdCoeff`.
57
+ Recall that we use cooper-left if the coefficient of the lower bound is smaller, and cooper-right otherwise.
58
+ This is way we use the `min lowerCoeff upperCoeff`. The coefficient of the divisibility constraint also
59
+ impacts the size of the search space.
60
+ Then, we break ties using the max of all of them, and then the variable original order.
61
+ -/
62
+ def cost₁ (info : VarInfo) : Nat :=
63
+ max info.maxDvdCoeff (min info.maxLowerCoeff info.maxUpperCoeff)
64
+
65
+ private def cmp₁ (infos : Array VarInfo) (x y : Var) : Ordering :=
66
+ compare (cost₁ infos[x]!) (cost₁ infos[y]!) |>.swap
67
+
68
+ def cost₂ (info : VarInfo) : Nat :=
69
+ max info.maxDvdCoeff (max info.maxLowerCoeff info.maxUpperCoeff)
70
+
71
+ private def cmp₂ (infos : Array VarInfo) (x y : Var) : Ordering :=
72
+ compare (cost₂ infos[x]!) (cost₂ infos[y]!) |>.swap
73
+
74
+ private def cmp (infos : Array VarInfo) (x y : Var) : Ordering :=
75
+ cmp₁ infos x y |>.then (cmp₂ infos x y) |>.then (compare x y)
76
+
77
+ private def sortVars : GoalM (Array Var) := do
78
+ let infos ← collectVarInfo
79
+ let result := Array.range (← get').vars.size
80
+ let result := result.qsort (fun x y => cmp infos x y == .lt)
81
+ return result
82
+
83
+ private def mkPermInv (perm : Array Var) : Array Var := Id.run do
84
+ let mut inv := Array.replicate perm.size 0
85
+ for h : i in [: perm.size] do
86
+ inv := inv.set! perm[i] i
87
+ return inv
88
+
89
+ def _root_.Int.Linear.Poly.reorder (p : Poly) (old2new : Array Var) : Poly :=
90
+ match p with
91
+ | .num k => .num k
92
+ | .add a x p => .add a old2new[x]! (p.reorder old2new)
93
+
94
+ def DvdCnstr.reorder (c : DvdCnstr) (old2new : Array Var) : DvdCnstr :=
95
+ { c with p := c.p.reorder old2new, h := .reorder c : DvdCnstr }.norm
96
+
97
+ def EqCnstr.reorder (c : EqCnstr) (old2new : Array Var) : EqCnstr :=
98
+ { p := c.p.reorder old2new, h := .reorder c : EqCnstr }.norm
99
+
100
+ def LeCnstr.reorder (c : LeCnstr) (old2new : Array Var) : LeCnstr :=
101
+ { p := c.p.reorder old2new, h := .reorder c : LeCnstr }.norm
102
+
103
+ def DiseqCnstr.reorder (c : DiseqCnstr) (old2new : Array Var) : DiseqCnstr :=
104
+ { p := c.p.reorder old2new, h := .reorder c : DiseqCnstr }.norm
105
+
106
+ def reorderVarMap [Inhabited α] (m : PArray α) (new2old : Array Var) : PArray α := Id.run do
107
+ let mut r := {}
108
+ for h : i in [:new2old.size] do
109
+ let j : Nat := new2old[i]
110
+ r := r.push (m.get! j)
111
+ return r
112
+
113
+ def reorderDiseqSplits (m : PHashMap Poly FVarId) (old2new : Array Var) : PHashMap Poly FVarId := Id.run do
114
+ let mut m' := {}
115
+ for (p, h) in m do
116
+ m' := m'.insert (p.reorder old2new) h
117
+ return m'
118
+
119
+ def reorderVars : GoalM Unit := do
120
+ let s ← get'
121
+ if s.vars.isEmpty then return () -- nothing to reorder
122
+ /-
123
+ We currently reorder variables at most once.
124
+ It is feasible to implement dynamic variable reordering, but we would have to
125
+ store a trail of vars and varMaps.
126
+
127
+ The other option is change our representation and relax the assumption our polynomials are
128
+ sorted. It is unclear how it will impact performance.
129
+ -/
130
+ unless s.vars'.isEmpty do return ()
131
+ checkInvariants
132
+ let new2old ← sortVars
133
+ let old2new := mkPermInv new2old
134
+ -- We save the constraints to reinsert them later.
135
+ let dvds := s.dvds.foldl (init := #[]) fun
136
+ | dvds, none => dvds
137
+ | dvds, some c => dvds.push c
138
+ let dvds := dvds.map (·.reorder old2new)
139
+ let ineqs := s.lowers.foldl (init := #[]) (·.append ·.toArray)
140
+ let ineqs := s.uppers.foldl (init := ineqs) (·.append ·.toArray)
141
+ let ineqs := ineqs.map (·.reorder old2new)
142
+ let diseqs := s.diseqs.foldl (init := #[]) (·.append ·.toArray)
143
+ let diseqs := diseqs.map (·.reorder old2new)
144
+ modify' fun s => { s with
145
+ vars := reorderVarMap s.vars new2old
146
+ varMap := s.varMap.map fun x => old2new[x]!
147
+ vars' := s.vars
148
+ varMap' := s.varMap
149
+ natDef := s.natDef.map fun x => old2new[x]!
150
+ dvds := s.dvds.map fun _ => none
151
+ lowers := s.lowers.map fun _ => {}
152
+ uppers := s.uppers.map fun _ => {}
153
+ diseqs := s.diseqs.map fun _ => {}
154
+ elimEqs := reorderVarMap s.elimEqs new2old |>.map fun c? => (·.reorder old2new) <$> c?
155
+ elimStack := s.elimStack.map fun x => old2new[x]!
156
+ occurs := s.occurs.map fun _ => {}
157
+ diseqSplits := {}
158
+ }
159
+ for c in dvds do c.assert
160
+ for c in ineqs do c.assert
161
+ for c in diseqs do c.assert
162
+ trace[grind.debug.cutsat.search.reorder] "new2old: {new2old}"
163
+ trace[grind.debug.cutsat.search.reorder] "old2new: {old2new}"
164
+ checkInvariants
165
+
166
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Search.lean ADDED
@@ -0,0 +1,588 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
8
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
9
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.DvdCnstr
10
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.LeCnstr
11
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
12
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.SearchM
13
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
14
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.ReorderVars
15
+
16
+ namespace Lean.Meta.Grind.Arith.Cutsat
17
+
18
+ /-- Asserts constraints implied by a `CooperSplit`. -/
19
+ def CooperSplit.assert (cs : CooperSplit) : GoalM Unit := do
20
+ let { c₁, c₂, c₃?, left, .. } := cs.pred
21
+ let k := cs.k
22
+ let p₁ := c₁.p
23
+ let p₂ := c₂.p
24
+ let p := p₁.tail
25
+ let q := p₂.tail
26
+ let a := p₁.leadCoeff
27
+ let b := p₂.leadCoeff
28
+ let p₁' := p.mul b |>.combine (q.mul (-a))
29
+ let p₁' := p₁'.addConst <| if left then b*k else (-a)*k
30
+ let c₁' := { p := p₁', h := .cooper cs : LeCnstr }
31
+ c₁'.assert
32
+ if (← inconsistent) then return ()
33
+ let d := if left then a else b
34
+ if d.natAbs != 1 then
35
+ let p₂' := if left then p else q
36
+ let p₂' := p₂'.addConst k
37
+ let c₂' := { d, p := p₂', h := .cooper₁ cs : DvdCnstr }
38
+ c₂'.assert
39
+ if (← inconsistent) then return ()
40
+ let some c₃ := c₃? | return ()
41
+ let p₃ := c₃.p
42
+ let d := c₃.d
43
+ let s := p₃.tail
44
+ let c := p₃.leadCoeff
45
+ let c₃' := if left then
46
+ let p₃' := p.mul c |>.combine (s.mul (-a))
47
+ let p₃' := p₃'.addConst (c*k)
48
+ { d := a*d, p := p₃', h := .cooper₂ cs : DvdCnstr }
49
+ else
50
+ let p₃' := q.mul (-c) |>.combine (s.mul b)
51
+ let p₃' := p₃'.addConst (-c*k)
52
+ { d := b*d, p := p₃', h := .cooper₂ cs : DvdCnstr }
53
+ c₃'.assert
54
+
55
+ private def checkIsNextVar (x : Var) : GoalM Unit := do
56
+ if x != (← get').assignment.size then
57
+ throwError "`grind` internal error, assigning variable out of order"
58
+
59
+ private def traceAssignment (x : Var) (v : Rat) : GoalM Unit := do
60
+ trace[grind.debug.cutsat.search.assign] "{quoteIfArithTerm (← getVar x)} := {v}"
61
+
62
+ private def setAssignment (x : Var) (v : Rat) : GoalM Unit := do
63
+ checkIsNextVar x
64
+ traceAssignment x v
65
+ modify' fun s => { s with assignment := s.assignment.push v }
66
+
67
+ private def skipAssignment (x : Var) : GoalM Unit := do
68
+ checkIsNextVar x
69
+ modify' fun s => { s with assignment := s.assignment.push 0 }
70
+
71
+ /-- Assign eliminated variables using `elimEqs` field. -/
72
+ private def assignElimVars : GoalM Unit := do
73
+ if (← inconsistent) then return ()
74
+ go (← get').elimStack
75
+ where
76
+ go (xs : List Var) : GoalM Unit := do
77
+ match xs with
78
+ | [] => return ()
79
+ | x :: xs =>
80
+ let some c := (← get').elimEqs[x]!
81
+ | throwError "`grind` internal error, eliminated variable must have equation associated with it"
82
+ -- `x` may not be the max variable
83
+ let a := c.p.coeff x
84
+ if a == 0 then c.throwUnexpected
85
+ -- ensure `x` is 0 when evaluating `c.p`
86
+ modify' fun s => { s with assignment := s.assignment.set x 0 }
87
+ let some v ← c.p.eval? | c.throwUnexpected
88
+ let v := (-v) / a
89
+ traceAssignment x v
90
+ modify' fun s => { s with assignment := s.assignment.set x v }
91
+ go xs
92
+
93
+ private def eqCoeffs (p₁ p₂ : Poly) (neg : Bool) :=
94
+ match p₁, p₂ with
95
+ | .num _, .num _ => true
96
+ | .add k₁ x₁ p₁, .add k₂ x₂ p₂ =>
97
+ (if neg then k₁ == -k₂ else k₁ == k₂) && x₁ == x₂ && eqCoeffs p₁ p₂ neg
98
+ | _, _ => false
99
+
100
+ def tightUsingDvd (c : LeCnstr) (dvd? : Option DvdCnstr) : GoalM LeCnstr := do
101
+ let some dvd := dvd? | return c
102
+ let d := dvd.d
103
+ let b₁ := dvd.p.getConst
104
+ if eqCoeffs dvd.p c.p false then
105
+ let b₂ := c.p.getConst
106
+ if (b₂ - b₁) % d != 0 then
107
+ let b₂' := b₁ - d * ((b₁ - b₂) / d)
108
+ let p := c.p.addConst (b₂'-b₂)
109
+ return { p, h := .dvdTight dvd c }
110
+ if eqCoeffs dvd.p c.p true then
111
+ let b₁ := -b₁
112
+ let b₂ := c.p.getConst
113
+ if (b₂ - b₁) % d != 0 then
114
+ let b₂' := b₁ - d * ((b₁ - b₂) / d)
115
+ let p := c.p.addConst (b₂'-b₂)
116
+ return { p, h := .negDvdTight dvd c }
117
+ return c
118
+
119
+ /--
120
+ Assuming all variables smaller than `x` have already been assigned,
121
+ returns the best lower bound for `x` using the given partial assignment and
122
+ inequality constraints where `x` is the maximal variable.
123
+ -/
124
+ def getBestLower? (x : Var) (dvd? : Option DvdCnstr) : GoalM (Option (Rat × LeCnstr)) := do
125
+ let s ← get'
126
+ let mut best? := none
127
+ for c in s.lowers[x]! do
128
+ let c ← tightUsingDvd c dvd?
129
+ let .add k _ p := c.p | c.throwUnexpected
130
+ let some v ← p.eval? | c.throwUnexpected
131
+ let lower' := v / (-k)
132
+ if let some (lower, _) := best? then
133
+ if lower' > lower then
134
+ best? := some (lower', c)
135
+ else
136
+ best? := some (lower', c)
137
+ return best?
138
+
139
+ /--
140
+ Assuming all variables smaller than `x` have already been assigned,
141
+ returns the best upper bound for `x` using the given partial assignment and
142
+ inequality constraints where `x` is the maximal variable.
143
+ -/
144
+ def getBestUpper? (x : Var) (dvd? : Option DvdCnstr) : GoalM (Option (Rat × LeCnstr)) := do
145
+ let s ← get'
146
+ let mut best? := none
147
+ for c in s.uppers[x]! do
148
+ let c ← tightUsingDvd c dvd?
149
+ let .add k _ p := c.p | c.throwUnexpected
150
+ let some v ← p.eval? | c.throwUnexpected
151
+ let upper' := (-v) / k
152
+ if let some (upper, _) := best? then
153
+ if upper' < upper then
154
+ best? := some (upper', c)
155
+ else
156
+ best? := some (upper', c)
157
+ return best?
158
+
159
+ /-- Returns values we cannot assign `x` because of disequality constraints. -/
160
+ def getDiseqValues (x : Var) : SearchM (Array (Rat × DiseqCnstr)) := do
161
+ let s ← get'
162
+ let mut r := #[]
163
+ for c in s.diseqs[x]! do
164
+ let .add k _ p := c.p | c.throwUnexpected
165
+ let some v ← p.eval? | c.throwUnexpected
166
+ if (← isApprox) then
167
+ r := r.push (((-v)/k), c)
168
+ else
169
+ -- We are building an integer model,
170
+ -- if `k` does not divide `v`, we can just ignore the disequality.
171
+ let v := v.num
172
+ if v % k == 0 then
173
+ r := r.push ((-v)/k, c)
174
+ return r
175
+
176
+ /--
177
+ Solution space for a divisibility constraint of the form `d ∣ a*x + b`
178
+ See `DvdCnstr.getSolutions?` to understand how it is computed.
179
+ -/
180
+ structure DvdSolution where
181
+ d : Int := 1
182
+ b : Int := 0
183
+
184
+ def DvdCnstr.getSolutions? (c : DvdCnstr) : SearchM (Option DvdSolution) := do
185
+ let d := c.d
186
+ let .add a _ p := c.p | c.throwUnexpected
187
+ let some b ← p.eval? | c.throwUnexpected
188
+ if b.den != 1 then
189
+ -- `b` is a rational number, mark model as imprecise, and ignore the constraint
190
+ setImprecise
191
+ return none
192
+ let b := b.num
193
+ -- We must solve `d ∣ a*x + b`
194
+ let g := d.gcd a
195
+ if b % g != 0 then
196
+ return none -- no solutions
197
+ let d := d / g
198
+ let a := a / g
199
+ let b := b / g
200
+ -- `α*a + β*d = 1`
201
+ -- `α*a = 1 (mod d)`
202
+ let (_, α, _β) := gcdExt a d
203
+ -- `a'*a = 1 (mod d)`
204
+ let a' := if α < 0 then α % d else α
205
+ -- `a*x = -b (mod d)`
206
+ -- `x = -b*a' (mod d)`
207
+ -- `x = k*d + -b*a'` for any k
208
+ return some { d, b := -b*a' }
209
+
210
+ def resolveDvdConflict (c : DvdCnstr) : GoalM Unit := do
211
+ trace[grind.debug.cutsat.search.conflict] "{← c.pp}"
212
+ let d := c.d
213
+ let .add a _ p := c.p | c.throwUnexpected
214
+ { d := a.gcd d, p, h := .elim c : DvdCnstr }.assert
215
+
216
+ /--
217
+ Given a divisibility constraint solution space `s := { b, d }`,
218
+ and a candidate assignment `v`, we want to find
219
+ an assignment `w` such that `w ≥ v` such that exists `k`, `w = k*d + b`
220
+ Thus,
221
+ - `k*d + b ≥ v`
222
+ - `k ≥ cdiv (v - b) d`
223
+ So, we take `w = (cdiv (v - b) d)*d + b`
224
+ -/
225
+ def DvdSolution.ge (s : DvdSolution) (v : Int) : Int :=
226
+ (Int.Linear.cdiv (v - s.b) s.d)*s.d + s.b
227
+
228
+ /--
229
+ Given a divisibility constraint solution space `s := { b, d }`,
230
+ and a candidate assignment `v`, we want to find
231
+ an assignment `w` such that `w ≤ v` such that exists `k`, `w = k*d + b`
232
+ Thus,
233
+ - `k*d + b ≤ v`
234
+ - `k ≤ (v - b) / d`
235
+ So, we take `w = ((v - b) / d)*d + b`
236
+ -/
237
+ def DvdSolution.le (s : DvdSolution) (v : Int) : Int :=
238
+ ((v - s.b)/s.d)*s.d + s.b
239
+
240
+ def findDiseq? (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Option DiseqCnstr :=
241
+ (·.2) <$> dvals.find? fun (d, _) =>
242
+ d.den == 1 && d.num == v
243
+
244
+ def inDiseqValues (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Bool :=
245
+ Option.isSome <| findDiseq? v dvals
246
+
247
+ def findRatDiseq? (v : Rat) (dvals : Array (Rat × DiseqCnstr)) : Option DiseqCnstr :=
248
+ (·.2) <$> dvals.find? fun (d, _) => v == d
249
+
250
+ partial def DvdSolution.geAvoiding (s : DvdSolution) (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Int :=
251
+ let v := s.ge v
252
+ if inDiseqValues v dvals then
253
+ geAvoiding s (v+1) dvals
254
+ else
255
+ v
256
+
257
+ partial def DvdSolution.leAvoiding (s : DvdSolution) (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Int :=
258
+ let v := s.le v
259
+ if inDiseqValues v dvals then
260
+ leAvoiding s (v-1) dvals
261
+ else
262
+ v
263
+
264
+ inductive FindIntValResult where
265
+ | found (val : Int)
266
+ | diseq (c : DiseqCnstr)
267
+ | dvd
268
+ deriving Inhabited
269
+
270
+ /--
271
+ Tries to find an integer `v` s.t. `lower ≤ v ≤ upper`, `v ∉ dvals`, and `v ∈ s`.
272
+ Returns `.found v` if result was found, `.dvd` if it failed because of the divisibility constraint,
273
+ and `.diseq c` because of the disequality constraint `c`.
274
+ -/
275
+ partial def findIntVal (s : DvdSolution) (lower : Int) (upper : Int) (dvals : Array (Rat × DiseqCnstr)) : FindIntValResult :=
276
+ let v := s.ge lower
277
+ if v > upper then
278
+ .dvd
279
+ else
280
+ go v
281
+ where
282
+ go (v : Int) : FindIntValResult :=
283
+ if let some c := findDiseq? v dvals then
284
+ let v := s.ge (v+1)
285
+ if v > upper then .diseq c else go v
286
+ else
287
+ .found v
288
+
289
+ partial def findRatVal (lower upper : Rat) (diseqVals : Array (Rat × DiseqCnstr)) : Rat :=
290
+ let v := (lower + upper)/2
291
+ if (findRatDiseq? v diseqVals).isSome then
292
+ findRatVal lower v diseqVals
293
+ else
294
+ v
295
+
296
+ def resolveRealLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Bool := do
297
+ trace[grind.debug.cutsat.search.conflict] "{← c₁.pp}, {← c₂.pp}"
298
+ let .add a₁ _ p₁ := c₁.p | c₁.throwUnexpected
299
+ let .add a₂ _ p₂ := c₂.p | c₂.throwUnexpected
300
+ let p := p₁.mul a₂.natAbs |>.combine (p₂.mul a₁.natAbs)
301
+ if (← p.satisfiedLe) != .false then
302
+ trace[grind.cutsat.conflict] "not resolved"
303
+ return false
304
+ else
305
+ let k := p.gcdCoeffs'
306
+ let c := if k == 1 then
307
+ { p, h := .combine c₁ c₂ : LeCnstr }
308
+ else
309
+ { p := p.div k, h := .combineDivCoeffs c₁ c₂ k : LeCnstr }
310
+ trace[grind.debug.cutsat.search.conflict] "resolved: {← c.pp}"
311
+ c.assert
312
+ return true
313
+
314
+ def resolveCooperUnary (pred : CooperSplitPred) : SearchM Bool := do
315
+ let some c₃ := pred.c₃? | return false
316
+ let .add (-1) _ (.num a) := pred.c₁.p | return false
317
+ let .add 1 _ (.num b) := pred.c₂.p | return false
318
+ let .add c _ (.num e) := c₃.p | return false
319
+ let d := c₃.d
320
+ let (1, α, _) := gcdExt c d | return false
321
+ unless -b < Int.Linear.cdiv (a - -α * e % d) d * d + -α * e % d do
322
+ return false
323
+ setInconsistent (.cooper pred.c₁ pred.c₂ c₃)
324
+ return true
325
+
326
+ def resolveCooperPred (pred : CooperSplitPred) : SearchM Unit := do
327
+ trace[grind.debug.cutsat.search.conflict] "[{pred.numCases}]: {← pred.pp}"
328
+ if (← resolveCooperUnary pred) then
329
+ return
330
+ let n := pred.numCases
331
+ let fvarId ← mkCase (.cooper pred #[] {})
332
+ { pred, k := n - 1, h := .dec fvarId : CooperSplit }.assert
333
+
334
+ def resolveCooper (c₁ c₂ : LeCnstr) : SearchM Unit := do
335
+ let left : Bool := c₁.p.leadCoeff.natAbs < c₂.p.leadCoeff.natAbs
336
+ resolveCooperPred { c₁, c₂, left, c₃? := none }
337
+
338
+ def resolveCooperDvd (c₁ c₂ : LeCnstr) (c₃ : DvdCnstr) : SearchM Unit := do
339
+ let left : Bool := c₁.p.leadCoeff.natAbs < c₂.p.leadCoeff.natAbs
340
+ resolveCooperPred { c₁, c₂, left, c₃? := some c₃ }
341
+
342
+ def DiseqCnstr.split (c : DiseqCnstr) : SearchM LeCnstr := do
343
+ let fvarId ← if let some fvarId := (← get').diseqSplits.find? c.p then
344
+ trace[grind.debug.cutsat.search.split] "{← c.pp}, reusing {fvarId.name}"
345
+ pure fvarId
346
+ else
347
+ let fvarId ← mkCase (.diseq c)
348
+ trace[grind.debug.cutsat.search.split] "{← c.pp}, {fvarId.name}"
349
+ modify' fun s => { s with diseqSplits := s.diseqSplits.insert c.p fvarId }
350
+ pure fvarId
351
+ let p₂ := c.p.addConst 1
352
+ return { p := p₂, h := .dec fvarId }
353
+
354
+ /--
355
+ Given `c₁` of the form `a₁*x + p₁ ≠ 0`, and `c₂` of the form `b*x + p ≤ 0`
356
+ splits `c₁` and resolve with `c₂`.
357
+ -/
358
+ def resolveCooperDiseq (c₁ : DiseqCnstr) (c₂ : LeCnstr) (c₃? : Option DvdCnstr) : SearchM Unit := do
359
+ -- Ensure the coefficient is positive
360
+ let c₁ := if c₁.p.leadCoeff > 0 then
361
+ { p := c₁.p.mul (-1), h := .neg c₁ : DiseqCnstr }
362
+ else
363
+ c₁
364
+ let c₁ ← c₁.split
365
+ -- After the `c₁.split`, the real shadow may resolve the conflict
366
+ if (← resolveRealLowerUpperConflict c₁ c₂) then
367
+ return ()
368
+ if let some c₃ := c₃? then
369
+ resolveCooperDvd c₁ c₂ c₃
370
+ else
371
+ resolveCooper c₁ c₂
372
+
373
+ /--
374
+ Given `c₁` of the form `-a₁*x + p₁ ≤ 0`, and `c` of the form `b*x + p ≠ 0`,
375
+ splits `c` and resolve with `c₁`.
376
+ -/
377
+ def resolveRatDiseq (c₁ : LeCnstr) (c : DiseqCnstr) : SearchM Unit := do
378
+ let c := if c.p.leadCoeff < 0 then
379
+ { p := c.p.mul (-1), h := .neg c : DiseqCnstr }
380
+ else
381
+ c
382
+ let c₂ ← c.split
383
+ let b ← resolveRealLowerUpperConflict c₁ c₂
384
+ assert! b
385
+
386
+ def processVar (x : Var) : SearchM Unit := do
387
+ if (← eliminated x) then
388
+ trace[grind.debug.cutsat.search] "eliminated: {← getVar x}"
389
+ /-
390
+ Variable has been eliminated, and will be assigned later after we have assigned
391
+ variables that have not been eliminated.
392
+ -/
393
+ skipAssignment x
394
+ return ()
395
+ -- Solution space for divisibility constraint is `x = k*d + b`
396
+ let dvd? := (← get').dvds[x]!
397
+ let dvdSol ← if let some c := dvd? then
398
+ if let some solutions ← c.getSolutions? then
399
+ pure solutions
400
+ else
401
+ trace[grind.debug.cutsat.search] "dvd conflict: {← c.pp}"
402
+ resolveDvdConflict c
403
+ return ()
404
+ else
405
+ pure {}
406
+ let lower? ← getBestLower? x dvd?
407
+ let upper? ← getBestUpper? x dvd?
408
+ let diseqVals ← getDiseqValues x
409
+ match lower?, upper? with
410
+ | none, none =>
411
+ let v := dvdSol.geAvoiding 0 diseqVals
412
+ setAssignment x v
413
+ | some (lower, _), none =>
414
+ let lower := lower.ceil
415
+ let v := dvdSol.geAvoiding lower diseqVals
416
+ trace[grind.debug.cutsat.search] "{lower} ≤ {quoteIfArithTerm (← getVar x)} := {v}"
417
+ setAssignment x v
418
+ | none, some (upper, _) =>
419
+ let upper := upper.floor
420
+ let v := dvdSol.leAvoiding upper diseqVals
421
+ trace[grind.debug.cutsat.search] "{quoteIfArithTerm (← getVar x)} := {v} ≤ {upper}"
422
+ setAssignment x v
423
+ | some (lower, c₁), some (upper, c₂) =>
424
+ trace[grind.debug.cutsat.search] "{lower} ≤ {lower.ceil} ≤ {quoteIfArithTerm (← getVar x)} ≤ {upper.floor} ≤ {upper}"
425
+ if lower > upper then
426
+ let .true ← resolveRealLowerUpperConflict c₁ c₂
427
+ | throwError "`grind` internal error, conflict resolution failed"
428
+ return ()
429
+ -- `lower ≤ upper` here
430
+ if lower.ceil > upper.floor then
431
+ if (← resolveRealLowerUpperConflict c₁ c₂) then
432
+ -- Resolved conflict using "real" shadow
433
+ return ()
434
+ if !(← isApprox) then
435
+ resolveCooper c₁ c₂
436
+ return ()
437
+ let r := findIntVal dvdSol lower.ceil upper.floor diseqVals
438
+ if let .found v := r then
439
+ setAssignment x v
440
+ return ()
441
+ if (← isApprox) then
442
+ setImprecise
443
+ if lower < upper then
444
+ setAssignment x <| findRatVal lower upper diseqVals
445
+ else if let some c := findRatDiseq? lower diseqVals then
446
+ resolveRatDiseq c₁ c
447
+ else
448
+ setAssignment x lower
449
+ else
450
+ match r with
451
+ | .dvd => resolveCooperDvd c₁ c₂ dvd?.get!
452
+ | .diseq c => resolveCooperDiseq c c₂ dvd?
453
+ | _ => unreachable!
454
+
455
+ /-- Returns `true` if we already have a complete assignment / model. -/
456
+ def hasAssignment : GoalM Bool := do
457
+ return (← get').vars.size == (← get').assignment.size
458
+
459
+ private def findCase (decVars : FVarIdSet) : SearchM Case := do
460
+ repeat
461
+ let numCases := (← get).cases.size
462
+ assert! numCases > 0
463
+ let case := (← get).cases[numCases-1]!
464
+ modify fun s => { s with cases := s.cases.pop }
465
+ if decVars.contains case.fvarId then
466
+ return case
467
+ -- Conflict does not depend on this case.
468
+ trace[grind.debug.cutsat.search.backtrack] "skipping {case.fvarId.name}"
469
+ unreachable!
470
+
471
+ def resolveConflict (h : UnsatProof) : SearchM Unit := do
472
+ trace[grind.debug.cutsat.search.backtrack] "resolve conflict, decision stack: {(← get).cases.toList.map fun c => c.fvarId.name}"
473
+ let decVars := h.collectDecVars.run (← get).decVars
474
+ trace[grind.debug.cutsat.search.backtrack] "dec vars: {decVars.toList.map (·.name)}"
475
+ if decVars.isEmpty then
476
+ trace[grind.debug.cutsat.search.backtrack] "close goal: {← h.pp}"
477
+ closeGoal (← h.toExprProof)
478
+ return ()
479
+ let c ← findCase decVars
480
+ modify' fun _ => c.saved
481
+ trace[grind.debug.cutsat.search.backtrack] "backtracking {c.fvarId.name}"
482
+ let decVars := decVars.erase c.fvarId
483
+ match c.kind with
484
+ | .diseq c₁ =>
485
+ let decVars := decVars.toArray
486
+ let p' := c₁.p.mul (-1) |>.addConst 1
487
+ let c' := { p := p', h := .ofDiseqSplit c₁ c.fvarId h decVars : LeCnstr }
488
+ trace[grind.debug.cutsat.search.backtrack] "resolved diseq split: {← c'.pp}"
489
+ c'.assert
490
+ | .cooper pred hs decVars' =>
491
+ let decVars' := decVars.union decVars'
492
+ let n := pred.numCases
493
+ let hs := hs.push (c.fvarId, h)
494
+ trace[grind.debug.cutsat.search.backtrack] "cooper #{hs.size + 1}, {← pred.pp}, {hs.map fun p => p.1.name}"
495
+ let s ← if hs.size + 1 < n then
496
+ let fvarId ← mkCase (.cooper pred hs decVars')
497
+ pure { pred, k := n - hs.size - 1, h := .dec fvarId : CooperSplit }
498
+ else
499
+ let decVars' := decVars'.toArray
500
+ trace[grind.debug.cutsat.search.backtrack] "cooper last case, {← pred.pp}, dec vars: {decVars'.map (·.name)}"
501
+ pure { pred, k := 0, h := .last hs decVars' : CooperSplit }
502
+ s.assert
503
+
504
+ /-- Search for an assignment/model for the linear constraints. -/
505
+ private def searchAssignmentMain : SearchM Unit := do
506
+ repeat
507
+ trace[grind.debug.cutsat.search] "main loop"
508
+ checkSystem "cutsat"
509
+ if (← hasAssignment) then
510
+ return ()
511
+ if (← isInconsistent) then
512
+ -- `grind` state is inconsistent
513
+ return ()
514
+ if let some c := (← get').conflict? then
515
+ resolveConflict c
516
+ else
517
+ let x : Var := (← get').assignment.size
518
+ trace[grind.debug.cutsat.search] "next var: {← getVar x}, {x}, {(← get').assignment.toList}"
519
+ processVar x
520
+
521
+ private def resetDecisionStack : SearchM Unit := do
522
+ if (← get).cases.isEmpty then
523
+ -- Nothing to reset
524
+ return ()
525
+ -- Backtrack changes but keep the assignment
526
+ let first := (← get).cases[0]!
527
+ modify' fun s => { first.saved with assignment := s.assignment }
528
+
529
+ private def searchQLiaAssignment : GoalM Bool := do
530
+ let go : SearchM Bool := do
531
+ searchAssignmentMain
532
+ let precise := (← get).precise
533
+ resetDecisionStack
534
+ return precise
535
+ go .rat |>.run' {}
536
+
537
+ private def traceActiveCnstrs : GoalM Unit := do
538
+ unless (← isTracingEnabledFor `grind.debug.cutsat.search.cnstrs) do return ()
539
+ let s ← get'
540
+ for x in [: s.vars.size] do
541
+ unless (← eliminated x) do
542
+ if let some dvd := s.dvds[x]! then
543
+ trace[grind.debug.cutsat.search.cnstrs] "{← dvd.pp}"
544
+ for c in s.lowers[x]! do
545
+ trace[grind.debug.cutsat.search.cnstrs] "{← c.pp}"
546
+ for c in s.uppers[x]! do
547
+ trace[grind.debug.cutsat.search.cnstrs] "{← c.pp}"
548
+ for c in s.diseqs[x]! do
549
+ trace[grind.debug.cutsat.search.cnstrs] "{← c.pp}"
550
+
551
+ private def searchLiaAssignment : GoalM Unit := do
552
+ let go : SearchM Unit := do
553
+ searchAssignmentMain
554
+ resetDecisionStack
555
+ traceActiveCnstrs
556
+ reorderVars
557
+ traceActiveCnstrs
558
+ go .int |>.run' {}
559
+
560
+ def searchAssignment : GoalM Unit := do
561
+ let precise ← searchQLiaAssignment
562
+ if (← isInconsistent) then return ()
563
+ if !(← getConfig).qlia && !precise then
564
+ -- Search for a new model using `.int` mode.
565
+ trace[grind.debug.cutsat.search] "restart using Cooper resolution"
566
+ modify' fun s => { s with assignment := {} }
567
+ searchLiaAssignment
568
+ trace[grind.debug.cutsat.search] "after search int model, inconsistent: {← isInconsistent}"
569
+ if (← isInconsistent) then return ()
570
+ -- TODO: constructing a model is only worth if `grind` will **not** continue searching.
571
+ assignElimVars
572
+
573
+ /--
574
+ Returns `true` if work/progress has been done.
575
+ There are two kinds of progress:
576
+ - An assignment for satisfying constraints was constructed.
577
+ - An inconsistency was detected.
578
+
579
+ The result is `false` if module already has a satisfying assignment.
580
+ -/
581
+ def check : GoalM Bool := do
582
+ if (← hasAssignment) then
583
+ return false
584
+ else
585
+ searchAssignment
586
+ return true
587
+
588
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/SearchM.lean ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
8
+
9
+ namespace Lean.Meta.Grind.Arith.Cutsat
10
+ /--
11
+ In principle, we only need to support two kinds of case split.
12
+ - Disequalities.
13
+ - Cooper-Left, but we have 4 different variants of this one.
14
+ -/
15
+ inductive CaseKind where
16
+ | diseq (d : DiseqCnstr)
17
+ | cooper (s : CooperSplitPred) (hs : Array (FVarId × UnsatProof)) (decVars : FVarIdSet)
18
+ deriving Inhabited
19
+
20
+ structure Case where
21
+ kind : CaseKind
22
+ /--
23
+ Decision variable used to represent the case-split.
24
+ For example, suppose we are splitting on `p ≠ 0`. Then,
25
+ we create a decision variable `h : p + 1 ≤ 0`
26
+ -/
27
+ fvarId : FVarId
28
+ /--
29
+ Snapshot of the cutsat state for backtracking purposes.
30
+ We do not use a trail stack.
31
+ -/
32
+ saved : State
33
+ deriving Inhabited
34
+
35
+ inductive Search.Kind where
36
+ | /--
37
+ Allow variables to be assigned to rational numbers during model
38
+ construction.
39
+ -/
40
+ rat
41
+ | /--
42
+ Variables must be assigned to integer numbers.
43
+ Cooper case splits are required in this mode.
44
+ -/
45
+ int
46
+ deriving Inhabited, BEq
47
+
48
+ /--
49
+ State of the model search procedure.
50
+ -/
51
+ structure Search.State where
52
+ /-- Decision stack (aka case-split stack) -/
53
+ cases : PArray Case := {}
54
+ /-- `precise := false` if not all constraints were satisfied during the search. -/
55
+ precise : Bool := true
56
+ /-- Set of decision variables in `cases`. -/
57
+ decVars : FVarIdSet := {}
58
+
59
+ abbrev SearchM := ReaderT Search.Kind (StateRefT Search.State GoalM)
60
+
61
+ /-- Returns `true` if approximations are allowed. -/
62
+ def isApprox : SearchM Bool :=
63
+ return (← read) == .rat
64
+
65
+ /-- Sets `precise` to `false` to indicate that some constraint was not satisfied. -/
66
+ def setImprecise : SearchM Unit := do
67
+ modify fun s => { s with precise := false }
68
+
69
+ def mkCase (kind : CaseKind) : SearchM FVarId := do
70
+ let fvarId ← mkFreshFVarId
71
+ let saved ← get'
72
+ modify fun s => { s with
73
+ cases := s.cases.push { saved, fvarId, kind }
74
+ decVars := s.decVars.insert fvarId
75
+ }
76
+ modify' fun s => { s with caseSplits := true }
77
+ return fvarId
78
+
79
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/ToInt.lean ADDED
@@ -0,0 +1,378 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.ToIntLemmas
8
+ import Lean.Meta.Tactic.Grind.Simp
9
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
10
+
11
+ namespace Lean.Meta.Grind.Arith.Cutsat
12
+
13
+ private def reportMissingToIntAdapter (type : Expr) (instType : Expr) : MetaM Unit := do
14
+ trace[grind.debug.cutsat.debug] "`ToInt` initialization failure, failed to synthesize{indentExpr instType}\nfor type{indentExpr type}"
15
+
16
+ private def throwMissingDecl (declName : Name) : MetaM Unit :=
17
+ throwError "`grind cutsat`, unexpected missing declaration {declName}"
18
+
19
+ private def checkDecl (declName : Name) : MetaM Unit := do
20
+ unless (← getEnv).contains declName do
21
+ throwMissingDecl declName
22
+
23
+ private def mkOfNatThm? (type : Expr) (u : Level) (toIntInst : Expr) (rangeExpr : Expr) : MetaM (Option Expr) := do
24
+ -- ∀ n, OfNat α n
25
+ let ofNat := mkForall `n .default (mkConst ``Nat) (mkApp2 (mkConst ``OfNat [u]) type (mkBVar 0))
26
+ let .some ofNatInst ← trySynthInstance ofNat
27
+ | reportMissingToIntAdapter type ofNat; return none
28
+ let toIntOfNat := mkApp4 (mkConst ``Grind.ToInt.OfNat [u]) type ofNatInst rangeExpr toIntInst
29
+ let .some toIntOfNatInst ← trySynthInstance toIntOfNat
30
+ | reportMissingToIntAdapter type toIntOfNat; return none
31
+ return mkApp5 (mkConst ``Grind.ToInt.ofNat_eq [u]) type rangeExpr toIntInst ofNatInst toIntOfNatInst
32
+
33
+ /-- Helper function for `mkSimpleOpThm?` and `mkPowThm?` -/
34
+ private def mkSimpleOpThmCore? (type : Expr) (u : Level) (toIntInst : Expr) (rangeExpr : Expr) (op : Expr) (opSuffix : Name) (thmName : Name) : MetaM (Option Expr) := do
35
+ let .some opInst ← trySynthInstance op | return none
36
+ let toIntOpName := ``Grind.ToInt ++ opSuffix
37
+ checkDecl toIntOpName
38
+ let toIntOp := mkApp4 (mkConst toIntOpName [u]) type opInst rangeExpr toIntInst
39
+ let .some toIntOpInst ← trySynthInstance toIntOp
40
+ | reportMissingToIntAdapter type toIntOp; return none
41
+ checkDecl thmName
42
+ return mkApp5 (mkConst thmName [u]) type rangeExpr toIntInst opInst toIntOpInst
43
+
44
+ /-- Simpler version of `mkBinOpThms` for operators that have only one congruence theorem. -/
45
+ private def mkSimpleOpThm? (type : Expr) (u : Level) (toIntInst : Expr) (rangeExpr : Expr) (opBaseName : Name) (thmName : Name) : MetaM (Option Expr) := do
46
+ let op := mkApp (mkConst opBaseName [u]) type
47
+ mkSimpleOpThmCore? type u toIntInst rangeExpr op opBaseName thmName
48
+
49
+ /-- Simpler version of `mkBinOpThms` for operators that have only one congruence theorem. -/
50
+ private def mkPowThm? (type : Expr) (u : Level) (toIntInst : Expr) (rangeExpr : Expr) : MetaM (Option Expr) := do
51
+ let op := mkApp3 (mkConst ``HPow [u, 0, u]) type Nat.mkType type
52
+ mkSimpleOpThmCore? type u toIntInst rangeExpr op `Pow ``Grind.ToInt.pow_congr
53
+
54
+ private def mkBinOpThms (type : Expr) (u : Level) (toIntInst : Expr) (rangeExpr : Expr) (range : Grind.IntInterval) (opBaseName : Name) (thmName : Name) : MetaM ToIntThms := do
55
+ let some c ← mkSimpleOpThm? type u toIntInst rangeExpr opBaseName thmName | return {}
56
+ let opInst := c.appFn!.appArg!
57
+ let toIntOpInst := c.appArg!
58
+ let env ← getEnv
59
+ let cwwName := thmName ++ `ww
60
+ let cwlName := thmName ++ `wl
61
+ let cwrName := thmName ++ `wr
62
+ let c_ww? := if range.isFinite && env.contains cwwName then
63
+ some <| mkApp6 (mkConst cwwName [u]) type rangeExpr toIntInst opInst toIntOpInst reflBoolTrue
64
+ else
65
+ none
66
+ let c_wl? := if range.isFinite && range.nonEmpty && env.contains cwwName then
67
+ some <| mkApp7 (mkConst cwlName [u]) type rangeExpr toIntInst opInst toIntOpInst reflBoolTrue reflBoolTrue
68
+ else
69
+ none
70
+ let c_wr? := if range.isFinite && range.nonEmpty && env.contains cwwName then
71
+ some <| mkApp7 (mkConst cwrName [u]) type rangeExpr toIntInst opInst toIntOpInst reflBoolTrue reflBoolTrue
72
+ else
73
+ none
74
+ return { c? := some c, c_ww?, c_wl?, c_wr? }
75
+
76
+ -- TODO: improve this function
77
+ private def evalInt? (e : Expr) : MetaM (Option Int) := do
78
+ let e ← whnfD e
79
+ match_expr e with
80
+ | Int.ofNat a =>
81
+ let some a ← getNatValue? (← whnfD a) | return none
82
+ return some (a : Int)
83
+ | Int.negSucc a =>
84
+ let some a ← getNatValue? (← whnfD a) | return none
85
+ return some (- (a : Int) - 1)
86
+ | _ => return none
87
+
88
+ def getToIntInfo? (type : Expr) : GoalM (Option ToIntInfo) := do
89
+ if let some id? := (← get').toIntInfos.find? { expr := type } then
90
+ return id?
91
+ else
92
+ let info? ← go?
93
+ modify' fun s => { s with toIntInfos := s.toIntInfos.insert { expr := type } info? }
94
+ return info?
95
+ where
96
+ toIntInterval? (rangeExpr : Expr) : GoalM (Option Grind.IntInterval) := do
97
+ let rangeExpr ← whnfD rangeExpr
98
+ match_expr rangeExpr with
99
+ | Grind.IntInterval.co lo hi =>
100
+ let some lo ← evalInt? lo
101
+ | trace[grind.debug.cutsat.toInt] "`ToInt` lower bound could not be reduced to an integer{indentExpr (← whnfD lo)}\nfor type{indentExpr type}"
102
+ return none
103
+ let some hi ← evalInt? hi
104
+ | trace[grind.debug.cutsat.toInt] "`ToInt` upper bound could not be reduced to an integer{indentExpr hi}\nfor type{indentExpr type}"
105
+ return none
106
+ return some (.co lo hi)
107
+ | _ =>
108
+ trace[grind.debug.cutsat.toInt] "unsupported `ToInt` interval{indentExpr rangeExpr}\nfor type{indentExpr type}"
109
+ return none
110
+
111
+ go? : GoalM (Option ToIntInfo) := withNewMCtxDepth do
112
+ let u' ← getLevel type
113
+ let some u ← decLevel? u' | return none
114
+ let rangeExpr ← mkFreshExprMVar (mkConst ``Grind.IntInterval)
115
+ let toIntType := mkApp2 (mkConst ``Grind.ToInt [u]) type rangeExpr
116
+ let .some toIntInst ← trySynthInstance toIntType |
117
+ trace[grind.debug.cutsat.toInt] "failed to synthesize {indentExpr toIntType}"
118
+ return none
119
+ let rangeExpr ← instantiateMVars rangeExpr
120
+ let some range ← toIntInterval? rangeExpr | return none
121
+ let toInt := mkApp3 (mkConst ``Grind.ToInt.toInt [u]) type rangeExpr toIntInst
122
+ let wrap := mkApp (mkConst ``Grind.IntInterval.wrap) rangeExpr
123
+ let ofWrap0? := if let .co 0 hi := range then
124
+ some <| mkApp3 (mkConst ``Grind.ToInt.of_eq_wrap_co_0) rangeExpr (toExpr hi) reflBoolTrue
125
+ else
126
+ none
127
+ let ofEq := mkApp3 (mkConst ``Grind.ToInt.of_eq [u]) type rangeExpr toIntInst
128
+ let ofDiseq := mkApp3 (mkConst ``Grind.ToInt.of_diseq [u]) type rangeExpr toIntInst
129
+ let (ofLE?, ofNotLE?) ← do
130
+ let toLE := mkApp (mkConst ``LE [u]) type
131
+ let .some leInst ← LOption.toOption <$> trySynthInstance toLE | pure (none, none)
132
+ let toIntLE := mkApp4 (mkConst ``Grind.ToInt.LE [u]) type leInst rangeExpr toIntInst
133
+ let .some toIntLEInst ← LOption.toOption <$> trySynthInstance toIntLE
134
+ | reportMissingToIntAdapter type toIntLE; pure (none, none)
135
+ let ofLE := mkApp5 (mkConst ``Grind.ToInt.of_le [u]) type rangeExpr toIntInst leInst toIntLEInst
136
+ let ofNotLE := mkApp5 (mkConst ``Grind.ToInt.of_not_le [u]) type rangeExpr toIntInst leInst toIntLEInst
137
+ pure (some ofLE, some ofNotLE)
138
+ let (ofLT?, ofNotLT?) ← do
139
+ let toLT := mkApp (mkConst ``LT [u]) type
140
+ let .some ltInst ← LOption.toOption <$> trySynthInstance toLT | pure (none, none)
141
+ let toIntLT := mkApp4 (mkConst ``Grind.ToInt.LT [u]) type ltInst rangeExpr toIntInst
142
+ let .some toIntLTInst ← LOption.toOption <$> trySynthInstance toIntLT
143
+ | reportMissingToIntAdapter type toIntLT; pure (none, none)
144
+ let ofLT := mkApp5 (mkConst ``Grind.ToInt.of_lt [u]) type rangeExpr toIntInst ltInst toIntLTInst
145
+ let ofNotLT := mkApp5 (mkConst ``Grind.ToInt.of_not_lt [u]) type rangeExpr toIntInst ltInst toIntLTInst
146
+ pure (some ofLT, some ofNotLT)
147
+ let mkBinOpThms (opBaseName : Name) (thmName : Name) :=
148
+ mkBinOpThms type u toIntInst rangeExpr range opBaseName thmName
149
+ let mkSimpleOpThm? (opBaseName : Name) (thmName : Name) :=
150
+ mkSimpleOpThm? type u toIntInst rangeExpr opBaseName thmName
151
+ let addThms ← mkBinOpThms ``Add ``Grind.ToInt.add_congr
152
+ let mulThms ← mkBinOpThms ``Mul ``Grind.ToInt.mul_congr
153
+ let subThm? ← mkSimpleOpThm? ``Sub ``Grind.ToInt.sub_congr
154
+ let negThm? ← mkSimpleOpThm? ``Neg ``Grind.ToInt.neg_congr
155
+ let divThm? ← mkSimpleOpThm? ``Div ``Grind.ToInt.div_congr
156
+ let modThm? ← mkSimpleOpThm? ``Mod ``Grind.ToInt.mod_congr
157
+ let powThm? ← mkPowThm? type u toIntInst rangeExpr
158
+ let zeroThm? ← mkSimpleOpThm? ``Zero ``Grind.ToInt.zero_eq
159
+ let ofNatThm? ← mkOfNatThm? type u toIntInst rangeExpr
160
+ let lowerThm? := if let some lo := range.lo? then
161
+ if lo == 0 then
162
+ some <| mkApp4 (mkConst ``Grind.ToInt.ge_lower0 [u]) type rangeExpr toIntInst reflBoolTrue
163
+ else
164
+ some <| mkApp5 (mkConst ``Grind.ToInt.ge_lower [u]) type rangeExpr toIntInst (toExpr lo) reflBoolTrue
165
+ else none
166
+ let upperThm? := if let some hi := range.hi? then
167
+ some <| mkApp5 (mkConst ``Grind.ToInt.le_upper [u]) type rangeExpr toIntInst (toExpr (-hi + 1)) reflBoolTrue
168
+ else none
169
+ trace[grind.debug.cutsat.toInt] "registered toInt: {type}"
170
+ return some {
171
+ type, u, toIntInst, rangeExpr, range, toInt, wrap, ofWrap0?, ofEq, ofDiseq, ofLE?, ofNotLE?, ofLT?, ofNotLT?, addThms, mulThms,
172
+ subThm?, negThm?, divThm?, modThm?, powThm?, zeroThm?, ofNatThm?, lowerThm?, upperThm?
173
+ }
174
+
175
+ structure ToIntM.Context where
176
+ info : ToIntInfo
177
+
178
+ abbrev ToIntM := ReaderT ToIntM.Context GoalM
179
+
180
+ def getInfo : ToIntM ToIntInfo :=
181
+ return (← read).info
182
+
183
+ def ToIntM.run? (type : Expr) (x : ToIntM α) : GoalM (Option α) := do
184
+ let some info ← getToIntInfo? type | return none
185
+ return some (← x { info })
186
+
187
+ def ToIntM.run (type : Expr) (x : ToIntM Unit) : GoalM Unit := do
188
+ let some info ← getToIntInfo? type | return ()
189
+ x { info }
190
+
191
+ private def intRfl := mkApp (mkConst ``Eq.refl [1]) Int.mkType
192
+
193
+ def mkToIntVar (e : Expr) : ToIntM (Expr × Expr) := do
194
+ if let some info := (← get').toIntTermMap.find? { expr := e } then
195
+ return (info.eToInt, info.he)
196
+ let eToInt := mkApp (← getInfo).toInt e
197
+ let he := mkApp intRfl eToInt
198
+ let α := (← getInfo).type
199
+ modify' fun s => { s with
200
+ toIntTermMap := s.toIntTermMap.insert { expr := e } { eToInt, he, α }
201
+ }
202
+ markAsCutsatTerm e
203
+ return (eToInt, he)
204
+
205
+ def getToIntTermType? (e : Expr) : GoalM (Option Expr) := do
206
+ let some info := (← get').toIntTermMap.find? { expr := e } | return none
207
+ return some info.α
208
+
209
+ def isToIntTerm (e : Expr) : GoalM Bool :=
210
+ return (← get').toIntTermMap.contains { expr := e }
211
+
212
+ private def isHomo (α β γ : Expr) : Bool :=
213
+ isSameExpr α β && isSameExpr α γ
214
+
215
+ private def isWrap (e : Expr) : Option Expr :=
216
+ match_expr e with
217
+ | Grind.IntInterval.wrap _ a => some a
218
+ | _ => none
219
+
220
+ /--
221
+ Given `h : toInt a = i.wrap b`, return `(b', h)` where
222
+ `b'` is the expanded form of `i.wrap b`, and `h : toInt a = b'`
223
+ -/
224
+ private def expandWrap (a b : Expr) (h : Expr) : ToIntM (Expr × Expr) := do
225
+ match (← getInfo).range with
226
+ | .ii => return (b, h)
227
+ | .co 0 hi =>
228
+ let b' := mkIntMod b (toExpr hi)
229
+ let toA := mkApp (← getInfo).toInt a
230
+ let h := mkApp3 (← getInfo).ofWrap0?.get! toA b h
231
+ return (b', h)
232
+ | .co lo hi =>
233
+ let b' := mkIntAdd (mkIntMod (mkIntSub b (toExpr lo)) (toExpr (hi - lo))) (toExpr lo)
234
+ return (b', h)
235
+ | _ => throwError "`grind cutsat`, `ToInt` interval not supported yet"
236
+
237
+ /--
238
+ Given `h : toInt a = b`, if `b` is of the form `i.wrap b'`,
239
+ invokes `expandWrap a b' h`
240
+ -/
241
+ private def expandIfWrap (a b : Expr) (h : Expr) : ToIntM (Expr × Expr) := do
242
+ match isWrap b with
243
+ | none => return (b, h)
244
+ | some b => expandWrap a b h
245
+
246
+ private def mkWrap (a : Expr) : ToIntM Expr := do
247
+ return mkApp (← getInfo).wrap a
248
+
249
+ private def ToIntThms.mkResult (toIntThms : ToIntThms) (mkBinOp : Expr → Expr → Expr) (a b : Expr) (a' b' : Expr) (h₁ h₂ : Expr) : ToIntM (Expr × Expr) := do
250
+ let f := toIntThms.c?.get!
251
+ let mk (f : Expr) (a' b' : Expr) : ToIntM (Expr × Expr) := do
252
+ -- If the appropriate `wrap` cancellation theorem is missing, we have to expand the nested wrap.
253
+ let (a', h₁) ← expandIfWrap a a' h₁
254
+ let (b', h₂) ← expandIfWrap b b' h₂
255
+ let h := mkApp6 f a b a' b' h₁ h₂
256
+ let r ← mkWrap (mkBinOp a' b')
257
+ return (r, h)
258
+ match isWrap a', isWrap b' with
259
+ | none, none => mk f a' b'
260
+ | some a'', none => if let some f := toIntThms.c_wl? then mk f a'' b' else mk f a' b'
261
+ | none, some b'' => if let some f := toIntThms.c_wr? then mk f a' b'' else mk f a' b'
262
+ | some a'', some b'' => if let some f := toIntThms.c_ww? then mk f a'' b'' else mk f a' b'
263
+
264
+ private partial def toInt' (e : Expr) : ToIntM (Expr × Expr) := do
265
+ match_expr e with
266
+ | HAdd.hAdd α β γ _ a b =>
267
+ unless isHomo α β γ do return (← mkToIntVar e)
268
+ toIntBin (← getInfo).addThms mkIntAdd a b
269
+ | HMul.hMul α β γ _ a b =>
270
+ unless isHomo α β γ do return (← mkToIntVar e)
271
+ toIntBin (← getInfo).mulThms mkIntMul a b
272
+ | HDiv.hDiv α β γ _ a b =>
273
+ unless isHomo α β γ do return (← mkToIntVar e)
274
+ processDivMod (isDiv := true) a b
275
+ | HMod.hMod α β γ _ a b =>
276
+ unless isHomo α β γ do return (← mkToIntVar e)
277
+ processDivMod (isDiv := false) a b
278
+ | HSub.hSub α β γ _ a b =>
279
+ unless isHomo α β γ do return (← mkToIntVar e)
280
+ processSub a b
281
+ | Neg.neg _ _ a =>
282
+ processNeg a
283
+ | HPow.hPow α β γ _ a b =>
284
+ unless isSameExpr α γ && β.isConstOf ``Nat do return (← mkToIntVar e)
285
+ processPow a b
286
+ | Zero.zero _ _ =>
287
+ let some thm := (← getInfo).zeroThm? | mkToIntVar e
288
+ return (mkIntLit 0, thm)
289
+ | OfNat.ofNat _ n _ =>
290
+ let some thm := (← getInfo).ofNatThm? | mkToIntVar e
291
+ let some n ← getNatValue? n | mkToIntVar e
292
+ let r := mkIntLit ((← getInfo).range.wrap n)
293
+ let h := mkApp thm (toExpr n)
294
+ return (r, h)
295
+ | _ => mkToIntVar e
296
+ where
297
+ toIntBin (toIntOp : ToIntThms) (mkBinOp : Expr → Expr → Expr) (a b : Expr) : ToIntM (Expr × Expr) := do
298
+ unless toIntOp.c?.isSome do return (← mkToIntVar e)
299
+ let (a', h₁) ← toInt' a
300
+ let (b', h₂) ← toInt' b
301
+ toIntOp.mkResult mkBinOp a b a' b' h₁ h₂
302
+
303
+ toIntAndExpandWrap (a : Expr) : ToIntM (Expr × Expr) := do
304
+ let (a', h₁) ← toInt' a
305
+ expandIfWrap a a' h₁
306
+
307
+ processDivMod (isDiv : Bool) (a b : Expr) : ToIntM (Expr × Expr) := do
308
+ let some thm ← if isDiv then pure (← getInfo).divThm? else pure (← getInfo).modThm?
309
+ | return (← mkToIntVar e)
310
+ let (a', h₁) ← toIntAndExpandWrap a
311
+ let (b', h₂) ← toIntAndExpandWrap b
312
+ let r := if isDiv then mkIntDiv a' b' else mkIntMod a' b'
313
+ let h := mkApp6 thm a b a' b' h₁ h₂
314
+ return (r, h)
315
+
316
+ processSub (a b : Expr) : ToIntM (Expr × Expr) := do
317
+ let some thm := (← getInfo).subThm? | return (← mkToIntVar e)
318
+ let (a', h₁) ← toIntAndExpandWrap a
319
+ let (b', h₂) ← toIntAndExpandWrap b
320
+ let r ← mkWrap (mkIntSub a' b')
321
+ let h := mkApp6 thm a b a' b' h₁ h₂
322
+ return (r, h)
323
+
324
+ processNeg (a : Expr) : ToIntM (Expr × Expr) := do
325
+ let some thm := (← getInfo).negThm? | return (← mkToIntVar e)
326
+ let (a', h₁) ← toIntAndExpandWrap a
327
+ let r ← mkWrap (mkIntNeg a')
328
+ let h := mkApp3 thm a a' h₁
329
+ return (r, h)
330
+
331
+ processPow (a b : Expr) : ToIntM (Expr × Expr) := do
332
+ let some thm := (← getInfo).powThm? | return (← mkToIntVar e)
333
+ let (a', h₁) ← toIntAndExpandWrap a
334
+ let r ← mkWrap (mkIntPowNat a' b)
335
+ let h := mkApp4 thm a b a' h₁
336
+ return (r, h)
337
+
338
+ def toInt (a : Expr) : ToIntM (Expr × Expr) := do
339
+ let (b, h) ← toInt' a
340
+ let (b, h) ← match isWrap b with
341
+ | some b' => expandWrap a b' h
342
+ | _ => pure (b, h)
343
+ let r ← preprocess b
344
+ if let some proof := r.proof? then
345
+ return (r.expr, (← mkEqTrans h proof))
346
+ else
347
+ return (r.expr, h)
348
+
349
+ def toInt? (a : Expr) (type : Expr) : GoalM (Option (Expr × Expr)) := do
350
+ ToIntM.run? type do toInt a
351
+
352
+ def isSupportedType (type : Expr) : GoalM Bool := do
353
+ if type == Nat.mkType || type == Int.mkType then
354
+ return true
355
+ else
356
+ return (← getToIntInfo? type).isSome
357
+
358
+ /--
359
+ Given `x` whose denotation is `e`, if `e` is of the form `ToInt a`,
360
+ asserts its lower and upper bounds if available
361
+ -/
362
+ def assertToIntBounds (e : Expr) (x : Var) : GoalM Unit := do
363
+ let_expr Grind.ToInt.toInt α _ _ a := e | return ()
364
+ ToIntM.run α do
365
+ let info ← getInfo
366
+ let i := info.range
367
+ if let some lo := i.lo? then
368
+ let some thm := info.lowerThm? | unreachable!
369
+ let p := .add (-1) x (.num lo)
370
+ let c := { p, h := .bound (mkApp thm a) : LeCnstr }
371
+ c.assert
372
+ if let some hi := i.hi? then
373
+ let some thm := info.upperThm? | unreachable!
374
+ let p := .add 1 x (.num (-hi + 1))
375
+ let c := { p, h := .bound (mkApp thm a) : LeCnstr }
376
+ c.assert
377
+
378
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/ToIntInfo.lean ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.ToInt
8
+ import Lean.Meta.Tactic.Grind.Arith.Util
9
+
10
+ namespace Lean.Meta.Grind.Arith.Cutsat
11
+ open Lean Grind
12
+
13
+ /--
14
+ Theorems for operators that have support for `i.wrap` over `i.wrap` simplification.
15
+ Currently only addition and multiplication have `wrap` cancellation theorems
16
+ -/
17
+ structure ToIntThms where
18
+ /--
19
+ Basic theorem of the form
20
+ ```
21
+ toInt a = a' → toInt b = b' → toInt (a ⊞ b) = i.wrap (a' ⊞ b')`
22
+ ```
23
+ -/
24
+ c? : Option Expr := none
25
+ /--
26
+ Left-right `wrap` cancellation theorem of the form
27
+ ```
28
+ toInt a = i.wrap a' → toInt b = i.wrap b' → toInt (a ⊞ b) = i.wrap (a' ⊞ b')
29
+ ```
30
+ -/
31
+ c_ww? : Option Expr := none
32
+ /--
33
+ Left `wrap` cancellation theorem of the form
34
+ ```
35
+ toInt a = i.wrap a' → toInt b = b' → toInt (a ⊞ b) = i.wrap (a' ⊞ b')
36
+ ```
37
+ -/
38
+ c_wl? : Option Expr := none
39
+ /--
40
+ Right `wrap` cancellation theorem of the form
41
+ ```
42
+ toInt a = a' → toInt b = i.wrap b' → toInt (a ⊞ b) = i.wrap (a' ⊞ b')
43
+ ```
44
+ -/
45
+ c_wr? : Option Expr := none
46
+
47
+ structure ToIntInfo where
48
+ type : Expr
49
+ u : Level
50
+ toIntInst : Expr
51
+ rangeExpr : Expr
52
+ range : IntInterval
53
+ toInt : Expr
54
+ wrap : Expr
55
+ -- theorem `of_eq_wrap_co_0` if `range == .co 0 hi`
56
+ ofWrap0? : Option Expr
57
+ ofEq : Expr
58
+ ofDiseq : Expr
59
+ ofLE? : Option Expr
60
+ ofNotLE? : Option Expr
61
+ ofLT? : Option Expr
62
+ ofNotLT? : Option Expr
63
+ addThms : ToIntThms
64
+ mulThms : ToIntThms
65
+ subThm? : Option Expr
66
+ negThm? : Option Expr
67
+ divThm? : Option Expr
68
+ modThm? : Option Expr
69
+ powThm? : Option Expr
70
+ zeroThm? : Option Expr
71
+ ofNatThm? : Option Expr
72
+ lowerThm? : Option Expr
73
+ upperThm? : Option Expr
74
+
75
+ /--
76
+ For each term `e` of type `α` which implements the `ToInt α i` class,
77
+ we store its corresponding `Int` term `eToInt`, a proof `he : toInt e = eToInt`,
78
+ and the type `α`.
79
+ -/
80
+ structure ToIntTermInfo where
81
+ eToInt : Expr
82
+ α : Expr
83
+ he : Expr
84
+
85
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Types.lean ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Data.Int.Linear
8
+ import Std.Internal.Rat
9
+ import Lean.Data.PersistentArray
10
+ import Lean.Meta.Tactic.Grind.ExprPtr
11
+ import Lean.Meta.Tactic.Grind.Arith.Util
12
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.ToIntInfo
13
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Types
14
+
15
+ namespace Lean.Meta.Grind.Arith.Cutsat
16
+
17
+ export Int.Linear (Var Poly)
18
+ export Std.Internal (Rat)
19
+
20
+ deriving instance Hashable for Poly
21
+
22
+ /-!
23
+ This module implements a model-based decision procedure for linear integer arithmetic,
24
+ inspired by Section 4 of "Cutting to the Chase: Solving Linear Integer Arithmetic".
25
+ Our implementation includes several enhancements and modifications:
26
+ Key Features:
27
+ - Extended constraint support (equality and disequality)
28
+ - Optimized encoding of `Cooper-Left` rule using "big"-disjunction instead of fresh variables
29
+ - Decision variable tracking for case splits (disequalities, `Cooper-Left`, `Cooper-Right`)
30
+
31
+ Constraint Types:
32
+ We handle four categories of linear polynomial constraints (where p is a linear polynomial):
33
+ 1. Equality: `p = 0`
34
+ 2. Divisibility: `d ∣ p`
35
+ 3. Inequality: `p ≤ 0`
36
+ 4. Disequality: `p ≠ 0`
37
+
38
+ Implementation Details:
39
+ - Polynomials use `Int.Linear.Poly` with sorted linear monomials (leading monomial contains max variable)
40
+ - Equalities are eliminated eagerly
41
+ - Divisibility constraints are maintained in solved form (one constraint per variable) using `Div-Solve`
42
+
43
+ Model Construction:
44
+ The procedure builds a model incrementally, resolving conflicts through constraint generation.
45
+ For example:
46
+ Given a partial model `{x := 1}` and constraint `3 ∣ 3*y + x + 1`:
47
+ - Cannot extend to `y` because `3 ∣ 3*y + 2` is unsatisfiable
48
+ - Generate implied constraint `3 ∣ x + 1`
49
+ - Force model update for `x`
50
+
51
+ Variable Assignment:
52
+ When assigning a variable `y`, we consider:
53
+ - Best upper and lower bounds (inequalities)
54
+ - Divisibility constraint
55
+ - Disequality constraints
56
+ `Cooper-Left` and `Cooper-Right` rules handle the combination of inequalities and divisibility.
57
+ For unsatisfiable disequalities p ≠ 0, we generate case split: `p + 1 ≤ 0 ∨ -p + 1 ≤ 0`
58
+
59
+ Contradiction Handling:
60
+ - Check dependency on decision variables
61
+ - If independent, use contradiction to close current grind goal
62
+ - Otherwise, trigger backtracking
63
+
64
+ Optimization:
65
+ We employ rational approximation for model construction:
66
+ - Continue with rational solutions when integer solutions aren't immediately found
67
+ - Helps identify simpler unsatisfiability proofs before full integer model construction
68
+ -/
69
+
70
+ /-
71
+ Remark: we will not define a parent structure `Cnstr` with the common
72
+ fields until the compiler provides support for avoiding the performance overhead.
73
+ -/
74
+
75
+ mutual
76
+ /-- A equality constraint and its justification/proof. -/
77
+ structure EqCnstr where
78
+ p : Poly
79
+ h : EqCnstrProof
80
+
81
+ inductive EqCnstrProof where
82
+ | /-- An equality `a = 0` coming from the core. -/
83
+ core0 (a : Expr) (zero : Expr)
84
+ | /--
85
+ An equality `a = b` coming from the core.
86
+ `p₁` and `p₂` are the polynomials corresponding to `a` and `b`.
87
+ -/
88
+ core (a b : Expr) (p₁ p₂ : Poly)
89
+ | coreNat (a b : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr)
90
+ | coreToInt (a b : Expr) (toIntThm : Expr) (lhs rhs : Int.Linear.Expr)
91
+ | /-- `e` is `p` -/
92
+ defn (e : Expr) (p : Poly)
93
+ | defnNat (e : Int.OfNat.Expr) (x : Var) (e' : Int.Linear.Expr)
94
+ | norm (c : EqCnstr)
95
+ | divCoeffs (c : EqCnstr)
96
+ | subst (x : Var) (c₁ : EqCnstr) (c₂ : EqCnstr)
97
+ | ofLeGe (c₁ : LeCnstr) (c₂ : LeCnstr)
98
+ | reorder (c : EqCnstr)
99
+ | commRingNorm (c : EqCnstr) (e : CommRing.RingExpr) (p : CommRing.Poly)
100
+ | defnCommRing (e : Expr) (p : Poly) (re : CommRing.RingExpr) (rp : CommRing.Poly) (p' : Poly)
101
+ | defnNatCommRing (e : Int.OfNat.Expr) (x : Var) (e' : Int.Linear.Expr) (p : Poly) (re : CommRing.RingExpr) (rp : CommRing.Poly) (p' : Poly)
102
+
103
+ /-- A divisibility constraint and its justification/proof. -/
104
+ structure DvdCnstr where
105
+ d : Int
106
+ p : Poly
107
+ h : DvdCnstrProof
108
+
109
+ /--
110
+ The predicate of type `Nat → Prop`, which serves as the conclusion of the
111
+ `cooper_left`, `cooper_right`, `cooper_dvd_left`, and `cooper_dvd_right` theorems.
112
+
113
+ The specific predicate used is determined as follows:
114
+ - `cooper_left_split` (if `left` is `true` and `c₃?` is `none`)
115
+ - `cooper_right_split` (if `left` is `false` and `c₃?` is `none`)
116
+ - `cooper_dvd_left_split` (if `left` is `true` and `c₃?` is `some`)
117
+ - `cooper_dvd_right_split` (if `left` is `false` and `c₃?` is `some`)
118
+
119
+ See `CooperSplit`
120
+ -/
121
+ structure CooperSplitPred where
122
+ left : Bool
123
+ c₁ : LeCnstr
124
+ c₂ : LeCnstr
125
+ c₃? : Option DvdCnstr
126
+
127
+ /--
128
+ An instance of the `CooperSplitPred` at `k`.
129
+ -/
130
+ structure CooperSplit where
131
+ pred : CooperSplitPred
132
+ k : Nat
133
+ h : CooperSplitProof
134
+
135
+ /--
136
+ The `cooper_left`, `cooper_right`, `cooper_dvd_left`, and `cooper_dvd_right` theorems have a resulting type
137
+ that is a big-or of the form `OrOver n (cooper_*_split ...)`. The predicate `(cooper_*_split ...)` has type `Nat → Prop`.
138
+ The `cutsat` procedure performs case splitting on `(cooper_*_split ... (n-1))` down to `(cooper_*_split ... 1)`.
139
+ If it derives `False` from each case, it uses `orOver_resolve` and `orOver_one` to deduce the final case,
140
+ which has type `(cooper_*_split ... 0)`.
141
+ -/
142
+ inductive CooperSplitProof where
143
+ | /-- The first `n-1` cases are decisions (aka case-splits). -/
144
+ dec (h : FVarId)
145
+ | /-- The last case which has type `(cooper_*_split ... 0)` -/
146
+ last (hs : Array (FVarId × UnsatProof)) (decVars : Array FVarId)
147
+
148
+ inductive DvdCnstrProof where
149
+ | /-- Given `e` of the form `k ∣ p` s.t. `e = True` in the core. -/
150
+ core (e : Expr)
151
+ | coreNat (e : Expr) (d : Nat) (b : Int.OfNat.Expr) (b' : Int.Linear.Expr)
152
+ | norm (c : DvdCnstr)
153
+ | divCoeffs (c : DvdCnstr)
154
+ | solveCombine (c₁ c₂ : DvdCnstr)
155
+ | solveElim (c₁ c₂ : DvdCnstr)
156
+ | elim (c : DvdCnstr)
157
+ | ofEq (x : Var) (c : EqCnstr)
158
+ | subst (x : Var) (c₁ : EqCnstr) (c₂ : DvdCnstr)
159
+ | cooper₁ (c : CooperSplit)
160
+ /-- `c.c₃?` must be `some` -/
161
+ | cooper₂ (c : CooperSplit)
162
+ | reorder (c : DvdCnstr)
163
+ | commRingNorm (c : DvdCnstr) (e : CommRing.RingExpr) (p : CommRing.Poly)
164
+
165
+ /-- An inequality constraint and its justification/proof. -/
166
+ structure LeCnstr where
167
+ p : Poly
168
+ h : LeCnstrProof
169
+
170
+ inductive LeCnstrProof where
171
+ | core (e : Expr)
172
+ | coreNeg (e : Expr) (p : Poly)
173
+ | coreNat (e : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr)
174
+ | coreNatNeg (e : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr)
175
+ | coreToInt (e : Expr) (pos : Bool) (toIntThm : Expr) (lhs rhs : Int.Linear.Expr)
176
+ | denoteAsIntNonneg (rhs : Int.OfNat.Expr) (rhs' : Int.Linear.Expr)
177
+ | bound (h : Expr)
178
+ | dec (h : FVarId)
179
+ | norm (c : LeCnstr)
180
+ | divCoeffs (c : LeCnstr)
181
+ | combine (c₁ c₂ : LeCnstr)
182
+ | combineDivCoeffs (c₁ c₂ : LeCnstr) (k : Int)
183
+ | subst (x : Var) (c₁ : EqCnstr) (c₂ : LeCnstr)
184
+ | ofLeDiseq (c₁ : LeCnstr) (c₂ : DiseqCnstr)
185
+ | ofDiseqSplit (c₁ : DiseqCnstr) (decVar : FVarId) (h : UnsatProof) (decVars : Array FVarId)
186
+ | cooper (c : CooperSplit)
187
+ | dvdTight (c₁ : DvdCnstr) (c₂ : LeCnstr)
188
+ | negDvdTight (c₁ : DvdCnstr) (c₂ : LeCnstr)
189
+ | reorder (c : LeCnstr)
190
+ | commRingNorm (c : LeCnstr) (e : CommRing.RingExpr) (p : CommRing.Poly)
191
+
192
+ /-- A disequality constraint and its justification/proof. -/
193
+ structure DiseqCnstr where
194
+ p : Poly
195
+ h : DiseqCnstrProof
196
+
197
+ inductive DiseqCnstrProof where
198
+ | /-- An disequality `a != 0` coming from the core. That is, `(a = 0) = False` in the core. -/
199
+ core0 (a : Expr) (zero : Expr)
200
+ | /--
201
+ An disequality `a ≠ b` coming from the core. That is, `(a = b) = False` in the core.
202
+ `p₁` and `p₂` are the polynomials corresponding to `a` and `b`.
203
+ -/
204
+ core (a b : Expr) (p₁ p₂ : Poly)
205
+ | coreNat (a b : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr)
206
+ | coreToInt (a b : Expr) (toIntThm : Expr) (lhs rhs : Int.Linear.Expr)
207
+ | norm (c : DiseqCnstr)
208
+ | divCoeffs (c : DiseqCnstr)
209
+ | neg (c : DiseqCnstr)
210
+ | subst (x : Var) (c₁ : EqCnstr) (c₂ : DiseqCnstr)
211
+ | reorder (c : DiseqCnstr)
212
+ | commRingNorm (c : DiseqCnstr) (e : CommRing.RingExpr) (p : CommRing.Poly)
213
+
214
+ /--
215
+ A proof of `False`.
216
+ Remark: We will later add support for a backtracking search inside of cutsat.
217
+ -/
218
+ inductive UnsatProof where
219
+ | dvd (c : DvdCnstr)
220
+ | le (c : LeCnstr)
221
+ | eq (c : EqCnstr)
222
+ | diseq (c : DiseqCnstr)
223
+ | cooper (c₁ c₂ : LeCnstr) (c₃ : DvdCnstr)
224
+
225
+ end
226
+
227
+ instance : Inhabited LeCnstr where
228
+ default := { p := .num 0, h := .core default}
229
+
230
+ instance : Inhabited DvdCnstr where
231
+ default := { d := 0, p := .num 0, h := .core default }
232
+
233
+ instance : Inhabited CooperSplitPred where
234
+ default := { left := false, c₁ := default, c₂ := default, c₃? := none }
235
+
236
+ instance : Inhabited CooperSplit where
237
+ default := { pred := default, k := 0, h := .dec default }
238
+
239
+ abbrev VarSet := RBTree Var compare
240
+
241
+ /-- State of the cutsat procedure. -/
242
+ structure State where
243
+ /-- Mapping from variables to their denotations. -/
244
+ vars : PArray Expr := {}
245
+ /-- Mapping from `Expr` to a variable representing it. -/
246
+ varMap : PHashMap ExprPtr Var := {}
247
+ /--
248
+ `vars` before they were reordered.
249
+ This array is empty if the variables were not reordered.
250
+ We need them to generate the proof term because some
251
+ justification objects contain terms using variables before the reordering.
252
+ -/
253
+ vars' : PArray Expr := {}
254
+ /-- `varVap` before variables were reordered. -/
255
+ varMap' : PHashMap ExprPtr Var := {}
256
+ /--
257
+ Mapping from `Nat` terms to their variable. They are also marked using `markAsCutsatTerm`.
258
+ -/
259
+ natVarMap : PHashMap ExprPtr Var := {}
260
+ natVars : PArray Expr := {}
261
+ /--
262
+ Some `Nat` variables encode nested terms such as `b+1`.
263
+ This is a mapping from this kind of variable to the integer variable
264
+ representing `natCast (b+1)`.
265
+ -/
266
+ natDef : PHashMap ExprPtr Var := {}
267
+ /--
268
+ Mapping from variables to divisibility constraints. Recall that we keep the divisibility constraint in solved form.
269
+ Thus, we have at most one divisibility per variable. -/
270
+ dvds : PArray (Option DvdCnstr) := {}
271
+ /--
272
+ Mapping from variables to their "lower" bounds. We say a relational constraint `c` is a lower bound for a variable `x`
273
+ if `x` is the maximal variable in `c`, and `x` coefficient in `c` is negative.
274
+ -/
275
+ lowers : PArray (PArray LeCnstr) := {}
276
+ /--
277
+ Mapping from variables to their "upper" bounds. We say a relational constraint `c` is a upper bound for a variable `x`
278
+ if `x` is the maximal variable in `c`, and `x` coefficient in `c` is positive.
279
+ -/
280
+ uppers : PArray (PArray LeCnstr) := {}
281
+ /--
282
+ Mapping from variables to their disequalities. We say a disequality constraint `c` is a disequality for a variable `x`
283
+ if `x` is the maximal variable in `c`.
284
+ -/
285
+ diseqs : PArray (PArray DiseqCnstr) := {}
286
+ /--
287
+ Mapping from variable to equation constraint used to eliminate it. `solved` variables should not occur in
288
+ `dvdCnstrs`, `lowers`, or `uppers`.
289
+ -/
290
+ elimEqs : PArray (Option EqCnstr) := {}
291
+ /--
292
+ Elimination stack. For every variable in `elimStack`. If `x` in `elimStack`, then `elimEqs[x]` is not `none`.
293
+ -/
294
+ elimStack : List Var := []
295
+ /--
296
+ Mapping from variable to occurrences. For example, an entry `x ↦ {y, z}` means that `x` may occur in `dvdCnstrs`, `lowers`, or `uppers` of
297
+ variables `y` and `z`.
298
+ If `x` occurs in `dvdCnstrs[y]`, `lowers[y]`, or `uppers[y]`, then `y` is in `occurs[x]`, but the reverse is not true.
299
+ If `x` is in `elimStack`, then `occurs[x]` is the empty set.
300
+ -/
301
+ occurs : PArray VarSet := {}
302
+ /-- Partial assignment being constructed by cutsat. -/
303
+ assignment : PArray Rat := {}
304
+ /-- Next unique id for a constraint. -/
305
+ nextCnstrId : Nat := 0
306
+ /--
307
+ `caseSplits` is `true` if cutsat is searching for model and already performed case splits.
308
+ This information is used to decide whether a conflict should immediately close the
309
+ current `grind` goal or not.
310
+ -/
311
+ caseSplits : Bool := false
312
+ /--
313
+ `conflict?` is `some ..` if a contradictory constraint was derived.
314
+ This field is only set when `caseSplits` is `true`. Otherwise, we
315
+ can convert `UnsatProof` into a Lean term and close the current `grind` goal.
316
+ -/
317
+ conflict? : Option UnsatProof := none
318
+ /--
319
+ Cache decision variables used when splitting on disequalities.
320
+ This is necessary because the same disequality may be in different conflicts.
321
+ -/
322
+ diseqSplits : PHashMap Poly FVarId := {}
323
+ /--
324
+ Pairs `(x, n)` s.t. we have expanded the theorems
325
+ - `Int.Linear.ediv_emod`
326
+ - `Int.Linear.emod_nonneg`
327
+ - `Int.Linear.emod_le`
328
+ -/
329
+ divMod : PHashSet (Expr × Int) := {}
330
+ /--
331
+ Mapping from a type `α` to its corresponding `ToIntInfo` object, which contains
332
+ the information needed to embed `α` terms into `Int` terms.
333
+ -/
334
+ toIntInfos : PHashMap ExprPtr (Option ToIntInfo) := {}
335
+ /--
336
+ For each type `α` in `toIntInfos`, the mapping `toIntVarMap` contains a mapping
337
+ from a α-term `e` to the pair `(toInt e, α)`.
338
+ -/
339
+ toIntTermMap : PHashMap ExprPtr ToIntTermInfo := {}
340
+ /--
341
+ `usedCommRing` is `true` if the `CommRing` has been used to normalize expressions.
342
+ -/
343
+ usedCommRing : Bool := false
344
+ deriving Inhabited
345
+
346
+ end Lean.Meta.Grind.Arith.Cutsat
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Internalize.lean ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Offset
8
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
9
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.Internalize
10
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Internalize
11
+
12
+ namespace Lean.Meta.Grind.Arith
13
+
14
+ def internalize (e : Expr) (parent? : Option Expr) : GoalM Unit := do
15
+ Offset.internalize e parent?
16
+ Cutsat.internalize e parent?
17
+ CommRing.internalize e parent?
18
+ Linear.internalize e parent?
19
+
20
+ end Lean.Meta.Grind.Arith
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Inv.lean ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Offset
8
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Inv
9
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Inv
10
+
11
+ namespace Lean.Meta.Grind.Arith
12
+
13
+ def checkInvariants : GoalM Unit := do
14
+ Offset.checkInvariants
15
+ Cutsat.checkInvariants
16
+ Linear.checkInvariants
17
+
18
+ end Lean.Meta.Grind.Arith
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Linear.lean ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Types
8
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Util
9
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Var
10
+ import Lean.Meta.Tactic.Grind.Arith.Linear.StructId
11
+ import Lean.Meta.Tactic.Grind.Arith.Linear.IneqCnstr
12
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Reify
13
+ import Lean.Meta.Tactic.Grind.Arith.Linear.DenoteExpr
14
+ import Lean.Meta.Tactic.Grind.Arith.Linear.ToExpr
15
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Proof
16
+ import Lean.Meta.Tactic.Grind.Arith.Linear.SearchM
17
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Search
18
+ import Lean.Meta.Tactic.Grind.Arith.Linear.PropagateEq
19
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Internalize
20
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Model
21
+ import Lean.Meta.Tactic.Grind.Arith.Linear.PP
22
+ import Lean.Meta.Tactic.Grind.Arith.Linear.MBTC
23
+
24
+ namespace Lean
25
+
26
+ builtin_initialize registerTraceClass `grind.linarith
27
+ builtin_initialize registerTraceClass `grind.linarith.internalize
28
+ builtin_initialize registerTraceClass `grind.linarith.assert
29
+ builtin_initialize registerTraceClass `grind.linarith.model
30
+ builtin_initialize registerTraceClass `grind.linarith.assert.unsat (inherited := true)
31
+ builtin_initialize registerTraceClass `grind.linarith.assert.trivial (inherited := true)
32
+ builtin_initialize registerTraceClass `grind.linarith.assert.store (inherited := true)
33
+ builtin_initialize registerTraceClass `grind.linarith.assert.ignored (inherited := true)
34
+
35
+ builtin_initialize registerTraceClass `grind.debug.linarith.search
36
+ builtin_initialize registerTraceClass `grind.debug.linarith.search.conflict (inherited := true)
37
+ builtin_initialize registerTraceClass `grind.debug.linarith.search.assign (inherited := true)
38
+ builtin_initialize registerTraceClass `grind.debug.linarith.search.split (inherited := true)
39
+ builtin_initialize registerTraceClass `grind.debug.linarith.search.backtrack (inherited := true)
40
+ builtin_initialize registerTraceClass `grind.debug.linarith.subst
41
+
42
+ end Lean
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Main.lean ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.PropagatorAttr
8
+ import Lean.Meta.Tactic.Grind.Arith.Offset
9
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.LeCnstr
10
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Search
11
+ import Lean.Meta.Tactic.Grind.Arith.CommRing.EqCnstr
12
+ import Lean.Meta.Tactic.Grind.Arith.Linear.IneqCnstr
13
+ import Lean.Meta.Tactic.Grind.Arith.Linear.Search
14
+
15
+ namespace Lean.Meta.Grind.Arith
16
+
17
+ namespace Offset
18
+ def isCnstr? (e : Expr) : GoalM (Option (Cnstr NodeId)) :=
19
+ return (← get).arith.offset.cnstrs.find? { expr := e }
20
+
21
+ def assertTrue (c : Cnstr NodeId) (p : Expr) : GoalM Unit := do
22
+ addEdge c.u c.v c.k (← mkOfEqTrue p)
23
+
24
+ def assertFalse (c : Cnstr NodeId) (p : Expr) : GoalM Unit := do
25
+ let p := mkOfNegEqFalse (← get').nodes c p
26
+ let c := c.neg
27
+ addEdge c.u c.v c.k p
28
+
29
+ end Offset
30
+
31
+ builtin_grind_propagator propagateLE ↓LE.le := fun e => do
32
+ if (← isEqTrue e) then
33
+ if let some c ← Offset.isCnstr? e then
34
+ Offset.assertTrue c (← mkEqTrueProof e)
35
+ Cutsat.propagateLe e (eqTrue := true)
36
+ Linear.propagateIneq e (eqTrue := true)
37
+ else if (← isEqFalse e) then
38
+ if let some c ← Offset.isCnstr? e then
39
+ Offset.assertFalse c (← mkEqFalseProof e)
40
+ Cutsat.propagateLe e (eqTrue := false)
41
+ Linear.propagateIneq e (eqTrue := false)
42
+
43
+ builtin_grind_propagator propagateLT ↓LT.lt := fun e => do
44
+ if (← isEqTrue e) then
45
+ Linear.propagateIneq e (eqTrue := true)
46
+ Cutsat.propagateLt e (eqTrue := true)
47
+ else if (← isEqFalse e) then
48
+ Linear.propagateIneq e (eqTrue := false)
49
+ Cutsat.propagateLt e (eqTrue := false)
50
+
51
+ def check : GoalM Bool := do
52
+ let c₁ ← Cutsat.check
53
+ let c₂ ← CommRing.check
54
+ let c₃ ← Linear.check
55
+ if c₁ || c₂ || c₃ then
56
+ processNewFacts
57
+ return true
58
+ else
59
+ return false
60
+
61
+ end Lean.Meta.Grind.Arith
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Model.lean ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Offset.Model
8
+ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/ModelUtil.lean ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Std.Internal.Rat
8
+ import Lean.Meta.Tactic.Grind.Types
9
+
10
+ namespace Lean.Meta.Grind.Arith
11
+ open Std.Internal
12
+ /-!
13
+ Helper functions for constructing counterexamples in the `linarith` and `cutsat` modules
14
+ -/
15
+
16
+ /--
17
+ Returns `true` if adding the assignment `e := v` to `a` will falsify any asserted disequality in core.
18
+ -/
19
+ private partial def satisfyDiseqs (goal : Goal) (a : Std.HashMap Expr Rat) (e : Expr) (v : Int) : Bool := Id.run do
20
+ let some parents := goal.parents.find? { expr := e } | return true
21
+ for parent in parents do
22
+ let_expr Eq _ lhs rhs := parent | continue
23
+ let some root := goal.getRoot? parent | continue
24
+ if root.isConstOf ``False then
25
+ let some lhsRoot := goal.getRoot? lhs | continue
26
+ let some rhsRoot := goal.getRoot? rhs | continue
27
+ if lhsRoot == e && !checkDiseq rhsRoot then return false
28
+ if rhsRoot == e && !checkDiseq lhsRoot then return false
29
+ return true
30
+ where
31
+ checkDiseq (other : Expr) : Bool :=
32
+ if let some v' := a[other]? then
33
+ v' != v
34
+ else
35
+ true
36
+
37
+ /--
38
+ Returns an integer value `i` for assigning to `e` s.t. adding `e := i` to `a` will not falsify any disequality
39
+ and `i` is not in `alreadyUsed`.
40
+ -/
41
+ partial def pickUnusedValue (goal : Goal) (a : Std.HashMap Expr Rat) (e : Expr) (next : Int) (alreadyUsed : Std.HashSet Int) : Int :=
42
+ go next
43
+ where
44
+ go (next : Int) : Int :=
45
+ if alreadyUsed.contains next then
46
+ go (next+1)
47
+ else if satisfyDiseqs goal a e next then
48
+ next
49
+ else
50
+ go (next + 1)
51
+
52
+ /--
53
+ Returns `true` if `e` should be treated as an interpreted value by the arithmetic modules.
54
+ -/
55
+ def isInterpretedTerm (e : Expr) : Bool :=
56
+ isNatNum e || isIntNum e || e.isAppOf ``HAdd.hAdd || e.isAppOf ``HMul.hMul || e.isAppOf ``HSub.hSub
57
+ || e.isAppOf ``Neg.neg || e.isAppOf ``HDiv.hDiv || e.isAppOf ``HMod.hMod || e.isAppOf ``One.one || e.isAppOf ``Zero.zero
58
+ || e.isAppOf ``NatCast.natCast || e.isIte || e.isDIte || e.isAppOf ``OfNat.ofNat || e.isAppOf ``Grind.ToInt.toInt
59
+ || e matches .lit (.natVal _)
60
+
61
+ /--
62
+ Adds the assignments `e' := v` to `a` for each `e'` in the equivalence class os `e`.
63
+ -/
64
+ def assignEqc (goal : Goal) (e : Expr) (v : Rat) (a : Std.HashMap Expr Rat) : Std.HashMap Expr Rat := Id.run do
65
+ let mut a := a
66
+ for e in goal.getEqc e do
67
+ a := a.insert e v
68
+ return a
69
+
70
+ /--
71
+ Assigns terms in the goal that satisfy `isTarget`.
72
+ Recall that not all terms are communicated to `linarith` and `cutsat` modules if they do not appear in relevant constraints.
73
+ The idea is to assign unused integer values that have not been used in the model and do not falsify equalities and disequalities
74
+ in core.
75
+ -/
76
+ private def assignUnassigned (goal : Goal) (isTarget : ENode → MetaM Bool) (model : Std.HashMap Expr Rat) : MetaM (Std.HashMap Expr Rat) := do
77
+ let mut nextVal : Int := 0
78
+ -- Collect used values
79
+ let mut used : Std.HashSet Int := {}
80
+ for (_, v) in model do
81
+ if v.den == 1 then
82
+ used := used.insert v.num
83
+ let mut model := model
84
+ -- Assign the remaining ones with values not used by cutsat
85
+ for e in goal.exprs do
86
+ let node ← goal.getENode e
87
+ if node.isRoot then
88
+ if (← isTarget node) then
89
+ if model[node.self]?.isNone then
90
+ let v := pickUnusedValue goal model node.self nextVal used
91
+ model := assignEqc goal node.self v model
92
+ used := used.insert v
93
+ nextVal := v + 1
94
+ return model
95
+
96
+ /-- Sorts assignment first by expression generation and then `Expr.lt` -/
97
+ private def sortModel (goal : Goal) (m : Array (Expr × Rat)) : Array (Expr × Rat) :=
98
+ m.qsort fun (e₁, _) (e₂, _) =>
99
+ let g₁ := goal.getGeneration e₁
100
+ let g₂ := goal.getGeneration e₂
101
+ if g₁ != g₂ then g₁ < g₂ else e₁.lt e₂
102
+
103
+ /--
104
+ Converts the given model into a sorted array of pairs `(e, v)` representing assignments `e := v`.
105
+ `isTarget` is a predicate used to detect terms that must be in the model but have not been assigned a value (see: `assignUnassigned`)
106
+ The pairs are sorted using `e`s generation and then `Expr.lt`.
107
+ Only terms s.t. `isInterpretedTerm e = false` are included into the resulting array.
108
+ -/
109
+ def finalizeModel (goal : Goal) (isTarget : ENode → MetaM Bool) (model : Std.HashMap Expr Rat) : MetaM (Array (Expr × Rat)) := do
110
+ let model ← assignUnassigned goal isTarget model
111
+ let mut r := #[]
112
+ for (e, v) in model do
113
+ unless isInterpretedTerm e do
114
+ r := r.push (e, v)
115
+ return sortModel goal r
116
+
117
+ /-- If the given trace class is enabled, trace the model using the class. -/
118
+ def traceModel (traceClass : Name) (model : Array (Expr × Rat)) : MetaM Unit := do
119
+ if (← isTracingEnabledFor traceClass) then
120
+ for (x, v) in model do
121
+ addTrace traceClass m!"{quoteIfArithTerm x} := {v}"
122
+
123
+ end Lean.Meta.Grind.Arith
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Offset.lean ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Arith.Offset.Main
8
+ import Lean.Meta.Tactic.Grind.Arith.Offset.Proof
9
+ import Lean.Meta.Tactic.Grind.Arith.Offset.Util
10
+ import Lean.Meta.Tactic.Grind.Arith.Offset.Types
11
+
12
+ namespace Lean
13
+
14
+ builtin_initialize registerTraceClass `grind.offset
15
+ builtin_initialize registerTraceClass `grind.offset.dist
16
+ builtin_initialize registerTraceClass `grind.offset.internalize
17
+ builtin_initialize registerTraceClass `grind.offset.internalize.term (inherited := true)
18
+ builtin_initialize registerTraceClass `grind.offset.propagate
19
+ builtin_initialize registerTraceClass `grind.offset.eq
20
+ builtin_initialize registerTraceClass `grind.offset.eq.to (inherited := true)
21
+ builtin_initialize registerTraceClass `grind.offset.eq.from (inherited := true)
22
+ builtin_initialize registerTraceClass `grind.offset.model
23
+
24
+ builtin_initialize registerTraceClass `grind.debug.offset
25
+ builtin_initialize registerTraceClass `grind.debug.offset.proof
26
+
27
+ end Lean
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/ProofUtil.lean ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Lean.Meta.Tactic.Grind.Types
8
+
9
+ namespace Lean.Meta.Grind.Arith
10
+
11
+ def mkLetOfMap {_ : Hashable α} {_ : BEq α} (m : Std.HashMap α Expr) (e : Expr)
12
+ (varPrefix : Name) (varType : Expr) (toExpr : α → Expr) : GoalM Expr := do
13
+ if m.isEmpty then
14
+ return e
15
+ else
16
+ let as := m.toArray
17
+ let mut e := e.abstract <| as.map (·.2)
18
+ let mut i := as.size
19
+ for (p, _) in as.reverse do
20
+ e := mkLet (varPrefix.appendIndexAfter i) varType (toExpr p) e
21
+ i := i - 1
22
+ return e
23
+
24
+ end Lean.Meta.Grind.Arith
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Simproc.lean ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-
2
+ Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ Released under Apache 2.0 license as described in the file LICENSE.
4
+ Authors: Leonardo de Moura
5
+ -/
6
+ prelude
7
+ import Init.Grind.Ring.Basic
8
+ import Init.Simproc
9
+ import Lean.Meta.Tactic.Simp.Simproc
10
+
11
+ namespace Lean.Meta.Grind.Arith
12
+
13
+ private def mkSemiringThm (declName : Name) (α : Expr) : MetaM (Option Expr) := do
14
+ let some u ← getDecLevel? α | return none
15
+ let semiring := mkApp (mkConst ``Grind.Semiring [u]) α
16
+ let .some semiringInst ← trySynthInstance semiring | return none
17
+ return mkApp2 (mkConst declName [u]) α semiringInst
18
+
19
+ /--
20
+ Applies `a^(m+n) = a^m * a^n`, `a^0 = 1`, `a^1 = a`.
21
+
22
+ We do normalize `a^0` and `a^1` when converting expressions into polynomials,
23
+ but we need to normalize them here when for other preprocessing steps such as
24
+ `a / b = a*b⁻¹`. If `b` is of the form `c^1`, it will be treated as an
25
+ atom in the comm ring module.
26
+ -/
27
+ builtin_simproc_decl expandPowAdd (_ ^ _) := fun e => do
28
+ let_expr HPow.hPow α nat α' _ a k := e | return .continue
29
+ let_expr Nat ← nat | return .continue
30
+ if let some k ← getNatValue? k then
31
+ if k == 0 then
32
+ unless (← isDefEq α α') do return .continue
33
+ let some h ← mkSemiringThm ``Grind.Semiring.pow_zero α | return .continue
34
+ let r ← mkNumeral α 1
35
+ return .done { expr := r, proof? := some (mkApp h a) }
36
+ else if k == 1 then
37
+ unless (← isDefEq α α') do return .continue
38
+ let some h ← mkSemiringThm ``Grind.Semiring.pow_one α | return .continue
39
+ return .done { expr := a, proof? := some (mkApp h a) }
40
+ else
41
+ return .continue
42
+ else
43
+ let_expr HAdd.hAdd _ _ _ _ m n := k | return .continue
44
+ unless (← isDefEq α α') do return .continue
45
+ let some h ← mkSemiringThm ``Grind.Semiring.pow_add α | return .continue
46
+ let pwFn := e.appFn!.appFn!
47
+ let r ← mkMul (mkApp2 pwFn a m) (mkApp2 pwFn a n)
48
+ return .visit { expr := r, proof? := some (mkApp3 h a m n) }
49
+
50
+ def addSimproc (s : Simprocs) : CoreM Simprocs := do
51
+ s.add ``expandPowAdd (post := true)
52
+
53
+ end Lean.Meta.Grind.Arith