Skip to content

Commit 9c13e67

Browse files
committed
resolves #52
1 parent b5b0d32 commit 9c13e67

5 files changed

Lines changed: 161 additions & 29 deletions

File tree

Auto/Lib/MetaExtra.lean

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,72 @@ def Meta.Context.modifyLCtx (ctx : Context) (lctx : LocalContext) : CoreM Contex
156156
def Meta.Context.modifyLocalInstances (ctx : Context) (localInsts : LocalInstances) : CoreM Context :=
157157
MetaM.run' (withLCtx ctx.lctx localInsts read) ctx
158158

159+
/--
160+
Replace occurrences of `p` in non-dependent positions in `e` with `q`. The type
161+
of the resulting expression should be identical to the type of `e`
162+
· Both `e` and `q` are not supposed to contain loose bvars
163+
· We detect subterms equivalent to `p` using key-matching.
164+
That is, only perform `isDefEq` tests when the head symbol of substerm is equivalent to head symbol of `p`.
165+
· We only abstract non-dependent positions
166+
That is, if there is a function application `f a` and the argument of
167+
`f` is dependent, then occurrences of `p` in `a` will not be abstracted
168+
169+
By default, all occurrences are abstracted,
170+
but this behavior can be controlled using the `occs` parameter.
171+
172+
All matches of `p` in `e` are considered for occurrences,
173+
but for each match that is included by the `occs` parameter,
174+
metavariables appearing in `p` (or `e`) may become instantiated,
175+
affecting the possibility of subsequent matches.
176+
For matches that are not included in the `occs` parameter, the metavariable context is rolled back
177+
to prevent blocking subsequent matches which require different instantiations.
178+
-/
179+
partial def Meta.replaceNonDep (e : Expr) (p : Expr) (q : Expr) (occs : Occurrences := .all) : MetaM Expr := do
180+
let e ← instantiateMVars e
181+
let pHeadIdx := p.toHeadIndex
182+
let pNumArgs := p.headNumArgs
183+
let rec visit (e : Expr) : StateRefT Nat MetaM Expr := do
184+
let visitChildren : Unit → StateRefT Nat MetaM Expr := fun _ => do
185+
match e with
186+
| .app f a => do
187+
let type ← Meta.whnf (← Meta.inferType f)
188+
let .forallE _ _ b _ := type
189+
| throwError "{decl_name%} :: {type} is not a `∀`"
190+
if b.hasLooseBVar 0 then
191+
return e.updateApp! (← visit f) a
192+
else
193+
return e.updateApp! (← visit f) (← visit a)
194+
| .mdata _ b => return e.updateMData! (← visit b)
195+
| .proj _ _ b => return e.updateProj! (← visit b)
196+
| .letE n t v b _ =>
197+
Meta.withLetDecl n t (← visit v) fun x =>
198+
return ← mkLetFVars #[x] (← visit (b.instantiate1 x))
199+
| .lam .. =>
200+
Meta.lambdaTelescope e fun xs b => do
201+
return ← mkLambdaFVars xs (← visit b)
202+
| .forallE n d b bi => do
203+
let d' ← (if b.hasLooseBVar 0 then return d else visit d)
204+
Meta.withLocalDecl n bi d' fun x => do
205+
return ← mkForallFVars #[x] (← visit (b.instantiate1 x))
206+
| e => return e
207+
if e.toHeadIndex != pHeadIdx || e.headNumArgs != pNumArgs then
208+
visitChildren ()
209+
else
210+
-- We save the metavariable context here,
211+
-- so that it can be rolled back unless `occs.contains i`.
212+
let mctx ← getMCtx
213+
if (← isDefEq e p) then
214+
let i ← get
215+
set (i+1)
216+
if occs.contains i then
217+
return q
218+
else
219+
-- Revert the metavariable context,
220+
-- so that other matches are still possible.
221+
setMCtx mctx
222+
visitChildren ()
223+
else
224+
visitChildren ()
225+
visit e |>.run' 1
226+
159227
end Auto

Auto/Tactic.lean

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -454,13 +454,12 @@ def queryNative
454454
def rewriteIteCondDecide (lemmas : Array Lemma) : MetaM (Array Lemma) := do
455455
-- Simplify `ite`
456456
let ite_simp_lem ← Lemma.ofConst ``Auto.Bool.ite_simp (.leaf "hw Auto.Bool.ite_simp")
457-
let lemmas ← lemmas.mapM (fun lem => Lemma.rewriteUPolyRigid lem ite_simp_lem)
458457
-- Simplify `cond`
459458
let cond_simp_lem ← Lemma.ofConst ``Auto.Bool.cond_simp (.leaf "hw Auto.Bool.cond_simp")
460-
let lemmas ← lemmas.mapM (fun lem => Lemma.rewriteUPolyRigid lem cond_simp_lem)
461459
-- Simplify `decide`
462460
let decide_simp_lem ← Lemma.ofConst ``Auto.Bool.decide_simp (.leaf "hw Auto.Bool.decide_simp")
463-
let lemmas ← lemmas.mapM (fun lem => Lemma.rewriteUPolyRigid lem decide_simp_lem)
461+
let lemmas ← lemmas.mapM (fun lem => Lemma.rewriteUPolyRigidNonDep
462+
lem #[ite_simp_lem, cond_simp_lem, decide_simp_lem])
464463
return lemmas
465464

466465
/--

Auto/Translation/Assumptions.lean

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Auto.Lib.BoolExtra
33
import Auto.Lib.MessageData
44
import Auto.Lib.ExprExtra
55
import Auto.Lib.ListExtra
6+
import Auto.Lib.MetaExtra
67
import Auto.Lib.Containers
78
import Auto.Lib.AbstractMVars
89
open Lean
@@ -122,20 +123,29 @@ def Lemma.reorderForallInstDep (lem : Lemma) : MetaM Lemma := do
122123
· If `lhs` occurs in `lem.type`, perform rewrite and return result
123124
· If `lhs` does not occur in `lem.type`, return `.none`
124125
-/
125-
def Lemma.rewriteUMonoRigid? (lem : Lemma) (rw : UMonoFact) : MetaM (Option Lemma) := do
126+
def Lemma.rewriteUMonoRigid? (lem : Lemma) (rw : UMonoFact) (nonDep : Bool) : MetaM (Option Lemma) := do
126127
let ⟨rwproof, rwtype, rwDeriv⟩ := rw
127128
let .some (α, lhs, rhs) ← Meta.matchEq? rwtype
128129
| throwError "{decl_name%} :: {rwtype} is not an equality"
129130
let ⟨⟨proof, e, lemDeriv⟩, params⟩ := lem
130-
let eAbst ← Meta.kabstract e lhs
131+
let eAbst ← (do
132+
if nonDep then
133+
Meta.withLocalDeclD `_a α fun x => do return (← Meta.replaceNonDep e lhs x).abstract #[x]
134+
else Meta.kabstract e lhs)
131135
unless eAbst.hasLooseBVars do
132136
return .none
133137
let eNew := eAbst.instantiate1 rhs
134138
let motive := mkLambda `_a BinderInfo.default α eAbst
135139
unless (← Meta.isTypeCorrect motive) do
136140
throwError "{decl_name%} :: Motive {motive} is not type correct"
137141
let eqPrf ← Meta.mkEqNDRec motive proof rwproof
138-
return .some ⟨⟨eqPrf, eNew, .node "rw" #[lemDeriv, rwDeriv]⟩, params⟩
142+
return .some ⟨⟨eqPrf, ← Core.betaReduce eNew, .node "rw" #[lemDeriv, rwDeriv]⟩, params⟩
143+
144+
def checkNonRecEquality (e : Expr) : MetaM Unit := do
145+
let .some (_, lhs, rhs) ← Meta.matchEq? e
146+
| throwError "{decl_name%} :: {e} is not an equality"
147+
if (← Meta.kabstract rhs lhs).hasLooseBVars then
148+
throwError "{decl_name%} :: Right-hand side {rhs} of equality contains left-hand side {lhs}"
139149

140150
/--
141151
Exhaustively rewrite using a universe-polymorphic rigid equality
@@ -147,19 +157,16 @@ def Lemma.rewriteUMonoRigid? (lem : Lemma) (rw : UMonoFact) : MetaM (Option Lemm
147157
def Lemma.rewriteUPolyRigid (lem : Lemma) (rw : Lemma) : MetaM Lemma := do
148158
let mut lem := lem
149159
let s ← saveState
150-
-- Test whether `rhs` contains `lhs
151-
let .some (_, lhs, rhs) ← Meta.matchEq? rw.type
152-
| throwError "{decl_name%} :: {rw.type} is not an equality"
153-
let lhs' := lhs.instantiateLevelParamsArray rw.params (← rw.params.mapM (fun _ => Meta.mkFreshLevelMVar))
154-
let rhs' := rhs.instantiateLevelParamsArray rw.params (← rw.params.mapM (fun _ => Meta.mkFreshLevelMVar))
155-
if (← Meta.kabstract rhs' lhs').hasLooseBVars then
156-
throwError "{decl_name%} :: Right-hand side {rhs} of equality contains left-hand side {lhs}"
160+
-- Test whether `rhs` contains `lhs`
161+
let rwty' := rw.type.instantiateLevelParamsArray
162+
rw.params (← rw.params.mapM (fun _ => Meta.mkFreshLevelMVar))
163+
checkNonRecEquality rwty'
157164
restoreState s
158165
while true do
159166
let umvars ← rw.params.mapM (fun _ => Meta.mkFreshLevelMVar)
160167
let .some urw := (rw.instantiateLevelParamsArray umvars).toUMonoFact?
161168
| throwError "{decl_name%} :: Unexpected error"
162-
let .some lem' ← Lemma.rewriteUMonoRigid? lem urw
169+
let .some lem' ← Lemma.rewriteUMonoRigid? lem urw false
163170
| break
164171
let restmvars := (← umvars.mapM Level.collectLevelMVars).flatMap id
165172
for lmvar in restmvars do
@@ -169,6 +176,41 @@ def Lemma.rewriteUPolyRigid (lem : Lemma) (rw : Lemma) : MetaM Lemma := do
169176
restoreState s
170177
return ← lem.betaReduceType
171178

179+
/--
180+
Exhaustively rewrite using an array of universe-polymorphic rigid equalities
181+
· Only `lhs`s occurring in non-dependent positions will be replaced by `rhs`
182+
· If there are multiple instances of `lhs` with different universe
183+
level instantiations, all of these instances will be replaced with `rhs`
184+
· `rw.snd` should have the form `lhs = rhs`, where both sides are rigid
185+
and `rhs` should not contain `lhs`
186+
-/
187+
def Lemma.rewriteUPolyRigidNonDep (lem : Lemma) (rws : Array Lemma) : MetaM Lemma := do
188+
let mut lem := lem
189+
let s ← saveState
190+
-- Test whether `rhs` contains `lhs`
191+
for rw in rws do
192+
let rwty' := rw.type.instantiateLevelParamsArray
193+
rw.params (← rw.params.mapM (fun _ => Meta.mkFreshLevelMVar))
194+
checkNonRecEquality rwty'
195+
restoreState s
196+
while true do
197+
let lemRef := lem
198+
for rw in rws do
199+
let umvars ← rw.params.mapM (fun _ => Meta.mkFreshLevelMVar)
200+
let .some urw := (rw.instantiateLevelParamsArray umvars).toUMonoFact?
201+
| throwError "{decl_name%} :: Unexpected error"
202+
let .some lem' ← Lemma.rewriteUMonoRigid? lem urw true
203+
| continue
204+
let restmvars := (← umvars.mapM Level.collectLevelMVars).flatMap id
205+
for lmvar in restmvars do
206+
if !(← Meta.isLevelDefEq (.mvar lmvar) .zero) then
207+
break
208+
lem ← lem'.instantiateMVars
209+
if lem == lemRef then
210+
break
211+
restoreState s
212+
return lem
213+
172214
/-
173215
An instance of a `Lemma`. If a lemma has proof `H`,
174216
then an instance of the lemma would be like

Test/Bugs.lean

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,6 @@ set_option auto.smt.solver.name "z3"
1515
set_option trace.auto.native.printFormulas true
1616
attribute [rebind Auto.Native.solverFunc] Auto.Solver.Native.emulateNative
1717

18-
set_option auto.native true
19-
set_option trace.auto.lamReif.printResult true
20-
set_option trace.auto.lamReif.printValuation true
21-
22-
set_option trace.auto.printLemmas true
23-
set_option auto.redMode "instances"
24-
example : (∀ (xs ys zs : List α), xs ++ ys ++ zs = xs ++ (ys ++ zs)) := by
25-
intro xs; induction xs <;> (mono [*] d[List.append]; sorry)
26-
27-
example (x : α) : List.head? [x] = .some x := by
28-
have list_head_unfold : @List.head? α = (fun x =>
29-
@List.casesOn α (fun x => (fun x => Option α) x) x ((fun _ => @none α) Unit.unit) fun head tail =>
30-
(fun a tail => @some α a) head tail) := by sorry
31-
mono [list_head_unfold] d[List.rec]; sorry
32-
3318
-- set_option auto.tptp true
3419
-- set_option trace.auto.tptp.premiseSelection true
3520

@@ -90,3 +75,18 @@ end Set
9075
example (x : Nat) (primeset : Nat → Prop) (dvd : Nat → Nat → Prop) :
9176
((∃ (i : _) (i_1 : primeset i), dvd i x) ↔ (∃ p, primeset p ∧ dvd p x)) := by
9277
auto
78+
79+
section
80+
81+
variable (world : Type)
82+
83+
@[reducible] def F: Type := Nat → world
84+
85+
@[reducible] def G : Type := F world → (Nat → Prop)
86+
87+
set_option trace.auto.lamReif.printValuation true
88+
set_option auto.mono.mode "fol"
89+
example (f : Nat → world) (p : G world) : p f 0 ∨ ¬p f 0 := by
90+
auto
91+
92+
end

Test/Test_Regression.lean

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,29 @@ section Adhoc
599599
(if True then x else y) = x ∧ (if False then z else t) = t := by
600600
auto
601601

602+
-- Nested `ite`
603+
604+
set_option auto.smt true in
605+
set_option auto.tptp false in
606+
example
607+
(h : if (if 2 = 3 then 1 else 2) = (if 3 = 2 then 0 else 1) then True else False)
608+
: False := by
609+
auto
610+
611+
set_option trace.auto.mono.printInputLemmas true
612+
open Classical
613+
example
614+
(node : Type) [node_dec : DecidableEq node] (ring_bottom : node)
615+
(st0_x st0_up st1_x st1_up : node → Prop) (s x x_1 : node)
616+
(bad_motive :
617+
¬if s = ring_bottom then True
618+
else if st0_x s = st0_x x then True
619+
else if ((¬st0_x s) = if x_1 = s then ¬st0_x s else st0_x x_1) ∧ ¬x_1 = s ∧ ¬st0_up x_1 then
620+
(∀ (a_1 : node), (if a_1 = s then ¬st0_x s else st0_x a_1) = st1_x a_1) → ∃ x, ¬(¬x = s ∧ (¬x = s → st0_up x)) = st1_up x
621+
else True) :
622+
True := by
623+
auto
624+
602625
-- Boolean
603626
example : truefalse := by
604627
auto

0 commit comments

Comments
 (0)