/*
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 .
*/
// Class for difference calculation of NNUE evaluation function
#ifndef NNUE_ACCUMULATOR_H_INCLUDED
#define NNUE_ACCUMULATOR_H_INCLUDED
#include
#include
#include
#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, COLOR_NB> accumulation;
std::array, COLOR_NB> psqtAccumulation;
std::array 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
AccumulatorCaches(const Network& network) {
clear(network);
}
struct alignas(CacheLineSize) Entry {
std::array accumulation;
std::array psqtAccumulation;
std::array 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& biases) {
accumulation = biases;
std::memset(reinterpret_cast(this) + offsetof(Entry, psqtAccumulation), 0,
sizeof(Entry) - offsetof(Entry, psqtAccumulation));
}
};
template
void clear(const Network& network) {
for (auto& entries1D : entries)
for (auto& entry : entries1D)
entry.clear(network.featureTransformer.biases);
}
std::array& operator[](Square sq) { return entries[sq]; }
std::array, 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 accumulators;
usize size = 1;
};
} // namespace Stockfish::Eval::NNUE
#endif // NNUE_ACCUMULATOR_H_INCLUDED