Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +8 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Lean/Meta/Tactic/Grind/Arith/Linear/PropagateEq.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Lean/Meta/Tactic/Grind/Arith/Linear/Reify.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/Iterators/Combinators/Monadic/TakeWhile.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/Iterators/Lemmas/Producers/Monadic/List.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/Triple/Basic.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Impl/Substructure.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Lemmas/Var.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Time/Date/Basic.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing.lean +43 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/DenoteExpr.lean +92 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/EqCnstr.lean +517 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Internalize.lean +130 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Inv.lean +57 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/PP.lean +55 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Poly.lean +236 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Proof.lean +586 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Reify.lean +170 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/RingId.lean +197 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/SafePoly.lean +114 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/ToExpr.lean +79 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Types.lean +279 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Util.lean +184 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/CommRing/Var.lean +38 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat.lean +44 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/CommRing.lean +58 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/DvdCnstr.lean +130 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/EqCnstr.lean +542 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Inv.lean +119 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/LeCnstr.lean +212 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/MBTC.lean +45 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Model.lean +77 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Nat.lean +183 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Norm.lean +49 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Proof.lean +520 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/ReorderVars.lean +166 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Search.lean +588 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/SearchM.lean +79 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/ToInt.lean +378 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/ToIntInfo.lean +85 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Cutsat/Types.lean +346 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Internalize.lean +20 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Inv.lean +18 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Linear.lean +42 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Main.lean +61 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Model.lean +8 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/ModelUtil.lean +123 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/Offset.lean +27 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith/ProofUtil.lean +24 -0
- 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
|