Cycle Consistency Pilot

Setup

The question: does giving a model access to the formal graph's dependency context improve autoformalization quality?

We measure this with a cycle-consistency test: take a real Lean 4 declaration F from Mathlib, have a model describe it in English (NL), then have a formalizer try to reconstruct Lean from NL alone. If the reconstruction F' matches the original F, the model preserved the meaning. The formal graph lets us provide F's dependencies — the declarations F actually uses — as context to the formalizer.

In a sentence: can knowing what a theorem depends on help you write it back in Lean?

The four conditions

We ran the formalizer (Claude Haiku 4.5 via Bedrock, same model for all) on the same 60 NLs under four conditions that vary only what context is appended:

Signature of a Lean declaration is the typed API — everything before the := (definition body) or by (tactic proof). For a theorem it's the statement itself; for a definition it's the type. It does not include the proof or the implementation. So the signature of ack_injective_left is (n : ℕ) : Function.Injective fun m => ack m n — enough to know how to invoke it, but not how it's proved. Throughout this page, "T provides dep signatures" means the formalizer sees the typed API of each predecessor, not its proof.

Predecessors of F are the nodes F directly depends on — the declarations whose names and types appear in F's signature or proof. They are the 1-hop outgoing neighbors of F in the formal graph (source_id = F, across all edge types: proof, sig, def, field, extends, docref).

Concrete example: what each condition actually sends

The candidate below is ack_inj_left from Mathlib.Computability.Ackermann — 7 predecessors, short signature, non-dense stratum. Toggle conditions to see exactly what the formalizer receives in each case.

How we scored each output

Type-check (primary): each output is wrapped in import Mathlib and run through lake env lean against Mathlib v4.29.0. Pass = elaborates without errors (sorry is allowed; it suppresses proof obligations but not type errors). This is model-free ground truth.

Judge (secondary): Claude Sonnet 4.5 is shown the target signature F and two candidate outputs with randomised A/B labels. It answers: which candidate more accurately matches the target semantically? The judge sees no condition labels and has no knowledge of which output came from which condition. Blinding map stored separately from results.

Edit distance: token-level Levenshtein between F's original signature and each output. Descriptive only.

Sample

60 Mathlib theorems and definitions (seed 42), stratified by in-degree (how widely cited: dense/medium/non-dense) × signature length (large/small). 10 per cell. All four conditions share the same 60 NLs — only the formalizer input varies.


1. Core Finding: Signatures Are the Load-Bearing Component

The type-check rate across all four conditions is the cleanest summary.

The marginal typecheck rates for B and T-names are identical (both 33%), but the paired picture is more interesting. T-names doesn't help the same candidates as B — it lifts some and drops others, net zero:

Only 13/20 B-passes overlap with T-names passes. T-names shifts which candidates typecheck — 7 that baseline would fail it passes, and 7 that baseline would pass it fails. A plausible mechanism: dep names push the formalizer toward specific declarations in the neighbourhood; when it guesses the wrong one the output is worse than no hint at all.

What reliably moves the needle is type signatures: T-random (55%) shows that any same-area signatures help, and the full predecessor graph (63%) adds another 8pp by providing signatures directly constraining F's own types.


2. Pilot: Baseline vs. Treatment

Before running the ablation we ran the core B vs T comparison on all 60 candidates. The judge (Sonnet 4.5) was shown the original signature and the two outputs — B and T — with A/B labels randomised per item. It chose which matched the target better.

The judge strongly prefers treatment outputs (82% of cases). The Wilcoxon test on the paired +1/−1/0 coding is significant (p ≈ 0), but n=60 is underpowered for subtle effects — read this as a strong directional signal.

Stratified by in-degree × signature size:

Treatment wins in every cell. The strongest cell is dense × large: 10/10/0 (T/tie/B) — the regime where F is both widely cited and has a complex signature, meaning its dep context is richest and there is the most structure to get right.


3. Ablation: What Is Actually Doing the Work?

Why we ran it

The B vs T result is strong, but the treatment context does two things at once: it gives the model the right type signatures (structural) and reveals the mathematical domain via the predecessor names (topical). We can't tell from B vs T alone which of these is load-bearing.

Three new conditions let us pull them apart:

What the judge saw

For each of the 60 candidates the judge was shown the original signature F and two outputs with randomised A/B labels — in one run T vs T-names, in another T vs T-random. Same prompt as the pilot: "which candidate more accurately matches the target signature semantically?" B vs T-names and B vs T-random were not judged directly; those comparisons come from the typecheck rates (which are model-free and don't require transitivity).

What it tells us

The judge and the type-checker agree directionally but not in magnitude:

The judge and typecheck diverge most on T vs T-random (judge: 45/37 nearly tied; typecheck: 63% vs 55% with an 8pp gap). The next section shows why.


4. Why Judge and Type-Check Diverge on T-random

The judge evaluates whether an output statement is semantically plausible given the NL. The type-checker asks whether it elaborates — are the names, argument types, and return type actually well-typed in Lean?

A T-random output can look plausible — it uses the right vocabulary because same-module declarations share types and namespaces — but fail to elaborate because the specific argument structure is wrong. The judge rates it highly; the type-checker rejects it. These are different failure modes, not one metric being "wrong."

The scatter below maps each of the 60 candidates on two axes: did the judge prefer T or T-random? Did T type-check when T-random didn't (or vice versa)? The shaded top-right region contains candidates where the judge preferred T-random but T was the one that actually type-checked.

If the scatter is dense to read quickly, here's the same data as a 2×2 count table — judge verdict (rows) × whether T was the one that type-checked (columns):


5. Edit Distance to Target Signature

Token-level Levenshtein between F's original signature and each output. Smaller = closer to the ground truth structurally. This is a distance metric, not a validity metric — an output can be close in edit distance but still wrong, or far but semantically equivalent.


6. Per-Candidate Detail


7. Generalization: 5 Projects, 300 Candidates

The pilot and ablation in §1–§6 were Mathlib-only (n=60). To test whether the findings generalize, we replicated the entire protocol — same models, same prompts, same judge, same seed — on 4 additional Lean 4 projects downstream of Mathlib, spanning very different mathematical domains:

Project Eligible pool Domain
Mathlib 329,981 general mathematics
combinatorial-games 2,435 surreal numbers, game theory
PersistentDecomp 153 topological data analysis
cam-combi 128 extremal / additive combinatorics
gibbs-measure 114 probability, measure theory

Each project contributed 60 uniformly-sampled candidates (unstratified — small projects can't fill the 3×2 in-degree × signature-size grid). Total: 300 pilot B-vs-T pairs and 300 ablation pairs. Per-project judge counts in the three tables below.

7.1 Headline (B vs T) replicates everywhere

7.2 Signatures finding (T vs T-names) replicates everywhere

7.3 Predecessor-vs-random (T vs T-random) does NOT replicate

7.4 What the five projects tell us

The three findings now have very different levels of empirical support:

Finding Pooled gap (n=300) Replication across 5 projects
Any same-namespace signatures beats no context (B vs T) +71 pp 5/5 projects, all p < 1e-5
Signatures beat names (T vs T-names) +43 pp 5/5 projects, every gap > +30pp
Predecessor signatures beat random same-module (T vs T-random) +0.7 pp 0/5 projects show clear effect

The original Mathlib pilot's T-vs-T-random gap (+8pp, CI [−8.3, +25.0]pp) does not survive replication. Across four additional projects the gap centres on zero (−3, −3, 0, +2). What the formal-dependency graph actually buys you at this formalizer scale is the typed API of namespace-adjacent declarations — not the specific selection of predecessors.

This is a sharper result for the talk than "the graph helps". It says: a small, locally-typed context window helps the formalizer enormously, and the graph is a fine source of such a context — but if the goal is just "give the formalizer relevant typed API", you could draw from the same Lean module instead and get the same outcome. The interesting graph-specific claims live elsewhere (e.g. ranking, distant retrieval, multi-step proof assistance) and would require different experiments.

What this scaled study still doesn't cover:


8. Limitations