Drop7 Research
lifetime-objective

Can a leaf-sized network hold the four-ply search's ordering?

completedevidence: task-record onlydiagnostic

Trains the smallest network that could run inside a search leaf on the exact values the four-ply search gives every sibling move, and asks whether it ranks those moves the way the search does.

Train the smallest network that could run inside a search leaf on the exact values the four-ply search gives every sibling move, and ask whether it ranks those moves the way the search does.

completedevidence: task-record onlydiagnostica measurement tool, not a policy

Why anyone expected this to work

The search-guided self-play loop needs a learned board evaluator that does two things at once: agree with the depth-4 search about which column is best, and cost about a microsecond, because the search asks that question hundreds of thousands of times per decision. The earlier attempt used a 3.4-million-parameter convolutional network, which is far too slow for a leaf and, as it turned out, agreed with the search on the best column only 37.5% of the time (result RS-20260821T104500Z-77d21e90) — worse than the one-ply search's 48.6%.

The one model class that is cheap enough for a leaf is the NNUE-shaped network from learned-leaf: 8,902 binary features looked up and summed, then two small dense layers. It had only ever been trained to predict how long a game would last, never on the search's own sibling values. The probe asks whether feature-space saturation on one target says anything about ranking on another. The designer's prior was about 8% for a pass.

How it works

  1. Inputs. Every row of the existing label file is one sibling move's resulting board (after the drop and any clears), the visible next disc, and the moves until the next rise, under one of five chance strata, together with the exact value the pinned depth-4 search assigns it. 291,890 rows over 8,639 root positions; 6,551 roots are the training fold and 2,088 the validation ("calibration") fold, exactly as the afterstate experiment wrote them.
  2. Student. leaf_features.py from learned-leaf (imported, not copied) turns each board into 135 active features; the network is EmbeddingBag(8902, 64) with a bias, ReLU, Linear(64, 32), ReLU, Linear(32, 1): 571,905 parameters.
  3. Per-move score. As in the comparator's d4q.py, a move's score is the average over the five strata of the immediate score change plus the student's value of the resulting board.
  4. Loss, within each root. A listwise softmax cross-entropy against the search's values (softened at 0.18 times the root's value spread, the d4-q-clone convention), a gap-weighted pairwise margin, and a 0.1-weight mean-squared anchor on the standardised value. Adam at 1e-3, 256 roots per batch, 30 epochs.
  5. Selection and reading. Five initialisation seeds (0xA52E01 to 0xA52E05). The seed with the best validation top-1 is chosen, and only then is the 3,030-root held-out panel read — once — and reported for all five seeds, split into two halves by a hash of each root's origin game. The metrics (top-1, top-2, pairwise agreement, normalised regret) are the functions of d4q.py, imported.

What happened

It failed clearly. The selected seed agreed with the depth-4 search on the best column about 30% of the time on the held-out positions — below the convolutional network it was meant to replace, and below the one-ply search. Every one of the three preregistered thresholds failed in both half-folds, for every seed.

resultvalid · failpilotRS-20260823T194142Z-946e3cd1

The run was valid; the outcome was fail, at the a pilot — a small run to find bugs and project cost, not a strength claim level. Of 5 preregistered checks, 2 passed and 3 failed.

The leaf-affordable NNUE-class student does not reproduce fair D4's within-root ordering from the existing successor-closed labels. Five initialisation seeds of the LeafNet h64/m32 student (571,905 parameters; features from learned-leaf/leaf_features.py) were trained for 30 epochs on the 6,551 training roots of runs/RUN-20260821T085042Z-c5cf0e71/d4q-labels with the preregistered listwise + gap-weighted pairwise + 0.1 MSE loss; the seed with the best calibration-fold top-1 (0xA52E02, 0.3281) was then read once on the 3,030-root held-out panel of that experiment. Held-out top-1 was 0.296 / 0.3011 in the two origin-seed half-folds (gate >= 0.60), pairwise 0.6266 / 0.6259 (>= 0.78), normalised regret 0.3354 / 0.3379 (<= 0.13): every criterion fails in both half-folds. The other four seeds lie between 0.2987 and 0.3135 pooled top-1, all below the 3.4M-parameter afterstate CNN of RS-20260821T104500Z-77d21e90 (0.375) and below exact D1 (0.486). The student is leaf-affordable as claimed (1.27 us per state through the existing d7leaf export and leaf-check), but at this size it holds less of D4's ordering than the CNN did. Per the preregistered failure action: no leaf-affordable NNUE-class student of this size reproduces D4's ordering from these labels; the self-play loop TH-20260821-...-299ed02f stays blocked in its leaf form and its redesign is routed to the larger-student question. Diagnostic tier: the held-out panel had already been read once by the d4q experiment, so this is a diagnostic read on existing data, not new evidence about fresh roots.

Limitations recorded with the result
  • Diagnostic-tier read: the 3,030-root held-out panel (d4q-labels-gate) was already opened once by EX-20260821-afterstate-d4q-stage1; no fresh roots were labelled. The result schema has no diagnostic tier, so it is recorded at the lowest tier (pilot); it is not development-tier evidence.
  • The preregistration names only the d4q-labels file, which carries folds train and calibration but no held-out fold; the held-out panel and its origin-seed half-folds were taken from the same run's d4q-labels-gate/d4q-labels.tsv and corpus-gate/roots.tsv, exactly as d4q.py consumed them, which is the reading the protocol's 'train/validation/held-out origins are reused unchanged' clause intends.
  • Epoch count was fixed at 30 by the protocol and the final-epoch model was used. Calibration-fold top-1 peaked at 0.352-0.359 between epochs 5 and 10 for every seed and then declined as the training listwise loss kept falling (overfitting); even the best epoch is far below the 0.60 gate, so early stopping would not change the verdict, but the reported held-out numbers are for the overfitted final epoch.
  • Loss coefficients not fixed by the hypothesis text were set to 1.0 for the pairwise term (d4-q-clone used 0.35) and the prediction-side softmax temperature was 1 in standardised value units; only the listed configuration is rejected.
  • CPU training with 8 torch threads; the EmbeddingBag backward is not bit-deterministic across thread counts, so a re-run reproduces the numbers only approximately.
  • Inference cost is from leaf-check on the RUN-A51D-corpus mix-d3 states on a shared host; informational only.

Full record →

Numbers and records

Result record RS-20260823T194142Z-946e3cd1, run RUN-20260823T193853Z-5abd7c95, experiment EX-20260823-nnue-d4q-ordering-probe-0ca09bb1, theory TH-20260823-nnue-class-holds-d4-ordering-c7b397a5. Artifacts under runs/RUN-20260823T191900Z-b9f8f80d/probe/ (train.log, metrics.json, model-seed*.pt, commands.log, leaf-check.log).

Selected seed 0xA52E02 (validation top-1 0.3281). Held-out, half-fold 1 / 2: top-1 0.2960 / 0.3011 (gate: at least 0.60); pairwise 0.6266 / 0.6259 (at least 0.78); normalised regret 0.3354 / 0.3379 (at most 0.13). Pooled top-1 across the five seeds ranged from 0.2987 to 0.3135. Comparators from RS-20260821T104500Z-77d21e90: afterstate CNN 0.375, exact D1 0.486, exact D2 0.568.

Validation top-1 peaked at 0.352 to 0.359 between epochs 5 and 10 for every seed and then fell while the training loss kept falling; the protocol fixed 30 epochs and the final-epoch model was used. Even the best epoch is far below the gate.

Exported through the existing export_leaf.py and timed with leaf-check (single thread, 400,000 evaluations): 1.27 microseconds per state. The student is leaf-affordable; it just does not know the ordering.

Total wall time 166 s on 8 CPU threads; no GPU. Run validity valid, outcome fail, recorded at the lowest result tier (the schema has no diagnostic tier) because the panel had already been read once.

What this taught us, and what is still open

A leaf-sized NNUE student of this size trained on the existing successor-closed depth-4 labels does not hold the search's ordering; the self-play loop cannot be unblocked in its leaf form by this route, and its redesign goes to the larger-student question. This rejects only the tested configuration: 64/32 hidden units, these loss weights, 30 fixed epochs, 6,551 training roots. Whether a wider NNUE, early stopping, or far more labelled roots would change the picture is untested here; the fact that every seed overfits after about ten epochs suggests the label volume, not only the model, is binding.

Source files