Skip to content

Commit feadf9e

Browse files
committed
Retreat demo changes.
1 parent d6c048b commit feadf9e

File tree

7 files changed

+156
-18
lines changed

7 files changed

+156
-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: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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

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: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ structure Config where
3232
mono : Bool := false
3333
/-- Whether to eliminate `↔` in the Lean goal before sending it to the SMT solver. -/
3434
elimIff : Bool := true
35+
/-- Whether to replace `ite` instances in the Lean goal before sending it to the SMT solver. -/
36+
replaceIteInst : Bool := true
3537
/-- Whether to trust the result of the SMT solver. Closes the current goal with a `sorry` if the
3638
SMT solver returns `unsat`. **Warning**: use with caution, as this may lead to unsoundness.
3739
Additionally adds the translation from Lean to SMT to the trusted code base, which is not
@@ -71,11 +73,12 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
7173
let ⟨map₁, hs₁, mv₂⟩ ← (if cfg.mono then Preprocess.mono else Preprocess.pushHintsToCtx) mv₁ hs
7274
-- 2. Preprocess the hypotheses and goal.
7375
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
76+
let ⟨map₃, hs₃, mv₃⟩ ← if cfg.replaceIteInst then Preprocess.replaceIteInst mv₂ hs₂ else pure ⟨map₂, hs₂, mv₂⟩
77+
mv₃.withContext do
78+
let goalType : Q(Prop) ← mv₃.getType
7679
-- 3. Generate the SMT query.
7780
let (fvNames₁, fvNames₂) ← genUniqueFVarNames
78-
let cmds ← prepareSmtQuery hs.toList (← mv.getType) fvNames₁
81+
let cmds ← prepareSmtQuery hs.toList (← mv.getType) fvNames₁
7982
let cmds := .setLogic "ALL" :: cmds
8083
if cfg.showQuery then
8184
logInfo m!"goal: {goalType}\n\nquery:\n{Command.cmdsAsQuery (cmds ++ [.checkSat])}"
@@ -85,6 +88,7 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
8588
trace[smt] "goal: {goalType}"
8689
trace[smt] "\nquery:\n{Command.cmdsAsQuery (cmds ++ [.checkSat])}"
8790
-- 4. Run the solver.
91+
-- throwError "here1"
8892
let res ← solve (Command.cmdsAsQuery cmds) cfg.timeout (defaultSolverOptions ++ cfg.extraSolverOptions)
8993
-- trace[smt] "\nresult: {res}"
9094
match res with
@@ -101,9 +105,12 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
101105
let ctx := { userNames := fvNames₂, native := cfg.native }
102106
let (uc, _) ← (uc.mapM Reconstruct.reconstructTerm).run ctx {}
103107
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₂[·]?)
108+
trace[smt] "unsat core: {mv₃}"
109+
-- throwError "here2"
110+
let ts₃ ← hs₃.mapM Meta.inferType
111+
let uc := uc.filterMap fun p => ts₃.findIdx? (· == p) >>= (hs₃[·]?)
112+
let uc := uc.filterMap (map₃[·]?)
113+
let uc := uc.flatten.filterMap (map₂[·]?)
107114
let uc := uc.flatten.filterMap (map₁[·]?)
108115
let uc := hs.filter uc.flatten.contains
109116
if cfg.trust then
@@ -112,10 +119,10 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
112119
return .unsat [] uc
113120
-- 7. Reconstruct proof.
114121
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))
122+
let mv ← mv.assert (← mkFreshId) p hp
123+
let ⟨_, mv⟩ ← mv.intro1
124+
let mut gs ← mv.apply (← Meta.mkAppOptM ``Prop.implies_of_not_and #[listExpr ps.dropLast q(Prop), goalType])
125+
mv.withContext (gs.forM (·.assumption))
119126
mv.assign (.mvar mv₁)
120127
return .unsat mvs uc
121128
| .ok (.sat model) =>
@@ -242,11 +249,29 @@ def elabHints : TSyntax ``smtHints → TacticM (Std.HashMap Expr (TSyntax ``smtH
242249
| `(smtHints| ) => return ({}, #[])
243250
| _ => throwUnsupportedSyntax
244251

252+
/-- Returns `true` if `fv` corresponds to a proposition in the local context. -/
253+
def isPropHyp (fv : FVarId) : MetaM Bool := do
254+
let localDecl ← fv.getDecl
255+
unless localDecl.isImplementationDetail do
256+
if ← pure !(isNonEmpty localDecl.type) <&&> Meta.isProp localDecl.type then
257+
return true
258+
return false
259+
where
260+
isNonEmpty (e : Expr) : Bool :=
261+
match e with
262+
| .app (.const ``Nonempty _) _ => true
263+
| .forallE _ _ b _ => isNonEmpty b
264+
| _ => false
265+
245266
def evalSmtCore (cfg : TSyntax ``Parser.Tactic.optConfig) (hs : TSyntax ``smtHints) := withMainContext do
246267
let cfg ← elabConfig cfg
247-
let mv ← Tactic.getMainGoal
248268
let (map, hs) ← elabHints hs
249-
let res ← Smt.smt cfg mv hs
269+
let mv ← Tactic.getMainGoal
270+
let (fvs, mv) ← mv.intros
271+
Tactic.replaceMainGoal [mv]
272+
let fvs ← liftM (mv.withContext (fvs.filterM isPropHyp))
273+
let hs' := fvs.map Expr.fvar
274+
let res ← Smt.smt cfg mv (hs ++ hs')
250275
match res with
251276
| .sat none =>
252277
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 +285,7 @@ def evalSmtCore (cfg : TSyntax ``Parser.Tactic.optConfig) (hs : TSyntax ``smtHin
260285
throwError "unable to prove goal, either it is false or you need to provide more facts. Here is a potential counter-example:\n{md}"
261286
| .unsat mvs uc =>
262287
Tactic.replaceMainGoal mvs
288+
let uc := uc.filter (!hs'.contains ·)
263289
let uc := uc.filterMap map.get?
264290
let uc := uc.toList.eraseDups.toArray
265291
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)