#!/usr/bin/env python3 """Extracts the FeatureTransformer's psqtWeights table from a real Stockfish .nnue file (nn-fdc1d0fe6455.nnue, added to Stockfish 2023-06-12, rev 38e61663d836e062af0bc002814ad5149c4b7729 -- see https://github.com/official-stockfish/networks). Parsed against that exact commit's header-reading code (fetched and read directly, not guessed/reverse-engineered by eye -- see src/nnue/evaluate_nnue.cpp:94-125, src/nnue/nnue_feature_transformer.h:254-258, src/nnue/nnue_architecture.h:44, src/nnue/features/half_ka_v2_hm.h): Top-level header (evaluate_nnue.cpp read_header): version uint32 LE hashValue uint32 LE size uint32 LE (length of description string) description `size` bytes FeatureTransformer section (Detail::read_parameters + its own read_parameters): ft_header_hash uint32 LE, must equal HalfKAv2_hm::HashValue (0x7f234cb8) XOR (2*L1) biases L1 int16 values weights L1 * InputDimensions int16 values psqtWeights PSQTBuckets * InputDimensions int32 values, laid out psqtWeights[index*PSQTBuckets + k] InputDimensions = SQUARE_NB*PS_NB/2 = 22528, IDENTICAL formula/indexing to typhoon's own HalfKAv2_hm spec (docs/NNUE.md) -- same king buckets, same orientation table, same piece-square planes. PSQTBuckets=8 (nnue_architecture.h). L1 for this net is NOT assumed -- derived below from the stored ft hash, since Stockfish's own loader treats that hash as the authoritative check, not file size or guessed offsets. This script ONLY extracts psqtWeights (a literal per-feature-index scalar linear value, one per material-count bucket) for comparison/ diagnostic purposes against our own training coverage gaps -- NOT for copying into our net's weights. See the conversation: the goal is a from-scratch net, this is a sanity check on whether our dusty corners are in the right ballpark, not a transplant. Usage: python3 parse_stockfish_net.py nn-fdc1d0fe6455.nnue --out sf_psqt.npy """ import argparse import struct import sys import numpy as np HALFKAV2_HM_HASH = 0x7F234CB8 PS_NB = 11 * 64 # 704 INPUT_DIMENSIONS = (64 * PS_NB) // 2 # 22528 PSQT_BUCKETS = 8 def read_u32(f): return struct.unpack("