The combined model, and where its gains come from

For context, see last week’s post.

Combining models #

Dev theorems solved, by pipeline

Where the 762 extra theorems come from, averaging each piece’s effect over the other two:

piece naive → best average effect
pretraining autoresearch run 012’s baseline → the autoresearch incumbent (186) +408
RL the harness’s plain expert iteration → Leon’s recipe + verifier-guided sampling +268
data format token format → Dan’s Lean format +92
How the effects are computed

A factor’s effect is the mean of the four cells at its best level minus the mean of the four at its naive level. Interactions are the same contrast on products of ±1 codes, divided by 4: format × pretraining +82, format × RL −46, pretraining × RL +6, all three −6.

The combined model #

stage piece from
data 155k cap-6 proofs, the ladder RL and transfer pools Dan
format lean_seq: Lean 4 tactic proofs, one symbol per token; reward = Lean ∧ nd_verify Dan
pretraining autoresearch incumbent 186: 6 × 384 Peri-LN, 4 ALiBi + 4 NoPE heads, Muon, MTP, 300 s autoresearch (last week’s post)
RL expert iteration in 15 small rounds (400 targets, ~32 samples each), hardest-first queue, training samples at T 1.0, fresh AdamW per round, GEM loss Leon (evolved by his GEAR agents)
RL sampling verifier-guided: every have the model writes is checked against the rules given the lines before it; a bad line is rolled back in the KV cache and resampled (up to 4 times) Leon’s run-2 sampler, ported to the Lean format this week

It got there in four steps on the same pretrained weights (details in our private group repo): Leon’s RL recipe +117 (687 → 804), the full RL target pool +4 (noise), guided sampling +101 (905, but over the time budget), then the same guided run sped up and cut off at 1,800 s so the whole pipeline fits in 2,400 s (904). Charles’s result that plain expert iteration matches GRPO agreed with Leon’s choice, so there was nothing of his to add.

Verifier-guided sampling in the Lean format

Leon’s sampler checked abs-format proof lines, which are already natural-deduction lines. In the Lean format a line is a have n7 : F := term ;, possibly inside nested ( fun ( n3 : A ) => by ... ) boxes. At every ;, the Lean prefix is parsed with Dan’s own Lean → ND parser (stopped at the cut) and each new ND line goes through Leon’s incremental checker. Checks: 0 false rejects on 23,936 line checks of real proofs, 0 false accepts on 9,000 mutated proofs, and the KV-cache rollback reproduces an un-rolled-back forward to 7e-7 in the logits. On the pretrained model it accepts ~55% more samples per round. The final proof is still checked by Lean and nd_verify; guidance only prunes.

Where the gains come from #

All 8 combinations of {naive, best} × {format, pretraining, RL}, three seeds each, fresh, under one protocol: same data, same 1,500 RL targets, 300 s of pretraining, at most 2,400 s end to end (the longest took 2,374 s), one A40 (three runs got an RTX A6000, the same chip).

pass@k on 250 held-out theorems

250 theorems from the held-out half of the transfer pool, all 7-14 lines, never used to pick anything; 256 samples per theorem. pass@k per theorem is the unbiased estimator 1 − C(n − c, k) / C(n, k) from n = 256 samples with c verified, averaged over theorems and then over the three seeds.

Without Leon’s RL (orange on the right) the curve starts highest and flattens early: plain expert iteration sharpens the model onto proofs it already finds. Leon’s recipe samples its training proofs at T 1.0 and trains with GEM, a loss built to keep the output distribution spread out, and the curves show it: lower at k = 1, far higher by k = 8.

pass@k on 72 textbook problems

Dmitry’s textbook set, 72 problems nobody trained on: 58 with reference proofs of 1 to 33 lines plus 14 without a reference, scored like the held-out set. The best seed solves 32 of 72. On the naive pretraining the data format adds nothing and RL about 3 problems; only better pretraining lifts the left panel clearly. On the right, removing the pretraining collapses the model back to naive.

pipeline dev (of 1,108) held-out pass@1 pass@64 pass@256 textbook pass@256
naive 142 7% 13% 14% 13%
+ Lean format 193 8% 14% 17% 11%
+ best pretraining 457 26% 45% 48% 24%
+ best RL 444 12% 40% 43% 17%
best 904 30% 79% 82% 44%
− Lean format 782 28% 68% 71% 35%
− best pretraining 414 7% 34% 41% 14%
− best RL 682 44% 59% 62% 28%

Test-time scaling #

Everything above spends 64 independent samples per theorem. Here the weights are frozen, and the question is what smarter inference buys.

Setup: five inference methods, all 1,177 held-out theorems (114 longer than 10 lines), three seeds. Compute is tokens sampled per theorem, rejected attempts included.

Solve rate vs sampled tokens per theorem, by inference method

What this says about the model:

pass@k on the 72 textbook problems by inference method, with a 30% reference line

The textbook set. Dmitry wanted about 30% on his 72 textbook problems. The combined model gets there at 8 plain samples or 2 with resampling, but not at 1.

k plain resample resample, no repeats
1 17.6% 26.0% 27.4%
2 22.1% 31.1% 32.9%
8 29.8% 38.6% 41.1%
64 38.3% 46.1% 48.7%
256 41.7% (30 of 72) 50.5% (36) 51.9% (37)

Test-time RL: making up easier problems #

Search over a frozen model tops out: at 1,024 attempts the model puts almost no probability on any proof of about 90% of the theorems it misses. Search can’t fix that, so the weights have to change. AlphaProof (Hubert et al., 2025) does exactly this for its hardest problems, with what it calls test-time RL. For each target it generates hundreds of thousands of related problems (“variants”: simplifications, generalizations, lemmas, sub-steps) and runs its normal RL loop on the target plus variants until the target falls. The logic: RL learns only from successes, so a target the model never solves gives no gradient. A variant the model solves sometimes does, and it sits right next to the target. On IMO and Putnam problems this added 15 percentage points beyond a 12-TPU-hour search, at a cost of 50 to several hundred TPU-days per problem.

This is the simple version of that, on our model.

Where the variants come from #

AlphaProof uses Gemini, prompted with 791 hand-made (problem, variant) pairs, plus code that perturbs hypotheses and goals. We don’t need a language model. A theorem here is a sequent Γ ⊢ G over four atoms, and every variant below is built from the statement alone: no reference proof, no model. Each is kept only if it is valid. Validity is a 16-row truth table, because the logic is classical and has four atoms, so valid means provable. AlphaProof has no such check (Lean mathematics is undecidable) and instead lets its prover try to disprove each variant.

kind move example: P>Q, Q>R, R>S ⊢ P>S
goal A > B assume A: Γ, A ⊢ B P>Q, Q>R, R>S, P ⊢ S
goal ~A assume A: Γ, A ⊢ F
any goal G reductio: Γ, ~G ⊢ F …, ~(P>S) ⊢ F
goal A & B each half: Γ ⊢ A, Γ ⊢ B
goal A v B one side, if still valid: Γ ⊢ A or Γ ⊢ B
premise A & B split it into A, B
premise A v B the two cases: replace it by A; by B
premise A > B strengthen it to B, or to ~A Q, Q>R, R>S ⊢ P>S
premise ~(A & B) strengthen it to ~A, or to ~B
lemma (cut) any subformula ψ of Γ or G with Γ ⊨ ψ gives two variants: Γ ⊢ ψ and Γ, ψ ⊢ G none: every subformula that follows from Γ is a premise or the goal

Every move is applied to the target, then once more to each result (depth 2). Duplicates are dropped: same premises as a set, same goal. So is any variant whose goal is already a premise. On the 64 targets below this gives 3,397 variants (median 42 per target, 12 to 263). 78% are lemma cuts.

A true lemma need not be a useful one. The reference proof would say which lemmas lie on the path to the target, but it is off limits, so the solve rates have to sort useful from useless.

The loop #

Model: c005 seed 0, the combined model above. One copy is adapted on all 64 targets at once, as AlphaProof does for several targets. Each round:

  1. Sample 16 proofs of every active target and variant. The sampler is the one Leon’s RL uses: guided resampling of bad lines (4 redraws per line, 24 per proof), T 1.0.
  2. A target with any verified proof is solved. It leaves the pool with all its variants.
  3. Mastered variants leave. A variant solved in more than 75% of its 16 samples (13+) goes out: the model already has it, and its proofs would only reinforce what it does anyway.
  4. Train on the frontier. Variants solved in 1-12 of 16 samples contribute up to 4 distinct proofs each, repeated max(1, round(8 × (1 − rate))) times, so rarely solved variants count up to 8×. That is Leon’s weighting. Also in the mix: 20,000 random cap-6 training proofs, against forgetting. Variants at 0 of 16 stay in the pool untrained. Learning on their neighbours may make them solvable next round, which is how a curriculum is meant to work.
  5. Update with one round of Leon’s RL step: GEM loss, fresh AdamW, 135 steps of 128.

8 rounds. The band (0, 0.75] is my call. It follows AlphaProof’s matchmaker, which prioritises problems with mixed recent results and lowers the priority of those that always or never succeed. A variant at 0 gives no data. One at 16 of 16 gives data the model doesn’t need.

Targets. 64 dev theorems that c005 seed 0 fails at 64 plain samples, chosen by hash: lengths 7-14, mostly 9-10. These are dev theorems, not held-out ones: this is the development run. If TTRL works here, the held-out theorems that every inference method misses are the real test. That would reverse “training on these evaluation theorems is off the table” above: TTRL trains on the statement, never on a proof of it.

Control. The same sampler on the 64 targets alone: no variants, no training, 256 samples per target per round for 16 rounds (4,096 per target). TTRL counts as a win only if it solves more targets than the control at the same number of samples. The control is plain sampling at matched compute. TTRL also pays for fine-tuning, which is reported separately.

Results #

The stepping stones exist. Round 1 samples the unadapted model, so it tests the premise directly: does the model solve variants of targets it can’t solve?

round 1, 16 samples each items any proof in the band (1-12 of 16) mean solve rate
targets 64 12% 12% 0.01
all variants 3,397 43% 27% 0.24
cuts, depth 1 / 2 243 / 2,391 44% / 39% 24% / 26% 0.26 / 0.21
decompositions, depth 1 / 2 166 / 425 40% / 61% 33% / 35% 0.17 / 0.35
specializations, depth 1 / 2 26 / 146 73% / 58% 19% / 29% 0.60 / 0.36

TTRL solves 48 of 64. Sampling more solves 31. Numbers below are seed 0 unless marked.

Targets solved vs samples per target: TTRL, unrelated-variant TTRL and sampling more, two seeds

Is it the variants, or just more RL? #

Variants of other theorems don’t do it. The same loop on seed 0, with the variants swapped for those of 64 other unsolved dev theorems: 4,499 variants from similar lengths, the same band and the same updates. The 64 targets are sampled exactly as in TTRL, 16 per round. This is the orange line above.

round 1 2 3 4 5 6 7 8
own variants (TTRL) 8 23 28 29 36 40 46 48
unrelated variants 7 11 20 23 25 25 26 28

Neither arm trains on a proof of a target, so the 20-target gap comes from which theorems the variants were built from. What this run can’t say is whether plain expert iteration on the targets, with no variants at all, would do as well. That arm is missing here: see “At scale” below.

What’s left #

16 targets are still unsolved. For 9 of them every variant has been solved at least once, and most are mastered. la_transfer_1167, for one, has all 19 variants mastered, and the target is still unsolved. The model can prove every piece but doesn’t write the whole proof in one pass. That’s the composition gap: AlphaProof closes it with tree search, which assembles solved subgoals automatically. Our model writes proofs left to right without search.

The cheap fix is mechanical. A cut gives proofs of Γ ⊢ ψ and Γ, ψ ⊢ G. Paste the first into the second where ψ is cited as a premise, renumber, and verify: that’s a proof of the target, built from the model’s own pieces. Training on it teaches the long proof. This wasn’t in this run.

Caveats #

Code: code/experiments/current/ttrl/ on branch robbie-experiments. Per-item results live in the checkout’s artifacts/ttrl/: dev64_ttrl, dev64_control, dev64_unrelated, s1_ttrl, s1_control.

At scale: every dev failure, up to 5× the pipeline’s compute #

The 64-target runs leave two questions:

  1. How much does TTRL add to the real metric, per unit of compute?
  2. Is it the variants, or just being allowed to train on the eval theorems at all? Plain expert iteration on the eval statements, with no variants, might buy most of the gain. If it does, “test-time RL helps” is the headline, not “variants help”. Neither run so far separates the two: the control never trains, and the unrelated-variants arm never trains on the targets.

Setup.

Three arms, same targets, same compute:

arm samples trains on
sample more targets, 256 per round nothing
targets-only EI targets, 256 per round proofs of the targets it has solved
TTRL targets and variants, 16 each per round solved targets plus variants in the band

Every arm uses the same sampler (guided, T 1.0), the same RL step (Leon’s), and the same band and weights as above. One change from the 64-target runs: solved targets now stay in the training mix, so TTRL is exactly targets-only EI plus variants.

Compute. x is what the pipeline spent producing the model: pretraining plus RL, 1,641-1,774 s of one A40 per seed. Each arm runs until it has used 5x (2.3-2.5 h), and every round records its cumulative time. The result is a curve of dev solved = baseline + newly solved, read off at 1x, 2x, 3x, 4x and 5x. Evaluation costs are left out: every arm pays the same 64-sample eval first. The clock is wall-clock on one GPU from the start of the loop. Sampling, Lean checks and fine-tuning all count; loading the model doesn’t. The score at “2x” counts only rounds that finished within 2x.

Plain expert iteration on the eval statements wins. Variants add little.

Dev solved vs test-time compute, three arms, three seeds

Dev theorems solved (of 1,108; baseline 904), mean of three seeds, at each multiple of the pipeline’s own pretrain + RL compute:

arm 1x 2x 3x 4x 5x
sample more 1,002 1,014 1,017 1,019 1,020
targets-only EI 1,069 1,082 1,085 1,086 1,088
TTRL 929 986 1,034 1,067 1,079

What this means for the earlier sections: the 64-target result (“TTRL 48, sampling more 31”) stands, and so does its control: variants of other theorems don’t help. It never asked whether training on the targets alone would do. It does, at least when there are ~200 of them.

What each arm does, round by round #

Seed 0, from the logs. All three arms start from the same weights, the same 193 targets and the same sampler.

sample more targets-only EI TTRL
round 1 samples 193 targets × 256 = 49,408 the same 49,408 193 targets + 12,254 variants, × 16 = 199,152
round 1 time 6.0 min 7.3 min (23 s of it training) 22.9 min
solved after round 1 72 72 (same samples, same seed) 26 (16 samples per target)
then sample the 121 left train on the 72 solved, resample the 121 left train on the 26 solved plus 40,663 records from in-band variants
solved after round 2 81 97 68 (at 44.5 min)
rounds in 5x 50 112 16
final 104 169 160

Round 1 is a free consistency check: with the same samples and the same seed, EI and sample more must agree, and they do on all three seeds (72, 84 and 73). Round 2 is the first time training shows: EI gains 25 targets with no variants at all.

Why TTRL loses to EI #

  1. Far fewer updates. TTRL samples every active variant every round, so its rounds last ~9 minutes against EI’s ~1.3. In 5x it gets 16, 19 and 36 updates (seeds 0-2); EI gets 112, 166 and 110. Its first update comes at 0.7-0.9x; EI’s at 0.25x. Seed 2, where TTRL ran the most rounds, is the one where it ended ahead.
  2. Each update sees under half of TTRL’s data. Every round is 135 steps of 128 (17,280 examples), Leon’s setting. EI’s mix is ~22k records (its own proofs plus the 20,000 retained), so one round covers most of it. TTRL’s early mixes are 50-60k, so a round covers 28-35% of what it just collected.
  3. The targets are already each other’s variants. About 200 failures from one generator share schemas, so a proof of one teaches the next. EI gets that transfer free. The statement-made variants add mostly easy neighbours that it pays to sample every round.

Points 1 and 2 are settings I made for the 64-target run and kept unchanged, so this run would be pre-declared: k = 16 per variant, every variant every round, Leon’s 135 steps. At 3× the targets they make TTRL sampling-bound. The honest claim is “TTRL as configured loses to EI”, not “variants can’t help”. Two things say the variants do something: TTRL was still climbing at 5x while EI had plateaued, and it solves 4-12 theorems per seed that EI never does.

Caveats for this run.

Next. Start with EI on the targets, then add variants only for the theorems EI leaves. The two arms’ solved sets overlap only partly, so the combination should beat either: their union leaves 12, 1 and 19 per seed. Per-theorem results: artifacts/ttrl/devall_s{0,1,2}_{ttrl, selftrain,control}/ in the checkout.

Showing the model the proof state #

Every model above writes a whole proof in one pass. It sees the theorem and the proof so far, and it has to work out for itself which hypotheses are in scope, which boxes are open and what it is currently trying to prove. Dan built a different interface this week, modelled on AlphaProof (Hubert et al., 2025). The model sees only the current proof state, the way Lean prints it: every hypothesis in scope, then the goal. It writes one step. An environment applies the step and prints the new state, and the model writes the next step. The finished steps concatenate into an ordinary Lean proof, which Lean checks as before.

An illustration, halfway through proving P → Q, Q → R ⊢ P → R, inside the box that assumes P:

h1 : P → Q          the premises
h2 : Q → R
n1 : P → Q          lines proved so far, still in scope
n2 : Q → R
n4 : P              the box's assumption
n5 : Q
⊢ R                 what this box still has to prove

The model’s next step is have n6 : R := n2 n5 ;. Nothing else is in its context: no theorem statement (the first state is the theorem) and no proof history. The state is Markov: it summarises everything the history would tell the model.

Dan found that on his 3.2M-parameter model this lifts what the pretrained model can reach nearly 6× (with no RL: 779 vs 158 transfer theorems). I wanted to know two things. Does it help my pretraining recipes as much? And does my autoresearch ranking of recipes survive the change of format?

Dev theorems solved by pretraining recipe: whole proof, whole proof with 6x RL samples, proof state, and proof so far + state

Setup. Four recipes from my autoresearch loop: 198, the incumbent (186 above plus two small deletions), and three of my simplified versions of it (207, 201, 204). Each was trained four ways, three seeds each:

Everything else is the frozen autoresearch harness: the same data, 300 s of pretraining, and plain expert iteration (4 rounds × 1,500 targets), not Leon’s RL. The metric is the same dev metric as above.

recipe whole proof 6× RL samples proof state proof so far + state
198 669 / 692 / 216 743 / 345 / 682 852 / 824 / 798 708 / 744 / –
207 612 / 304 / 616 713 / 625 / 396 781 / 777 / 814 665 / 676 / 687
201 609 / 638 / 579 674 / 678 / 626 723 / 720 / 720 594 / 672 / 602
204 566 / 552 / 571 610 / 642 / 629 705 / 744 / 790 660 / 702 / 658

Cost. A proof-state run takes about 3× the wall-clock of a whole-proof run: 58 minutes against 17 for 198, on an A40. It doesn’t fit the 2,400 s budget every other run in this post uses. Sampling is about 4× slower and fine-tuning about 2.6× (a proof is about 5 training pairs). Most of the sampling cost is engineering, not the format. The model re-reads the whole state at every step, with no reuse between steps. My recipes’ ALiBi attention builds a bias tensor that grows with the square of the context, which forced the batch down from 2,048 to 512 to fit in memory. And the environment steps a few thousand attempts in Python between model calls.

From next week the pipeline uses the proof state.

Caveats
  • Plain expert iteration only. None of these runs use Leon’s RL or the guided sampler, so “proof state + Leon’s RL” is untested.
  • Dev metric, three seeds. No held-out or textbook numbers yet for the proof-state models.
  • Fork. All arms, the whole-proof ones included, ran on Dan’s current code, which judges with Lean alone; the rest of the post uses Lean ∧ nd_verify. Whole-proof scores here match the autoresearch loop’s (198: 669 and 692 vs 681).
  • Batch sizes. The proof-state batch was 512, and the proof-so-far batch started at 256 and halved whenever memory ran out (down to 32 on the weakest recipes). Batch size changes the random draws, not what is measured.
  • One lost run. 198’s third proof-so-far seed was lost to a network drop and not re-run.
  • GPUs. 7 of 47 runs got an RTX A6000 instead of an A40 (the same chip).

Code: code/experiments/current/state_recipes_20260930/ on branch robbie-experiments; the environment is Dan’s state_env.py (fork branch dan_best-state). Per-run numbers: charts/state_runs.csv.

Methodology #

factor naive best
format the token era’s abs format; reward nd_verify Dan’s lean_seq; reward Lean ∧ nd_verify
pretraining autoresearch 012’s pretrain.py: fork GPT, 4 × 256, RoPE, AdamW autoresearch 186’s pretrain.py
RL frozen harness: 4 rounds × 1,500 targets × 32 samples, 600 fine-tune steps each Leon’s recipe + guided sampling, stopped at 1,800 s

Everything else is fixed: the same 155k training proofs (in the two formats), 300 s of pretraining, the same seeded 1,500 RL targets, the same evaluation (64 samples at T 0.8 on 1,108 dev-transfer theorems; the charts’ pass@k at T 0.8 too). The two pretraining recipes differ only in a tokenizer switch from the files the autoresearch loop ran. Code: code/experiments/current/factorial_20260929/ and combined_pipeline_20260928/, branch robbie-experiments.

Caveats #