Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/Stockfish
Path: blob/master/src/nnue/nnue_accumulator.h
648 views
1
/*
2
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
4
5
Stockfish is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Stockfish is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
// Class for difference calculation of NNUE evaluation function
20
21
#ifndef NNUE_ACCUMULATOR_H_INCLUDED
22
#define NNUE_ACCUMULATOR_H_INCLUDED
23
24
#include <array>
25
#include <cstddef>
26
#include <cstdint>
27
#include <cstring>
28
#include <utility>
29
30
#include "../types.h"
31
#include "nnue_architecture.h"
32
#include "nnue_common.h"
33
34
namespace Stockfish {
35
class Position;
36
}
37
38
namespace Stockfish::Eval::NNUE {
39
40
template<IndexType Size>
41
struct alignas(CacheLineSize) Accumulator;
42
43
template<IndexType TransformedFeatureDimensions>
44
class FeatureTransformer;
45
46
// Class that holds the result of affine transformation of input features
47
template<IndexType Size>
48
struct alignas(CacheLineSize) Accumulator {
49
std::array<std::array<std::int16_t, Size>, COLOR_NB> accumulation;
50
std::array<std::array<std::int32_t, PSQTBuckets>, COLOR_NB> psqtAccumulation;
51
std::array<bool, COLOR_NB> computed = {};
52
};
53
54
55
// AccumulatorCaches struct provides per-thread accumulator caches, where each
56
// cache contains multiple entries for each of the possible king squares.
57
// When the accumulator needs to be refreshed, the cached entry is used to more
58
// efficiently update the accumulator, instead of rebuilding it from scratch.
59
// This idea, was first described by Luecx (author of Koivisto) and
60
// is commonly referred to as "Finny Tables".
61
struct AccumulatorCaches {
62
63
template<typename Networks>
64
AccumulatorCaches(const Networks& networks) {
65
clear(networks);
66
}
67
68
template<IndexType Size>
69
struct alignas(CacheLineSize) Cache {
70
71
struct alignas(CacheLineSize) Entry {
72
std::array<BiasType, Size> accumulation;
73
std::array<PSQTWeightType, PSQTBuckets> psqtAccumulation;
74
std::array<Piece, SQUARE_NB> pieces;
75
Bitboard pieceBB;
76
77
// To initialize a refresh entry, we set all its bitboards empty,
78
// so we put the biases in the accumulation, without any weights on top
79
void clear(const std::array<BiasType, Size>& biases) {
80
accumulation = biases;
81
std::memset(reinterpret_cast<std::byte*>(this) + offsetof(Entry, psqtAccumulation),
82
0, sizeof(Entry) - offsetof(Entry, psqtAccumulation));
83
}
84
};
85
86
template<typename Network>
87
void clear(const Network& network) {
88
for (auto& entries1D : entries)
89
for (auto& entry : entries1D)
90
entry.clear(network.featureTransformer.biases);
91
}
92
93
std::array<Entry, COLOR_NB>& operator[](Square sq) { return entries[sq]; }
94
95
std::array<std::array<Entry, COLOR_NB>, SQUARE_NB> entries;
96
};
97
98
template<typename Networks>
99
void clear(const Networks& networks) {
100
big.clear(networks.big);
101
small.clear(networks.small);
102
}
103
104
Cache<TransformedFeatureDimensionsBig> big;
105
Cache<TransformedFeatureDimensionsSmall> small;
106
};
107
108
109
template<typename FeatureSet>
110
struct AccumulatorState {
111
Accumulator<TransformedFeatureDimensionsBig> accumulatorBig;
112
Accumulator<TransformedFeatureDimensionsSmall> accumulatorSmall;
113
typename FeatureSet::DiffType diff;
114
115
template<IndexType Size>
116
auto& acc() noexcept {
117
static_assert(Size == TransformedFeatureDimensionsBig
118
|| Size == TransformedFeatureDimensionsSmall,
119
"Invalid size for accumulator");
120
121
if constexpr (Size == TransformedFeatureDimensionsBig)
122
return accumulatorBig;
123
else if constexpr (Size == TransformedFeatureDimensionsSmall)
124
return accumulatorSmall;
125
}
126
127
template<IndexType Size>
128
const auto& acc() const noexcept {
129
static_assert(Size == TransformedFeatureDimensionsBig
130
|| Size == TransformedFeatureDimensionsSmall,
131
"Invalid size for accumulator");
132
133
if constexpr (Size == TransformedFeatureDimensionsBig)
134
return accumulatorBig;
135
else if constexpr (Size == TransformedFeatureDimensionsSmall)
136
return accumulatorSmall;
137
}
138
139
void reset(const typename FeatureSet::DiffType& dp) noexcept {
140
diff = dp;
141
accumulatorBig.computed.fill(false);
142
accumulatorSmall.computed.fill(false);
143
}
144
145
typename FeatureSet::DiffType& reset() noexcept {
146
accumulatorBig.computed.fill(false);
147
accumulatorSmall.computed.fill(false);
148
return diff;
149
}
150
};
151
152
class AccumulatorStack {
153
public:
154
static constexpr std::size_t MaxSize = MAX_PLY + 1;
155
156
template<typename T>
157
[[nodiscard]] const AccumulatorState<T>& latest() const noexcept;
158
159
void reset() noexcept;
160
std::pair<DirtyPiece&, DirtyThreats&> push() noexcept;
161
void pop() noexcept;
162
163
template<IndexType Dimensions>
164
void evaluate(const Position& pos,
165
const FeatureTransformer<Dimensions>& featureTransformer,
166
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
167
168
private:
169
template<typename T>
170
[[nodiscard]] AccumulatorState<T>& mut_latest() noexcept;
171
172
template<typename T>
173
[[nodiscard]] const std::array<AccumulatorState<T>, MaxSize>& accumulators() const noexcept;
174
175
template<typename T>
176
[[nodiscard]] std::array<AccumulatorState<T>, MaxSize>& mut_accumulators() noexcept;
177
178
template<typename FeatureSet, IndexType Dimensions>
179
void evaluate_side(Color perspective,
180
const Position& pos,
181
const FeatureTransformer<Dimensions>& featureTransformer,
182
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
183
184
template<typename FeatureSet, IndexType Dimensions>
185
[[nodiscard]] std::size_t find_last_usable_accumulator(Color perspective) const noexcept;
186
187
template<typename FeatureSet, IndexType Dimensions>
188
void forward_update_incremental(Color perspective,
189
const Position& pos,
190
const FeatureTransformer<Dimensions>& featureTransformer,
191
const std::size_t begin) noexcept;
192
193
template<typename FeatureSet, IndexType Dimensions>
194
void backward_update_incremental(Color perspective,
195
const Position& pos,
196
const FeatureTransformer<Dimensions>& featureTransformer,
197
const std::size_t end) noexcept;
198
199
std::array<AccumulatorState<PSQFeatureSet>, MaxSize> psq_accumulators;
200
std::array<AccumulatorState<ThreatFeatureSet>, MaxSize> threat_accumulators;
201
std::size_t size = 1;
202
};
203
204
} // namespace Stockfish::Eval::NNUE
205
206
#endif // NNUE_ACCUMULATOR_H_INCLUDED
207
208