Compare commits

...

4 Commits

Author SHA1 Message Date
Leonardo de Moura
7181b02e96 chore: cleanup 2025-01-01 21:15:38 -08:00
Leonardo de Moura
d31fd653ed feat: move intro-like actions to new file 2025-01-01 21:15:37 -08:00
Leonardo de Moura
45f48e80d4 refactor: rename GrindCoreM => GrindM
The goal management monad will be implemented in `Lean/Elab/Tactic`.
2025-01-01 21:15:37 -08:00
Leonardo de Moura
5462d85646 feat: simple strategy for grind 2025-01-01 21:15:37 -08:00
17 changed files with 355 additions and 324 deletions

View File

@@ -7,7 +7,6 @@ prelude
import Lean.Meta.Tactic.Grind.Attr
import Lean.Meta.Tactic.Grind.RevertAll
import Lean.Meta.Tactic.Grind.Types
import Lean.Meta.Tactic.Grind.Preprocessor
import Lean.Meta.Tactic.Grind.Util
import Lean.Meta.Tactic.Grind.Cases
import Lean.Meta.Tactic.Grind.Injection
@@ -22,6 +21,9 @@ import Lean.Meta.Tactic.Grind.Simp
import Lean.Meta.Tactic.Grind.Ctor
import Lean.Meta.Tactic.Grind.Parser
import Lean.Meta.Tactic.Grind.EMatchTheorem
import Lean.Meta.Tactic.Grind.EMatch
import Lean.Meta.Tactic.Grind.Main
namespace Lean
@@ -41,8 +43,8 @@ builtin_initialize registerTraceClass `grind.simp
builtin_initialize registerTraceClass `grind.debug
builtin_initialize registerTraceClass `grind.debug.proofs
builtin_initialize registerTraceClass `grind.debug.congr
builtin_initialize registerTraceClass `grind.debug.pre
builtin_initialize registerTraceClass `grind.debug.proof
builtin_initialize registerTraceClass `grind.debug.proj
builtin_initialize registerTraceClass `grind.debug.parent
end Lean

View File

@@ -41,6 +41,7 @@ This is an auxiliary function performed while merging equivalence classes.
private def removeParents (root : Expr) : GoalM ParentSet := do
let parents getParentsAndReset root
for parent in parents do
trace[grind.debug.parent] "remove: {parent}"
modify fun s => { s with congrTable := s.congrTable.erase { e := parent } }
return parents
@@ -50,6 +51,7 @@ This is an auxiliary function performed while merging equivalence classes.
-/
private def reinsertParents (parents : ParentSet) : GoalM Unit := do
for parent in parents do
trace[grind.debug.parent] "reinsert: {parent}"
addCongrTable parent
/-- Closes the goal when `True` and `False` are in the same equivalence class. -/
@@ -223,10 +225,8 @@ where
internalize rhs generation
addEqCore lhs rhs proof isHEq
/--
Adds a new hypothesis.
-/
def addHyp (fvarId : FVarId) (generation := 0) : GoalM Unit := do
/-- Adds a new hypothesis. -/
def addHypothesis (fvarId : FVarId) (generation := 0) : GoalM Unit := do
add ( fvarId.getType) (mkFVar fvarId) generation
end Lean.Meta.Grind

View File

@@ -5,7 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Meta.Tactic.Grind.Types
import Lean.Meta.Tactic.Grind.Internalize
import Lean.Meta.Tactic.Grind.Intro
namespace Lean.Meta.Grind
namespace EMatch
@@ -278,4 +278,15 @@ def ematch : GoalM Unit := do
gmt := s.gmt + 1
}
/-- Performs one round of E-matching, and assert new instances. -/
def ematchAndAssert? (goal : Goal) : GrindM (Option (List Goal)) := do
let numInstances := goal.numInstances
let goal GoalM.run' goal ematch
if goal.numInstances == numInstances then
return none
assertAll goal
def ematchStar (goal : Goal) : GrindM (List Goal) := do
iterate goal ematchAndAssert?
end Lean.Meta.Grind

View File

@@ -20,7 +20,7 @@ def propagateForallProp (parent : Expr) : GoalM Unit := do
unless ( isEqTrue p) do return ()
let h₁ mkEqTrueProof p
let qh₁ := q.instantiate1 (mkApp2 (mkConst ``of_eq_true) p h₁)
let r pre qh₁
let r simp qh₁
let q := mkLambda n bi p q
let q' := r.expr
internalize q' ( getGeneration parent)

View File

@@ -0,0 +1,139 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Grind.Lemmas
import Lean.Meta.Tactic.Assert
import Lean.Meta.Tactic.Grind.Simp
import Lean.Meta.Tactic.Grind.Types
import Lean.Meta.Tactic.Grind.Cases
import Lean.Meta.Tactic.Grind.Injection
import Lean.Meta.Tactic.Grind.Core
namespace Lean.Meta.Grind
private inductive IntroResult where
| done
| newHyp (fvarId : FVarId) (goal : Goal)
| newDepHyp (goal : Goal)
| newLocal (fvarId : FVarId) (goal : Goal)
deriving Inhabited
private def introNext (goal : Goal) : GrindM IntroResult := do
let target goal.mvarId.getType
if target.isArrow then
goal.mvarId.withContext do
let p := target.bindingDomain!
if !( isProp p) then
let (fvarId, mvarId) goal.mvarId.intro1P
return .newLocal fvarId { goal with mvarId }
else
let tag goal.mvarId.getTag
let q := target.bindingBody!
-- TODO: keep applying simp/eraseIrrelevantMData/canon/shareCommon until no progress
let r simp p
let fvarId mkFreshFVarId
let lctx := ( getLCtx).mkLocalDecl fvarId target.bindingName! r.expr target.bindingInfo!
let mvarNew mkFreshExprMVarAt lctx ( getLocalInstances) q .syntheticOpaque tag
let mvarIdNew := mvarNew.mvarId!
mvarIdNew.withContext do
let h mkLambdaFVars #[mkFVar fvarId] mvarNew
match r.proof? with
| some he =>
let hNew := mkAppN (mkConst ``Lean.Grind.intro_with_eq) #[p, r.expr, q, he, h]
goal.mvarId.assign hNew
return .newHyp fvarId { goal with mvarId := mvarIdNew }
| none =>
-- `p` and `p'` are definitionally equal
goal.mvarId.assign h
return .newHyp fvarId { goal with mvarId := mvarIdNew }
else if target.isLet || target.isForall then
let (fvarId, mvarId) goal.mvarId.intro1P
mvarId.withContext do
let localDecl fvarId.getDecl
if ( isProp localDecl.type) then
-- Add a non-dependent copy
let mvarId mvarId.assert ( mkFreshUserName localDecl.userName) localDecl.type (mkFVar fvarId)
return .newDepHyp { goal with mvarId }
else
return .newLocal fvarId { goal with mvarId }
else
return .done
private def isCasesCandidate (fvarId : FVarId) : MetaM Bool := do
let .const declName _ := ( fvarId.getType).getAppFn | return false
isGrindCasesTarget declName
private def applyCases? (goal : Goal) (fvarId : FVarId) : MetaM (Option (List Goal)) := goal.mvarId.withContext do
if ( isCasesCandidate fvarId) then
let mvarIds cases goal.mvarId fvarId
return mvarIds.map fun mvarId => { goal with mvarId }
else
return none
private def applyInjection? (goal : Goal) (fvarId : FVarId) : MetaM (Option Goal) := do
if let some mvarId injection? goal.mvarId fvarId then
return some { goal with mvarId }
else
return none
/-- Introduce new hypotheses (and apply `by_contra`) until goal is of the form `... ⊢ False` -/
partial def intros (goal : Goal) (generation : Nat) : GrindM (List Goal) := do
let rec go (goal : Goal) : StateRefT (Array Goal) GrindM Unit := do
if goal.inconsistent then
return ()
match ( introNext goal) with
| .done =>
if let some mvarId goal.mvarId.byContra? then
go { goal with mvarId }
else
modify fun s => s.push goal
| .newHyp fvarId goal =>
if let some goals applyCases? goal fvarId then
goals.forM go
else if let some goal applyInjection? goal fvarId then
go goal
else
go ( GoalM.run' goal <| addHypothesis fvarId generation)
| .newDepHyp goal =>
go goal
| .newLocal fvarId goal =>
if let some goals applyCases? goal fvarId then
goals.forM go
else
go goal
let (_, goals) (go goal).run #[]
return goals.toList
/-- Asserts a new fact `prop` with proof `proof` to the given `goal`. -/
def assertAt (goal : Goal) (proof : Expr) (prop : Expr) (generation : Nat) : GrindM (List Goal) := do
-- TODO: check whether `prop` may benefit from `intros` or not. If not, we should avoid the `assert`+`intros` step and use `Grind.add`
let mvarId goal.mvarId.assert ( mkFreshUserName `h) prop proof
let goal := { goal with mvarId }
intros goal generation
/-- Asserts next fact in the `goal` fact queue. -/
def assertNext? (goal : Goal) : GrindM (Option (List Goal)) := do
let some (fact, newFacts) := goal.newFacts.dequeue?
| return none
assertAt { goal with newFacts } fact.proof fact.prop fact.generation
partial def iterate (goal : Goal) (f : Goal GrindM (Option (List Goal))) : GrindM (List Goal) := do
go [goal] []
where
go (todo : List Goal) (result : List Goal) : GrindM (List Goal) := do
match todo with
| [] => return result
| goal :: todo =>
if let some goalsNew f goal then
go (goalsNew ++ todo) result
else
go todo (goal :: result)
/-- Asserts all facts in the `goal` fact queue. -/
partial def assertAll (goal : Goal) : GrindM (List Goal) := do
iterate goal assertNext?
end Lean.Meta.Grind

View File

@@ -99,4 +99,7 @@ def checkInvariants (expensive := false) : GoalM Unit := do
if expensive && grind.debug.proofs.get ( getOptions) then
checkProofs
def Goal.checkInvariants (goal : Goal) (expensive := false) : GrindM Unit :=
discard <| GoalM.run' goal <| Grind.checkInvariants expensive
end Lean.Meta.Grind

View File

@@ -0,0 +1,93 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Grind.Lemmas
import Lean.Meta.Tactic.Util
import Lean.Meta.Tactic.Grind.RevertAll
import Lean.Meta.Tactic.Grind.PropagatorAttr
import Lean.Meta.Tactic.Grind.Proj
import Lean.Meta.Tactic.Grind.ForallProp
import Lean.Meta.Tactic.Grind.Util
import Lean.Meta.Tactic.Grind.Inv
import Lean.Meta.Tactic.Grind.Intro
import Lean.Meta.Tactic.Grind.EMatch
namespace Lean.Meta.Grind
def mkMethods : CoreM Methods := do
let builtinPropagators builtinPropagatorsRef.get
return {
propagateUp := fun e => do
propagateForallProp e
let .const declName _ := e.getAppFn | return ()
propagateProjEq e
if let some prop := builtinPropagators.up[declName]? then
prop e
propagateDown := fun e => do
let .const declName _ := e.getAppFn | return ()
if let some prop := builtinPropagators.down[declName]? then
prop e
}
def GrindM.run (x : GrindM α) (mainDeclName : Name) (config : Grind.Config) : MetaM α := do
let scState := ShareCommon.State.mk _
let (falseExpr, scState) := ShareCommon.State.shareCommon scState (mkConst ``False)
let (trueExpr, scState) := ShareCommon.State.shareCommon scState (mkConst ``True)
let thms grindNormExt.getTheorems
let simprocs := #[( grindNormSimprocExt.getSimprocs)]
let simp Simp.mkContext
(config := { arith := true })
(simpTheorems := #[thms])
(congrTheorems := ( getSimpCongrTheorems))
x ( mkMethods).toMethodsRef { mainDeclName, config, simprocs, simp } |>.run' { scState, trueExpr, falseExpr }
private def mkGoal (mvarId : MVarId) : GrindM Goal := do
let trueExpr getTrueExpr
let falseExpr getFalseExpr
let thmMap getEMatchTheorems
GoalM.run' { mvarId, thmMap } do
mkENodeCore falseExpr (interpreted := true) (ctor := false) (generation := 0)
mkENodeCore trueExpr (interpreted := true) (ctor := false) (generation := 0)
private def initCore (mvarId : MVarId) : GrindM (List Goal) := do
mvarId.ensureProp
-- TODO: abstract metavars
mvarId.ensureNoMVar
let mvarId mvarId.clearAuxDecls
let mvarId mvarId.revertAll
let mvarId mvarId.unfoldReducible
let mvarId mvarId.betaReduce
let goals intros ( mkGoal mvarId) (generation := 0)
goals.forM (·.checkInvariants (expensive := true))
return goals.filter fun goal => !goal.inconsistent
def all (goals : List Goal) (f : Goal GrindM (List Goal)) : GrindM (List Goal) := do
goals.foldlM (init := []) fun acc goal => return acc ++ ( f goal)
/-- A very simple strategy -/
private def simple (goals : List Goal) : GrindM (List Goal) := do
all goals ematchStar
def main (mvarId : MVarId) (config : Grind.Config) (mainDeclName : Name) : MetaM (List MVarId) := do
let go : GrindM (List MVarId) := do
let goals initCore mvarId
let goals simple goals
trace[grind.debug.final] "{← ppGoals goals}"
return goals.map (·.mvarId)
go.run mainDeclName config
/-- Helper function for debugging purposes -/
def preprocessAndProbe (mvarId : MVarId) (mainDeclName : Name) (p : GoalM Unit) : MetaM Unit :=
let go : GrindM Unit := do
let goals initCore mvarId
trace[grind.debug.final] "{← ppGoals goals}"
goals.forM fun goal =>
discard <| GoalM.run' goal p
return ()
withoutModifyingMCtx do
go.run mainDeclName {}
end Lean.Meta.Grind

View File

@@ -56,4 +56,11 @@ def ppState : GoalM Format := do
r := r ++ "\n" ++ "{" ++ (Format.joinSep ( eqc.mapM ppENodeRef) ", ") ++ "}"
return r
def ppGoals (goals : List Goal) : GrindM Format := do
let mut r := f!""
for goal in goals do
let (f, _) GoalM.run goal ppState
r := r ++ Format.line ++ f
return r
end Lean.Meta.Grind

View File

@@ -1,177 +0,0 @@
/-
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Grind.Lemmas
import Lean.Meta.Canonicalizer
import Lean.Meta.Tactic.Util
import Lean.Meta.Tactic.Intro
import Lean.Meta.Tactic.Simp.Main
import Lean.Meta.Tactic.Grind.Attr
import Lean.Meta.Tactic.Grind.RevertAll
import Lean.Meta.Tactic.Grind.Types
import Lean.Meta.Tactic.Grind.Util
import Lean.Meta.Tactic.Grind.Cases
import Lean.Meta.Tactic.Grind.Injection
import Lean.Meta.Tactic.Grind.Core
import Lean.Meta.Tactic.Grind.Simp
import Lean.Meta.Tactic.Grind.Run
import Lean.Meta.Tactic.Grind.EMatch
namespace Lean.Meta.Grind
namespace Preprocessor
structure State where
goals : PArray Goal := {}
deriving Inhabited
abbrev PreM := StateRefT State GrindCoreM
def PreM.run (x : PreM α) : GrindCoreM α := do
x.run' {}
inductive IntroResult where
| done
| newHyp (fvarId : FVarId) (goal : Goal)
| newDepHyp (goal : Goal)
| newLocal (fvarId : FVarId) (goal : Goal)
def introNext (goal : Goal) : PreM IntroResult := do
let target goal.mvarId.getType
if target.isArrow then
goal.mvarId.withContext do
let p := target.bindingDomain!
if !( isProp p) then
let (fvarId, mvarId) goal.mvarId.intro1P
return .newLocal fvarId { goal with mvarId }
else
let tag goal.mvarId.getTag
let q := target.bindingBody!
-- TODO: keep applying simp/eraseIrrelevantMData/canon/shareCommon until no progress
let r pre p
let fvarId mkFreshFVarId
let lctx := ( getLCtx).mkLocalDecl fvarId target.bindingName! r.expr target.bindingInfo!
let mvarNew mkFreshExprMVarAt lctx ( getLocalInstances) q .syntheticOpaque tag
let mvarIdNew := mvarNew.mvarId!
mvarIdNew.withContext do
let h mkLambdaFVars #[mkFVar fvarId] mvarNew
match r.proof? with
| some he =>
let hNew := mkAppN (mkConst ``Lean.Grind.intro_with_eq) #[p, r.expr, q, he, h]
goal.mvarId.assign hNew
return .newHyp fvarId { goal with mvarId := mvarIdNew }
| none =>
-- `p` and `p'` are definitionally equal
goal.mvarId.assign h
return .newHyp fvarId { goal with mvarId := mvarIdNew }
else if target.isLet || target.isForall then
let (fvarId, mvarId) goal.mvarId.intro1P
mvarId.withContext do
let localDecl fvarId.getDecl
if ( isProp localDecl.type) then
-- Add a non-dependent copy
let mvarId mvarId.assert ( mkFreshUserName localDecl.userName) localDecl.type (mkFVar fvarId)
return .newDepHyp { goal with mvarId }
else
return .newLocal fvarId { goal with mvarId }
else
return .done
def pushResult (goal : Goal) : PreM Unit :=
modify fun s => { s with goals := s.goals.push goal }
def isCasesCandidate (fvarId : FVarId) : MetaM Bool := do
let .const declName _ := ( fvarId.getType).getAppFn | return false
isGrindCasesTarget declName
def applyCases? (goal : Goal) (fvarId : FVarId) : MetaM (Option (List Goal)) := goal.mvarId.withContext do
if ( isCasesCandidate fvarId) then
let mvarIds cases goal.mvarId fvarId
return mvarIds.map fun mvarId => { goal with mvarId }
else
return none
def applyInjection? (goal : Goal) (fvarId : FVarId) : MetaM (Option Goal) := do
if let some mvarId injection? goal.mvarId fvarId then
return some { goal with mvarId }
else
return none
partial def loop (goal : Goal) : PreM Unit := do
if goal.inconsistent then
return ()
match ( introNext goal) with
| .done =>
if let some mvarId goal.mvarId.byContra? then
loop { goal with mvarId }
else
pushResult goal
| .newHyp fvarId goal =>
if let some goals applyCases? goal fvarId then
goals.forM loop
else if let some goal applyInjection? goal fvarId then
loop goal
else
loop ( GoalM.run' goal <| addHyp fvarId)
| .newDepHyp goal =>
loop goal
| .newLocal fvarId goal =>
if let some goals applyCases? goal fvarId then
goals.forM loop
else
loop goal
def ppGoals (goals : PArray Goal) : PreM Format := do
let mut r := f!""
for goal in goals do
let (f, _) GoalM.run goal ppState
r := r ++ Format.line ++ f
return r
-- TODO: refactor this code
def preprocess (mvarId : MVarId) : PreM State := do
mvarId.ensureProp
-- TODO: abstract metavars
mvarId.ensureNoMVar
let mvarId mvarId.clearAuxDecls
let mvarId mvarId.revertAll
mvarId.ensureNoMVar
let mvarId mvarId.abstractNestedProofs ( getMainDeclName)
let mvarId mvarId.unfoldReducible
let mvarId mvarId.betaReduce
loop ( mkGoal mvarId)
let goals := ( get).goals
-- Testing `ematch` module here. We will rewrite this part later.
let goals goals.mapM fun goal => GoalM.run' goal (discard <| ematch)
if ( isTracingEnabledFor `grind.pre) then
trace[grind.debug.pre] ( ppGoals goals)
for goal in goals do
discard <| GoalM.run' goal <| checkInvariants (expensive := true)
get
def preprocessAndProbe (mvarId : MVarId) (p : GoalM Unit) : PreM Unit := do
let s preprocess mvarId
s.goals.forM fun goal =>
discard <| GoalM.run' goal p
end Preprocessor
open Preprocessor
def preprocessAndProbe (mvarId : MVarId) (mainDeclName : Name) (p : GoalM Unit) : MetaM Unit :=
withoutModifyingMCtx do
Preprocessor.preprocessAndProbe mvarId p |>.run |>.run mainDeclName {}
def preprocess (mvarId : MVarId) (mainDeclName : Name) (config : Grind.Config) : MetaM Preprocessor.State :=
Preprocessor.preprocess mvarId |>.run |>.run mainDeclName config
def main (mvarId : MVarId) (config : Grind.Config) (mainDeclName : Name) : MetaM (List MVarId) := do
let go : GrindCoreM (List MVarId) := do
let s Preprocessor.preprocess mvarId |>.run
let goals := s.goals.toList.filter fun goal => !goal.inconsistent
return goals.map (·.mvarId)
go.run mainDeclName config
end Lean.Meta.Grind

View File

@@ -1,56 +0,0 @@
/-
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Grind.Lemmas
import Lean.Meta.Tactic.Grind.Types
import Lean.Meta.Tactic.Grind.PropagatorAttr
import Lean.Meta.Tactic.Grind.Proj
import Lean.Meta.Tactic.Grind.ForallProp
namespace Lean.Meta.Grind
def mkMethods : CoreM Methods := do
let builtinPropagators builtinPropagatorsRef.get
return {
propagateUp := fun e => do
propagateForallProp e
let .const declName _ := e.getAppFn | return ()
propagateProjEq e
if let some prop := builtinPropagators.up[declName]? then
prop e
propagateDown := fun e => do
let .const declName _ := e.getAppFn | return ()
if let some prop := builtinPropagators.down[declName]? then
prop e
}
def GrindCoreM.run (x : GrindCoreM α) (mainDeclName : Name) (config : Grind.Config) : MetaM α := do
let scState := ShareCommon.State.mk _
let (falseExpr, scState) := ShareCommon.State.shareCommon scState (mkConst ``False)
let (trueExpr, scState) := ShareCommon.State.shareCommon scState (mkConst ``True)
let thms grindNormExt.getTheorems
let simprocs := #[( grindNormSimprocExt.getSimprocs)]
let simp Simp.mkContext
(config := { arith := true })
(simpTheorems := #[thms])
(congrTheorems := ( getSimpCongrTheorems))
x ( mkMethods).toMethodsRef { mainDeclName, config, simprocs, simp } |>.run' { scState, trueExpr, falseExpr }
@[inline] def GoalM.run (goal : Goal) (x : GoalM α) : GrindCoreM (α × Goal) :=
goal.mvarId.withContext do StateRefT'.run x goal
@[inline] def GoalM.run' (goal : Goal) (x : GoalM Unit) : GrindCoreM Goal :=
goal.mvarId.withContext do StateRefT'.run' (x *> get) goal
def mkGoal (mvarId : MVarId) : GrindCoreM Goal := do
let trueExpr getTrueExpr
let falseExpr getFalseExpr
let thmMap getEMatchTheorems
GoalM.run' { mvarId, thmMap } do
mkENodeCore falseExpr (interpreted := true) (ctor := false) (generation := 0)
mkENodeCore trueExpr (interpreted := true) (ctor := false) (generation := 0)
end Lean.Meta.Grind

View File

@@ -4,18 +4,16 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Grind.Lemmas
import Lean.Meta.Tactic.Assert
import Lean.Meta.Tactic.Simp.Main
import Lean.Meta.Tactic.Grind.Util
import Lean.Meta.Tactic.Grind.Types
import Lean.Meta.Tactic.Grind.MarkNestedProofs
namespace Lean.Meta.Grind
-- TODO: use congruence closure and decision procedures during pre-processing
-- TODO: implement `simp` discharger using preprocessor state
/-- Simplifies the given expression using the `grind` simprocs and normalization theorems. -/
def simp (e : Expr) : GrindCoreM Simp.Result := do
def simpCore (e : Expr) : GrindM Simp.Result := do
let simpStats := ( get).simpStats
let (r, simpStats) Meta.simp e ( readThe Context).simp ( readThe Context).simprocs (stats := simpStats)
modify fun s => { s with simpStats }
@@ -25,9 +23,11 @@ def simp (e : Expr) : GrindCoreM Simp.Result := do
Simplifies `e` using `grind` normalization theorems and simprocs,
and then applies several other preprocessing steps.
-/
def pre (e : Expr) : GrindCoreM Simp.Result := do
let r simp e
def simp (e : Expr) : GrindM Simp.Result := do
let e instantiateMVars e
let r simpCore e
let e' := r.expr
let e' abstractNestedProofs e'
let e' markNestedProofs e'
let e' unfoldReducible e'
let e' eraseIrrelevantMData e'

View File

@@ -47,7 +47,7 @@ register_builtin_option grind.debug.proofs : Bool := {
descr := "check proofs between the elements of all equivalence classes"
}
/-- Context for `GrindCoreM` monad. -/
/-- Context for `GrindM` monad. -/
structure Context where
simp : Simp.Context
simprocs : Array Simp.Simprocs
@@ -67,7 +67,7 @@ instance : BEq CongrTheoremCacheKey where
instance : Hashable CongrTheoremCacheKey where
hash a := mixHash (unsafe ptrAddrUnsafe a.f).toUInt64 (hash a.numArgs)
/-- State for the `GrindCoreM` monad. -/
/-- State for the `GrindM` monad. -/
structure CoreState where
canon : Canon.State := {}
/-- `ShareCommon` (aka `Hashconsing`) state. -/
@@ -88,34 +88,34 @@ private opaque MethodsRefPointed : NonemptyType.{0}
private def MethodsRef : Type := MethodsRefPointed.type
instance : Nonempty MethodsRef := MethodsRefPointed.property
abbrev GrindCoreM := ReaderT MethodsRef $ ReaderT Context $ StateRefT CoreState MetaM
abbrev GrindM := ReaderT MethodsRef $ ReaderT Context $ StateRefT CoreState MetaM
/-- Returns the user-defined configuration options -/
def getConfig : GrindCoreM Grind.Config :=
def getConfig : GrindM Grind.Config :=
return ( readThe Context).config
/-- Returns the internalized `True` constant. -/
def getTrueExpr : GrindCoreM Expr := do
def getTrueExpr : GrindM Expr := do
return ( get).trueExpr
/-- Returns the internalized `False` constant. -/
def getFalseExpr : GrindCoreM Expr := do
def getFalseExpr : GrindM Expr := do
return ( get).falseExpr
def getMainDeclName : GrindCoreM Name :=
def getMainDeclName : GrindM Name :=
return ( readThe Context).mainDeclName
@[inline] def getMethodsRef : GrindCoreM MethodsRef :=
@[inline] def getMethodsRef : GrindM MethodsRef :=
read
/-- Returns maximum term generation that is considered during ematching. -/
def getMaxGeneration : GrindCoreM Nat := do
def getMaxGeneration : GrindM Nat := do
return ( getConfig).gen
/--
Abtracts nested proofs in `e`. This is a preprocessing step performed before internalization.
-/
def abstractNestedProofs (e : Expr) : GrindCoreM Expr := do
def abstractNestedProofs (e : Expr) : GrindM Expr := do
let nextIdx := ( get).nextThmIdx
let (e, s') AbstractNestedProofs.visit e |>.run { baseName := ( getMainDeclName) } |>.run |>.run { nextIdx }
modify fun s => { s with nextThmIdx := s'.nextIdx }
@@ -125,7 +125,7 @@ def abstractNestedProofs (e : Expr) : GrindCoreM Expr := do
Applies hash-consing to `e`. Recall that all expressions in a `grind` goal have
been hash-consing. We perform this step before we internalize expressions.
-/
def shareCommon (e : Expr) : GrindCoreM Expr := do
def shareCommon (e : Expr) : GrindM Expr := do
modifyGet fun { canon, scState, nextThmIdx, congrThms, trueExpr, falseExpr, simpStats } =>
let (e, scState) := ShareCommon.State.shareCommon scState e
(e, { canon, scState, nextThmIdx, congrThms, trueExpr, falseExpr, simpStats })
@@ -133,24 +133,24 @@ def shareCommon (e : Expr) : GrindCoreM Expr := do
/--
Canonicalizes nested types, type formers, and instances in `e`.
-/
def canon (e : Expr) : GrindCoreM Expr := do
def canon (e : Expr) : GrindM Expr := do
let canonS modifyGet fun s => (s.canon, { s with canon := {} })
let (e, canonS) Canon.canon e |>.run canonS
modify fun s => { s with canon := canonS }
return e
/-- Returns `true` if `e` is the internalized `True` expression. -/
def isTrueExpr (e : Expr) : GrindCoreM Bool :=
def isTrueExpr (e : Expr) : GrindM Bool :=
return isSameExpr e ( getTrueExpr)
/-- Returns `true` if `e` is the internalized `False` expression. -/
def isFalseExpr (e : Expr) : GrindCoreM Bool :=
def isFalseExpr (e : Expr) : GrindM Bool :=
return isSameExpr e ( getFalseExpr)
/--
Creates a congruence theorem for a `f`-applications with `numArgs` arguments.
-/
def mkHCongrWithArity (f : Expr) (numArgs : Nat) : GrindCoreM CongrTheorem := do
def mkHCongrWithArity (f : Expr) (numArgs : Nat) : GrindM CongrTheorem := do
let key := { f, numArgs }
if let some result := ( get).congrThms.find? key then
return result
@@ -363,7 +363,13 @@ structure Goal where
def Goal.admit (goal : Goal) : MetaM Unit :=
goal.mvarId.admit
abbrev GoalM := StateRefT Goal GrindCoreM
abbrev GoalM := StateRefT Goal GrindM
@[inline] def GoalM.run (goal : Goal) (x : GoalM α) : GrindM (α × Goal) :=
goal.mvarId.withContext do StateRefT'.run x goal
@[inline] def GoalM.run' (goal : Goal) (x : GoalM Unit) : GrindM Goal :=
goal.mvarId.withContext do StateRefT'.run' (x *> get) goal
abbrev Propagator := Expr GoalM Unit
@@ -655,7 +661,7 @@ def Methods.toMethodsRef (m : Methods) : MethodsRef :=
private def MethodsRef.toMethods (m : MethodsRef) : Methods :=
unsafe unsafeCast m
@[inline] def getMethods : GrindCoreM Methods :=
@[inline] def getMethods : GrindM Methods :=
return ( getMethodsRef).toMethods
def propagateUp (e : Expr) : GoalM Unit := do

View File

@@ -1,6 +1,5 @@
import Lean
def g (s : Type) := s
def f (a : α) := a

View File

@@ -1,31 +1,46 @@
set_option trace.grind.ematch.pattern true
grind_pattern Array.get_set_eq => a.set i v h
grind_pattern Array.get_set_ne => (a.set i v hi)[j]
grind_pattern Array.size_set => Array.set a i v h
-- Trace instances of the theorems above found using ematching
set_option grind.debug true
set_option trace.grind.ematch.instance true
set_option grind.debug.proofs true
/--
info: [grind.ematch.instance] Array.get_set_eq: (bs.set j w ⋯)[j] = w
[grind.ematch.instance] Array.get_set_eq: (as.set i v ⋯)[i] = v
[grind.ematch.instance] Array.get_set_ne: ∀ (hj : i < bs.size), j ≠ i → (bs.set j w ⋯)[i] = bs[i]
-/
#guard_msgs (info) in
example (as : Array α)
example (as bs : Array α) (v : α)
(i : Nat)
(h₁ : i < as.size)
(h₂ : bs = as.set i v)
(_ : ds = bs)
(h₂ : j < bs.size)
(h₃ : cs = bs.set j w)
: as.size = bs.size := by
grind
example (as bs : Array α) (v : α)
(i : Nat)
(h₁ : i < as.size)
(h₂ : bs = as.set i v)
: as.size = bs.size := by
have : as.size = bs.size := by
grind
exact this
set_option trace.grind.ematch.instance true
grind_pattern Array.get_set_eq => a.set i v h
grind_pattern Array.get_set_ne => (a.set i v hi)[j]
/--
info: [grind.ematch.instance] Array.get_set_eq: (as.set i v ⋯)[i] = v
[grind.ematch.instance] Array.size_set: (as.set i v ⋯).size = as.size
[grind.ematch.instance] Array.get_set_ne: ∀ (hj : j < as.size), i ≠ j → (as.set i v ⋯)[j] = as[j]
-/
#guard_msgs (info) in
example (as bs cs : Array α) (v : α)
(i : Nat)
(h₁ : i < as.size)
(h₂ : bs = as.set i v)
(h₃ : cs = bs)
(h₄ : i j)
(h₅ : i < cs.size)
: p (cs[i] = as[i]) := by
fail_if_success grind
sorry
(h₅ : j < cs.size)
(h₆ : j < as.size)
: cs[j] = as[j] := by
grind
opaque R : Nat Nat Prop
theorem Rtrans (a b c : Nat) : R a b R b c R a c := sorry
@@ -33,31 +48,24 @@ theorem Rtrans (a b c : Nat) : R a b → R b c → R a c := sorry
grind_pattern Rtrans => R a b, R b c
/--
info: [grind.ematch.instance] Rtrans: R b c → R c d → R b d
[grind.ematch.instance] Rtrans: R a b → R b c → R a c
info: [grind.ematch.instance] Rtrans: R a b → R b c → R a c
-/
#guard_msgs (info) in
example : R a b R b c R c d False := by
fail_if_success grind
sorry
example : R a b R b c R a c := by
grind
-- In the following test we are performing one round of ematching only
/--
info: [grind.ematch.instance] Rtrans: R c d → R d e → R c e
[grind.ematch.instance] Rtrans: R c d → R d n → R c n
info: [grind.ematch.instance] Rtrans: R a d → R d e → R a e
[grind.ematch.instance] Rtrans: R c d → R d e → R c e
[grind.ematch.instance] Rtrans: R b c → R c d → R b d
[grind.ematch.instance] Rtrans: R a b → R b c → R a c
[grind.ematch.instance] Rtrans: R a c → R c d → R a d
[grind.ematch.instance] Rtrans: R a c → R c e → R a e
[grind.ematch.instance] Rtrans: R b d → R d e → R b e
[grind.ematch.instance] Rtrans: R a b → R b d → R a d
[grind.ematch.instance] Rtrans: R b c → R c e → R b e
-/
#guard_msgs (info) in
example : R a b R b c R c d R d e R d n False := by
fail_if_success grind
sorry
/--
info: [grind.ematch.instance] Rtrans: R c d → R d e → R c e
[grind.ematch.instance] Rtrans: R c d → R d n → R c n
-/
#guard_msgs (info) in
example : R a b R b c R c d R d e R d n False := by
fail_if_success grind (instances := 2)
sorry
example : R a b R b c R c d R d e R a d := by
grind

View File

@@ -12,8 +12,9 @@ elab "grind_test" : tactic => withMainContext do
let [_, n, _] := nodes.toList | unreachable!
logInfo ( getEqc n.self)
set_option grind.debug true
set_option grind.debug.proofs true
-- TODO: fix
-- set_option grind.debug true
-- set_option grind.debug.proofs true
/-
Recall that array access terms, such as `a[i]`, have nested proofs.
@@ -21,7 +22,7 @@ The following test relies on `grind` `nestedProof` wrapper to
detect equalities between array access terms.
-/
/--
/-
info: [Lean.Grind.nestedProof (i < a.toList.length) (_example.proof_1 i j a b h1 h2),
Lean.Grind.nestedProof (j < a.toList.length) h1,
Lean.Grind.nestedProof (j < b.toList.length) h]
@@ -30,11 +31,15 @@ info: [a[i], b[j], a[j]]
---
warning: declaration uses 'sorry'
-/
#guard_msgs in
-- #guard_msgs in
set_option trace.Meta.debug true
example (i j : Nat) (a b : Array Nat) (h1 : j < a.size) (h : j < b.size) (h2 : i j) : a[i] < a[j] + b[j] i = j a = b False := by
grind_test
sorry
#exit
/--
info: [Lean.Grind.nestedProof (i < a.toList.length) (_example.proof_1 i j a b h1 h2),

View File

@@ -17,27 +17,21 @@ set_option trace.grind.ematch true
set_option trace.grind.ematch.pattern true
/--
warning: declaration uses 'sorry'
---
info: [grind.ematch] activated `contains_insert`, [@contains #3 (@insertElem ? #2 #1 #0) #0]
-/
#guard_msgs in
#guard_msgs (info) in
example [DecidableEq α] (s₁ s₂ : Set α) (a₁ a₂ : α) :
s₂ = insertElem s₁ a₁ a₁ = a₂ contains s₂ a₂ := by
fail_if_success grind
sorry
grind
/--
warning: declaration uses 'sorry'
---
info: [grind.ematch] reinsert `contains_insert`
[grind.ematch] activated `contains_insert`, [@contains #3 (@insertElem ? #2 #1 #0) #0]
-/
#guard_msgs in
#guard_msgs (info) in
example [DecidableEq α] (s₁ s₂ : Set α) (a₁ a₂ : α) :
¬ contains s₂ a₂ s₂ = insertElem s₁ a₁ a₁ = a₂ False := by
fail_if_success grind
sorry
grind
def a := 10
def b := 20

View File

@@ -74,12 +74,10 @@ theorem ex3 (h : a₁ :: { x := a₂, y := a₃ : Point } :: as = b₁ :: { x :=
def h (a : α) := a
set_option trace.grind.debug.pre true in
example (p : Prop) (a b c : Nat) : p a = 0 a = b h a = h c a = c c = a a = b b = a a = c := by
grind
set_option trace.grind.debug.proof true
set_option trace.grind.debug.pre true
/--
error: `grind` failed
α : Type
@@ -112,7 +110,6 @@ info: [grind.issues] found congruence between
#guard_msgs in
set_option trace.grind.issues true in
set_option trace.grind.debug.proof false in
set_option trace.grind.debug.pre false in
example (f : Nat Bool) (g : Int Bool) (a : Nat) (b : Int) : HEq f g HEq a b f a = g b := by
fail_if_success grind
sorry