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

Theorem 5.12 (ii)B structured heads and an AC0 readout select an optimal move with O(BN2^B) rollout steps

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

1 Paper text

Theorem 5.12 (Restricted bound and bypasses) The following statements hold under Definition 5.7.

  1. (i) For each fixed B≥2B\ge2, let PP be uniform on {0,…,2B−1}N\{0,\ldots,2^B-1\}^N conditional on σ(P)≠0\sigma(P)\ne0. For one fixed structured head πj\pi_j and an AC0\AC^0 readout FN(P,ROπj(P))F_N(P,\mathrm{RO}_{\pi_j}(P)), winning-move success is at most 22B−1+negl⁡(N).\frac{2}{2^B-1}+\operatorname{negl}(N). Here N≥1N\ge1, and a negligible error is a nonnegative sequence smaller than every inverse polynomial for sufficiently large NN, with circuit depth, polynomial size exponent and BB fixed. More precisely, a uniform parity-correlation bound ηN\eta_N for the fixed-bit readout indicators gives the finite upper bound 2/(2B−1)+(2B−1)ηN2/(2^B-1)+(2^B-1)\eta_N; the indicators and correlation premise are specified in Appendix H. The leading constant is asymptotically attainable by guessing one nonzero nim-sum in each channel fibre.

  2. (ii) For any BB, the BB structured heads and an AC0\AC^0 readout select an optimal move using O(BN2B)O(BN2^B) rollout steps, as in Proposition 5.9.

  3. (iii) Given a legal transition from a known zero-nim-sum predecessor, a two-frame AC0\AC^0 selector restores zero. Once a player has established that invariant, it maintains it against every legal opponent response. This statement does not supply the initial winning move from an arbitrary position.

Generated from paper_jmlr.tex, lines 802–814.

2 Explain this

With all BB structured heads, the rollout outcomes reconstruct the nim-sum exactly and a shallow readout plays an optimal move. The cost is the rollouts themselves: O(BN2B)O(BN2^B) simulated moves per decision.

Multi-head rollouts and the outer controller, tick by tick

Exact construction trace Every value is computed from the construction’s definitions for the example shown.

Policy πj\pi_j subtracts 2j2^j from the first heap of size at least 2j2^j, and otherwise subtracts 1 from the first nonzero heap. A rollout lets both simulated players follow πj\pi_j until no move is left; its outcome bit is the parity of the number of moves. Step through the outer controller one tick at a time: it resets, runs head 0's rollout move by move on a working copy of the board, records the outcome when the rollout ends, reloads the original board for the next head, and, in the all-head schedule, finally reads out a move. Every outcome bit here is produced by actually running the rollout; nothing is supplied from outside. The early-exit schedule (Figure 3) stops at the first outcome 1 and only classifies the position.

35 component evaluations (34 controller ticks and one readout). Colours: reset move terminal test store and reload store and stop readout.

  1. tick 1: reset
  2. tick 2: move, head 0
  3. tick 3: move, head 0
  4. tick 4: move, head 0
  5. tick 5: move, head 0
  6. tick 6: move, head 0
  7. tick 7: move, head 0
  8. tick 8: move, head 0
  9. tick 9: move, head 0
  10. tick 10: move, head 0
  11. tick 11: move, head 0
  12. tick 12: move, head 0
  13. tick 13: move, head 0
  14. tick 14: move, head 0
  15. tick 15: terminal test, head 0
  16. tick 16: store outcome and reload, head 0
  17. tick 17: move, head 1
  18. tick 18: move, head 1
  19. tick 19: move, head 1
  20. tick 20: move, head 1
  21. tick 21: move, head 1
  22. tick 22: move, head 1
  23. tick 23: move, head 1
  24. tick 24: terminal test, head 1
  25. tick 25: store outcome and reload, head 1
  26. tick 26: move, head 2
  27. tick 27: move, head 2
  28. tick 28: move, head 2
  29. tick 29: move, head 2
  30. tick 30: move, head 2
  31. tick 31: move, head 2
  32. tick 32: move, head 2
  33. tick 33: terminal test, head 2
  34. tick 34: store outcome and stop, head 2
  35. readout

35 / 35 Readout: one evaluation of the readout circuit on the original board and the 3 stored outcome bits.

Working board (0, 0, 0)
012
Original board (read-only input) (3, 4, 6)
012
head j
2
parity of moves
1
rollout finished
1
running
0
some outcome 1
1
stored outcomes
o0 = 1 o1 = 1 o2 = 1

Readout

σ0 = o0 = 1; σ1 = o1 ⊕ o0 = 0; σ2 = o2 ⊕ o0 = 0, so σ = 1. The readout tests every heap in parallel and takes the first that decreases:

  • heap 0: 3 ⊕ 1 = 2 smaller
  • heap 1: 4 ⊕ 1 = 5 not smaller
  • heap 2: 6 ⊕ 1 = 7 not smaller

Move: set heap 0 to 2 → (2, 4, 6), nim-sum 0.

Counting the work

  • Simulated moves M = 13 + 7 + 7 = 27 (3 heads run).
  • Evaluations M + 2B + 2 = 27 + 6 + 2 = 35: 27 move updates, 3 terminal tests, 3 outcome updates, 1 reset, 1 readout.
  • Bound on M: BN(2B − 1) = 63.
  • Each single evaluation is a shallow circuit: one policy step has Boolean depth ≤ 17 (≤ 36 threshold layers), the readout ≤ 19 (≤ 40), one controller tick ≤ 34 (≤ 70). Those bounds are per evaluation; the number of evaluations grows with the board. That no shallow family computes the complete board-to-move map is a separate statement, conditional on the named classical input that parity is not in AC0.
All 34 controller ticks as a table
TickKindWorking boardjparityo₀…What happens
1reset(3, 4, 6)00000Reset: load the board, select head 0, clear the outcomes.
2move(2, 4, 6)01000Head 0 moves: heap 0 from 3 to 2 (subtract 1 from the first heap of size at least 1).
3move(1, 4, 6)00000Head 0 moves: heap 0 from 2 to 1 (subtract 1 from the first heap of size at least 1).
4move(0, 4, 6)01000Head 0 moves: heap 0 from 1 to 0 (subtract 1 from the first heap of size at least 1).
5move(0, 3, 6)00000Head 0 moves: heap 1 from 4 to 3 (subtract 1 from the first heap of size at least 1).
6move(0, 2, 6)01000Head 0 moves: heap 1 from 3 to 2 (subtract 1 from the first heap of size at least 1).
7move(0, 1, 6)00000Head 0 moves: heap 1 from 2 to 1 (subtract 1 from the first heap of size at least 1).
8move(0, 0, 6)01000Head 0 moves: heap 1 from 1 to 0 (subtract 1 from the first heap of size at least 1).
9move(0, 0, 5)00000Head 0 moves: heap 2 from 6 to 5 (subtract 1 from the first heap of size at least 1).
10move(0, 0, 4)01000Head 0 moves: heap 2 from 5 to 4 (subtract 1 from the first heap of size at least 1).
11move(0, 0, 3)00000Head 0 moves: heap 2 from 4 to 3 (subtract 1 from the first heap of size at least 1).
12move(0, 0, 2)01000Head 0 moves: heap 2 from 3 to 2 (subtract 1 from the first heap of size at least 1).
13move(0, 0, 1)00000Head 0 moves: heap 2 from 2 to 1 (subtract 1 from the first heap of size at least 1).
14move(0, 0, 0)01000Head 0 moves: heap 2 from 1 to 0 (subtract 1 from the first heap of size at least 1).
15terminal test(0, 0, 0)01000Head 0: no legal move, so the rollout has ended after an odd number of moves; the outcome bit is 1.
16store outcome and reload(3, 4, 6)10100Store outcome o0 = 1, reload the original board and select head 1.
17move(1, 4, 6)11100Head 1 moves: heap 0 from 3 to 1 (subtract 2 from the first heap of size at least 2).
18move(1, 2, 6)10100Head 1 moves: heap 1 from 4 to 2 (subtract 2 from the first heap of size at least 2).
19move(1, 0, 6)11100Head 1 moves: heap 1 from 2 to 0 (subtract 2 from the first heap of size at least 2).
20move(1, 0, 4)10100Head 1 moves: heap 2 from 6 to 4 (subtract 2 from the first heap of size at least 2).
21move(1, 0, 2)11100Head 1 moves: heap 2 from 4 to 2 (subtract 2 from the first heap of size at least 2).
22move(1, 0, 0)10100Head 1 moves: heap 2 from 2 to 0 (subtract 2 from the first heap of size at least 2).
23move(0, 0, 0)11100Head 1 moves: heap 0 from 1 to 0 (no heap has 2 or more: subtract 1 from the first nonzero heap).
24terminal test(0, 0, 0)11100Head 1: no legal move, so the rollout has ended after an odd number of moves; the outcome bit is 1.
25store outcome and reload(3, 4, 6)20110Store outcome o1 = 1, reload the original board and select head 2.
26move(3, 0, 6)21110Head 2 moves: heap 1 from 4 to 0 (subtract 4 from the first heap of size at least 4).
27move(3, 0, 2)20110Head 2 moves: heap 2 from 6 to 2 (subtract 4 from the first heap of size at least 4).
28move(2, 0, 2)21110Head 2 moves: heap 0 from 3 to 2 (no heap has 4 or more: subtract 1 from the first nonzero heap).
29move(1, 0, 2)20110Head 2 moves: heap 0 from 2 to 1 (no heap has 4 or more: subtract 1 from the first nonzero heap).
30move(0, 0, 2)21110Head 2 moves: heap 0 from 1 to 0 (no heap has 4 or more: subtract 1 from the first nonzero heap).
31move(0, 0, 1)20110Head 2 moves: heap 2 from 2 to 1 (no heap has 4 or more: subtract 1 from the first nonzero heap).
32move(0, 0, 0)21110Head 2 moves: heap 2 from 1 to 0 (no heap has 4 or more: subtract 1 from the first nonzero heap).
33terminal test(0, 0, 0)21110Head 2: no legal move, so the rollout has ended after an odd number of moves; the outcome bit is 1.
34store outcome and stop(0, 0, 0)21111Store outcome o2 = 1 and stop (last head).

How this relates to the Lean proof. Each tick is one step of Shootout.ostep, with the policy step SelectCircuit.policyIdx inside the rollout registers RolloutExec.rsStep. Rollout.run_parity identifies the outcome bits with σ0\sigma_0 and σj⊕σ0\sigma_j\oplus\sigma_0, and Shootout.ah_counts proves that the all-head schedule takes M+2B+1M+2B+1 controller ticks, the reset included; with the single readout this gives M+2B+2M+2B+2 evaluations. The browser re-implements these steps; the agreement is checked against Lean's own evaluation of them.

What this does not show. Simulated moves and circuit evaluations are different counts: the MM moves are part of the M+2B+2M+2B+2 evaluations. The depth bounds (17 per policy step, 19 for the readout, 34 per controller tick) apply to one evaluation each. The complete board-to-move computation repeats them a number of times that grows with the board. That no shallow family computes the complete map is a separate statement (Proposition 5.14 and the text after it), conditional on the named classical input that parity is not in AC0\mathrm{AC}^0.

Depicted in Lean: SelectCircuit.policyIdx, RolloutExec.rsStep, Shootout.ostep, Shootout.initOS, Shootout.query_step, Rollout.run_parity, Game.recoverBit, Game.reconstruct_eq, Shootout.ahMove_eq, Shootout.ah_counts, Shootout.ahL_counts, Shootout.ee_spec, Shootout.ee_counts. Explains the paper’s Figure 3.

Computed in your browser by a short re-implementation of the definitions above (tools/visuals/nimvis-core.js). It is not itself checked by Lean. At build time it agreed with Lean's own evaluation of those definitions on all 313 reference cases.

3 How this is formalised

Game.agent_wins and RolloutExec.mhExec_wins (winning against every legal opponent from every nonzero position, the second with fully executed rollouts) and Game.totalRolloutMoves_le (at most BN(2B−1)BN(2^B-1) simulated moves). Details in Proposition 5.9.

Coverage record
Paper anchorthm:search-gap (manuscript label)
Displayed asTheorem 5.12 (ii)
ClaimB structured heads and an AC0 readout select an optimal move with O(BN2^B) rollout steps
Coverage categoryChecked theorem
Statement sourcepaper_jmlr.tex lines 802–814; SHA-256 of the normalised block source 6a8c9a4107356a14…
Lean declarations
Depends onProposition 5.9
Audited correspondence rowsthm:search-gap
Explanationsexplain/thm:search-gap.ii, formal/thm:search-gap.ii
Build evidencefresh isolated rebuild, audit report

4 Lean statement and source

Principal declarations

theorem Game.agent_wins

Module NimGame (lean/phase2/NimGame.lean, lines 376–388) · file SHA-256 23df6bb87ecfd344… Axioms reported by the fresh build: propext, Quot.sound

END-TO-END STRATEGY GUARANTEE (Target 1C): on B-bit boards, the agent that queries the B structured rollouts, reconstructs the nim-sum and plays the selected move wins from every nonzero-nim-sum position against every legal opponent.

Statement as elaborated and printed by Lean 4.26.0

∀ {B : ℕ} (p : List ℕ),
  NimAudit.Game.Width B p → NimAudit.nimSum p ≠ 0 → NimAudit.Game.Wins (NimAudit.Game.agent B) p
Exact source text, lines 376–388
376/-- END-TO-END STRATEGY GUARANTEE (Target 1C): on B-bit boards, the agent that377    queries the B structured rollouts, reconstructs the nim-sum and plays the378    selected move wins from every nonzero-nim-sum position against every legal379    opponent. -/380theorem agent_wins {B : Nat} (p : List Nat) (hw : Width B p) (h : nimSum p ≠ 0) :381    Wins (agent B) p := by382  obtain ⟨q, hsel, hm, hq⟩ := selectMove_zero h383  refine Wins.intro p q (by rw [agent_eq hw]; exact hsel) hm ?_384  intro r hr385  have h1 := move_sum_lt hm386  have h2 := move_sum_lt hr387  exact agent_wins r (width_move (width_move hw hm) hr) (move_from_zero hr hq)388termination_by p.sum

Project declarations in the statement: Game.Width, Game.Wins, Game.agent, nimSum

Project declarations used by the proof or definition body

Game.Move, Game.Width, Game.Wins, Game.Wins.intro, Game.agent, Game.agent_eq, Game.move_from_zero, Game.move_sum_lt, Game.selectMove, Game.selectMove_zero, Game.width_move, nimSum

theorem RolloutExec.mhExec_wins

Module RolloutExec (lean/phase6/RolloutExec.lean, lines 517–520) · file SHA-256 c7b63a6e02d1d425… Axioms reported by the fresh build: propext, Classical.choice, Quot.sound

WHOLE-GAME THEOREM FOR THE FULLY EXECUTED MULTI-HEAD PLAYER: from every nonzero board of width B it wins against every legal opponent; no outcome bit is supplied for free.

Statement as elaborated and printed by Lean 4.26.0

∀ {B : ℕ} {P : List ℕ},
  NimAudit.Game.Width B P →
    NimAudit.nimSum P ≠ 0 → NimAudit.Game.Wins (NimAudit.RolloutExec.mhExecSel B) P
Exact source text, lines 517–520
517/-- WHOLE-GAME THEOREM FOR THE FULLY EXECUTED MULTI-HEAD PLAYER: from every nonzero board518    of width B it wins against every legal opponent; no outcome bit is supplied for free. -/519theorem mhExec_wins {B : Nat} {P : List Nat} (hw : Width B P) (h : nimSum P ≠ 0) : Wins (mhExecSel B) P :=520  wins_congr (fun Q hQ => mhExecSel_eq hQ) hw (ctrl_wins P h)

Project declarations in the statement: Game.Width, Game.Wins, RolloutExec.mhExecSel, nimSum

Project declarations used by the proof or definition body

Controller.ctrlSel, Controller.ctrl_wins, Game.Width, RolloutExec.mhExecSel, RolloutExec.mhExecSel_eq, WholeGame.wins_congr, nimSum

theorem Game.totalRolloutMoves_le

Module NimGame (lean/phase2/NimGame.lean, lines 454–467) · file SHA-256 23df6bb87ecfd344… Axioms reported by the fresh build: propext, Quot.sound

Statement as elaborated and printed by Lean 4.26.0

∀ {B : ℕ} {p : List ℕ},
  NimAudit.Game.Width B p → NimAudit.Game.totalRolloutMoves B p ≤ B * (p.length * (2 ^ B - 1))
Exact source text, lines 454–467
454theorem totalRolloutMoves_le {B : Nat} {p : List Nat} (hw : Width B p) :455    totalRolloutMoves B p ≤ B * (p.length * (2 ^ B - 1)) := by456  unfold totalRolloutMoves457  have key : ∀ k, ((List.range k).map (fun j => rolloutMoves j p)).sum ≤458      k * (p.length * (2 ^ B - 1)) := by459    intro k460    induction k with461    | zero => simp462    | succ k ih =>463      rw [List.range_succ, List.map_append, List.sum_append_nat, Nat.succ_mul]464      simp only [List.map_cons, List.map_nil, List.sum_cons, List.sum_nil, Nat.add_zero]465      have := rolloutMoves_le (j := k) hw466      omega467  exact key B

Project declarations in the statement: Game.Width, Game.totalRolloutMoves

Project declarations used by the proof or definition body

Game.Width, Game.rolloutMoves_le, Game.totalRolloutMoves, Rollout.rolloutMoves

Definitions these statements use

def Game.Width

Module NimGame (lean/phase2/NimGame.lean, lines 31–32) · file SHA-256 23df6bb87ecfd344…

B-bit board: every heap is below 2^B.

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → Prop
Exact source text, lines 31–32
31/-- B-bit board: every heap is below 2^B. -/32def Width (B : Nat) (p : List Nat) : Prop := ∀ x ∈ p, x < 2 ^ B

Project declarations in the statement: none

Project declarations used by the proof or definition body

none

inductive Game.Wins

Module NimGame (lean/phase2/NimGame.lean, lines 125–132) · file SHA-256 23df6bb87ecfd344…

`Wins sel p`: the player to move at `p`, always playing the successor chosen by `sel`, wins against EVERY legal opponent. The selected move is legal, and after every legal opponent reply the player is again in a `Wins` position. Because the predicate is inductive, every such play is finite and ends with the opponent facing a terminal board (normal play: the opponent loses).

Statement as elaborated and printed by Lean 4.26.0

(List ℕ → Option (List ℕ)) → List ℕ → Prop
Exact source text, lines 125–132
125/-- `Wins sel p`: the player to move at `p`, always playing the successor chosen by126    `sel`, wins against EVERY legal opponent.  The selected move is legal, and after127    every legal opponent reply the player is again in a `Wins` position.  Because128    the predicate is inductive, every such play is finite and ends with the129    opponent facing a terminal board (normal play: the opponent loses). -/130inductive Wins (sel : List Nat → Option (List Nat)) : List Nat → Prop131  | intro (p q : List Nat) : sel p = some q → Move p q →132      (∀ r, Move q r → Wins sel r) → Wins sel p

Project declarations in the statement: none

Project declarations used by the proof or definition body

Game.Move

def Game.agent

Module NimGame (lean/phase2/NimGame.lean, lines 257–259) · file SHA-256 23df6bb87ecfd344…

The agent: reconstruct the nim-sum from the B root rollouts, then select.

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → Option (List ℕ)
Exact source text, lines 257–259
257/-- The agent: reconstruct the nim-sum from the B root rollouts, then select. -/258def agent (B : Nat) (p : List Nat) : Option (List Nat) :=259  selectMove (reconstruct B (outcomes p)) p

Project declarations in the statement: none

Project declarations used by the proof or definition body

Game.outcomes, Game.reconstruct, Game.selectMove

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

def RolloutExec.mhExecSel

Module RolloutExec (lean/phase6/RolloutExec.lean, lines 501–501) · file SHA-256 c7b63a6e02d1d425…

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → Option (List ℕ)
Exact source text, lines 501–501
501def mhExecSel (B : Nat) (P : List Nat) : Option (List Nat) := applyM P (mhExec B P)

Project declarations in the statement: none

Project declarations used by the proof or definition body

RolloutExec.mhExec, SelectCircuit.applyM

def Game.totalRolloutMoves

Module NimGame (lean/phase2/NimGame.lean, lines 450–452) · file SHA-256 23df6bb87ecfd344…

Total simulated moves of the B root rollouts.

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → ℕ
Exact source text, lines 450–452
450/-- Total simulated moves of the B root rollouts. -/451def totalRolloutMoves (B : Nat) (p : List Nat) : Nat :=452  ((List.range B).map (fun j => rolloutMoves j p)).sum

Project declarations in the statement: none

Project declarations used by the proof or definition body

Rollout.rolloutMoves

Full modules

ModuleFileLinesImports
NimCorelean/NimCore.lean139Std
NimGamelean/phase2/NimGame.lean634NimRollout
RolloutExeclean/phase6/RolloutExec.lean896Barrier

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).