diff options
| author | Scott Gasch <[email protected]> | 2026-10-20 09:26:44 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-20 09:26:44 -0700 |
| commit | c8c6bbc2c1a103e5b07b079a0f8bdf66c7724d80 (patch) | |
| tree | b6c2137c9ac577fa83b1e948fa0311d53dfe445d /tests/lop/compare_lop.cpp | |
| parent | 3b5d3677bb14ae14728a1c4b221176bb83667d92 (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.cpp | 230 |
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; +} + |
