satp-policy-v4.27
41.4% on miniF2F test — vs 34.0% expert-tuned aesop (DSP+), 13.1% LeanHammer, and 12.7% bare aesop. All 244 problems, greedy decode, Lean 4 v4.27.0.
A 0.2B ByT5 policy that reads a Lean 4 goal state and emits a per-problem aesop configuration — search budget, rule-set options, and extra tactic and lemma rules. This repository is a self-contained inference bundle: checkpoints plus a standalone infer.py (no training-repo dependencies), forward-equivalent to the training policy. Everything runs in a Lean 4 v4.27.0 environment.
Results on miniF2F
The dataset is ChristianZ97/minif2f-satp-v4.27, pinned by infer.py at revision 94424f13 and overridable with SATP_DATASET_REVISION (maxHeartbeats 200000).
| training | validation (final) | validation (best) | test | |
|---|---|---|---|---|
satp | 10 epochs | 40.0 ± 2.4% | 42.6 ± 0.4% | 39.9 ± 0.6% |
| w/o DPO | 10 epochs | 24.5 ± 4.4% | 41.1 ± 0.2% | 38.4 ± 1.6% |
| DPO → BC | 10 epochs | 31.4 ± 3.9% | 32.4 ± 3.8% | 30.7 ± 1.8% |
satp | 3 epochs | 38.5 ± 2.2% | 40.0 ± 2.8% | 38.7 ± 2.7% |
| w/o DPO | 3 epochs | 29.1 ± 6.1% | 34.3 ± 3.0% | 33.6 ± 2.5% |
| DPO → BC | 3 epochs | 15.6 ± 4.0% | 15.9 ± 3.8% | 17.5 ± 3.5% |
Inference Ablation & Baselines — test split
| configuration | solved |
|---|---|
satp (best_checkpoint.pt) | 41.4% |
| w/ default search budget | 41.4% |
| w/o additional tactic | 38.1% |
| w/o additional lemma | 33.2% |
| distilled static configuration (tactic only) | 34.8% |
Expert-tuned aesop, DSP+ source limits (1200 s, maxRecDepth 1024) | 34.8% |
Expert-tuned aesop (from DSP+, w/o BFS) | 34.0% |
hammer (LeanHammer v4.27.0) | 13.1% |
aesop | 12.7% |
- Rows 1–5: greedy decodes of
best_checkpoint.pt; the static row applies its per-head modes (block below) as one constant configuration. - Rows 6–9: no policy. Rows 6–7 take the expert configuration verbatim from microsoft/DSP-Plus@
eb98320— the two model-free attempts ofbfsaesopLoopplus its 16add_aesop_rules, the in-search 7B-prover attempt excluded; card protocol = 300 s,maxRecDepth 512. DSP+ reports 35.2% on its own Lean 4 v4.17-rc1 snapshot. - Row 8 reruns LeanHammer's released
hammertactic out of the box —aesoppreprocessing, cloud premise selection, and the Zipperposition→Duper pipeline — at 600 s/problem;v4.27.0is the release matching this environment's exact Lean/Mathlib commit, and uncappingmaxHeartbeatschanges nothing (the same 32 problems solve). Its current release (v4.33.0, a different Lean environment) yields 37.3% on the same statements, with 58 of the 91 proofs closed by Lean core'sgrindand at most 14 citing a retrieved premise — the jump comes from addinggrindto theaesoprule set, not from lemma retrieval. SATP instead couples each retrieved lemma to a learned tactic and priority (the w/o-additional-lemma row). - Verifiers: every row
lake env lean; the full-configuration decode matches its Kimina-measured result exactly on both splits.
Model
| verifier | Kimina-Lean Server, Lean 4 v4.27.0 |
| training data | ChristianZ97/NuminaMath-LEAN-satp-v4.27 (75,008 problems; 5,000 subsampled per epoch) |
| architecture | ReProver's Retrieval Encoder+LoRA(r16,α32,q/k/v/o) mean-pool → shared MLP → 69-head joint action (28 tactic ×10-way + 32 lemma ×172-way + 4 config_level + 5 config_binary) + dense premise retrieval |
Distilled static configuration
One constant configuration for every problem, lemma rules off: each value is the per-head mode over best_checkpoint.pt's 244 validation decodes, and -- % is how often that mode was chosen. Priorities are integers, smaller = higher (0 first, 100 last).
aesop (config := {
maxRuleApplications := 1600 -- 54%
maxRuleApplicationDepth := 10 -- 54%
maxNormIterations := 320 -- 86%
})
(add norm 100 (by norm_cast)) -- 99%
(add norm 0 (by linarith)) -- 100%
(add norm 0 (by nlinarith)) -- 100%
(add norm 0 (by positivity)) -- 36%
(add norm 0 (by omega)) -- 49%
(add norm 0 (by bound)) -- 100%
(add norm 1 (by ext)) -- 61%
(add norm 1 (by field_simp [*] at *)) -- 97%
(add safe 100 (by ring)) -- 73%
(add safe 100 (by field_simp)) -- 99%
(add safe 100 (by norm_num)) -- 99%
(add safe 100 (by interval_cases)) -- 96%
(add safe 0 (by split)) -- 93%
(add safe 100 (by decide)) -- 100%
(add safe 0 (by push_cast)) -- 86%
(add safe 100 (by assumption_mod_cast)) -- 98%
Files
best_checkpoint.pt # infer.py's default; copy of ckpt_3ep_val103_test101.pt
ckpt_10ep_val104_test99.pt # ── 10-epoch runs
ckpt_10ep_val105_test97.pt
ckpt_10ep_val103_test96.pt
ckpt_3ep_val90_test88.pt # ── 3-epoch runs
ckpt_3ep_val100_test94.pt
ckpt_3ep_val103_test101.pt
ckpt_10ep_nodpo_val100_test92.pt # ── 10-epoch runs, w/o DPO
ckpt_10ep_nodpo_val100_test91.pt
ckpt_10ep_nodpo_val101_test98.pt
ckpt_3ep_nodpo_val78_test78.pt # ── 3-epoch runs, w/o DPO
ckpt_3ep_nodpo_val81_test79.pt
ckpt_3ep_nodpo_val92_test89.pt
ckpt_10ep_bc_val89_test77.pt # ── 10-epoch runs, DPO → BC
ckpt_10ep_bc_val77_test70.pt
ckpt_10ep_bc_val71_test78.pt
ckpt_3ep_bc_val49_test49.pt # ── 3-epoch runs, DPO → BC
ckpt_3ep_bc_val36_test33.pt
ckpt_3ep_bc_val31_test46.pt
infer.py # load → retrieve → greedy-decode → Kimina verify
reproduce.py # test-split repro, kimina | lake verifier (imports infer.py)
cache/premise_embeddings.npy # 1.07 GB — 180,957 × 1472 premise embeddings
cache/mathlib4_premises.txt # 38.9 MB — premise names, index-aligned
Checkpoint names carry their own scores: val{V}_test{T} = problems solved out of 244 on each split; each file is its run's best-val checkpoint (~1.12 GB each). infer.py reads model_state_dict only; the checkpoint files are interchangeable at load time. Premise corpus = LeanDojo Benchmark-4 v10; embeddings come from the frozen retriever → verifier-version independent. ByT5 base auto-downloads on first run.
Run
pip install torch transformers numpy requests huggingface_hub datasets
hf download ChristianZ97/satp-policy-v4.27 --local-dir satp-policy-v4.27
cd satp-policy-v4.27
KIMINA_URL=http://localhost:8000 python infer.py # default = best_checkpoint.pt
SATP_CKPT=./ckpt_3ep_val103_test101.pt KIMINA_URL=http://localhost:8000 python infer.py # any other checkpoint
The Kimina server must run a Lean 4 v4.27.0 workspace.
| var | default | meaning |
|---|---|---|
KIMINA_URL | http://localhost:8000 | Kimina /verify endpoint |
SATP_SPLIT | validation | or test |
SATP_DATASET_REVISION | 94424f13… | dataset commit infer.py resolves (pinned) |
SATP_LIMIT | 0 | >0 → first N problems only |
SATP_CKPT | ./best_checkpoint.pt | checkpoint path — point it at any other ckpt_*.pt |
SATP_CACHE | ./cache | premise files dir |
SATP_HEARTBEATS0 | unset | 1 removes the heartbeat cap; reported numbers use the default |
SATP_LEAN_TIMEOUT | 300 | server-side Lean timeout (s); HTTP timeout = +60 |
Verification is sequential (one POST per problem, retried with backoff). Header-less statements get the training header prepended.
Reproduce
reproduce.py re-runs the miniF2F test split end-to-end over the same greedy decode and checks the result against its built-in target, with either verifier. It always loads best_checkpoint.pt — it ignores SATP_CKPT — so the target it prints can never drift from the weights it graded:
KIMINA_URL=http://localhost:8000 python reproduce.py kimina # target 101/244
SATP_SPLIT=validation python reproduce.py kimina # target 103/244
SATP_LAKE_DIR=/path/to/lean-v4.27-project python reproduce.py lake # no server
lake mode runs one lake env lean per problem inside any Lean 4 v4.27 project with Mathlib built — no server. Both modes write reproduce_<backend>.jsonl; greedy decode is deterministic.