blueprint-gen
This is a quick demo for a new blueprint tool for Lean. It is currently completely in proof-of-concept stage.
Blueprint-gen is a tool for generating the blueprint data of a Lean project directly from Lean.
The blueprint is a high-level plan of a Lean project, consisting of a series of nodes (theorems and definitions) and the dependency relations between them. The purpose of blueprint-gen is to make it easier to write the blueprint by generating blueprint data directly from Lean.
Start by annotating definitions and theorems in Lean with the @[blueprint]
tag. They will then be exported to LaTeX, which you may then put in the blueprint.
This tool is built to complement leanblueprint and its structure is inspired by doc-gen4. The idea is inspired by leanblueprint-extract.
Instructions
First, install leanblueprint and follow the instructions there to set up a blueprint project using leanblueprint new
, if not already done.
Add blueprint-gen to the lakefile. For example:
[[require]]
name = "blueprint-gen"
git = "https://github.com/hanwenzhu/blueprint-gen.git"
rev = "main"
To generate the blueprint for a module, first import BlueprintGen
and then annotate key theorems and definitions in the file with @[blueprint]
:
import BlueprintGen
@[blueprint]
theorem my_theorem : Foo Bar := by foo
(See also a full example below.)
Then input the generated blueprint source to the blueprint document (typically, blueprint/src/content.tex
):
% This makes the macros `\inputleanmodule` and `\inputleannode` available.
\input{../../.lake/build/blueprint/library/Example}
% Input the blueprint contents of module `Example.MyNat`:
\inputleanmodule{Example.MyNat}
% You may also input only a single node using:
% \inputleannode{MyNat.add}.
Then run:
# Generate the blueprint to .lake/build/blueprint
lake build :blueprint
# Build the blueprint using leanblueprint
leanblueprint pdf
leanblueprint web
If you see LaTeX errors here, you may need to manually fix some docstrings so that the generated LaTeX compiles.
(See also the instructions for converting from an existing blueprint below.)
Example
This example is hosted at blueprint-gen-example. Consider the following MyNat
API:
-- Example/MyNat.lean
/-! # Natural numbers -/
@[blueprint "Natural numbers"]
inductive MyNat : Type where
| zero : MyNat
| succ : MyNat → MyNat
namespace MyNat
/-!
## Addition
Here we define addition of natural numbers.
-/
/-- Natural number addition. -/
@[blueprint]
def add (a b : MyNat) : MyNat :=
match b with
| zero => a
| succ b => succ (add a b)
/-- For any natural number $a$, $0 + a = a$, where $+$ is Def. `MyNat.add`. -/
@[blueprint, simp]
theorem zero_add (a : MyNat) : add zero a = a := by
/-- The proof follows by induction. -/
induction a <;> simp [*, add]
/-- For any natural numbers $a, b$, $(a + 1) + b = (a + b) + 1$. -/
@[blueprint]
theorem succ_add (a b : MyNat) : add (succ a) b = succ (add a b) := by
/-- Proof by induction on `b`. -/
-- If the proof contains sorry, the `\leanok` command will not be added
sorry
/-- For any natural numbers $a, b$, $a + b = b + a$. -/
@[blueprint]
theorem add_comm (a b : MyNat) : add a b = add b a := by
induction b with
| zero =>
-- The inline code `abc` is converted to \ref{abc} if possible.
/-- The base case follows from `MyNat.zero_add`. -/
simp [add]
| succ b ih =>
/-- The inductive case follows from `MyNat.succ_add`. -/
sorry_using [succ_add] -- the `sorry_using` tactic declares that the proof uses succ_add
-- Additional content omitted
end MyNat
The (automatic) output of the above example Lean script is:
With depedency graph:
Specifying the blueprint
After tagging with @[blueprint]
, blueprint-gen will:
- Extract the statement and proof of a node from docstrings.
- Infer the dependencies of a node from the constants used in the statement or proof.
- Infer whether the statement or proof is ready (i.e.
\leanok
) from whether it is sorry-free. - Add the node to the generated blueprint.
You may override the constants used in the statement or proof with the uses
and proofUses
options, or with the using
tactic.
To view the generated blueprint data of a node, use @[blueprint?]
.
The Markdown docstrings will be automatically parsed and converted to LaTeX.
Citations are supported using square brackets like [wiles1995]
, and references to other Lean nodes can be done by inline code like `Lean.theorem_name`
. The output can be modified by the options blueprint.bracketedCitations
, blueprint.refCommand
and blueprint.citeCommand
. Raw LaTeX is also supported.
Informal-only nodes
At the start of a project, it is possible to have theorems or definitions in the blueprint, whose statements are not formalized in Lean.
For these "informal-only nodes" without formal statements, you can write them in the LaTeX blueprint only, and for Lean theorems to reference the informal theorem (say with label \label{thm}
), you may write sorry_using ["thm"]
, @[blueprint (uses := ["thm"])]
, etc.
Converting from existing blueprint format
With a project that uses the existing leanblueprint format, there is a primitive script that tries to convert to the blueprint-gen format.
Currently, this script depends on a recent version of Python with loguru
and pydantic
installed (install by pip3 install loguru pydantic
); and requires an installation of Pandoc to be available.
First go to a clean branch without any uncomitted changes, to prevent overwriting any work you have done.
You can then convert to blueprint-gen format by adding blueprint-gen
as a dependency to lakefile, run lake update blueprint-gen
, ensure leanblueprint checkdecls
works (i.e. all \lean
are in Lean), and then run:
lake script run blueprintConvert
Note that this conversion is not perfect and not idempotent, and for large projects it may end in some small syntax errors. You would need to fix the errors in the converted files.
The informal-only nodes (nodes without \lean
) are by default retained in LaTeX and not converted to Lean. If you want them to be converted, you may add --convert_informal
to the command above, and then the script will convert them and save to the root Lean module.
The conversion will remove the \uses
information in LaTeX and let blueprint-gen automatically infer dependencies in Lean, unless the code contains sorry
(in which case uses :=
and proofUses :=
will be added). If --add_uses
is specified then all \uses
information is retained in Lean.
Docstrings are converted from LaTeX to Markdown using Pandoc. If there is informal description of a theorem in LaTeX and a docstring in Lean, they are concatenated to form the new docstring. You should tidy the existing Markdown docstrings (e.g. wrap code in backticks and math in dollar signs) for better rendering.
You may use --blueprint_root <root>
to specify the path to your blueprint, if it is not the default.
(For reference, it takes a few minutes to convert FLT to blueprint-gen format and fix all errors, and it might take longer to fix all warnings and make the output look nicer.)
GitHub Actions integration
If building the blueprint is part of the GitHub CI action, then you need to run lake build :blueprint
before building the blueprint,
so that the \input
line above works. Here are some typical examples for doing this:
- If you use
.github/workflows/blueprint.yml
from leanblueprint, then add the following step:
# Before "Build blueprint and copy to `home_page/blueprint`":
- name: Extract blueprint
run: ~/.elan/bin/lake build :blueprint
- If you use
.github/workflows/build-project.yml
from LeanProject, then add thisbuild-args
option toleanprover/lean-action
:
- name: Build the project
uses: leanprover/lean-action@...
with:
use-github-cache: false
build-args: :blueprint
Extracting nodes in JSON
To extract the blueprint nodes in machine-readable format, run:
lake build :blueprintJson
The output will be in .lake/build/blueprint
.
TODO
- Currently the LaTeX output (and hence PDF / web outputs) are only in a state of barely working, because it is difficult to translate Markdown to LaTeX. The immediate next improvement will be to explore supporting Verso docstrings (leanprover/lean4#10307), and more specifically, for statement / proof docstrings using
doc.verso
, to generate the LaTeX directly from Verso instead of converting to Markdown and then to LaTeX. One roadblock here is support for citations, which we may have to wait until there is a good solution (e.g. via an extension) that works for both doc-gen4 and our purpose.