feat: Stats page

This commit is contained in:
jstoobysmith 2024-11-11 11:23:27 +00:00
parent 811b96cf17
commit 1c303f8741
5 changed files with 272 additions and 129 deletions

View file

@ -47,6 +47,9 @@ jobs:
- name: make list of informal proofs and lemmas - name: make list of informal proofs and lemmas
run : lake exe informal mkFile mkDot mkHTML run : lake exe informal mkFile mkDot mkHTML
- name: make stats page
run : lake exe stats mkHTML
- name: Generate svg from dot - name: Generate svg from dot
run : dot -Tsvg -o ./docs/graph.svg ./docs/InformalDot.dot run : dot -Tsvg -o ./docs/graph.svg ./docs/InformalDot.dot

View file

@ -108,6 +108,7 @@ import HepLean.Mathematics.LinearMaps
import HepLean.Mathematics.PiTensorProduct import HepLean.Mathematics.PiTensorProduct
import HepLean.Mathematics.SO3.Basic import HepLean.Mathematics.SO3.Basic
import HepLean.Meta.AllFilePaths import HepLean.Meta.AllFilePaths
import HepLean.Meta.Basic
import HepLean.Meta.Informal import HepLean.Meta.Informal
import HepLean.Meta.TransverseTactics import HepLean.Meta.TransverseTactics
import HepLean.SpaceTime.Basic import HepLean.SpaceTime.Basic

178
HepLean/Meta/Basic.lean Normal file
View file

@ -0,0 +1,178 @@
/-
Copyright (c) 2024 Joseph Tooby-Smith. All rights reserved.
Released under Apache 2.0 license.
Authors: Joseph Tooby-Smith
-/
import Batteries.Lean.HashSet
import Lean
import Mathlib.Lean.Expr.Basic
import Mathlib.Lean.CoreM
import ImportGraph.RequiredModules
import HepLean.Meta.Informal
/-!
## Basic Lean meta programming commands
-/
namespace HepLean
open Lean System Meta
/-!
## Imports
-/
/-- Gets all imports within HepLean. -/
def allImports : IO (Array Import) := do
initSearchPath (← findSysroot)
let mods : Name := `HepLean
let imp : Import := {module := mods}
let mFile ← findOLean imp.module
unless (← mFile.pathExists) do
throw <| IO.userError s!"object file '{mFile}' of module {imp.module} does not exist"
let (hepLeanMod, _) ← readModuleData mFile
let imports := hepLeanMod.imports.filter (fun c => c.module ≠ `Init)
return imports
/-- Number of files within HepLean. -/
def noImports : IO Nat := do
let imports ← allImports
pure imports.size
/-- Gets all constants from an import. -/
def Imports.getConsts (imp : Import) : IO (Array ConstantInfo) := do
let mFile ← findOLean imp.module
let (modData, _) ← readModuleData mFile
pure modData.constants
/-- Gets all user defined constants from an import. -/
def Imports.getUserConsts (imp : Import) : MetaM (Array ConstantInfo) := do
let env ← getEnv
let x := (← Imports.getConsts imp).filter (fun c => ¬ c.name.isInternal)
let x := x.filter (fun c => ¬ Lean.isCasesOnRecursor env c.name)
let x := x.filter (fun c => ¬ Lean.isRecOnRecursor env c.name)
let x := x.filter (fun c => ¬ Lean.isNoConfusion env c.name)
let x := x.filter (fun c => ¬ Lean.isBRecOnRecursor env c.name)
let x := x.filter (fun c => ¬ Lean.isAuxRecursorWithSuffix env c.name Lean.binductionOnSuffix)
let x := x.filter (fun c => ¬ Lean.isAuxRecursorWithSuffix env c.name Lean.belowSuffix)
let x := x.filter (fun c => ¬ Lean.isAuxRecursorWithSuffix env c.name Lean.ibelowSuffix)
/- Removing syntax category declarations. -/
let x := x.filter (fun c => ¬ c.name.toString = "Informal.informalAssignment.quot" )
let x := x.filter (fun c => ¬ c.name.toString = "TensorTree.indexExpr.quot" )
let x := x.filter (fun c => ¬ c.name.toString = "TensorTree.tensorExpr.quot" )
pure x
/-- Lines from import. -/
def Imports.getLines (imp : Import) : IO (Array String) := do
let filePath := (mkFilePath (imp.module.toString.split (· == '.'))).addExtension "lean"
let lines ← IO.FS.lines filePath
return lines
/-!
## Name
-/
/-- Turns a name into a Lean file. -/
def Name.toFile (c : Name) : MetaM String := do
return s!"./{c.toString.replace "." "/" ++ ".lean"}"
/-- Given a name, returns the line number. -/
def Name.lineNumber (c : Name) : MetaM Nat := do
match ← findDeclarationRanges? c with
| some decl => pure decl.range.pos.line
| none => panic! s!"{c} is a declaration without position"
/-- Returns the location of a name. -/
def Name.location (c : Name) : MetaM String := do
let env ← getEnv
let x := env.getModuleFor? c
match x with
| some decl => pure ((← Name.toFile decl) ++ ":" ++ toString (← Name.lineNumber c) ++ " "
++ c.toString)
| none => panic! s!"{c} is a declaration without position"
/-- Determines if a name has a location. -/
def Name.hasPos (c : Name) : MetaM Bool := do
match ← findDeclarationRanges? c with
| some _ => pure true
| none => pure false
/-- Determines if a name has a doc string. -/
def Name.hasDocString (c : Name) : MetaM Bool := do
let env ← getEnv
let doc ← Lean.findDocString? env c
match doc with
| some _ => pure true
| none => pure false
/-- Number of definitions. -/
def noDefs : MetaM Nat := do
let imports ← allImports
let x ← imports.mapM Imports.getUserConsts
let x := x.flatten
let x := x.filter (fun c => c.isDef)
let x ← x.filterM (fun c => (Name.hasPos c.name))
pure x.toList.length
/-- Number of definitions. -/
def noLemmas : MetaM Nat := do
let imports ← allImports
let x ← imports.mapM Imports.getUserConsts
let x := x.flatten
let x := x.filter (fun c => ¬ c.isDef)
let x ← x.filterM (fun c => (Name.hasPos c.name))
pure x.toList.length
/-- Number of definitions without a doc-string. -/
def noDefsNoDocString : MetaM Nat := do
let imports ← allImports
let x ← imports.mapM Imports.getUserConsts
let x := x.flatten
let x := x.filter (fun c => c.isDef)
let x ← x.filterM (fun c => (Name.hasPos c.name))
let x ← x.filterM (fun c => do
return Bool.not (← (Name.hasDocString c.name)))
pure x.toList.length
/-- Number of definitions without a doc-string. -/
def noLemmasNoDocString : MetaM Nat := do
let imports ← allImports
let x ← imports.mapM Imports.getUserConsts
let x := x.flatten
let x := x.filter (fun c => ¬ c.isDef)
let x ← x.filterM (fun c => (Name.hasPos c.name))
let x ← x.filterM (fun c => do
return Bool.not (← (Name.hasDocString c.name)))
pure x.toList.length
/-- The number of lines in HepLean. -/
def noLines : IO Nat := do
let imports ← HepLean.allImports
let x ← imports.mapM HepLean.Imports.getLines
let x := x.flatten
pure x.toList.length
/-- The number of informal lemmas in HepLean. -/
def noInformalLemmas : MetaM Nat := do
let imports ← allImports
let x ← imports.mapM Imports.getUserConsts
let x := x.flatten
let x := x.filter (Informal.isInformal)
let x := x.filter (Informal.isInformalLemma)
pure x.toList.length
/-- The number of informal definitions in HepLean. -/
def noInformalDefs : MetaM Nat := do
let imports ← allImports
let x ← imports.mapM Imports.getUserConsts
let x := x.flatten
let x := x.filter (Informal.isInformal)
let x := x.filter (Informal.isInformalDef)
pure x.toList.length
end HepLean

View file

@ -3,68 +3,14 @@ Copyright (c) 2024 Joseph Tooby-Smith. All rights reserved.
Released under Apache 2.0 license. Released under Apache 2.0 license.
Authors: Joseph Tooby-Smith Authors: Joseph Tooby-Smith
-/ -/
import Batteries.Lean.HashSet import HepLean.Meta.Basic
import Lean
import Mathlib.Lean.Expr.Basic
import Mathlib.Lean.CoreM
import HepLean.Meta.Informal
import ImportGraph.RequiredModules
/-! /-!
# Extracting commands with no doc strings. # Extracting commands with no doc strings.
-/ -/
open Lean System Meta open Lean System Meta HepLean
def Imports.getConsts (imp : Import) : IO (Array ConstantInfo) := do
let mFile ← findOLean imp.module
let (modData, _) ← readModuleData mFile
pure modData.constants
def Imports.getUserConsts (imp : Import) : MetaM (Array ConstantInfo) := do
let env ← getEnv
let x := (← Imports.getConsts imp).filter (fun c => ¬ c.name.isInternal)
let x := x.filter (fun c => ¬ Lean.isCasesOnRecursor env c.name)
let x := x.filter (fun c => ¬ Lean.isRecOnRecursor env c.name)
let x := x.filter (fun c => ¬ Lean.isNoConfusion env c.name)
let x := x.filter (fun c => ¬ Lean.isBRecOnRecursor env c.name)
let x := x.filter (fun c => ¬ Lean.isAuxRecursorWithSuffix env c.name Lean.binductionOnSuffix)
let x := x.filter (fun c => ¬ Lean.isAuxRecursorWithSuffix env c.name Lean.belowSuffix)
let x := x.filter (fun c => ¬ Lean.isAuxRecursorWithSuffix env c.name Lean.ibelowSuffix)
/- Removing syntax category declarations. -/
let x := x.filter (fun c => ¬ c.name.toString = "Informal.informalAssignment.quot" )
let x := x.filter (fun c => ¬ c.name.toString = "TensorTree.indexExpr.quot" )
let x := x.filter (fun c => ¬ c.name.toString = "TensorTree.tensorExpr.quot" )
pure x
def Name.lineNumber (c : Name) : MetaM Nat := do
match ← findDeclarationRanges? c with
| some decl => pure decl.range.pos.line
| none => panic! s!"{c} is a declaration without position"
def Name.toFile (c : Name) : MetaM String := do
return s!"./{c.toString.replace "." "/" ++ ".lean"}"
def Name.location (c : Name) : MetaM String := do
let env ← getEnv
let x := env.getModuleFor? c
match x with
| some decl => pure ((← Name.toFile decl) ++ ":" ++ toString (← Name.lineNumber c) ++ " "
++ c.toString)
| none => panic! s!"{c} is a declaration without position"
def Name.hasPos (c : Name) : MetaM Bool := do
match ← findDeclarationRanges? c with
| some _ => pure true
| none => pure false
def Name.hasDocString (c : Name) : MetaM Bool := do
let env ← getEnv
let doc ← Lean.findDocString? env c
match doc with
| some _ => pure true
| none => pure false
def Imports.NoDocStringDef (imp : Import) : MetaM UInt32 := do def Imports.NoDocStringDef (imp : Import) : MetaM UInt32 := do
let x := (← Imports.getUserConsts imp).filter (fun c => c.isDef) let x := (← Imports.getUserConsts imp).filter (fun c => c.isDef)
@ -91,14 +37,7 @@ def Imports.NoDocStringLemma (imp : Import) : MetaM UInt32 := do
pure 0 pure 0
unsafe def main (args : List String) : IO UInt32 := do unsafe def main (args : List String) : IO UInt32 := do
initSearchPath (← findSysroot) let imports ← allImports
let mods : Name := `HepLean
let imp : Import := {module := mods}
let mFile ← findOLean imp.module
unless (← mFile.pathExists) do
throw <| IO.userError s!"object file '{mFile}' of module {imp.module} does not exist"
let (hepLeanMod, _) ← readModuleData mFile
let imports := hepLeanMod.imports.filter (fun c => c.module ≠ `Init)
let _ ← CoreM.withImportModules #[`HepLean] (imports.mapM Imports.NoDocStringDef).run' let _ ← CoreM.withImportModules #[`HepLean] (imports.mapM Imports.NoDocStringDef).run'
if "--lemmas" ∈ args then if "--lemmas" ∈ args then
let _ ← CoreM.withImportModules #[`HepLean] (imports.mapM Imports.NoDocStringLemma).run' let _ ← CoreM.withImportModules #[`HepLean] (imports.mapM Imports.NoDocStringLemma).run'

View file

@ -3,11 +3,7 @@ Copyright (c) 2024 Joseph Tooby-Smith. All rights reserved.
Released under Apache 2.0 license. Released under Apache 2.0 license.
Authors: Joseph Tooby-Smith Authors: Joseph Tooby-Smith
-/ -/
import Batteries.Lean.HashSet import HepLean.Meta.Basic
import Lean
import Mathlib.Lean.Expr.Basic
import Mathlib.Lean.CoreM
import HepLean
/-! /-!
# HepLean Stats # HepLean Stats
@ -17,72 +13,98 @@ This file concerns with statistics of HepLean.
-/ -/
open Lean System Meta open Lean System Meta HepLean
structure FileStats where
numDefs : Nat
numLemsWithoutDocString : Nat
numLemsWithDocString : Nat
def FileStats.add (a b : FileStats) : FileStats where def getStats : MetaM String := do
numDefs := a.numDefs + b.numDefs let noDefsVal ← noDefs
numLemsWithoutDocString := a.numLemsWithoutDocString + b.numLemsWithoutDocString let noLemmasVal ← noLemmas
numLemsWithDocString := a.numLemsWithDocString + b.numLemsWithDocString let noImportsVal ← noImports
let noDefsNoDocVal ← noDefsNoDocString
let noLemmasNoDocVal ← noLemmasNoDocString
let noLinesVal ← noLines
let s := s!"
Number of Imports: {noImportsVal}
Number of Definitions: {noDefsVal}
Number of Lemmas: {noLemmasVal}
Number of Definitions without doc strings: {noDefsNoDocVal} (out of {noDefsVal})
Number of Lemmas without doc strings: {noLemmasNoDocVal} (out of {noLemmasVal})
Number of Lines: {noLinesVal}"
pure s
def FileStats.zero : FileStats where unsafe def Stats.toHtml : MetaM String := do
numDefs := 0 let noDefsVal ← noDefs
numLemsWithoutDocString := 0 let noLemmasVal ← noLemmas
numLemsWithDocString := 0 let noImportsVal ← noImports
let noDefsNoDocVal ← noDefsNoDocString
let noLemmasNoDocVal ← noLemmasNoDocString
let noLinesVal ← noLines
let noInformalDefsVal ← noInformalDefs
let noInformalLemmasVal ← noInformalLemmas
let header := "---
layout: default
---
<!DOCTYPE html>
<html>
<head>
<title>Stats for HepLean</title>
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
<style>
/* Style the progress bar to be wider and take up more space */
progress {
width: 80%; /* Adjust to take up more horizontal space */
height: 30px; /* Increase height to make it wider */
appearance: none; /* Remove default browser styles */
}
instance : ToString FileStats where /* Change the color for WebKit browsers (Chrome, Safari, etc.) */
toString fs := s!"Number of definitions: {fs.numDefs}\n" ++ progress::-webkit-progress-bar {
s!"Number of lemmas without doc string: {fs.numLemsWithoutDocString}\n" ++ background-color: #f3f3f3; /* Background color */
s!"Number of lemmas with doc string: {fs.numLemsWithDocString}" border-radius: 5px;
overflow: hidden;
}
def getConstantDoc (constName : Array ConstantInfo) : MetaM (FileStats) := do progress::-webkit-progress-value {
let env ← getEnv background-color: #157878; /* Change this to your desired color */
let mut numDefs := 0 border-radius: 5px;
let mut numLemsWithoutDocString := 0 }
let mut numLemsWithDocString := 0
for c in constName do
if ¬ Lean.Name.isInternalDetail c.name then
if c.isDef then
numDefs := numDefs + 1
if c.isThm then
let doc ← Lean.findDocString? env c.name
if doc == none then
numLemsWithoutDocString := numLemsWithoutDocString + 1
else
numLemsWithDocString := numLemsWithDocString + 1
return {numDefs := numDefs,
numLemsWithoutDocString := numLemsWithoutDocString,
numLemsWithDocString := numLemsWithDocString}
def getStats (imp : Import) : MetaM FileStats := do /* Change the color for Firefox */
if imp.module == `Init then progress::-moz-progress-bar {
return {numDefs := 0, background-color: #157878; /* Change this to your desired color */
numLemsWithoutDocString := 0, }
numLemsWithDocString := 0} </style>
let mFile ← findOLean imp.module </head>
let (modData, _) ← readModuleData mFile <body>"
let cons := modData.constants let body := s!"
let out ← (getConstantDoc cons) <h1>Stats for HepLean</h1>
return out <h3>Number of Files 📄: {noImportsVal}</h3>
<h3>Number of lines 💻: {noLinesVal}</h3>
<h3>Number of Definitions (incl. instances): {noDefsVal - noInformalLemmasVal}</h3>
<p>- Of which {noDefsVal - noDefsNoDocVal- noInformalLemmasVal} have doc-strings:</p>
<progress value=\"{noDefsVal - noDefsNoDocVal- noInformalLemmasVal}\" max=\"{noDefsVal- noInformalLemmasVal}\"></progress>
<p>- Of which {noDefsVal - noInformalLemmasVal - noInformalDefsVal} are not informal definitions:</p>
<progress value=\"{noDefsVal - noInformalLemmasVal - noInformalDefsVal}\" max=\"{noDefsVal - noInformalLemmasVal}\"></progress>
def getStatsArray (a : Array Import) : MetaM (Array FileStats) := do <h3>Number of Lemmas: {noLemmasVal + noInformalLemmasVal}</h3>
return ← a.mapM getStats <p>- Of which {noLemmasVal - noLemmasNoDocVal + noInformalLemmasVal} have doc-strings:</p>
<progress value=\"{noLemmasVal - noLemmasNoDocVal + noInformalLemmasVal}\" max=\"{noLemmasVal + noInformalLemmasVal}\"></progress>
<p>- Of which {noLemmasVal} are not informal lemmas:</p>
<progress value=\"{noLemmasVal}\" max=\"{noLemmasVal + noInformalLemmasVal}\"></progress>
"
let footer := "
</body>
</html>
"
pure (header ++ "\n" ++ body ++ "\n" ++ footer)
def main (_ : List String) : IO UInt32 := do unsafe def main (args : List String) : IO UInt32 := do
initSearchPath (← findSysroot) let _ ← noImports
let mods : Name := `HepLean let statString ← CoreM.withImportModules #[`HepLean] (getStats).run'
let imp : Import := {module := mods} println! statString
let mFile ← findOLean imp.module if "mkHTML" ∈ args then
unless (← mFile.pathExists) do let html ← CoreM.withImportModules #[`HepLean] (Stats.toHtml).run'
throw <| IO.userError s!"object file '{mFile}' of module {imp.module} does not exist" let htmlFile : System.FilePath := {toString := "./docs/Stats.html"}
let (hepLeanMod, _) ← readModuleData mFile IO.FS.writeFile htmlFile html
let noFiles := hepLeanMod.imports.size - 1 IO.println (s!"HTML file made.")
let fileStats ← CoreM.withImportModules #[`HepLean] (getStatsArray hepLeanMod.imports).run'
let totalStats := Array.foldl FileStats.add FileStats.zero fileStats
IO.println s!"Total number of files: {noFiles}"
IO.println totalStats
pure 0 pure 0