summaryrefslogtreecommitdiff
path: root/src/root.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-25 09:55:30 -0700
committerScott Gasch <[email protected]>2026-10-25 09:55:30 -0700
commit874ef0478078ef4eb436af7d2b68a353f61f4d70 (patch)
treee9954c289393c64d88402beff04637b52732ca73 /src/root.c
parent5f901d737b17c5f99b6e456d6c0c7d5e3c093dc4 (diff)
Learned FH% logistic-regression model (alpha full, beta whole-node-only)
Replaces the data.c histogram tables (g_uJointFHThousandths/ g_uBetaFHThousandths and their _NNUE counterparts) with two quantized logistic regressions as the default FH%-prediction mechanism under FH_USE_REGRESSION_MODEL=1, trained against real collected search data (fhcollect.c) per docs/REGRESSION.md. Alpha side is a full per-move regression, validated against the old table (0.119pp weighted mean deviation across 10,684 matched cells). Beta side is whole-node-only (matching the old table's own scope -- no functionality lost), refit on the NO_MOVE population after an earlier mixed-tier fit saturated and over-pruned; its skip threshold (FH_BETA_SKIP_THRESHOLD=99000) was confirmed by direct self-play sweeps, not just ECM solve-count intuition (see docs/REGRESSION.md's "Category A vs B" discussion). Gated by an overnight SPRT match (regression vs HEAD tables, same NNUE eval, sd8): at 4120 games, llr=+2.53 (never dipped negative, peaked +2.8), score=0.517, candidate reaching comparable depth at higher nps/ebf than head. Did not cross the H1=+2.94 SPRT bound, but the trend across the full run is unambiguous and the marginal value of waiting out the remaining games was judged not worth it. The data.c tables are left in place as the inactive #else branch of FH_USE_REGRESSION_MODEL for now; a follow-up commit will flip the build default and remove them once this commit's head_reference baseline and ECM numbers are recorded. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/root.c')
-rw-r--r--src/root.c60
1 files changed, 30 insertions, 30 deletions
diff --git a/src/root.c b/src/root.c
index f25b33b..5739734 100644
--- a/src/root.c
+++ b/src/root.c
@@ -360,10 +360,32 @@ Return value:
**/
{
ULONG u;
+#ifdef FH_REGRESSION_COLLECTION
+ // ctx is frequently a function-static struct reused across many
+ // Think()/Ponder() calls within one process (root.c) -- the
+ // unconditional memset just below would otherwise silently close
+ // this thread's collection file and drop any buffered-but-
+ // unflushed records on every single search, not just at process
+ // startup. Flush (don't lose data) and preserve the open file
+ // across the wipe; uThreadNumber is NOT preserved here on purpose
+ // -- a helper thread's real uThreadNumber isn't known yet at this
+ // call site (split.c sets it on the line immediately after
+ // calling this), so FhCollectEmit opens the file lazily on first
+ // real use instead, by which point uThreadNumber is correct. See
+ // fhcollect.c's own comments.
+ FILE *pSavedFhFile = ctx->sFhCollectWriter.pFile;
+ if (NULL != pSavedFhFile)
+ {
+ FhCollectFlush(ctx);
+ }
+#endif
memset(ctx, 0, sizeof(SEARCHER_THREAD_CONTEXT));
ReInitializeSearcherContext(pos, ctx);
ctx->sMoveStack.uUnblockedKeyValue[0] = 1;
+#ifdef FH_REGRESSION_COLLECTION
+ ctx->sFhCollectWriter.pFile = pSavedFhFile;
+#endif
for (u = 1;
u < MAX_PLY_PER_SEARCH;
u++)
@@ -1674,6 +1696,9 @@ Return value:
g_Options.u64LeftoverTries = ctx->sCounters.tree.u64LeftoverTries;
g_Options.u64LeftoverAlpha = ctx->sCounters.tree.u64LeftoverAlpha;
g_Options.u64LeftoverFH = ctx->sCounters.tree.u64LeftoverFH;
+ g_Options.u64QuickNullMoves = ctx->sCounters.tree.u64QuickNullMoves;
+ g_Options.u64QuickNullMoveSuccess =
+ ctx->sCounters.tree.u64QuickNullMoveSuccess;
#ifdef HASHCAP_COLLECTION
for (u = 0; u < 2; u++)
{
@@ -1725,36 +1750,11 @@ Return value:
sizeof(g_Options.u64NullMoveTriesByR));
memcpy(g_Options.u64NullMoveSuccessByR, ctx->sCounters.tree.u64NullMoveSuccessByR,
sizeof(g_Options.u64NullMoveSuccessByR));
-#ifdef FH_TABLE_COLLECTION
- memcpy(g_Options.u64Alpha5Tries, ctx->sFhCollection.u64Alpha5Tries,
- sizeof(g_Options.u64Alpha5Tries));
- memcpy(g_Options.u64Alpha5FH, ctx->sFhCollection.u64Alpha5FH,
- sizeof(g_Options.u64Alpha5FH));
- memcpy(g_Options.u64BetaTries, ctx->sFhCollection.u64BetaTries,
- sizeof(g_Options.u64BetaTries));
- memcpy(g_Options.u64BetaFH, ctx->sFhCollection.u64BetaFH,
- sizeof(g_Options.u64BetaFH));
- memcpy(g_Options.u64ImproveEstimateTries, ctx->sFhCollection.u64ImproveEstimateTries,
- sizeof(g_Options.u64ImproveEstimateTries));
- memcpy(g_Options.u64ImproveEstimateFH, ctx->sFhCollection.u64ImproveEstimateFH,
- sizeof(g_Options.u64ImproveEstimateFH));
- memcpy(g_Options.u64BetaDepthEstimateTries, ctx->sFhCollection.u64BetaDepthEstimateTries,
- sizeof(g_Options.u64BetaDepthEstimateTries));
- memcpy(g_Options.u64BetaDepthEstimateFH, ctx->sFhCollection.u64BetaDepthEstimateFH,
- sizeof(g_Options.u64BetaDepthEstimateFH));
- memcpy(g_Options.u64BetaGapEstimateTries, ctx->sFhCollection.u64BetaGapEstimateTries,
- sizeof(g_Options.u64BetaGapEstimateTries));
- memcpy(g_Options.u64BetaGapEstimateFH, ctx->sFhCollection.u64BetaGapEstimateFH,
- sizeof(g_Options.u64BetaGapEstimateFH));
- memcpy(g_Options.u64BetaOwnImpEstimateTries, ctx->sFhCollection.u64BetaOwnImpEstimateTries,
- sizeof(g_Options.u64BetaOwnImpEstimateTries));
- memcpy(g_Options.u64BetaOwnImpEstimateFH, ctx->sFhCollection.u64BetaOwnImpEstimateFH,
- sizeof(g_Options.u64BetaOwnImpEstimateFH));
- memcpy(g_Options.u64BetaEnemyImpEstimateTries, ctx->sFhCollection.u64BetaEnemyImpEstimateTries,
- sizeof(g_Options.u64BetaEnemyImpEstimateTries));
- memcpy(g_Options.u64BetaEnemyImpEstimateFH, ctx->sFhCollection.u64BetaEnemyImpEstimateFH,
- sizeof(g_Options.u64BetaEnemyImpEstimateFH));
-#endif // FH_TABLE_COLLECTION
+// FH_REGRESSION_COLLECTION needs no per-search copy-out here, unlike
+// the old FH_TABLE_COLLECTION histogram design -- FhCollectEmit
+// (fhcollect.c) writes each record straight to this thread's own file
+// as it's produced, during the search itself, so there's nothing left
+// to accumulate into g_Options after the fact.
#ifdef JOINT_EXTRA_VERIFY
memcpy(g_Options.u64JointExtraVerifyTries, ctx->sCounters.tree.u64JointExtraVerifyTries,
sizeof(g_Options.u64JointExtraVerifyTries));