mirror of
https://github.com/leanprover/lean4.git
synced 2026-04-11 06:34:08 +00:00
Compare commits
4 Commits
grind_del_
...
grind_sear
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7181b02e96 | ||
|
|
d31fd653ed | ||
|
|
45f48e80d4 | ||
|
|
5462d85646 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
139
src/Lean/Meta/Tactic/Grind/Intro.lean
Normal file
139
src/Lean/Meta/Tactic/Grind/Intro.lean
Normal 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
|
||||
@@ -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
|
||||
|
||||
93
src/Lean/Meta/Tactic/Grind/Main.lean
Normal file
93
src/Lean/Meta/Tactic/Grind/Main.lean
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Lean
|
||||
|
||||
|
||||
def g (s : Type) := s
|
||||
def f (a : α) := a
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user