Skip to content

Commit 2efbb09

Browse files
committed
Retreat demo changes.
1 parent d6c048b commit 2efbb09

File tree

7 files changed

+209
-18
lines changed

7 files changed

+209
-18
lines changed

Smt/Preprocess.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
55
Authors: Abdalrhman Mohamed, Tomaz Gomes Mascarenhas
66
-/
77

8+
import Smt.Preprocess.Decidable
89
import Smt.Preprocess.Iff
910
import Smt.Preprocess.Mono
1011
import Smt.Preprocess.PushHintsToCtx

Smt/Preprocess/Basic.lean

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ def getPropHyps : MetaM (Array FVarId) := do
2727
return result
2828
where
2929
isNonEmpty (e : Expr) : Bool :=
30-
match_expr e with
31-
| Nonempty _ => true
30+
match e with
31+
| .app (.const ``Nonempty _) _ => true
32+
| .forallE _ _ b _ => isNonEmpty b
3233
| _ => false
3334

3435
end Smt.Preprocess

Smt/Preprocess/Decidable.lean

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/-
2+
Copyright (c) 2021-2024 by the authors listed in the file AUTHORS and their
3+
institutional affiliations. All rights reserved.
4+
Released under Apache 2.0 license as described in the file LICENSE.
5+
Authors: Abdalrhman Mohamed
6+
-/
7+
8+
import Lean
9+
import Smt.Preprocess.Basic
10+
11+
namespace Smt.Preprocess
12+
13+
theorem classical_ite_congr {α : Sort u} {c₁ c₂ : Prop} {h₁ : Decidable c₁} {t₁ t₂ e₁ e₂ : α}
14+
(hc : c₁ = c₂) (ht : t₁ = t₂) (he : e₁ = e₂) :
15+
@ite α c₁ h₁ t₁ e₁ = @ite α c₂ (Classical.propDecidable c₂) t₂ e₂ := by
16+
grind
17+
18+
open Lean
19+
20+
def replaceIteDecidableInst (e : Expr) : MetaM Expr := do
21+
go #[] e
22+
where
23+
go (xs : Array Expr) (e : Expr) : MetaM Expr := do
24+
match e with
25+
| mkApp3 (.const ``ite us) α c (.app (.const ``Classical.propDecidable []) _) =>
26+
let α ← go xs α
27+
let c ← go xs c
28+
match ← Meta.synthInstance? (.app (.const ``Decidable []) (c.instantiateRev xs)) with
29+
| some h => return mkApp3 (.const ``ite us) α c h
30+
| none => return e
31+
| .app f a =>
32+
let f ← go xs f
33+
let a ← go xs a
34+
return e.updateApp! f a
35+
| .lam n t b bi =>
36+
let t ← go xs t
37+
Meta.withLocalDecl n bi (t.instantiateRev xs) fun x => do
38+
Meta.mkLambdaFVars #[x] (← go (xs.push x) b) false false false false
39+
| .forallE n t b bi =>
40+
let t ← go xs t
41+
Meta.withLocalDecl n bi (t.instantiateRev xs) fun x => do
42+
Meta.mkForallFVars #[x] (← go (xs.push x) b) false false false
43+
| .letE n t v b nd =>
44+
let t ← go xs t
45+
let v ← go xs v
46+
Meta.withLetDecl n t v (nondep := nd) fun x => do
47+
Meta.mkLetFVars #[x] (← go (xs.push x) b) false false
48+
| .proj _ _ b =>
49+
return e.updateProj! (← go xs b)
50+
| .mdata _ a =>
51+
return e.updateMData! (← go xs a)
52+
| _ =>
53+
return e
54+
55+
def mkEqIteDecidableInst (e : Expr) : MetaM Expr := do
56+
let e' ← replaceIteDecidableInst e
57+
Meta.mkAppM ``Eq #[e, e']
58+
59+
def containsClassicalPropDecidable (e : Expr) : Bool :=
60+
(Expr.const ``Classical.propDecidable []).occurs e
61+
62+
def replaceIteInst (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.withContext do
63+
let t ← instantiateMVars (← mv.getType)
64+
let ts ← hs.mapM (Meta.inferType · >>= instantiateMVars)
65+
if !(containsClassicalPropDecidable t || ts.any containsClassicalPropDecidable) then
66+
return { map := Std.HashMap.insertMany ∅ (hs.zip (hs.map .singleton)), hs, mv }
67+
let simpTheorems ← #[``eq_self, ``classical_ite_congr].foldlM (·.addConst ·) {}
68+
let simpTheorems := #[simpTheorems]
69+
let congrTheorems := {}
70+
let ctx ← Meta.Simp.mkContext {} simpTheorems congrTheorems
71+
let (hs', mv') ← replaceIteInstLocalDecls mv hs.toList ctx #[]
72+
let mv' ← replaceIteInstTarget mv' ctx
73+
return { map := Std.HashMap.insertMany ∅ (hs'.zip (hs.map .singleton)), hs := hs', mv := mv' }
74+
where
75+
replaceIteInstLocalDecls mv hs ctx hs' := do match hs with
76+
| [] => return (hs', mv)
77+
| h :: hs =>
78+
let type ← Meta.inferType h
79+
let eq ← mkEqIteDecidableInst (← instantiateMVars type)
80+
let (_, l, r) := eq.eq?.get!
81+
if l == r then
82+
replaceIteInstLocalDecls mv hs ctx (hs'.push h)
83+
else
84+
let (res, _) ← Meta.simp eq ctx
85+
let h' := mkApp4 (.const ``Eq.mp [0]) l r (mkOfEqTrue eq (← res.getProof)) h
86+
if let .some fv := h.fvarId? then
87+
let res ← mv.replace fv h' (.some r)
88+
let hs' := hs'.map res.subst.apply
89+
let hs := hs.map res.subst.apply
90+
res.mvarId.withContext (replaceIteInstLocalDecls res.mvarId hs ctx (hs'.push (.fvar res.fvarId)))
91+
else
92+
replaceIteInstLocalDecls mv hs ctx (hs'.push h')
93+
termination_by hs.length
94+
replaceIteInstTarget mv ctx := mv.withContext do
95+
let eq ← mkEqIteDecidableInst (← instantiateMVars (← mv.getType))
96+
let (res, _) ← Meta.simp eq ctx
97+
if res.expr.isTrue then
98+
mv.replaceTargetEq eq.appArg! (mkOfEqTrue eq (← res.getProof))
99+
else
100+
return mv
101+
mkOfEqTrue p hpt :=
102+
mkApp2 (.const ``of_eq_true []) p hpt
103+
104+
end Smt.Preprocess
105+
106+
syntax (name := replaceIteInst) "replace_ite_inst " "[" term,* "]" : tactic
107+
108+
open Lean.Elab Tactic in
109+
@[tactic replaceIteInst] def evalReconstruct : Tactic
110+
| `(tactic| replace_ite_inst [$hs,*]) => withMainContext do
111+
let mv ← getMainGoal
112+
let hs ← hs.getElems.mapM (Term.elabTerm · none)
113+
Lean.logInfo m!"Before: {hs}"
114+
let ⟨_, hs, mv⟩ ← Smt.Preprocess.replaceIteInst mv hs
115+
mv.withContext (Lean.logInfo m!"After: {hs}")
116+
replaceMainGoal [mv]
117+
| _ => throwUnsupportedSyntax
118+
119+
example (x : Int) :
120+
@ite Int (x > 0) (Classical.propDecidable (x > 0)) (x + 1) (x - 1) =
121+
@ite Int (x > 0) (Int.decLt 0 x) (x + 1) (x - 1) := by
122+
replace_ite_inst []
123+
rfl
124+
125+
example :
126+
(∀ x : Int, @ite Int (x > 0) (Classical.propDecidable (x > 0)) (x + 1) (x - 1) = 0) =
127+
(∀ x : Int, @ite Int (x > 0) (Int.decLt 0 x) (x + 1) (x - 1) = 0) := by
128+
replace_ite_inst []
129+
rfl
130+
131+
example (h : ∀ x : Int, @ite Int (x > 0) (Classical.propDecidable (x > 0)) (x + 1) (x - 1) = 0)
132+
: ∀ x : Int, @ite Int (x > 0) (Int.decLt 0 x) (x + 1) (x - 1) = 0 := by
133+
replace_ite_inst [h]
134+
exact h
135+
136+
example (n : Int) :
137+
@ite Prop (@ite Prop (1 < n) (Classical.propDecidable _) False True) (Classical.propDecidable _)
138+
(@ite Prop (1 < n) (Classical.propDecidable _) False True)
139+
True =
140+
if if 1 < n then False else True then if 1 < n then False else True else True := by
141+
replace_ite_inst []
142+
rfl
143+
144+
open Smt.Preprocess
145+
146+
set_option trace.Meta.Tactic.simp true
147+
set_option trace.Meta.Tactic.simp.congr true
148+
149+
example (n : Int) :
150+
@ite Prop (@ite Prop (1 < n) (Classical.propDecidable _) False True) (Classical.propDecidable _)
151+
(@ite Prop (1 < n) (Classical.propDecidable _) False True)
152+
True =
153+
if if 1 < n then False else True then if 1 < n then False else True else True := by
154+
simp only [classical_ite_congr]
155+
156+
example (x : Int) :
157+
@ite Int (x > 0) (Classical.propDecidable (x > 0)) (x + 1) (x - 1) =
158+
@ite Int (x > 0) (Int.decLt 0 x) (x + 1) (x - 1) := by
159+
simp only [classical_ite_congr]

Smt/Reconstruct/UF.lean

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ def getFVarOrConstExpr! (n : String) : ReconstructM Expr := do
2424

2525
@[smt_sort_reconstruct] def reconstructUS : SortReconstructor := fun s => do match s.getKind with
2626
| .UNINTERPRETED_SORT => getFVarOrConstExpr! s.getSymbol!
27-
| _ => return none
27+
| _ =>
28+
if s.isInstantiated then
29+
let base ← reconstructSort s.getUninterpretedSortConstructor!
30+
let params ← s.getInstantiatedParameters!.mapM reconstructSort
31+
return some (mkAppN base params)
32+
else
33+
return none
2834

2935
@[smt_term_reconstruct] def reconstructUF : TermReconstructor := fun t => do match t.getKind with
3036
| .APPLY_UF =>

Smt/Tactic/Smt.lean

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,12 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
7171
let ⟨map₁, hs₁, mv₂⟩ ← (if cfg.mono then Preprocess.mono else Preprocess.pushHintsToCtx) mv₁ hs
7272
-- 2. Preprocess the hypotheses and goal.
7373
let ⟨map₂, hs₂, mv₂⟩ ← if cfg.elimIff then Preprocess.elimIff mv₂ hs₁ else pure ⟨map₁, hs₁, mv₂⟩
74-
mv₂.withContext do
75-
let goalType : Q(Prop) ← mv₂.getType
74+
let ⟨map₃, hs₃, mv₃⟩ ← if cfg.elimIff then Preprocess.replaceIteInst mv₂ hs₂ else pure ⟨map₂, hs₂, mv₂⟩
75+
mv₃.withContext do
76+
let goalType : Q(Prop) ← mv₃.getType
7677
-- 3. Generate the SMT query.
7778
let (fvNames₁, fvNames₂) ← genUniqueFVarNames
78-
let cmds ← prepareSmtQuery hs.toList (← mv.getType) fvNames₁
79+
let cmds ← prepareSmtQuery hs.toList (← mv.getType) fvNames₁
7980
let cmds := .setLogic "ALL" :: cmds
8081
if cfg.showQuery then
8182
logInfo m!"goal: {goalType}\n\nquery:\n{Command.cmdsAsQuery (cmds ++ [.checkSat])}"
@@ -85,6 +86,7 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
8586
trace[smt] "goal: {goalType}"
8687
trace[smt] "\nquery:\n{Command.cmdsAsQuery (cmds ++ [.checkSat])}"
8788
-- 4. Run the solver.
89+
-- throwError "here1"
8890
let res ← solve (Command.cmdsAsQuery cmds) cfg.timeout (defaultSolverOptions ++ cfg.extraSolverOptions)
8991
-- trace[smt] "\nresult: {res}"
9092
match res with
@@ -101,9 +103,12 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
101103
let ctx := { userNames := fvNames₂, native := cfg.native }
102104
let (uc, _) ← (uc.mapM Reconstruct.reconstructTerm).run ctx {}
103105
trace[smt] "unsat core: {uc}"
104-
let ts₂ ← hs₂.mapM Meta.inferType
105-
let uc := uc.filterMap fun p => ts₂.findIdx? (· == p) >>= (hs₂[·]?)
106-
let uc := uc.filterMap (map₂[·]?)
106+
trace[smt] "unsat core: {mv₃}"
107+
-- throwError "here2"
108+
let ts₃ ← hs₃.mapM Meta.inferType
109+
let uc := uc.filterMap fun p => ts₃.findIdx? (· == p) >>= (hs₃[·]?)
110+
let uc := uc.filterMap (map₃[·]?)
111+
let uc := uc.flatten.filterMap (map₂[·]?)
107112
let uc := uc.flatten.filterMap (map₁[·]?)
108113
let uc := hs.filter uc.flatten.contains
109114
if cfg.trust then
@@ -112,10 +117,10 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
112117
return .unsat [] uc
113118
-- 7. Reconstruct proof.
114119
let (_, ps, p, hp, mvs) ← reconstructProof pf ctx
115-
let mv ← mv.assert (← mkFreshId) p hp
116-
let ⟨_, mv⟩ ← mv.intro1
117-
let mut gs ← mv.apply (← Meta.mkAppOptM ``Prop.implies_of_not_and #[listExpr ps.dropLast q(Prop), goalType])
118-
mv.withContext (gs.forM (·.assumption))
120+
let mv ← mv.assert (← mkFreshId) p hp
121+
let ⟨_, mv⟩ ← mv.intro1
122+
let mut gs ← mv.apply (← Meta.mkAppOptM ``Prop.implies_of_not_and #[listExpr ps.dropLast q(Prop), goalType])
123+
mv.withContext (gs.forM (·.assumption))
119124
mv.assign (.mvar mv₁)
120125
return .unsat mvs uc
121126
| .ok (.sat model) =>
@@ -242,11 +247,29 @@ def elabHints : TSyntax ``smtHints → TacticM (Std.HashMap Expr (TSyntax ``smtH
242247
| `(smtHints| ) => return ({}, #[])
243248
| _ => throwUnsupportedSyntax
244249

250+
/-- Returns `true` if `fv` corresponds to a proposition in the local context. -/
251+
def isPropHyp (fv : FVarId) : MetaM Bool := do
252+
let localDecl ← fv.getDecl
253+
unless localDecl.isImplementationDetail do
254+
if ← pure !(isNonEmpty localDecl.type) <&&> Meta.isProp localDecl.type then
255+
return true
256+
return false
257+
where
258+
isNonEmpty (e : Expr) : Bool :=
259+
match e with
260+
| .app (.const ``Nonempty _) _ => true
261+
| .forallE _ _ b _ => isNonEmpty b
262+
| _ => false
263+
245264
def evalSmtCore (cfg : TSyntax ``Parser.Tactic.optConfig) (hs : TSyntax ``smtHints) := withMainContext do
246265
let cfg ← elabConfig cfg
247-
let mv ← Tactic.getMainGoal
248266
let (map, hs) ← elabHints hs
249-
let res ← Smt.smt cfg mv hs
267+
let mv ← Tactic.getMainGoal
268+
let (fvs, mv) ← mv.intros
269+
Tactic.replaceMainGoal [mv]
270+
let fvs ← liftM (mv.withContext (fvs.filterM isPropHyp))
271+
let hs' := fvs.map Expr.fvar
272+
let res ← Smt.smt cfg mv (hs ++ hs')
250273
match res with
251274
| .sat none =>
252275
throwError "unable to prove goal, either it is false or you need to provide more facts. Try adding '+model' config option to display a potential counter-example (experimental)."
@@ -260,6 +283,7 @@ def evalSmtCore (cfg : TSyntax ``Parser.Tactic.optConfig) (hs : TSyntax ``smtHin
260283
throwError "unable to prove goal, either it is false or you need to provide more facts. Here is a potential counter-example:\n{md}"
261284
| .unsat mvs uc =>
262285
Tactic.replaceMainGoal mvs
286+
let uc := uc.filter (!hs'.contains ·)
263287
let uc := uc.filterMap map.get?
264288
let uc := uc.toList.eraseDups.toArray
265289
return uc

lake-manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
"type": "git",
1616
"subDir": null,
1717
"scope": "",
18-
"rev": "77f45ae314d657466853138a61cba8fca00094c8",
18+
"rev": "5d96f48fbcee46a5bc35c49121ed3d81fd54af4b",
1919
"name": "cvc5",
2020
"manifestFile": "lake-manifest.json",
21-
"inputRev": "77f45ae314d657466853138a61cba8fca00094c8",
21+
"inputRev": "5d96f48fbcee46a5bc35c49121ed3d81fd54af4b",
2222
"inherited": false,
2323
"configFile": "lakefile.lean"},
2424
{"url": "https://github.com/leanprover-community/lean-auto.git",

lakefile.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require auto from
66
git "https://github.com/leanprover-community/lean-auto.git" @ "2c088e7617d6e2018386de23b5df3b127fae4634"
77

88
require cvc5 from
9-
git "https://github.com/abdoo8080/lean-cvc5.git" @ "77f45ae"
9+
git "https://github.com/abdoo8080/lean-cvc5.git" @ "5d96f48fbcee46a5bc35c49121ed3d81fd54af4b"
1010

1111
require mathlib from
1212
git "https://github.com/leanprover-community/mathlib4.git" @ "v4.23.0"

0 commit comments

Comments
 (0)