Files
stockfish/src/nnue/nnue_accumulator.h
T
f4bcd40409 Update nnue architecture to SFNNv16 and net nn-89cb98a217f7.nnue
Failed STC (https://tests.stockfishchess.org/tests/view/6a4f36355529b8472df7ff7e):
LLR: -2.94 (-2.94,2.94) <0.00,2.00>
Total: 144768 W: 38003 L: 38051 D: 68714
Ptnml(0-2): 521, 17154, 37050, 17170, 489

Passed LTC (https://tests.stockfishchess.org/tests/view/6a501dad5529b8472df80103):
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 189888 W: 49613 L: 48984 D: 91291
Ptnml(0-2): 122, 20406, 53256, 21041, 119

Passed VLTC (https://tests.stockfishchess.org/tests/view/6a52892c5529b8472df80481):
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 46124 W: 12156 L: 11865 D: 22103
Ptnml(0-2): 12, 4523, 13696, 4824, 7

Passed VVLTC (https://tests.stockfishchess.org/tests/view/6a566a285529b8472df80b84):
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 44698 W: 11805 L: 11507 D: 21386
Ptnml(0-2): 9, 3819, 14395, 4117, 9

This PR adds pawn pair features, a new set of features that is indexed by pairs of pawns occupying the same or adjacent files (hence "3-wide"). Because these are a superset of pawn–pawn interactions in threat inputs (including the recently added pawn-pusher inputs), those are removed. HalfKA features remain unchanged.

Pawn-pair features were invented by Jonathan for Pawnocchio, based on his observation that in a net trained on *all* pairs of pawns, the most important pawn pairs were those differing by at most one file.

sscg13 made the necessary changes to the trainer (https://github.com/official-stockfish/nnue-pytorch/pull/502) and initial changes to inference. The slowdown was originally far too large (some 10%); following major optimizations from Jonathan and a bit from me, the slowdown is small enough for a comfortable LTC pass; on my machine I get a 3.5% slowdown overall. I expect there are further "easy" speedups in the new code, so this gap should narrow a bit.

Nettest PR: https://github.com/vondele/nettest/pull/359

closes https://github.com/official-stockfish/Stockfish/pull/6982

Bench: 2718396

Co-Authored-By: anematode <timothy.herchen@gmail.com>
Co-Authored-By: Jonathan Hallström <lmj.hallstrom@gmail.com>
2026-07-20 11:06:41 +02:00

137 lines
4.9 KiB
C++

/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Class for difference calculation of NNUE evaluation function
#ifndef NNUE_ACCUMULATOR_H_INCLUDED
#define NNUE_ACCUMULATOR_H_INCLUDED
#include <array>
#include <cstddef>
#include <cstring>
#include "../types.h"
#include "../misc.h"
#include "nnue_architecture.h"
#include "nnue_common.h"
namespace Stockfish {
class Position;
}
namespace Stockfish::Eval::NNUE {
struct alignas(CacheLineSize) Accumulator;
class FeatureTransformer;
// Class that holds the result of affine transformation of input features,
// combined HalfKA + Threats
struct alignas(CacheLineSize) Accumulator {
std::array<std::array<i16, L1>, COLOR_NB> accumulation;
std::array<std::array<i32, PSQTBuckets>, COLOR_NB> psqtAccumulation;
std::array<bool, COLOR_NB> computed = {};
};
// AccumulatorCaches struct provides per-thread accumulator caches, where each
// cache contains multiple entries for each of the possible king squares.
// When the accumulator needs to be refreshed, the cached entry is used to more
// efficiently update the accumulator, instead of rebuilding it from scratch.
// This idea, was first described by Luecx (author of Koivisto) and
// is commonly referred to as "Finny Tables".
struct AccumulatorCaches {
template<typename Network>
AccumulatorCaches(const Network& network) {
clear(network);
}
struct alignas(CacheLineSize) Entry {
std::array<BiasType, L1> accumulation;
std::array<PSQTWeightType, PSQTBuckets> psqtAccumulation;
std::array<Piece, SQUARE_NB> pieces;
Bitboard pieceBB;
// To initialize a refresh entry, we set all its bitboards empty,
// so we put the biases in the accumulation, without any weights on top
void clear(const std::array<BiasType, L1>& biases) {
accumulation = biases;
std::memset(reinterpret_cast<std::byte*>(this) + offsetof(Entry, psqtAccumulation), 0,
sizeof(Entry) - offsetof(Entry, psqtAccumulation));
}
};
template<typename Network>
void clear(const Network& network) {
for (auto& entries1D : entries)
for (auto& entry : entries1D)
entry.clear(network.featureTransformer.biases);
}
std::array<Entry, COLOR_NB>& operator[](Square sq) { return entries[sq]; }
std::array<std::array<Entry, COLOR_NB>, SQUARE_NB> entries;
};
struct AccumulatorState: public Accumulator, Dirties {};
class AccumulatorStack {
public:
static constexpr usize MaxSize = MAX_PLY + 1;
[[nodiscard]] const AccumulatorState& latest() const noexcept;
void reset() noexcept;
Dirties& push() noexcept;
void pop() noexcept;
void evaluate(const Position& pos,
const FeatureTransformer& featureTransformer,
// Silence spurious warning on GCC 10
[[maybe_unused]] AccumulatorCaches& cache) noexcept;
private:
[[nodiscard]] AccumulatorState& mut_latest() noexcept;
void evaluate_side(Color perspective,
const Position& pos,
const FeatureTransformer& featureTransformer,
// Silence spurious warning on GCC 10
[[maybe_unused]] AccumulatorCaches& cache) noexcept;
[[nodiscard]] usize find_last_usable_accumulator(Color perspective) const noexcept;
void forward_update_incremental(Color perspective,
const Position& pos,
const FeatureTransformer& featureTransformer,
const usize begin) noexcept;
void backward_update_incremental(Color perspective,
const Position& pos,
const FeatureTransformer& featureTransformer,
const usize end) noexcept;
std::array<AccumulatorState, MaxSize> accumulators;
usize size = 1;
};
} // namespace Stockfish::Eval::NNUE
#endif // NNUE_ACCUMULATOR_H_INCLUDED