Skip to content

Commit 630577a

Browse files
authored
feat: truth value propagation for grind (#6441)
This PR adds basic truth value propagation rules to the (WIP) `grind` tactic.
1 parent cde35bc commit 630577a

File tree

9 files changed

+427
-95
lines changed

9 files changed

+427
-95
lines changed

src/Init/Grind/Lemmas.lean

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,40 @@ Authors: Leonardo de Moura
55
-/
66
prelude
77
import Init.Core
8+
import Init.SimpLemmas
9+
import Init.Classical
810

911
namespace Lean.Grind
1012

1113
theorem intro_with_eq (p p' q : Prop) (he : p = p') (h : p' → q) : p → q :=
1214
fun hp => h (he.mp hp)
1315

16+
/-! And -/
17+
18+
theorem and_eq_of_eq_true_left {a b : Prop} (h : a = True) : (a ∧ b) = b := by simp [h]
19+
theorem and_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a ∧ b) = a := by simp [h]
20+
theorem and_eq_of_eq_false_left {a b : Prop} (h : a = False) : (a ∧ b) = False := by simp [h]
21+
theorem and_eq_of_eq_false_right {a b : Prop} (h : b = False) : (a ∧ b) = False := by simp [h]
22+
23+
theorem eq_true_of_and_eq_true_left {a b : Prop} (h : (a ∧ b) = True) : a = True := by simp_all
24+
theorem eq_true_of_and_eq_true_right {a b : Prop} (h : (a ∧ b) = True) : b = True := by simp_all
25+
26+
/-! Or -/
27+
28+
theorem or_eq_of_eq_true_left {a b : Prop} (h : a = True) : (a ∨ b) = True := by simp [h]
29+
theorem or_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a ∨ b) = True := by simp [h]
30+
theorem or_eq_of_eq_false_left {a b : Prop} (h : a = False) : (a ∨ b) = b := by simp [h]
31+
theorem or_eq_of_eq_false_right {a b : Prop} (h : b = False) : (a ∨ b) = a := by simp [h]
32+
33+
theorem eq_false_of_or_eq_false_left {a b : Prop} (h : (a ∨ b) = False) : a = False := by simp_all
34+
theorem eq_false_of_or_eq_false_right {a b : Prop} (h : (a ∨ b) = False) : b = False := by simp_all
35+
36+
/-! Not -/
37+
38+
theorem not_eq_of_eq_true {a : Prop} (h : a = True) : (Not a) = False := by simp [h]
39+
theorem not_eq_of_eq_false {a : Prop} (h : a = False) : (Not a) = True := by simp [h]
40+
41+
theorem eq_false_of_not_eq_true {a : Prop} (h : (Not a) = True) : a = False := by simp_all
42+
theorem eq_true_of_not_eq_false {a : Prop} (h : (Not a) = False) : a = True := by simp_all
43+
1444
end Lean.Grind

src/Lean/Meta/Tactic/Grind.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import Lean.Meta.Tactic.Grind.Core
1515
import Lean.Meta.Tactic.Grind.Canon
1616
import Lean.Meta.Tactic.Grind.MarkNestedProofs
1717
import Lean.Meta.Tactic.Grind.Inv
18+
import Lean.Meta.Tactic.Grind.Proof
19+
import Lean.Meta.Tactic.Grind.Propagate
20+
import Lean.Meta.Tactic.Grind.PP
1821

1922
namespace Lean
2023

src/Lean/Meta/Tactic/Grind/Core.lean

Lines changed: 13 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -5,86 +5,13 @@ Authors: Leonardo de Moura
55
-/
66
prelude
77
import Init.Grind.Util
8+
import Lean.Meta.LitValues
89
import Lean.Meta.Tactic.Grind.Types
910
import Lean.Meta.Tactic.Grind.Inv
10-
import Lean.Meta.LitValues
11+
import Lean.Meta.Tactic.Grind.Propagate
12+
import Lean.Meta.Tactic.Grind.PP
1113

1214
namespace Lean.Meta.Grind
13-
/-- Helper function for pretty printing the state for debugging purposes. -/
14-
def ppENodeRef (e : Expr) : GoalM Format := do
15-
let some n ← getENode? e | return "_"
16-
return f!"#{n.idx}"
17-
18-
/-- Returns expressions in the given expression equivalence class. -/
19-
partial def getEqc (e : Expr) : GoalM (List Expr) :=
20-
go e e []
21-
where
22-
go (first : Expr) (e : Expr) (acc : List Expr) : GoalM (List Expr) := do
23-
let next ← getNext e
24-
let acc := e :: acc
25-
if isSameExpr first next then
26-
return acc
27-
else
28-
go first next acc
29-
30-
/-- Returns all equivalence classes in the current goal. -/
31-
partial def getEqcs : GoalM (List (List Expr)) := do
32-
let mut r := []
33-
let nodes ← getENodes
34-
for node in nodes do
35-
if isSameExpr node.root node.self then
36-
r := (← getEqc node.self) :: r
37-
return r
38-
39-
/-- Helper function for pretty printing the state for debugging purposes. -/
40-
def ppENodeDeclValue (e : Expr) : GoalM Format := do
41-
if e.isApp && !(← isLitValue e) then
42-
e.withApp fun f args => do
43-
let r ← if f.isConst then
44-
ppExpr f
45-
else
46-
ppENodeRef f
47-
let mut r := r
48-
for arg in args do
49-
r := r ++ " " ++ (← ppENodeRef arg)
50-
return r
51-
else
52-
ppExpr e
53-
54-
/-- Helper function for pretty printing the state for debugging purposes. -/
55-
def ppENodeDecl (e : Expr) : GoalM Format := do
56-
let mut r := f!"{← ppENodeRef e} := {← ppENodeDeclValue e}"
57-
let n ← getENode e
58-
unless isSameExpr e n.root do
59-
r := r ++ f!" ↦ {← ppENodeRef n.root}"
60-
if n.interpreted then
61-
r := r ++ ", [val]"
62-
if n.ctor then
63-
r := r ++ ", [ctor]"
64-
if grind.debug.get (← getOptions) then
65-
if let some target ← getTarget? e then
66-
r := r ++ f!" ↝ {← ppENodeRef target}"
67-
return r
68-
69-
/-- Pretty print goal state for debugging purposes. -/
70-
def ppState : GoalM Format := do
71-
let mut r := f!"Goal:"
72-
let nodes ← getENodes
73-
for node in nodes do
74-
r := r ++ "\n" ++ (← ppENodeDecl node.self)
75-
let eqcs ← getEqcs
76-
for eqc in eqcs do
77-
if eqc.length > 1 then
78-
r := r ++ "\n" ++ "{" ++ (Format.joinSep (← eqc.mapM ppENodeRef) ", ") ++ "}"
79-
return r
80-
81-
/--
82-
Returns `true` if `e` is `True`, `False`, or a literal value.
83-
See `LitValues` for supported literals.
84-
-/
85-
def isInterpreted (e : Expr) : MetaM Bool := do
86-
if e.isTrue || e.isFalse then return true
87-
isLitValue e
8815

8916
/--
9017
Creates an `ENode` for `e` if one does not already exist.
@@ -96,23 +23,14 @@ def mkENode (e : Expr) (generation : Nat) : GoalM Unit := do
9623
let interpreted ← isInterpreted e
9724
mkENodeCore e interpreted ctor generation
9825

99-
private def pushNewEqCore (lhs rhs proof : Expr) (isHEq : Bool) : GoalM Unit :=
100-
modify fun s => { s with newEqs := s.newEqs.push { lhs, rhs, proof, isHEq } }
101-
102-
@[inline] private def pushNewEq (lhs rhs proof : Expr) : GoalM Unit := do
103-
if (← isDefEq (← inferType lhs) (← inferType rhs)) then
104-
pushNewEqCore lhs rhs proof (isHEq := false)
105-
else
106-
pushNewEqCore lhs rhs proof (isHEq := true)
107-
10826
/-- We use this auxiliary constant to mark delayed congruence proofs. -/
10927
private def congrPlaceholderProof := mkConst (Name.mkSimple "[congruence]")
11028

11129
/-- Adds `e` to congruence table. -/
11230
def addCongrTable (e : Expr) : GoalM Unit := do
11331
if let some { e := e' } := (← get).congrTable.find? { e } then
11432
trace[grind.congr] "{e} = {e'}"
115-
pushNewEq e e' congrPlaceholderProof
33+
pushEqHEq e e' congrPlaceholderProof
11634
-- TODO: we must check whether the types of the functions are the same
11735
-- TODO: update cgRoot for `e`
11836
else
@@ -242,7 +160,8 @@ where
242160
}
243161
let parents ← removeParents lhsRoot.self
244162
-- TODO: set propagateBool
245-
updateRoots lhs rhsNode.root true -- TODO
163+
let isTrueOrFalse ← isTrueExpr rhsNode.root <||> isFalseExpr rhsNode.root
164+
updateRoots lhs rhsNode.root (isTrueOrFalse && !(← isInconsistent))
246165
trace[grind.debug] "{← ppENodeRef lhs} new root {← ppENodeRef rhsNode.root}, {← ppENodeRef (← getRoot lhs)}"
247166
reinsertParents parents
248167
setENode lhsNode.root { (← getENode lhsRoot.self) with -- We must retrieve `lhsRoot` since it was updated.
@@ -255,12 +174,18 @@ where
255174
heqProofs := isHEq || rhsRoot.heqProofs || lhsRoot.heqProofs
256175
}
257176
copyParentsTo parents rhsNode.root
177+
unless (← isInconsistent) do
178+
if isTrueOrFalse then
179+
for parent in parents do
180+
propagateConectivesUp parent
258181

259-
updateRoots (lhs : Expr) (rootNew : Expr) (_propagateBool : Bool) : GoalM Unit := do
182+
updateRoots (lhs : Expr) (rootNew : Expr) (propagateTruth : Bool) : GoalM Unit := do
260183
let rec loop (e : Expr) : GoalM Unit := do
261184
-- TODO: propagateBool
262185
let n ← getENode e
263186
setENode e { n with root := rootNew }
187+
if propagateTruth then
188+
propagateConnectivesDown e
264189
if isSameExpr lhs n.next then return ()
265190
loop n.next
266191
loop lhs

src/Lean/Meta/Tactic/Grind/Inv.lean

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ namespace Lean.Meta.Grind
1212
Debugging support code for checking basic invariants.
1313
-/
1414

15-
register_builtin_option grind.debug : Bool := {
16-
defValue := false
17-
group := "debug"
18-
descr := "check invariants after updates"
19-
}
20-
2115
private def checkEqc (root : ENode) : GoalM Unit := do
2216
let mut size := 0
2317
let mut curr := root.self

src/Lean/Meta/Tactic/Grind/PP.lean

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/-
2+
Copyright (c) 2024 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.Util
8+
import Lean.Meta.Tactic.Grind.Types
9+
10+
namespace Lean.Meta.Grind
11+
12+
/-- Helper function for pretty printing the state for debugging purposes. -/
13+
def ppENodeRef (e : Expr) : GoalM Format := do
14+
let some n ← getENode? e | return "_"
15+
return f!"#{n.idx}"
16+
17+
/-- Returns expressions in the given expression equivalence class. -/
18+
partial def getEqc (e : Expr) : GoalM (List Expr) :=
19+
go e e []
20+
where
21+
go (first : Expr) (e : Expr) (acc : List Expr) : GoalM (List Expr) := do
22+
let next ← getNext e
23+
let acc := e :: acc
24+
if isSameExpr first next then
25+
return acc
26+
else
27+
go first next acc
28+
29+
/-- Returns all equivalence classes in the current goal. -/
30+
partial def getEqcs : GoalM (List (List Expr)) := do
31+
let mut r := []
32+
let nodes ← getENodes
33+
for node in nodes do
34+
if isSameExpr node.root node.self then
35+
r := (← getEqc node.self) :: r
36+
return r
37+
38+
/-- Helper function for pretty printing the state for debugging purposes. -/
39+
def ppENodeDeclValue (e : Expr) : GoalM Format := do
40+
if e.isApp && !(← isLitValue e) then
41+
e.withApp fun f args => do
42+
let r ← if f.isConst then
43+
ppExpr f
44+
else
45+
ppENodeRef f
46+
let mut r := r
47+
for arg in args do
48+
r := r ++ " " ++ (← ppENodeRef arg)
49+
return r
50+
else
51+
ppExpr e
52+
53+
/-- Helper function for pretty printing the state for debugging purposes. -/
54+
def ppENodeDecl (e : Expr) : GoalM Format := do
55+
let mut r := f!"{← ppENodeRef e} := {← ppENodeDeclValue e}"
56+
let n ← getENode e
57+
unless isSameExpr e n.root do
58+
r := r ++ f!" ↦ {← ppENodeRef n.root}"
59+
if n.interpreted then
60+
r := r ++ ", [val]"
61+
if n.ctor then
62+
r := r ++ ", [ctor]"
63+
if grind.debug.get (← getOptions) then
64+
if let some target ← getTarget? e then
65+
r := r ++ f!" ↝ {← ppENodeRef target}"
66+
return r
67+
68+
/-- Pretty print goal state for debugging purposes. -/
69+
def ppState : GoalM Format := do
70+
let mut r := f!"Goal:"
71+
let nodes ← getENodes
72+
for node in nodes do
73+
r := r ++ "\n" ++ (← ppENodeDecl node.self)
74+
let eqcs ← getEqcs
75+
for eqc in eqcs do
76+
if eqc.length > 1 then
77+
r := r ++ "\n" ++ "{" ++ (Format.joinSep (← eqc.mapM ppENodeRef) ", ") ++ "}"
78+
return r
79+
80+
end Lean.Meta.Grind

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/-
2+
Copyright (c) 2024 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.Sorry -- TODO: remove
8+
import Lean.Meta.Tactic.Grind.Types
9+
10+
namespace Lean.Meta.Grind
11+
12+
/--
13+
Returns a proof that `a = b` (or `HEq a b`).
14+
It assumes `a` and `b` are in the same equivalence class.
15+
-/
16+
def mkEqProof (a b : Expr) : GoalM Expr := do
17+
-- TODO
18+
if (← isDefEq (← inferType a) (← inferType b)) then
19+
mkSorry (← mkEq a b) (synthetic := false)
20+
else
21+
mkSorry (← mkHEq a b) (synthetic := false)
22+
23+
/--
24+
Returns a proof that `a = True`.
25+
It assumes `a` and `True` are in the same equivalence class.
26+
-/
27+
def mkEqTrueProof (a : Expr) : GoalM Expr := do
28+
mkEqProof a (← getTrueExpr)
29+
30+
/--
31+
Returns a proof that `a = False`.
32+
It assumes `a` and `False` are in the same equivalence class.
33+
-/
34+
def mkEqFalseProof (a : Expr) : GoalM Expr := do
35+
mkEqProof a (← getFalseExpr)
36+
37+
end Lean.Meta.Grind

0 commit comments

Comments
 (0)