summaryrefslogtreecommitdiff
path: root/tests/lop/compare_lop.cpp
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-20 09:26:44 -0700
committerScott Gasch <[email protected]>2026-10-20 09:26:44 -0700
commitc8c6bbc2c1a103e5b07b079a0f8bdf66c7724d80 (patch)
treeb6c2137c9ac577fa83b1e948fa0311d53dfe445d /tests/lop/compare_lop.cpp
parent3b5d3677bb14ae14728a1c4b221176bb83667d92 (diff)
Wire NNUE eval into search, build the training pipeline, and add a fast
move/eval-agreement benchmark against Stockfish (tests/lop) Implements the architecture decisions in src/docs/NNUE.md: HalfKAv2_hm feature set, int16/int8 quantization, per-ply accumulator backup for king moves, and a lazy-eval-ready integration into search.c/eval.c. Adds the full annotate_pgns/ training pipeline (extraction, K-fit/ labeling, quiet filtering, tensor building, training, export) plus an end-to-end orchestrator (run_nnue_pipeline.py) and validation tooling (sanity_check_net.py, compare_vs_stockfish_net.py). Adds tests/lop: a 10k-position list-of-positions benchmark comparing engine eval rank and move choice against pre-computed Stockfish/lc0 references, much cheaper than a full self-play match for day-to-day progress checks. First sd10-matched self-play result against classic HCE: LLR +2.28 (H1=+2.94), -72=119+169 over 360 games, confirming the net is "smarter per node" at matched depth. A real-time (--st 4) match is currently weaker (candidate loses ~3 plies of depth to a ~3.3-3.7x nps gap against an unrecalibrated search), consistent with NNUE.md's adoption-criterion section -- pruning/reduction constants still need retuning for the new eval's score scale before a fair real-time comparison is possible. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'tests/lop/compare_lop.cpp')
-rw-r--r--tests/lop/compare_lop.cpp230
1 files changed, 230 insertions, 0 deletions
diff --git a/tests/lop/compare_lop.cpp b/tests/lop/compare_lop.cpp
new file mode 100644
index 0000000..690df01
--- /dev/null
+++ b/tests/lop/compare_lop.cpp
@@ -0,0 +1,230 @@
+#include <stdio.h>
+#include <memory.h>
+#include <stdlib.h>
+#include <cstdint>
+
+
+typedef struct
+{
+ char fen[100];
+ int leftFenNumber;
+ int rightFenNumber;
+ int leftEval;
+ char leftMove[10];
+ int leftRank;
+ int rightEval;
+ char rightMove[10];
+ int rightRank;
+} position_t;
+
+position_t *gPosition = NULL;
+int gPositions;
+
+
+void StripTrailingNewline(char *str)
+{
+ int sl = strlen(str);
+
+ if (sl == 0)
+ return;
+ if (str[sl - 1] == '\n')
+ str[sl - 1] = 0;
+}
+
+
+uint32_t MyHashString(const char *s)
+{
+ uint32_t hash = 2166136261u;
+
+ while (*s)
+ {
+ hash ^= (unsigned char)*s++;
+ hash *= 16777619u;
+ }
+ return hash;
+}
+
+
+int CompareLeftEvals(const void *a, const void *b)
+{
+ position_t *pa = (position_t *)a;
+ position_t *pb = (position_t *)b;
+
+ if (pa->leftEval == pb->leftEval)
+ return pa->leftFenNumber - pb->leftFenNumber;
+ return pb->leftEval - pa->leftEval;
+}
+
+
+int CompareRightEvals(const void *a, const void *b)
+{
+ position_t *pa = (position_t *)a;
+ position_t *pb = (position_t *)b;
+
+ if (pa->rightEval == pb->rightEval)
+ return pa->rightFenNumber - pb->rightFenNumber;
+ return pb->rightEval - pa->rightEval;
+}
+
+
+void PrintCorrelation()
+{
+ double accuracy;
+ double totalAccuracy = 0;
+ double sameMove = 0;
+ int i;
+
+ for (i = 0; i < gPositions; ++i)
+ {
+ accuracy = abs(gPosition[i].leftRank - gPosition[i].rightRank);
+ accuracy /= (double)gPositions;
+ accuracy = 1.0 - accuracy;
+ totalAccuracy += accuracy;
+
+ if (gPosition[i].leftMove[0] != 0 &&
+ !strcmp(gPosition[i].leftMove, gPosition[i].rightMove))
+ sameMove += 1.0;
+ }
+ printf(" Positions: %d\n", gPositions);
+ printf(" Eval correlation: %0.3f percent\n", (totalAccuracy / (double)gPositions) * 100.0);
+ printf(" Move correlation: %0.3f percent\n", (sameMove / (double)gPositions) * 100.0);
+}
+
+
+void StoreFileEval(char *fen, int fenNumber, int eval, char *moveString, bool isLeftFile)
+{
+ int fenHash = (int)MyHashString(fen);
+ int i;
+
+ i = abs(fenHash) % 1500000;
+ for (;;)
+ {
+ if (gPosition[i].fen[0] == 0)
+ {
+ strcpy(gPosition[i].fen, fen);
+ break;
+ }
+ if (!strcmp(gPosition[i].fen, fen))
+ break;
+ i++;
+ }
+ if (isLeftFile)
+ {
+ gPosition[i].leftFenNumber = fenNumber;
+ gPosition[i].leftEval = eval;
+ strcpy(gPosition[i].leftMove, moveString);
+ }
+ else
+ {
+ gPosition[i].rightFenNumber = fenNumber;
+ gPosition[i].rightEval = eval;
+ strcpy(gPosition[i].rightMove, moveString);
+ }
+}
+
+
+void ReadFileEvals(const char *fileName, bool isLeftFile)
+{
+ FILE *f;
+ char fenLine[1000 + 1];
+ char evalLine[1000 + 1];
+ char moveLine[1000 + 1];
+ int fenNumber;
+
+ f = fopen(fileName, "r");
+ if (!f)
+ {
+ printf("Couldn't open %s.\n", fileName);
+ exit(1);
+ }
+ fenNumber = 0;
+ for (;;)
+ {
+ fenLine[0] = 0;
+ evalLine[0] = 0;
+ moveLine[0] = 0;
+ fgets(fenLine, 1000, f);
+ fgets(evalLine, 1000, f);
+ fgets(moveLine, 1000, f);
+ if (fenLine[0] == 0 || evalLine[0] == 0 || moveLine[0] == 0)
+ break;
+ StripTrailingNewline(fenLine);
+ StripTrailingNewline(evalLine);
+ StripTrailingNewline(moveLine);
+ StoreFileEval(fenLine, fenNumber, atoi(evalLine), moveLine, isLeftFile);
+ fenNumber++;
+ }
+ fclose(f);
+}
+
+
+void CompareLopFiles(const char *leftFile, const char *rightFile)
+{
+ int i;
+
+ printf("Comparing %s to %s:\n", leftFile, rightFile);
+ fflush(stdout);
+
+ if (gPosition)
+ free(gPosition);
+ gPosition = (position_t *)malloc(2000000 * sizeof(position_t));
+ gPositions = 0;
+ for (i = 0; i < 2000000; ++i)
+ {
+ gPosition[i].fen[0] = 0;
+ gPosition[i].leftEval = -1000000;
+ gPosition[i].rightEval = -1000000;
+ }
+
+ ReadFileEvals(leftFile, true);
+ ReadFileEvals(rightFile, false);
+
+ position_t *oldPosition = gPosition;
+ gPosition = (position_t *)malloc(2000000 * sizeof(position_t));
+ gPositions = 0;
+ for (i = 0; i < 2000000; ++i)
+ {
+ if (oldPosition[i].fen[0] == 0 ||
+ oldPosition[i].leftEval == -1000000 ||
+ oldPosition[i].rightEval == -1000000)
+ continue;
+ gPosition[gPositions++] = oldPosition[i];
+ }
+ free(oldPosition);
+
+ qsort(gPosition, gPositions, sizeof(position_t), CompareLeftEvals);
+ for (i = 0; i < gPositions; ++i)
+ gPosition[i].leftRank = i;
+
+ qsort(gPosition, gPositions, sizeof(position_t), CompareRightEvals);
+ for (i = 0; i < gPositions; ++i)
+ gPosition[i].rightRank = i;
+
+ PrintCorrelation();
+
+ free(gPosition);
+ gPosition = NULL;
+}
+
+
+int main(int argc, char *argv[])
+{
+ char leftFile[1000 + 1];
+ char rightFile[1000 + 1];
+
+ /*strcpy(leftFile, "lop_10k_lc0_nosearch.txt");
+ strcpy(rightFile, "lop_10k_sf.txt");
+ CompareLopFiles(leftFile, rightFile);
+ return 0;*/
+
+ if (argc != 3)
+ {
+ printf("Usage: compare_lop file_one.txt file_two.txt\n");
+ exit(1);
+ }
+ strcpy(leftFile, argv[1]);
+ strcpy(rightFile, argv[2]);
+ CompareLopFiles(leftFile, rightFile);
+ return 0;
+}
+