Skip to content

Commit 3427630

Browse files
authored
feat: configuration options for the grind tactic (#6490)
This PR adds basic configuration options for the `grind` tactic.
1 parent 5ba4761 commit 3427630

File tree

8 files changed

+57
-23
lines changed

8 files changed

+57
-23
lines changed

src/Init/Grind/Tactics.lean

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,22 @@ The configuration for `grind`.
1212
Passed to `grind` using, for example, the `grind (config := { eager := true })` syntax.
1313
-/
1414
structure Config where
15+
/-- When `eager` is true (default: `false`), `grind` eagerly splits `if-then-else` and `match` expressions during internalization. -/
16+
eager : Bool := false
17+
/-- Maximum number of branches (i.e., case-splits) in the proof search tree. -/
18+
splits : Nat := 100
1519
/--
16-
When `eager` is true (default: `false`), `grind` eagerly splits `if-then-else` and `match`
17-
expressions.
20+
Maximum number of E-matching (aka heuristic theorem instantiation)
21+
in a proof search tree branch.
1822
-/
19-
eager : Bool := false
23+
ematch : Nat := 5
24+
/--
25+
Maximum term generation.
26+
The input goal terms have generation 0. When we instantiate a theorem using a term from generation `n`,
27+
the new terms have generation `n+1`. Thus, this parameter limits the length of an instantiation chain. -/
28+
gen : Nat := 5
29+
/-- Maximum number of theorem instances generated using E-matching in a proof search tree branch. -/
30+
instances : Nat := 1000
2031
deriving Inhabited, BEq
2132

2233
end Lean.Grind
@@ -27,7 +38,7 @@ namespace Lean.Parser.Tactic
2738
`grind` tactic and related tactics.
2839
-/
2940

30-
-- TODO: configuration option, parameters
31-
syntax (name := grind) "grind" : tactic
41+
-- TODO: parameters
42+
syntax (name := grind) "grind" optConfig : tactic
3243

3344
end Lean.Parser.Tactic

src/Lean/Elab/Tactic/Grind.lean

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import Init.Grind.Tactics
88
import Lean.Meta.Tactic.Grind
99
import Lean.Elab.Command
1010
import Lean.Elab.Tactic.Basic
11+
import Lean.Elab.Tactic.Config
1112

1213
namespace Lean.Elab.Tactic
1314
open Meta
1415

16+
declare_config_elab elabGrindConfig Grind.Config
17+
1518
open Command Term in
1619
@[builtin_command_elab Lean.Parser.Command.grindPattern]
1720
def elabGrindPattern : CommandElab := fun stx => do
@@ -29,17 +32,18 @@ def elabGrindPattern : CommandElab := fun stx => do
2932
Grind.addEMatchTheorem declName xs.size patterns.toList
3033
| _ => throwUnsupportedSyntax
3134

32-
def grind (mvarId : MVarId) (mainDeclName : Name) : MetaM Unit := do
33-
let mvarIds ← Grind.main mvarId mainDeclName
35+
def grind (mvarId : MVarId) (config : Grind.Config) (mainDeclName : Name) : MetaM Unit := do
36+
let mvarIds ← Grind.main mvarId config mainDeclName
3437
unless mvarIds.isEmpty do
3538
throwError "`grind` failed\n{goalsToMessageData mvarIds}"
3639

3740
@[builtin_tactic Lean.Parser.Tactic.grind] def evalApplyRfl : Tactic := fun stx => do
3841
match stx with
39-
| `(tactic| grind) =>
42+
| `(tactic| grind $config:optConfig) =>
4043
logWarningAt stx "The `grind` tactic is experimental and still under development. Avoid using it in production projects"
4144
let declName := (← Term.getDeclName?).getD `_grind
42-
withMainContext do liftMetaFinishingTactic (grind · declName)
45+
let config ← elabGrindConfig config
46+
withMainContext do liftMetaFinishingTactic (grind · config declName)
4347
| _ => throwUnsupportedSyntax
4448

4549
end Lean.Elab.Tactic

src/Lean/Meta/Tactic/Grind/Core.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ where
181181
if (← isInconsistent) then
182182
resetNewEqs
183183
return ()
184+
checkSystem "grind"
184185
let some { lhs, rhs, proof, isHEq } := (← popNextEq?) | return ()
185186
addEqStep lhs rhs proof isHEq
186187
processTodo

src/Lean/Meta/Tactic/Grind/EMatch.lean

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ private def addNewInstance (origin : Origin) (proof : Expr) (generation : Nat) :
167167
let prop ← inferType proof
168168
trace[grind.ematch.instance] "{← origin.pp}: {prop}"
169169
modify fun s => { s with newInstances := s.newInstances.push { proof, prop, generation } }
170+
modifyThe Goal fun s => { s with numInstances := s.numInstances + 1 }
170171

171172
/--
172173
After processing a (multi-)pattern, use the choice assignment to instantiate the proof.
@@ -231,6 +232,8 @@ where
231232
/-- Process choice stack until we don't have more choices to be processed. -/
232233
private partial def processChoices : M Unit := do
233234
unless (← get).choiceStack.isEmpty do
235+
checkSystem "ematch"
236+
if (← checkMaxInstancesExceeded) then return ()
234237
let c ← modifyGet fun s : State => (s.choiceStack.head!, { s with choiceStack := s.choiceStack.tail! })
235238
match c.cnstrs with
236239
| [] => instantiateTheorem c
@@ -246,6 +249,7 @@ private def main (p : Expr) (cnstrs : List Cnstr) : M Unit := do
246249
let useMT := (← read).useMT
247250
let gmt := (← getThe Goal).gmt
248251
for app in apps do
252+
if (← checkMaxInstancesExceeded) then return ()
249253
let n ← getENode app
250254
if (n.heqProofs || isSameExpr n.cgRoot app) &&
251255
(!useMT || n.mt == gmt) then
@@ -254,6 +258,7 @@ private def main (p : Expr) (cnstrs : List Cnstr) : M Unit := do
254258
processChoices
255259

256260
def ematchTheorem (thm : EMatchTheorem) : M Unit := do
261+
if (← checkMaxInstancesExceeded) then return ()
257262
withReader (fun ctx => { ctx with thm }) do
258263
let ps := thm.patterns
259264
match ps, (← read).useMT with
@@ -298,7 +303,6 @@ def ematch : GoalM (Array EMatchTheoremInstance) := do
298303
thms := s.thms ++ s.newThms
299304
newThms := {}
300305
gmt := s.gmt + 1
301-
numInstances := s.numInstances + insts.size
302306
}
303307
return insts
304308

src/Lean/Meta/Tactic/Grind/Preprocessor.lean

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,16 +162,16 @@ open Preprocessor
162162

163163
def preprocessAndProbe (mvarId : MVarId) (mainDeclName : Name) (p : GoalM Unit) : MetaM Unit :=
164164
withoutModifyingMCtx do
165-
Preprocessor.preprocessAndProbe mvarId p |>.run |>.run mainDeclName
165+
Preprocessor.preprocessAndProbe mvarId p |>.run |>.run mainDeclName {}
166166

167-
def preprocess (mvarId : MVarId) (mainDeclName : Name) : MetaM Preprocessor.State :=
168-
Preprocessor.preprocess mvarId |>.run |>.run mainDeclName
167+
def preprocess (mvarId : MVarId) (mainDeclName : Name) (config : Grind.Config) : MetaM Preprocessor.State :=
168+
Preprocessor.preprocess mvarId |>.run |>.run mainDeclName config
169169

170-
def main (mvarId : MVarId) (mainDeclName : Name) : MetaM (List MVarId) := do
170+
def main (mvarId : MVarId) (config : Grind.Config) (mainDeclName : Name) : MetaM (List MVarId) := do
171171
let go : GrindM (List MVarId) := do
172172
let s ← Preprocessor.preprocess mvarId |>.run
173173
let goals := s.goals.toList.filter fun goal => !goal.inconsistent
174174
return goals.map (·.mvarId)
175-
go.run mainDeclName
175+
go.run mainDeclName config
176176

177177
end Lean.Meta.Grind

src/Lean/Meta/Tactic/Grind/Run.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def mkMethods : CoreM Methods := do
2525
prop e
2626
}
2727

28-
def GrindM.run (x : GrindM α) (mainDeclName : Name) : MetaM α := do
28+
def GrindM.run (x : GrindM α) (mainDeclName : Name) (config : Grind.Config) : MetaM α := do
2929
let scState := ShareCommon.State.mk _
3030
let (falseExpr, scState) := ShareCommon.State.shareCommon scState (mkConst ``False)
3131
let (trueExpr, scState) := ShareCommon.State.shareCommon scState (mkConst ``True)
@@ -35,7 +35,7 @@ def GrindM.run (x : GrindM α) (mainDeclName : Name) : MetaM α := do
3535
(config := { arith := true })
3636
(simpTheorems := #[thms])
3737
(congrTheorems := (← getSimpCongrTheorems))
38-
x (← mkMethods).toMethodsRef { mainDeclName, simprocs, simp } |>.run' { scState, trueExpr, falseExpr }
38+
x (← mkMethods).toMethodsRef { mainDeclName, config, simprocs, simp } |>.run' { scState, trueExpr, falseExpr }
3939

4040
@[inline] def GoalM.run (goal : Goal) (x : GoalM α) : GrindM (α × Goal) :=
4141
goal.mvarId.withContext do StateRefT'.run x goal

src/Lean/Meta/Tactic/Grind/Types.lean

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Leonardo de Moura
55
-/
66
prelude
7+
import Init.Grind.Tactics
78
import Lean.Util.ShareCommon
89
import Lean.HeadIndex
910
import Lean.Meta.Basic
@@ -47,9 +48,10 @@ register_builtin_option grind.debug.proofs : Bool := {
4748

4849
/-- Context for `GrindM` monad. -/
4950
structure Context where
50-
simp : Simp.Context
51-
simprocs : Array Simp.Simprocs
51+
simp : Simp.Context
52+
simprocs : Array Simp.Simprocs
5253
mainDeclName : Name
54+
config : Grind.Config
5355

5456
/-- Key for the congruence theorem cache. -/
5557
structure CongrTheoremCacheKey where
@@ -87,6 +89,10 @@ instance : Nonempty MethodsRef := MethodsRefPointed.property
8789

8890
abbrev GrindM := ReaderT MethodsRef $ ReaderT Context $ StateRefT State MetaM
8991

92+
/-- Returns the user-defined configuration options -/
93+
def getConfig : GrindM Grind.Config :=
94+
return (← readThe Context).config
95+
9096
/-- Returns the internalized `True` constant. -/
9197
def getTrueExpr : GrindM Expr := do
9298
return (← get).trueExpr
@@ -101,10 +107,9 @@ def getMainDeclName : GrindM Name :=
101107
@[inline] def getMethodsRef : GrindM MethodsRef :=
102108
read
103109

104-
/--
105-
Returns maximum term generation that is considered during ematching. -/
110+
/-- Returns maximum term generation that is considered during ematching. -/
106111
def getMaxGeneration : GrindM Nat := do
107-
return 10000 -- TODO
112+
return (← getConfig).gen
108113

109114
/--
110115
Abtracts nested proofs in `e`. This is a preprocessing step performed before internalization.
@@ -355,7 +360,7 @@ def markTheorenInstance (proof : Expr) (assignment : Array Expr) : GoalM Bool :=
355360

356361
/-- Returns `true` if the maximum number of instances has been reached. -/
357362
def checkMaxInstancesExceeded : GoalM Bool := do
358-
return false -- TODO
363+
return (← get).numInstances >= (← getConfig).instances
359364

360365
/-- Returns `true` if `e` is the internalized `True` expression. -/
361366
def isTrueExpr (e : Expr) : GrindM Bool :=

tests/lean/run/grind_ematch1.lean

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,12 @@ info: [grind.ematch.instance] Rtrans: R c d → R d e → R c e
5151
example : R a b → R b c → R c d → R d e → R d n → False := by
5252
fail_if_success grind
5353
sorry
54+
55+
/--
56+
info: [grind.ematch.instance] Rtrans: R c d → R d e → R c e
57+
[grind.ematch.instance] Rtrans: R c d → R d n → R c n
58+
-/
59+
#guard_msgs (info) in
60+
example : R a b → R b c → R c d → R d e → R d n → False := by
61+
fail_if_success grind (instances := 2)
62+
sorry

0 commit comments

Comments
 (0)