This PR make some minor changes to the grind annotation analysis script, including sorting results and handling errors. Still need to add an external UI.
101 lines
3.6 KiB
Text
101 lines
3.6 KiB
Text
/-
|
|
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
|
|
-/
|
|
import Lean
|
|
|
|
namespace Lean.Meta.Grind.Analyzer
|
|
|
|
|
|
/-!
|
|
A simple E-matching annotation analyzer.
|
|
For each theorem annotated as an E-matching candidate, it creates an artificial goal, executes `grind` and shows the
|
|
number of instances created.
|
|
For a theorem of the form `params -> type`, the artificial goal is of the form `params -> type -> False`.
|
|
-/
|
|
|
|
/--
|
|
`grind` configuration for the analyzer. We disable case-splits and lookahead,
|
|
increase the number of generations, and limit the number of instances generated.
|
|
-/
|
|
def config : Grind.Config := {
|
|
splits := 0
|
|
lookahead := false
|
|
mbtc := false
|
|
ematch := 20
|
|
instances := 100
|
|
gen := 10
|
|
}
|
|
|
|
structure Config where
|
|
/-- Minimum number of instantiations to trigger summary report -/
|
|
min : Nat := 10
|
|
/-- Minimum number of instantiations to trigger detailed report -/
|
|
detailed : Nat := 50
|
|
|
|
def mkParams : MetaM Params := do
|
|
let params ← Grind.mkParams config
|
|
let ematch ← getEMatchTheorems
|
|
let casesTypes ← Grind.getCasesTypes
|
|
return { params with ematch, casesTypes }
|
|
|
|
/-- Returns the total number of generated instances. -/
|
|
private def sum (cs : PHashMap Origin Nat) : Nat := Id.run do
|
|
let mut r := 0
|
|
for (_, c) in cs do
|
|
r := r + c
|
|
return r
|
|
|
|
private def thmsToMessageData (thms : PHashMap Origin Nat) : MetaM MessageData := do
|
|
let data := thms.toArray.filterMap fun (origin, c) =>
|
|
match origin with
|
|
| .decl declName => some (declName, c)
|
|
| _ => none
|
|
let data := data.qsort fun (d₁, c₁) (d₂, c₂) => if c₁ == c₂ then Name.lt d₁ d₂ else c₁ > c₂
|
|
let data ← data.mapM fun (declName, counter) =>
|
|
return .trace { cls := `thm } m!"{.ofConst (← mkConstWithLevelParams declName)} ↦ {counter}" #[]
|
|
return .trace { cls := `thm } "instances" data
|
|
|
|
/--
|
|
Analyzes theorem `declName`. That is, creates the artificial goal based on `declName` type,
|
|
and invokes `grind` on it.
|
|
-/
|
|
def analyzeEMatchTheorem (declName : Name) (c : Config) : MetaM Unit := do
|
|
let info ← getConstInfo declName
|
|
let mvarId ← forallTelescope info.type fun _ type => do
|
|
withLocalDeclD `h type fun _ => do
|
|
return (← mkFreshExprMVar (mkConst ``False)).mvarId!
|
|
let result ← Grind.main mvarId (← mkParams) (pure ())
|
|
let thms := result.counters.thm
|
|
let s := sum thms
|
|
if s > c.min then
|
|
IO.println s!"{declName} : {s}"
|
|
if s > c.detailed then
|
|
logInfo m!"{declName}\n{← thmsToMessageData thms}"
|
|
|
|
-- Not sure why this is failing: `down_pure` perhaps has an unnecessary universe parameter?
|
|
run_meta analyzeEMatchTheorem ``Std.Do.SPred.down_pure {}
|
|
|
|
/-- Analyzes all theorems in the standard library marked as E-matching theorems. -/
|
|
def analyzeEMatchTheorems (c : Config := {}) : MetaM Unit := do
|
|
let origins := (← getEMatchTheorems).getOrigins
|
|
let decls := origins.filterMap fun | .decl declName => some declName | _ => none
|
|
for declName in decls.mergeSort Name.lt do
|
|
try
|
|
analyzeEMatchTheorem declName c
|
|
catch e =>
|
|
logError m!"{declName} failed with {e.toMessageData}"
|
|
logInfo m!"Finished analyzing {decls.length} theorems"
|
|
|
|
/-- Macro for analyzing E-match theorems with unlimited heartbeats -/
|
|
macro "#analyzeEMatchTheorems" : command => `(
|
|
set_option maxHeartbeats 0 in
|
|
run_meta analyzeEMatchTheorems
|
|
)
|
|
|
|
#analyzeEMatchTheorems
|
|
|
|
-- -- We can analyze specific theorems using commands such as
|
|
set_option trace.grind.ematch.instance true in
|
|
run_meta analyzeEMatchTheorem ``List.filterMap_some {}
|