# Re-imagining the baseline LMR, 2026-10-03 Question: the dumb flat `-1 ply on every leftover move` is load-bearing to ~40-50 Elo and +1.17 ply (`GET_LMR_REDUCTION.md` 10.12a). Five attempts to improve on it have all measured as noise. If we rebuilt the baseline from scratch with what we now know, what would it be? Not looking for a miracle. Looking for ~5 Elo and some sound depth. --- ## The reframing that matters **Every one of the five attempts pushed in the same direction: trade accuracy for depth.** Graded magnitude, wider admission, bigger cuts, LMP, deep pruning -- all of them reduce or remove more search in exchange for a higher root iteration number. We now have that direction's exchange rate: **~8 Elo per marginal ply** (10.12b). It is a bad rate, and it is bad for a structural reason -- a reduction is a *transfer*, not new resource. It lowers the depth of the refutation lines (where the move choice is actually decided) to raise the depth of the PV. Time-bought plies, which raise the whole distribution, are worth ~95 Elo/ply. **Nobody has ever tried the other direction: trade depth for accuracy.** That is the gap. If accuracy converts better than depth -- and there is no reason to assume it converts at 8 Elo/ply, because it is not buying plies at all -- then the remaining few Elo are on that side of the ledger, not this one. --- ## What the five attempts actually taught us | lesson | evidence | |---|---| | **Coverage beats magnitude.** Breadth of admission moves total tree size far more than depth-of-cut per move. | section 3 (HEAD-style reduces 76% of decisions and beat the "honest" 62-66% design on node count); section 6 | | **Magnitude is capped by the depth DISTRIBUTION, not the formula.** 76% of the LMR population sits at remaining depth buckets 2-3, where `iMaxExtra = depth - 3` permits zero extra plies -- and where the `log(d)*log(m)` curve prescribes ~1 ply anyway, i.e. exactly flat. The curve's whole contribution is **mean 0.164 extra plies ~= +0.05 root ply**. | 10.8, 10.12c | | **Learned per-situation grading loses to a flat formula.** Table-driven tiers vs flat `log(d)*log(m)`: 0.224 vs 0.244 mean extra plies -- a wash, and the tier version broke with a re-search-rate inversion when widened. | section 8, 10.8 | | **Per-move dynamic signals cannot identify the dangerous moves.** 11 of 23 verified low-band prune misses sit at own-FH-rate **exactly 0** -- the per-move history agreed with the static table and both were wrong. A veto catching the rest would refuse ~49,000 sound prunes per miss prevented. | 10.12h | | **`eNodeType` is uninformative here.** Full-width ALL nodes fail HIGH 69-71% of the time. The structural label is close to meaningless. | 10.12f, 10.12d | | **Move ordering is the real sophistication, and it is already good.** FMBC 90-95%; `r ~= 2.17`. By the time a move is in the leftover pool the generator has already triaged it -- the table is re-triaging an already-triaged population. | 10.11a, 10.12c | | **Only fail-HIGHS are verified.** The re-search path fires on `iScore >= iBeta`. A reduced search that fails low is accepted silently, forever. | search.c move loop | That last row is the one nobody has acted on. --- ## The redesign ### A. Keep (this is where the 40 Elo lives) - **Broad admission.** ~93% of eligible decisions get reduced. Five attempts to be cleverer about *which* moves all failed. - **One magnitude formula, `log(depth)*log(moves)`.** Already present (`g_iLMRQuietReduction`, dynamic.c, built by `InitLMRTable`). Keep it as the single source of magnitude. No table, no tiers. - **The one-full-width-ply floor.** `JOINT_EXTRA_MIN_REMAINING_DEPTH = ONE_PLY` guarantees a reduced move still gets a real node before qsearch. Stockfish keeps the identical invariant (`search.cpp:1370`, `std::max(1, ...)`) and gets its leanness from separate explicit pruning. Twice falsified as a target (10.9a). - **Killer-adjacency vetoes** (this ply's killers, ply-3's). Cheap, exact, principled -- a specific move at a specific ply, not an aggregate rate. ### B. ADD: verify fail-lows near the margin <-- the actual proposal The only unverified error direction, attacked with the search's own evidence rather than a static prior. Today, in `search.c`'s move loop: ```c iScore = -Search(ctx, -iAlpha - 1, -iAlpha, uNextDepth); /* reduced */ if ((iExtend < 0) && (iScore >= iBeta)) { ...re-search full depth... } ``` At a null window `iBeta == iAlpha + 1`, so that branch means "the reduced search beat alpha". A reduced search that returns `iScore <= iAlpha` is taken at face value. Proposed addition: ```c else if ((iExtend < 0) && (iScore > iAlpha - LMR_NEAR_MISS_MARGIN)) { /* Reduced search failed low, but only just. The reduced search is the weakest evidence in the tree and this is the one direction the existing machinery never checks. Re-search at full depth. */ uNextDepth -= iExtend; pi->iExtensionAmount = 0; /* defuse the null-move bailout */ iScore = -Search(ctx, -iAlpha - 1, -iAlpha, uNextDepth); } ``` Why this is different from everything tried: - **Sound by construction.** It only ever ADDS search. It cannot introduce a wrong answer; the worst case is wasted nodes. Every previous attempt risked correctness to buy depth. - **It trades depth for accuracy** -- the untried direction. It will cost nominal plies (marginal plies are worth ~8 Elo, so that is a cheap thing to spend) in exchange for catching refutations the reduction hid. - **Self-measuring, and falsifiable in one suite run.** Instrument `near_miss_researches` and `near_miss_flips` (how many came back `>= iBeta`). If the flip rate is ~0, the idea is dead immediately and cost nothing. If it is a few percent, those are refutations currently being missed, and the margin can be swept. - **It is targeted.** A move returning `alpha - 5` nearly made it; one returning `alpha - 300` did not. Score proximity is real evidence, unlike the cell averages the 4D table offers. Sweep `LMR_NEAR_MISS_MARGIN` over 0 / 10 / 25 / 50 / 100 cp. Measure flip rate and node cost per margin before any self-play. ### C. DELETE (each is unmeasured or measured-as-noise, and each costs real ### work per decision in a SHIPPING build) - **The 4D table admission gate.** `LMR_STATIC_ADMIT_PCT_THOUSANDTHS` vetoes 509 of 21,780 cells -- 2.3% of cell space, 7.4% of traffic -- and the threshold was chosen in section 6 on solve-count deltas 10.6 showed were noise. Removing it also removes `EstimateHangingMaterialCp` plus four bucket functions plus a table lookup *per eligible move*, which is the same class of per-move cost the PERF_COUNTERS cleanup just went after. (Hold if EFP ends up needing the table -- see 10.13 item 1.) - **`fDynamicDanger`'s extras gate.** Fires on 27.4% of decisions and only zeroes the extra plies -- which 85% of reduced moves never get anyway. The section 1 "honesty fix" that motivated the whole 2026-10-02 session is therefore nearly inert in shipped behaviour. - **The bare countermove-match veto.** Unconditional, unmeasured, and generate.c's own use of a countermove match is evidence-GATED precisely because a bare match was shown not to carry signal. - **`JOINT_EXTRA_TIER1/2/3_PCT_THOUSANDTHS`** -- vestigial (TIER1 in comments only, TIER3 only in the disabled LMP path). ### D. CONSIDER: widen coverage past the leftover pool `fPruneReduceEligible` requires `fIsLeftoverMove` (sub-`GOOD_MOVE`). Crafty and Stockfish reduce **all** quiet moves past a move-count threshold, including ones the generator scored into a higher tier. Coverage is the single lever with replicated evidence behind it (section 3, section 6, 10.2), and this is the largest untapped slice. But: it buys marginal plies, so expect ~8 Elo/ply and bound expectations accordingly. Do it *after* B, because B makes additional coverage safer -- the verification catches the extra mistakes wider coverage would make. ### E. DO NOT (all falsified; reasons in 10.13 item 8) Per-situation magnitude grading; `eNodeType` modulation; clamp removal / reducing into qsearch; dynamic per-move vetoes; chasing bigger cuts. --- ## What this adds up to A baseline LMR that is **simpler** than today's (no 4D table, no dynamic extras gate, no countermove veto, one magnitude formula) and has **one new mechanism** that is sound by construction and pushes in the only untried direction. Honest expectation: the deletions are roughly Elo-neutral and buy a little nps; the near-miss verification is the only part with real upside, and its upside is unknown rather than bounded at 8 Elo/ply, because it is not buying plies. If its flip rate comes back near zero it dies in one suite run, which is the cheapest possible failure mode. That is the whole pitch: stop buying depth at 8 Elo/ply, spend some of it back on the one error the search never checks. ## Order of work 1. Instrument the near-miss flip rate ONLY (no behaviour change): count how many reduced fail-lows land within each margin band, and -- behind a verify flag -- how many of those flip to `>= iBeta` at full depth. One suite run answers whether B is worth building. 2. If the flip rate is material, implement B and sweep the margin on node count + `sn` suites, then self-play with `elo0=0 elo1=15`. 3. Do the deletions in C as a separate commit, measured on nps (idle box, round-robin arms) rather than on Elo -- they are a complexity/speed play, not a strength play. 4. D last, and only if B worked.