lean4-htt/src/Lean/LibrarySuggestions/SymbolFrequency.lean
Kim Morrison 0db89d65b2
chore: use 'library suggestions' rather than 'premise selection' (#11029)
This PR changes the terminology used from "premise selection" to
"library suggestions". This will be more understandable to users (we
don't assume anyone is familiar with the premise selection literature),
and avoids a conflict with the existing use of "premise" in Lean
terminology (e.g. "major premise" in induction, as well as generally the
synonym for "hypothesis"/"argument").
2025-10-31 04:07:49 +00:00

120 lines
4.6 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
public import Lean.CoreM
public import Lean.Meta.Basic
import Lean.Meta.InferType
import Lean.Meta.FunInfo
import Lean.AddDecl
import Lean.LibrarySuggestions.Basic
/-!
# Symbol frequency
This module provides a persistent environment extension for computing the frequency of symbols in the environment.
-/
namespace Lean.LibrarySuggestions
/--
Collect the frequencies for constants occurring in declarations defined in the current module,
skipping instance arguments and proofs.
-/
public def localSymbolFrequencyMap : MetaM (NameMap Nat) := do
let env := (← getEnv)
env.constants.map₂.foldlM (init := ∅) (fun acc m ci => do
if isDeniedPremise env m || !Lean.wasOriginallyTheorem env m then
pure acc
else
ci.type.foldRelevantConstants (init := acc) fun n' acc => return acc.alter n' fun i? => some (i?.getD 0 + 1))
/--
A global `IO.Ref` containing the local symbol frequency map. This is initialized on first use.
-/
builtin_initialize localSymbolFrequencyMapRef : IO.Ref (Option (NameMap Nat)) ← IO.mkRef none
/--
A cached version of the local symbol frequency map.
Note that the local symbol frequency map changes during elaboration of a file,
so if this is called at different times it may give the wrong result.
The intended use case is that it is only called by environment extension export functions,
i.e. after all declarations have been elaborated.
-/
def cachedLocalSymbolFrequencyMap : MetaM (NameMap Nat) := do
match ← localSymbolFrequencyMapRef.get with
| some map => return map
| none =>
let map ← localSymbolFrequencyMap
localSymbolFrequencyMapRef.set (some map)
return map
/--
Return the number of times a `Name` appears
in the signatures of (non-internal) theorems in the current module,
skipping instance arguments and proofs.
Note that this is cached, and so returns the frequency within theorems that had been elaborated
when the function is first called (with any argument).
-/
public def localSymbolFrequency (n : Name) : MetaM Nat := do
return (← cachedLocalSymbolFrequencyMap) |>.getD n 0
/--
Helper function for running `MetaM` code during module export, when there is nothing but an `Environment` available.
Panics on errors.
-/
public def _root_.Lean.Environment.unsafeRunMetaM [Inhabited α] (env : Environment) (x : MetaM α) : α :=
match unsafe unsafeEIO ((((withoutExporting x).run' {} {}).run' { fileName := "symbolFrequency", fileMap := default } { env })) with
| Except.ok a => a
| Except.error ex => panic! match unsafe unsafeIO ex.toMessageData.toString with
| Except.ok s => s
| Except.error ex => ex.toString
/--
The state is just an array of array of maps.
We don't assemble these on import for efficiency reasons: most modules will not query this extension.
Instead, we use an `IO.Ref` below so that within each module we can assemble the global `NameMap Nat` once.
Since we never modify the extension state except on export, the `IO.Ref` does not need updating after first access.
-/
builtin_initialize symbolFrequencyExt : PersistentEnvExtension (NameMap Nat) Empty (Array (Array (NameMap Nat))) ←
registerPersistentEnvExtension {
name := `symbolFrequency
mkInitial := pure ∅
addImportedFn := fun mapss _ => pure mapss
addEntryFn := nofun
exportEntriesFnEx := fun env _ _ => env.unsafeRunMetaM do return #[← cachedLocalSymbolFrequencyMap]
statsFn := fun _ => "symbol frequency extension"
}
/-- A global `IO.Ref` containing the symbol frequency map. This is initialized on first use. -/
builtin_initialize symbolFrequencyMapRef : IO.Ref (Option (NameMap Nat)) ← IO.mkRef none
private local instance : Zero (NameMap Nat) := ⟨∅⟩
private local instance : Add (NameMap Nat) where
add x y := y.foldl (init := x) fun x' n c => x'.insert n (x'.getD n 0 + c)
/-- The symbol frequency map for imported constants. This is initialized on first use. -/
public def symbolFrequencyMap : CoreM (NameMap Nat) := do
match ← symbolFrequencyMapRef.get with
| some map => return map
| none =>
let mapss := symbolFrequencyExt.getState (← getEnv)
let map := mapss.foldl (init := 0) fun acc maps => maps.foldl (init := acc) fun acc map => acc + map
symbolFrequencyMapRef.set (some map)
return map
/--
Return the number of times a `Name` appears
in the signatures of (non-internal) theorems in the imported environment,
skipping instance arguments and proofs.
-/
public def symbolFrequency (n : Name) : CoreM Nat :=
return (← symbolFrequencyMap) |>.getD n 0