NIM paper · interactive edition Interactive edition · paper, explanations and Lean proofs.

Section 7.3, “Training the rollout heads”Every tie-breaking rule gives the same rollout length, hence the same outcome bit

Checked theorem Lean checks a formal statement corresponding to this claim.

1 Paper text

Training the rollout heads. We train a shared-trunk network with B=4B=4 policy heads by imitation learning against the structured policies π0,…,πB−1\pi_0,\dots,\pi_{B-1} from Proposition 5.9. Each head πj\pi_j subtracts 2j2^j from the leftmost heap of size at least 2j2^j (falling back to subtracting 1 from the leftmost nonzero heap). When multiple heaps qualify for the same phase of πj\pi_j (e.g. several heaps have hi≥2jh_i \geq 2^j), any choice among them produces a rollout with the same total move count and hence the same parity outcome. We therefore train with soft targets: for each position and head jj, the label is uniform over all equivalent actions, and the loss is cross-entropy against this soft distribution. This avoids penalising the model for an arbitrary tie-breaking choice among equally valid heaps.

Generated from paper_jmlr.tex, lines 1149–1154.

2 Explain this

When several heaps qualify for the same move of πj\pi_j (for example several heaps of size at least 2j2^j), the choice does not matter for the outcome: every complete rollout has the same number of moves, ∑i⌊hi/2j⌋+∑i(hi mod 2j)\sum_i\lfloor h_i/2^j\rfloor+\sum_i(h_i\bmod2^j), whichever eligible heap is used each time. So the training labels can spread probability evenly over all equivalent actions without changing the channel bit.

3 How this is formalised

Rollout.run_length: every complete run of πj\pi_j (Rollout.Run, built from Rollout.PolicyMove steps with arbitrary tie-breaking) has exactly that length. Rollout.run_parity: its parity is the channel bit of the nim-sum.

Coverage record
Paper anchorc-tie-breaking (website anchor; the manuscript has no label here)
Displayed asSection 7.3, “Training the rollout heads”
ClaimEvery tie-breaking rule gives the same rollout length, hence the same outcome bit
Coverage categoryChecked theorem
Statement sourcepaper_jmlr.tex lines 1149–1154; SHA-256 of the normalised block source 373eb3c467bdb240…
Lean declarations
Explanationsexplain/c-tie-breaking, formal/c-tie-breaking
Build evidencefresh isolated rebuild, audit report

4 Lean statement and source

Principal declarations

theorem Rollout.run_length

Module NimRollout (lean/NimRollout.lean, lines 88–94) · file SHA-256 1a99428d71573d31… Axioms reported by the fresh build: propext, Quot.sound

EXECUTION COUNT: every complete run of `pi_j`, under any tie-breaking rule, has exactly `sum_h (h / 2^j + h % 2^j)` moves.

Statement as elaborated and printed by Lean 4.26.0

∀ {j : ℕ} {p : List ℕ} {n : ℕ}, NimAudit.Rollout.Run j p n → n = NimAudit.rolloutCount j p
Exact source text, lines 88–94
88/-- EXECUTION COUNT: every complete run of `pi_j`, under any tie-breaking rule,89    has exactly `sum_h (h / 2^j + h % 2^j)` moves. -/90theorem run_length {j : Nat} {p : List Nat} {n : Nat} (hr : Run j p n) :91    n = rolloutCount j p := by92  induction hr with93  | done p hz => exact (rolloutCount_zero hz).symm94  | step p q n hm _ ih => rw [ih, policyMove_count hm]

Project declarations in the statement: Rollout.Run, rolloutCount

Project declarations used by the proof or definition body

Rollout.PolicyMove, Rollout.Run, Rollout.Run.rec, Rollout.policyMove_count, Rollout.rolloutCount_zero, rolloutCount

theorem Rollout.run_parity

Module NimRollout (lean/NimRollout.lean, lines 96–102) · file SHA-256 1a99428d71573d31… Axioms reported by the fresh build: propext, Quot.sound

PARITY OF EXECUTION: the number of moves of any complete run of `pi_j` has low bit equal to the channel value of the nim-sum (bit 0 for j = 0; bit j XOR bit 0 for j ≥ 1).

Statement as elaborated and printed by Lean 4.26.0

∀ {j : ℕ} {p : List ℕ} {n : ℕ},
  NimAudit.Rollout.Run j p n → n.testBit 0 = NimAudit.channel j (NimAudit.nimSum p)
Exact source text, lines 96–102
96/-- PARITY OF EXECUTION: the number of moves of any complete run of `pi_j` has97    low bit equal to the channel value of the nim-sum98    (bit 0 for j = 0; bit j XOR bit 0 for j ≥ 1). -/99theorem run_parity {j : Nat} {p : List Nat} {n : Nat} (hr : Run j p n) :100    n.testBit 0 = channel j (nimSum p) := by101  rw [run_length hr]102  exact rollout_count_channel j p

Project declarations in the statement: Rollout.Run, channel, nimSum

Project declarations used by the proof or definition body

Rollout.Run, Rollout.run_length, channel, nimSum, rolloutCount, rollout_count_channel

Definitions these statements use

inductive Rollout.Run

Module NimRollout (lean/NimRollout.lean, lines 34–38) · file SHA-256 1a99428d71573d31…

`Run j p n`: following `pi_j` from board `p` reaches the all-zero board after exactly `n` policy moves.

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → ℕ → Prop
Exact source text, lines 34–38
34/-- `Run j p n`: following `pi_j` from board `p` reaches the all-zero board after35    exactly `n` policy moves. -/36inductive Run (j : Nat) : List Nat → Nat → Prop37  | done (p : List Nat) : (∀ x ∈ p, x = 0) → Run j p 038  | step (p q : List Nat) (n : Nat) : PolicyMove j p q → Run j q n → Run j p (n + 1)

Project declarations in the statement: none

Project declarations used by the proof or definition body

Rollout.PolicyMove

def Rollout.PolicyMove

Module NimRollout (lean/NimRollout.lean, lines 28–32) · file SHA-256 1a99428d71573d31…

One legal move of the structured policy `pi_j` (tie-breaking unspecified).

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → List ℕ → Prop
Exact source text, lines 28–32
28/-- One legal move of the structured policy `pi_j` (tie-breaking unspecified). -/29def PolicyMove (j : Nat) (p q : List Nat) : Prop :=30  (∃ pre x post, p = pre ++ x :: post ∧ 2 ^ j ≤ x ∧ q = pre ++ (x - 2 ^ j) :: post) ∨31  ((∀ x ∈ p, x < 2 ^ j) ∧32    ∃ pre x post, p = pre ++ x :: post ∧ 0 < x ∧ q = pre ++ (x - 1) :: post)

Project declarations in the statement: none

Project declarations used by the proof or definition body

none

def rolloutCount

Module NimCore (lean/NimCore.lean, lines 82–82) · file SHA-256 b07db6c505210d5a…

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → ℕ
Exact source text, lines 82–82
82def rolloutCount (j : Nat) (rows : List Nat) : Nat := (rows.map (heapCount j)).sum

Project declarations in the statement: none

Project declarations used by the proof or definition body

heapCount

def channel

Module NimCore (lean/NimCore.lean, lines 77–78) · file SHA-256 b07db6c505210d5a…

Statement as elaborated and printed by Lean 4.26.0

ℕ → ℕ → Bool
Exact source text, lines 77–78
77def channel (j sigma : Nat) : Bool :=78  if j = 0 then sigma.testBit 0 else xor (sigma.testBit j) (sigma.testBit 0)

Project declarations in the statement: none

Project declarations used by the proof or definition body

none

def nimSum

Module NimCore (lean/NimCore.lean, lines 7–9) · file SHA-256 b07db6c505210d5a…

Statement as elaborated and printed by Lean 4.26.0

List ℕ → ℕ
Exact source text, lines 7–9
7def nimSum : List Nat → Nat8  | [] => 09  | h :: t => h ^^^ nimSum t

Project declarations in the statement: none

Project declarations used by the proof or definition body

none

Full modules

ModuleFileLinesImports
NimCorelean/NimCore.lean139Std
NimRolloutlean/NimRollout.lean349NimCore

Build evidence: fresh isolated rebuild of all 38 bundled modules with Lean (version 4.26.0, started 2026-10-10T15:21:56Z, finished 2026-10-10T15:27:00Z: 38/38 modules compiled, 1588 axiom reports, no errors. See Build for the commands and the independent audit of 2026-10-10 (dated evidence).