mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 12:47:08 +00:00
Update NNUE architecture to SFNNv10 with Threat Inputs and net nn-49c1193b131c.nnue
This commit introduces Full Threat Input features, which are a subset of Piece(Square)-Piece(Square) pairs. In any given position, the active features consist of pairs where the second piece’s square lies in the attack set of the first piece. This is an extremely simplified explanation that leaves out many details. The already-used HalfKAv2_hm feature set completes the input features. Minor quantization changes have also been made. The net nn-49c1193b131c.nnue was trained by vondele using the following setup: https://github.com/vondele/nettest/blob/7de71238e9b295e3f88ed7c9c5936af632c9b981/threats.yaml A graphical version of an earlier scheme (with less refinement) that illustrates the core concepts can be found attached. [NewInputs.pdf](https://github.com/user-attachments/files/23478441/NewInputs.pdf) Further information, as well as a brief description of the history of development, can be found attached. [Stockfish threat inputs PR summary.pdf](https://github.com/user-attachments/files/23478634/Stockfish.threat.inputs.PR.summary.pdf) This has been a huge effort spanning over half a year, with the original [discussion thread](https://discord.com/channels/435943710472011776/1336647760388034610) reaching over 11k messages. Thanks to everyone who has contributed. Monty PRs: https://github.com/official-monty/Monty/pull/87 (Initial threat input PR) https://github.com/official-monty/Monty/pull/114 (Fixed threat indexing to take into account colour correctly) https://github.com/official-monty/Monty/pull/116 (i8 quantisation of weights whilst keeping calculations in i16) Yukari commit: https://github.com/yukarichess/yukari/commit/2d482c64a79cec03cf4987d5289334b9cdc737bc (Threat inputs merged) Plentychess PRs: https://github.com/Yoshie2000/PlentyChess/pull/400 (Threat inputs merged) https://github.com/Yoshie2000/PlentyChess/pull/411 (Threat input weights quantised to i8) Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 63424 W: 16956 L: 16591 D: 29877 Ptnml(0-2): 276, 7522, 15797, 7795, 322 https://tests.stockfishchess.org/tests/view/69105b3dec1d00d2c195c569 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 27876 W: 7417 L: 7110 D: 13349 Ptnml(0-2): 23, 3033, 7530, 3318, 34 https://tests.stockfishchess.org/tests/view/6910d817ec1d00d2c195c66e Passed VVLTC (Hash accidentally set to 1/2 normal value for both sides): LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 12458 W: 3353 L: 3102 D: 6003 Ptnml(0-2): 0, 1106, 3767, 1355, 1 https://tests.stockfishchess.org/tests/view/69115a26ec1d00d2c195c7cd This version has also passed non-regression LTC against the originally passed version: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 51144 W: 13086 L: 12903 D: 25155 Ptnml(0-2): 22, 5167, 15018, 5336, 29 https://tests.stockfishchess.org/tests/view/69138a317ca87818523314bf LTC elo estimate on ARM: 1 patch : 13.9 1.9 38296.5 73728 52 2 master : 0.0 ---- 35431.5 73728 48 closes https://github.com/official-stockfish/Stockfish/pull/6411 bench: 2626086 Co-authored-by: Shawn Xu <xu107288696@gmail.com> Co-authored-by: Timothy Herchen <timothy.herchen@gmail.com> Co-authored-by: Viren6 <94880762+Viren6@users.noreply.github.com> Co-authored-by: Yoshie2000 <patrick.leonhardt@gmx.net> Co-authored-by: Joost Vandevondele <Joost.VandeVondele@gmail.com> Co-authored-by: rn5f107s2 <clemens.lerchl@gmail.com> Co-authored-by: cj5716 <125858804+cj5716@users.noreply.github.com> Co-authored-by: AliceRoselia <63040919+AliceRoselia@users.noreply.github.com> Co-authored-by: Linmiao Xu <linmiao.xu@gmail.com> Co-authored-by: Disservin <disservin.social@gmail.com>
This commit is contained in:
committed by
Joost VandeVondele
co-authored by
Shawn Xu
Timothy Herchen
Viren6
Yoshie2000
Joost Vandevondele
rn5f107s2
cj5716
AliceRoselia
Linmiao Xu
Disservin
parent
69a01b88f3
commit
8e5392d79a
@@ -200,6 +200,7 @@ Panthee
|
|||||||
Pascal Romaret
|
Pascal Romaret
|
||||||
Pasquale Pigazzini (ppigazzini)
|
Pasquale Pigazzini (ppigazzini)
|
||||||
Patrick Jansen (mibere)
|
Patrick Jansen (mibere)
|
||||||
|
Patrick Leonhardt (Yoshie2000)
|
||||||
Peter Schneider (pschneider1968)
|
Peter Schneider (pschneider1968)
|
||||||
Peter Zsifkovits (CoffeeOne)
|
Peter Zsifkovits (CoffeeOne)
|
||||||
PikaCat
|
PikaCat
|
||||||
|
|||||||
+7
-6
@@ -55,15 +55,16 @@ PGOBENCH = $(WINE_PATH) ./$(EXE) bench
|
|||||||
SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
|
SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
|
||||||
misc.cpp movegen.cpp movepick.cpp position.cpp \
|
misc.cpp movegen.cpp movepick.cpp position.cpp \
|
||||||
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
|
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
|
||||||
nnue/nnue_accumulator.cpp nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp \
|
nnue/nnue_accumulator.cpp nnue/nnue_misc.cpp nnue/network.cpp \
|
||||||
|
nnue/features/half_ka_v2_hm.cpp nnue/features/full_threats.cpp \
|
||||||
engine.cpp score.cpp memory.cpp
|
engine.cpp score.cpp memory.cpp
|
||||||
|
|
||||||
HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \
|
HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \
|
||||||
nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \
|
nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/features/full_threats.h \
|
||||||
nnue/layers/affine_transform_sparse_input.h nnue/layers/clipped_relu.h \
|
nnue/layers/affine_transform.h nnue/layers/affine_transform_sparse_input.h \
|
||||||
nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \
|
nnue/layers/clipped_relu.h nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h \
|
||||||
nnue/nnue_common.h nnue/nnue_feature_transformer.h nnue/simd.h position.h \
|
nnue/nnue_architecture.h nnue/nnue_common.h nnue/nnue_feature_transformer.h nnue/simd.h \
|
||||||
search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \
|
position.h search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \
|
||||||
tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h memory.h
|
tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h memory.h
|
||||||
|
|
||||||
OBJS = $(notdir $(SRCS:.cpp=.o))
|
OBJS = $(notdir $(SRCS:.cpp=.o))
|
||||||
|
|||||||
+5
-1
@@ -65,6 +65,10 @@ const std::vector<std::string> Defaults = {
|
|||||||
"3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
|
"3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
|
||||||
"4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
|
"4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
|
||||||
|
|
||||||
|
// Positions with high numbers of changed threats
|
||||||
|
"k7/2n1n3/1nbNbn2/2NbRBn1/1nbRQR2/2NBRBN1/3N1N2/7K w - - 0 1",
|
||||||
|
"K7/8/8/BNQNQNB1/N5N1/R1Q1q2r/n5n1/bnqnqnbk w - - 0 1",
|
||||||
|
|
||||||
// 5-man positions
|
// 5-man positions
|
||||||
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
|
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
|
||||||
"8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
|
"8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
|
||||||
@@ -509,4 +513,4 @@ BenchmarkSetup setup_benchmark(std::istream& is) {
|
|||||||
return setup;
|
return setup;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Stockfish
|
} // namespace Stockfish
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
|||||||
|
|
||||||
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||||
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||||
|
Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
|
||||||
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
|
|
||||||
alignas(64) Magic Magics[SQUARE_NB][2];
|
alignas(64) Magic Magics[SQUARE_NB][2];
|
||||||
@@ -105,6 +106,8 @@ void Bitboards::init() {
|
|||||||
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
|
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
|
||||||
BetweenBB[s1][s2] =
|
BetweenBB[s1][s2] =
|
||||||
(attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
|
(attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
|
||||||
|
RayPassBB[s1][s2] =
|
||||||
|
attacks_bb(pt, s1, 0) & (attacks_bb(pt, s2, square_bb(s1)) | s2);
|
||||||
}
|
}
|
||||||
BetweenBB[s1][s2] |= s2;
|
BetweenBB[s1][s2] |= s2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
|||||||
|
|
||||||
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||||
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||||
|
extern Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
|
||||||
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
|
|
||||||
|
|
||||||
@@ -252,6 +253,20 @@ inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Bitboard attacks_bb(Piece pc, Square s) {
|
||||||
|
if (type_of(pc) == PAWN)
|
||||||
|
return PseudoAttacks[color_of(pc)][s];
|
||||||
|
|
||||||
|
return PseudoAttacks[type_of(pc)][s];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
|
||||||
|
if (type_of(pc) == PAWN)
|
||||||
|
return PseudoAttacks[color_of(pc)][s];
|
||||||
|
|
||||||
|
return attacks_bb(type_of(pc), s, occupied);
|
||||||
|
}
|
||||||
|
|
||||||
// Counts the number of non-zero bits in a bitboard.
|
// Counts the number of non-zero bits in a bitboard.
|
||||||
inline int popcount(Bitboard b) {
|
inline int popcount(Bitboard b) {
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ namespace Eval {
|
|||||||
// for the build process (profile-build and fishtest) to work. Do not change the
|
// for the build process (profile-build and fishtest) to work. Do not change the
|
||||||
// name of the macro or the location where this macro is defined, as it is used
|
// name of the macro or the location where this macro is defined, as it is used
|
||||||
// in the Makefile/Fishtest.
|
// in the Makefile/Fishtest.
|
||||||
#define EvalFileDefaultNameBig "nn-1c0000000000.nnue"
|
#define EvalFileDefaultNameBig "nn-49c1193b131c.nnue"
|
||||||
#define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue"
|
#define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue"
|
||||||
|
|
||||||
namespace NNUE {
|
namespace NNUE {
|
||||||
|
|||||||
+2
-1
@@ -21,9 +21,9 @@
|
|||||||
|
|
||||||
#include "bitboard.h"
|
#include "bitboard.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "nnue/features/full_threats.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "tune.h"
|
#include "tune.h"
|
||||||
#include "types.h"
|
|
||||||
#include "uci.h"
|
#include "uci.h"
|
||||||
|
|
||||||
using namespace Stockfish;
|
using namespace Stockfish;
|
||||||
@@ -33,6 +33,7 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
Bitboards::init();
|
Bitboards::init();
|
||||||
Position::init();
|
Position::init();
|
||||||
|
Eval::NNUE::Features::init_threat_offsets();
|
||||||
|
|
||||||
auto uci = std::make_unique<UCIEngine>(argc, argv);
|
auto uci = std::make_unique<UCIEngine>(argc, argv);
|
||||||
|
|
||||||
|
|||||||
+7
-4
@@ -134,10 +134,13 @@ class ValueList {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
std::size_t size() const { return size_; }
|
std::size_t size() const { return size_; }
|
||||||
void push_back(const T& value) { values_[size_++] = value; }
|
void push_back(const T& value) {
|
||||||
const T* begin() const { return values_; }
|
assert(size_ < MaxSize);
|
||||||
const T* end() const { return values_ + size_; }
|
values_[size_++] = value;
|
||||||
const T& operator[](int index) const { return values_[index]; }
|
}
|
||||||
|
const T* begin() const { return values_; }
|
||||||
|
const T* end() const { return values_ + size_; }
|
||||||
|
const T& operator[](int index) const { return values_[index]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T values_[MaxSize];
|
T values_[MaxSize];
|
||||||
|
|||||||
@@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2025 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Definition of input features FullThreats of NNUE evaluation function
|
||||||
|
|
||||||
|
#include "full_threats.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
#include "../../bitboard.h"
|
||||||
|
#include "../../misc.h"
|
||||||
|
#include "../../position.h"
|
||||||
|
#include "../../types.h"
|
||||||
|
#include "../nnue_common.h"
|
||||||
|
|
||||||
|
namespace Stockfish::Eval::NNUE::Features {
|
||||||
|
|
||||||
|
// Lookup array for indexing threats
|
||||||
|
IndexType offsets[PIECE_NB][SQUARE_NB + 2];
|
||||||
|
|
||||||
|
// Information on a particular pair of pieces and whether they should be excluded
|
||||||
|
struct PiecePairData {
|
||||||
|
// Layout: bits 8..31 are the index contribution of this piece pair, bits 0 and 1 are exclusion info
|
||||||
|
uint32_t data;
|
||||||
|
PiecePairData() {}
|
||||||
|
PiecePairData(bool excluded_pair, bool semi_excluded_pair, IndexType feature_index_base) {
|
||||||
|
data =
|
||||||
|
excluded_pair << 1 | (semi_excluded_pair && !excluded_pair) | feature_index_base << 8;
|
||||||
|
}
|
||||||
|
// lsb: excluded if from < to; 2nd lsb: always excluded
|
||||||
|
uint8_t excluded_pair_info() const { return (uint8_t) data; }
|
||||||
|
IndexType feature_index_base() const { return data >> 8; }
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array<Piece, 12> AllPieces = {
|
||||||
|
W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
||||||
|
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The final index is calculated from summing data found in these two LUTs, as well
|
||||||
|
// as offsets[attacker][from]
|
||||||
|
PiecePairData index_lut1[PIECE_NB][PIECE_NB]; // [attacker][attacked]
|
||||||
|
uint8_t index_lut2[PIECE_NB][SQUARE_NB][SQUARE_NB]; // [attacker][from][to]
|
||||||
|
|
||||||
|
static void init_index_luts() {
|
||||||
|
for (Piece attacker : AllPieces)
|
||||||
|
{
|
||||||
|
for (Piece attacked : AllPieces)
|
||||||
|
{
|
||||||
|
bool enemy = (attacker ^ attacked) == 8;
|
||||||
|
PieceType attackerType = type_of(attacker);
|
||||||
|
PieceType attackedType = type_of(attacked);
|
||||||
|
|
||||||
|
int map = FullThreats::map[attackerType - 1][attackedType - 1];
|
||||||
|
bool semi_excluded = attackerType == attackedType && (enemy || attackerType != PAWN);
|
||||||
|
IndexType feature = offsets[attacker][65]
|
||||||
|
+ (color_of(attacked) * (numValidTargets[attacker] / 2) + map)
|
||||||
|
* offsets[attacker][64];
|
||||||
|
|
||||||
|
bool excluded = map < 0;
|
||||||
|
index_lut1[attacker][attacked] = PiecePairData(excluded, semi_excluded, feature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Piece attacker : AllPieces)
|
||||||
|
{
|
||||||
|
for (int from = 0; from < SQUARE_NB; ++from)
|
||||||
|
{
|
||||||
|
for (int to = 0; to < SQUARE_NB; ++to)
|
||||||
|
{
|
||||||
|
Bitboard attacks = attacks_bb(attacker, Square(from));
|
||||||
|
index_lut2[attacker][from][to] = popcount((square_bb(Square(to)) - 1) & attacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_threat_offsets() {
|
||||||
|
int cumulativeOffset = 0;
|
||||||
|
for (Piece piece : AllPieces)
|
||||||
|
{
|
||||||
|
int pieceIdx = piece;
|
||||||
|
int cumulativePieceOffset = 0;
|
||||||
|
|
||||||
|
for (Square from = SQ_A1; from <= SQ_H8; ++from)
|
||||||
|
{
|
||||||
|
offsets[pieceIdx][from] = cumulativePieceOffset;
|
||||||
|
|
||||||
|
if (type_of(piece) != PAWN)
|
||||||
|
{
|
||||||
|
Bitboard attacks = attacks_bb(piece, from, 0ULL);
|
||||||
|
cumulativePieceOffset += popcount(attacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (from >= SQ_A2 && from <= SQ_H7)
|
||||||
|
{
|
||||||
|
Bitboard attacks = (pieceIdx < 8) ? pawn_attacks_bb<WHITE>(square_bb(from))
|
||||||
|
: pawn_attacks_bb<BLACK>(square_bb(from));
|
||||||
|
cumulativePieceOffset += popcount(attacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offsets[pieceIdx][64] = cumulativePieceOffset;
|
||||||
|
offsets[pieceIdx][65] = cumulativeOffset;
|
||||||
|
|
||||||
|
cumulativeOffset += numValidTargets[pieceIdx] * cumulativePieceOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_index_luts();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index of a feature for a given king position and another piece on some square
|
||||||
|
template<Color Perspective>
|
||||||
|
IndexType
|
||||||
|
FullThreats::make_index(Piece attacker, Square from, Square to, Piece attacked, Square ksq) {
|
||||||
|
from = (Square) (int(from) ^ OrientTBL[Perspective][ksq]);
|
||||||
|
to = (Square) (int(to) ^ OrientTBL[Perspective][ksq]);
|
||||||
|
|
||||||
|
if (Perspective == BLACK)
|
||||||
|
{
|
||||||
|
attacker = ~attacker;
|
||||||
|
attacked = ~attacked;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto piecePairData = index_lut1[attacker][attacked];
|
||||||
|
|
||||||
|
// Some threats imply the existence of the corresponding ones in the opposite
|
||||||
|
// direction. We filter them here to ensure only one such threat is active.
|
||||||
|
|
||||||
|
// In the below addition, the 2nd lsb gets set iff either the pair is always excluded,
|
||||||
|
// or the pair is semi-excluded and from < to. By using an unsigned compare, the following
|
||||||
|
// sequence can use an add-with-carry instruction.
|
||||||
|
bool less_than = static_cast<uint8_t>(from) < static_cast<uint8_t>(to);
|
||||||
|
if ((piecePairData.excluded_pair_info() + less_than) & 2)
|
||||||
|
return Dimensions;
|
||||||
|
|
||||||
|
IndexType index =
|
||||||
|
piecePairData.feature_index_base() + offsets[attacker][from] + index_lut2[attacker][from][to];
|
||||||
|
|
||||||
|
sf_assume(index != Dimensions);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of indices for active features in ascending order
|
||||||
|
template<Color Perspective>
|
||||||
|
void FullThreats::append_active_indices(const Position& pos, IndexList& active) {
|
||||||
|
static constexpr Color order[2][2] = {{WHITE, BLACK}, {BLACK, WHITE}};
|
||||||
|
|
||||||
|
Square ksq = pos.square<KING>(Perspective);
|
||||||
|
Bitboard occupied = pos.pieces();
|
||||||
|
|
||||||
|
for (Color color : {WHITE, BLACK})
|
||||||
|
{
|
||||||
|
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||||
|
{
|
||||||
|
Color c = order[Perspective][color];
|
||||||
|
Piece attacker = make_piece(c, pt);
|
||||||
|
Bitboard bb = pos.pieces(c, pt);
|
||||||
|
|
||||||
|
if (pt == PAWN)
|
||||||
|
{
|
||||||
|
auto right = (c == WHITE) ? NORTH_EAST : SOUTH_WEST;
|
||||||
|
auto left = (c == WHITE) ? NORTH_WEST : SOUTH_EAST;
|
||||||
|
auto attacks_left =
|
||||||
|
((c == WHITE) ? shift<NORTH_EAST>(bb) : shift<SOUTH_WEST>(bb)) & occupied;
|
||||||
|
auto attacks_right =
|
||||||
|
((c == WHITE) ? shift<NORTH_WEST>(bb) : shift<SOUTH_EAST>(bb)) & occupied;
|
||||||
|
|
||||||
|
while (attacks_left)
|
||||||
|
{
|
||||||
|
Square to = pop_lsb(attacks_left);
|
||||||
|
Square from = to - right;
|
||||||
|
Piece attacked = pos.piece_on(to);
|
||||||
|
IndexType index = make_index<Perspective>(attacker, from, to, attacked, ksq);
|
||||||
|
|
||||||
|
if (index < Dimensions)
|
||||||
|
active.push_back(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (attacks_right)
|
||||||
|
{
|
||||||
|
Square to = pop_lsb(attacks_right);
|
||||||
|
Square from = to - left;
|
||||||
|
Piece attacked = pos.piece_on(to);
|
||||||
|
IndexType index = make_index<Perspective>(attacker, from, to, attacked, ksq);
|
||||||
|
|
||||||
|
if (index < Dimensions)
|
||||||
|
active.push_back(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (bb)
|
||||||
|
{
|
||||||
|
Square from = pop_lsb(bb);
|
||||||
|
Bitboard attacks = (attacks_bb(pt, from, occupied)) & occupied;
|
||||||
|
|
||||||
|
while (attacks)
|
||||||
|
{
|
||||||
|
Square to = pop_lsb(attacks);
|
||||||
|
Piece attacked = pos.piece_on(to);
|
||||||
|
IndexType index =
|
||||||
|
make_index<Perspective>(attacker, from, to, attacked, ksq);
|
||||||
|
|
||||||
|
if (index < Dimensions)
|
||||||
|
active.push_back(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit template instantiations
|
||||||
|
template void FullThreats::append_active_indices<WHITE>(const Position& pos, IndexList& active);
|
||||||
|
template void FullThreats::append_active_indices<BLACK>(const Position& pos, IndexList& active);
|
||||||
|
template IndexType
|
||||||
|
FullThreats::make_index<WHITE>(Piece attkr, Square from, Square to, Piece attkd, Square ksq);
|
||||||
|
template IndexType
|
||||||
|
FullThreats::make_index<BLACK>(Piece attkr, Square from, Square to, Piece attkd, Square ksq);
|
||||||
|
|
||||||
|
// Get a list of indices for recently changed features
|
||||||
|
template<Color Perspective>
|
||||||
|
void FullThreats::append_changed_indices(Square ksq,
|
||||||
|
const DiffType& diff,
|
||||||
|
IndexList& removed,
|
||||||
|
IndexList& added,
|
||||||
|
FusedUpdateData* fusedData,
|
||||||
|
bool first) {
|
||||||
|
for (const auto dirty : diff.list)
|
||||||
|
{
|
||||||
|
auto attacker = dirty.pc();
|
||||||
|
auto attacked = dirty.threatened_pc();
|
||||||
|
auto from = dirty.pc_sq();
|
||||||
|
auto to = dirty.threatened_sq();
|
||||||
|
auto add = dirty.add();
|
||||||
|
|
||||||
|
if (fusedData)
|
||||||
|
{
|
||||||
|
if (from == fusedData->dp2removed)
|
||||||
|
{
|
||||||
|
if (add)
|
||||||
|
{
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
fusedData->dp2removedOriginBoard |= square_bb(to);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fusedData->dp2removedOriginBoard & square_bb(to))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to != SQ_NONE && to == fusedData->dp2removed)
|
||||||
|
{
|
||||||
|
if (add)
|
||||||
|
{
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
fusedData->dp2removedTargetBoard |= square_bb(from);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fusedData->dp2removedTargetBoard & square_bb(from))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexType index = make_index<Perspective>(attacker, from, to, attacked, ksq);
|
||||||
|
|
||||||
|
if (index != Dimensions)
|
||||||
|
(add ? added : removed).push_back(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit template instantiations
|
||||||
|
template void FullThreats::append_changed_indices<WHITE>(Square ksq,
|
||||||
|
const DiffType& diff,
|
||||||
|
IndexList& removed,
|
||||||
|
IndexList& added,
|
||||||
|
FusedUpdateData* fd,
|
||||||
|
bool first);
|
||||||
|
template void FullThreats::append_changed_indices<BLACK>(Square ksq,
|
||||||
|
const DiffType& diff,
|
||||||
|
IndexList& removed,
|
||||||
|
IndexList& added,
|
||||||
|
FusedUpdateData* fd,
|
||||||
|
bool first);
|
||||||
|
|
||||||
|
bool FullThreats::requires_refresh(const DiffType& diff, Color perspective) {
|
||||||
|
return perspective == diff.us
|
||||||
|
&& OrientTBL[diff.us][diff.ksq] != OrientTBL[diff.us][diff.prevKsq];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Stockfish::Eval::NNUE::Features
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2025 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Definition of input features Simplified_Threats of NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_FEATURES_FULL_THREATS_INCLUDED
|
||||||
|
#define NNUE_FEATURES_FULL_THREATS_INCLUDED
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "../../misc.h"
|
||||||
|
#include "../../types.h"
|
||||||
|
#include "../nnue_common.h"
|
||||||
|
|
||||||
|
namespace Stockfish {
|
||||||
|
class Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Stockfish::Eval::NNUE::Features {
|
||||||
|
|
||||||
|
static constexpr int numValidTargets[PIECE_NB] = {0, 6, 12, 10, 10, 12, 8, 0,
|
||||||
|
0, 6, 12, 10, 10, 12, 8, 0};
|
||||||
|
extern IndexType offsets[PIECE_NB][SQUARE_NB + 2];
|
||||||
|
void init_threat_offsets();
|
||||||
|
|
||||||
|
class FullThreats {
|
||||||
|
public:
|
||||||
|
// Feature name
|
||||||
|
static constexpr const char* Name = "Full_Threats(Friend)";
|
||||||
|
|
||||||
|
// Hash value embedded in the evaluation file
|
||||||
|
static constexpr std::uint32_t HashValue = 0x8f234cb8u;
|
||||||
|
|
||||||
|
// Number of feature dimensions
|
||||||
|
static constexpr IndexType Dimensions = 79856;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
// Orient a square according to perspective (rotates by 180 for black)
|
||||||
|
static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = {
|
||||||
|
{ SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
|
||||||
|
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
|
||||||
|
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
|
||||||
|
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
|
||||||
|
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
|
||||||
|
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
|
||||||
|
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
|
||||||
|
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1 },
|
||||||
|
{ SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8,
|
||||||
|
SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8,
|
||||||
|
SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8,
|
||||||
|
SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8,
|
||||||
|
SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8,
|
||||||
|
SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8,
|
||||||
|
SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8,
|
||||||
|
SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr int map[PIECE_TYPE_NB-2][PIECE_TYPE_NB-2] = {
|
||||||
|
{0, 1, -1, 2, -1, -1},
|
||||||
|
{0, 1, 2, 3, 4, 5},
|
||||||
|
{0, 1, 2, 3, -1, 4},
|
||||||
|
{0, 1, 2, 3, -1, 4},
|
||||||
|
{0, 1, 2, 3, 4, 5},
|
||||||
|
{0, 1, 2, 3, -1, -1}
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
struct FusedUpdateData {
|
||||||
|
Bitboard dp2removedOriginBoard = 0;
|
||||||
|
Bitboard dp2removedTargetBoard = 0;
|
||||||
|
|
||||||
|
Square dp2removed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maximum number of simultaneously active features.
|
||||||
|
static constexpr IndexType MaxActiveDimensions = 128;
|
||||||
|
using IndexList = ValueList<IndexType, MaxActiveDimensions>;
|
||||||
|
using DiffType = DirtyThreats;
|
||||||
|
|
||||||
|
template<Color Perspective>
|
||||||
|
static IndexType make_index(Piece attkr, Square from, Square to, Piece attkd, Square ksq);
|
||||||
|
|
||||||
|
// Get a list of indices for active features
|
||||||
|
template<Color Perspective>
|
||||||
|
static void append_active_indices(const Position& pos, IndexList& active);
|
||||||
|
|
||||||
|
// Get a list of indices for recently changed features
|
||||||
|
template<Color Perspective>
|
||||||
|
static void append_changed_indices(Square ksq,
|
||||||
|
const DiffType& diff,
|
||||||
|
IndexList& removed,
|
||||||
|
IndexList& added,
|
||||||
|
FusedUpdateData* fd = nullptr,
|
||||||
|
bool first = false);
|
||||||
|
|
||||||
|
// Returns whether the change stored in this DirtyPiece means
|
||||||
|
// that a full accumulator refresh is required.
|
||||||
|
static bool requires_refresh(const DiffType& diff, Color perspective);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Stockfish::Eval::NNUE::Features
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_FEATURES_FULL_THREATS_INCLUDED
|
||||||
@@ -55,33 +55,33 @@ template IndexType HalfKAv2_hm::make_index<BLACK>(Square s, Piece pc, Square ksq
|
|||||||
|
|
||||||
// Get a list of indices for recently changed features
|
// Get a list of indices for recently changed features
|
||||||
template<Color Perspective>
|
template<Color Perspective>
|
||||||
void HalfKAv2_hm::append_changed_indices(Square ksq,
|
void HalfKAv2_hm::append_changed_indices(Square ksq,
|
||||||
const DirtyPiece& dp,
|
const DiffType& diff,
|
||||||
IndexList& removed,
|
IndexList& removed,
|
||||||
IndexList& added) {
|
IndexList& added) {
|
||||||
removed.push_back(make_index<Perspective>(dp.from, dp.pc, ksq));
|
removed.push_back(make_index<Perspective>(diff.from, diff.pc, ksq));
|
||||||
if (dp.to != SQ_NONE)
|
if (diff.to != SQ_NONE)
|
||||||
added.push_back(make_index<Perspective>(dp.to, dp.pc, ksq));
|
added.push_back(make_index<Perspective>(diff.to, diff.pc, ksq));
|
||||||
|
|
||||||
if (dp.remove_sq != SQ_NONE)
|
if (diff.remove_sq != SQ_NONE)
|
||||||
removed.push_back(make_index<Perspective>(dp.remove_sq, dp.remove_pc, ksq));
|
removed.push_back(make_index<Perspective>(diff.remove_sq, diff.remove_pc, ksq));
|
||||||
|
|
||||||
if (dp.add_sq != SQ_NONE)
|
if (diff.add_sq != SQ_NONE)
|
||||||
added.push_back(make_index<Perspective>(dp.add_sq, dp.add_pc, ksq));
|
added.push_back(make_index<Perspective>(diff.add_sq, diff.add_pc, ksq));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiations
|
// Explicit template instantiations
|
||||||
template void HalfKAv2_hm::append_changed_indices<WHITE>(Square ksq,
|
template void HalfKAv2_hm::append_changed_indices<WHITE>(Square ksq,
|
||||||
const DirtyPiece& dp,
|
const DiffType& dp,
|
||||||
IndexList& removed,
|
IndexList& removed,
|
||||||
IndexList& added);
|
IndexList& added);
|
||||||
template void HalfKAv2_hm::append_changed_indices<BLACK>(Square ksq,
|
template void HalfKAv2_hm::append_changed_indices<BLACK>(Square ksq,
|
||||||
const DirtyPiece& dp,
|
const DiffType& dp,
|
||||||
IndexList& removed,
|
IndexList& removed,
|
||||||
IndexList& added);
|
IndexList& added);
|
||||||
|
|
||||||
bool HalfKAv2_hm::requires_refresh(const DirtyPiece& dirtyPiece, Color perspective) {
|
bool HalfKAv2_hm::requires_refresh(const DiffType& diff, Color perspective) {
|
||||||
return dirtyPiece.pc == make_piece(perspective, KING);
|
return diff.pc == make_piece(perspective, KING);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Stockfish::Eval::NNUE::Features
|
} // namespace Stockfish::Eval::NNUE::Features
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ class HalfKAv2_hm {
|
|||||||
// Maximum number of simultaneously active features.
|
// Maximum number of simultaneously active features.
|
||||||
static constexpr IndexType MaxActiveDimensions = 32;
|
static constexpr IndexType MaxActiveDimensions = 32;
|
||||||
using IndexList = ValueList<IndexType, MaxActiveDimensions>;
|
using IndexList = ValueList<IndexType, MaxActiveDimensions>;
|
||||||
|
using DiffType = DirtyPiece;
|
||||||
|
|
||||||
// Index of a feature for a given king position and another piece on some square
|
// Index of a feature for a given king position and another piece on some square
|
||||||
template<Color Perspective>
|
template<Color Perspective>
|
||||||
@@ -116,11 +117,11 @@ class HalfKAv2_hm {
|
|||||||
// Get a list of indices for recently changed features
|
// Get a list of indices for recently changed features
|
||||||
template<Color Perspective>
|
template<Color Perspective>
|
||||||
static void
|
static void
|
||||||
append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added);
|
append_changed_indices(Square ksq, const DiffType& diff, IndexList& removed, IndexList& added);
|
||||||
|
|
||||||
// Returns whether the change stored in this DirtyPiece means
|
// Returns whether the change stored in this DirtyPiece means
|
||||||
// that a full accumulator refresh is required.
|
// that a full accumulator refresh is required.
|
||||||
static bool requires_refresh(const DirtyPiece& dirtyPiece, Color perspective);
|
static bool requires_refresh(const DiffType& diff, Color perspective);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Stockfish::Eval::NNUE::Features
|
} // namespace Stockfish::Eval::NNUE::Features
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ void Network<Arch, Transformer>::verify(std::string
|
|||||||
{
|
{
|
||||||
size_t size = sizeof(featureTransformer) + sizeof(Arch) * LayerStacks;
|
size_t size = sizeof(featureTransformer) + sizeof(Arch) * LayerStacks;
|
||||||
f("NNUE evaluation using " + evalfilePath + " (" + std::to_string(size / (1024 * 1024))
|
f("NNUE evaluation using " + evalfilePath + " (" + std::to_string(size / (1024 * 1024))
|
||||||
+ "MiB, (" + std::to_string(featureTransformer.InputDimensions) + ", "
|
+ "MiB, (" + std::to_string(featureTransformer.TotalInputDimensions) + ", "
|
||||||
+ std::to_string(network[0].TransformedFeatureDimensions) + ", "
|
+ std::to_string(network[0].TransformedFeatureDimensions) + ", "
|
||||||
+ std::to_string(network[0].FC_0_OUTPUTS) + ", " + std::to_string(network[0].FC_1_OUTPUTS)
|
+ std::to_string(network[0].FC_0_OUTPUTS) + ", " + std::to_string(network[0].FC_1_OUTPUTS)
|
||||||
+ ", 1))");
|
+ ", 1))");
|
||||||
|
|||||||
+470
-119
@@ -27,7 +27,9 @@
|
|||||||
#include "../misc.h"
|
#include "../misc.h"
|
||||||
#include "../position.h"
|
#include "../position.h"
|
||||||
#include "../types.h"
|
#include "../types.h"
|
||||||
|
#include "features/half_ka_v2_hm.h"
|
||||||
#include "nnue_architecture.h"
|
#include "nnue_architecture.h"
|
||||||
|
#include "nnue_common.h"
|
||||||
#include "nnue_feature_transformer.h" // IWYU pragma: keep
|
#include "nnue_feature_transformer.h" // IWYU pragma: keep
|
||||||
#include "simd.h"
|
#include "simd.h"
|
||||||
|
|
||||||
@@ -40,43 +42,90 @@ namespace {
|
|||||||
template<Color Perspective, IndexType TransformedFeatureDimensions>
|
template<Color Perspective, IndexType TransformedFeatureDimensions>
|
||||||
void double_inc_update(const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
void double_inc_update(const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
||||||
const Square ksq,
|
const Square ksq,
|
||||||
AccumulatorState& middle_state,
|
AccumulatorState<PSQFeatureSet>& middle_state,
|
||||||
AccumulatorState& target_state,
|
AccumulatorState<PSQFeatureSet>& target_state,
|
||||||
const AccumulatorState& computed);
|
const AccumulatorState<PSQFeatureSet>& computed);
|
||||||
|
|
||||||
template<Color Perspective, bool Forward, IndexType TransformedFeatureDimensions>
|
template<Color Perspective, IndexType TransformedFeatureDimensions>
|
||||||
|
void double_inc_update(const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
||||||
|
const Square ksq,
|
||||||
|
AccumulatorState<ThreatFeatureSet>& middle_state,
|
||||||
|
AccumulatorState<ThreatFeatureSet>& target_state,
|
||||||
|
const AccumulatorState<ThreatFeatureSet>& computed,
|
||||||
|
const DirtyPiece& dp2);
|
||||||
|
|
||||||
|
template<Color Perspective,
|
||||||
|
bool Forward,
|
||||||
|
typename FeatureSet,
|
||||||
|
IndexType TransformedFeatureDimensions>
|
||||||
void update_accumulator_incremental(
|
void update_accumulator_incremental(
|
||||||
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
||||||
const Square ksq,
|
const Square ksq,
|
||||||
AccumulatorState& target_state,
|
AccumulatorState<FeatureSet>& target_state,
|
||||||
const AccumulatorState& computed);
|
const AccumulatorState<FeatureSet>& computed);
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, IndexType Dimensions>
|
||||||
void update_accumulator_refresh_cache(const FeatureTransformer<Dimensions>& featureTransformer,
|
void update_accumulator_refresh_cache(const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
const Position& pos,
|
const Position& pos,
|
||||||
AccumulatorState& accumulatorState,
|
AccumulatorState<PSQFeatureSet>& accumulatorState,
|
||||||
AccumulatorCaches::Cache<Dimensions>& cache);
|
AccumulatorCaches::Cache<Dimensions>& cache);
|
||||||
|
|
||||||
|
template<Color Perspective, IndexType Dimensions>
|
||||||
|
void update_threats_accumulator_full(const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
|
const Position& pos,
|
||||||
|
AccumulatorState<ThreatFeatureSet>& accumulatorState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccumulatorState::reset(const DirtyPiece& dp) noexcept {
|
template<typename T>
|
||||||
dirtyPiece = dp;
|
const AccumulatorState<T>& AccumulatorStack::latest() const noexcept {
|
||||||
accumulatorBig.computed.fill(false);
|
return accumulators<T>()[size - 1];
|
||||||
accumulatorSmall.computed.fill(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AccumulatorState& AccumulatorStack::latest() const noexcept { return accumulators[size - 1]; }
|
// Explicit template instantiations
|
||||||
|
template const AccumulatorState<PSQFeatureSet>& AccumulatorStack::latest() const noexcept;
|
||||||
|
template const AccumulatorState<ThreatFeatureSet>& AccumulatorStack::latest() const noexcept;
|
||||||
|
|
||||||
AccumulatorState& AccumulatorStack::mut_latest() noexcept { return accumulators[size - 1]; }
|
template<typename T>
|
||||||
|
AccumulatorState<T>& AccumulatorStack::mut_latest() noexcept {
|
||||||
|
return mut_accumulators<T>()[size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const std::array<AccumulatorState<T>, AccumulatorStack::MaxSize>&
|
||||||
|
AccumulatorStack::accumulators() const noexcept {
|
||||||
|
static_assert(std::is_same_v<T, PSQFeatureSet> || std::is_same_v<T, ThreatFeatureSet>,
|
||||||
|
"Invalid Feature Set Type");
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, PSQFeatureSet>)
|
||||||
|
return psq_accumulators;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, ThreatFeatureSet>)
|
||||||
|
return threat_accumulators;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::array<AccumulatorState<T>, AccumulatorStack::MaxSize>&
|
||||||
|
AccumulatorStack::mut_accumulators() noexcept {
|
||||||
|
static_assert(std::is_same_v<T, PSQFeatureSet> || std::is_same_v<T, ThreatFeatureSet>,
|
||||||
|
"Invalid Feature Set Type");
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, PSQFeatureSet>)
|
||||||
|
return psq_accumulators;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, ThreatFeatureSet>)
|
||||||
|
return threat_accumulators;
|
||||||
|
}
|
||||||
|
|
||||||
void AccumulatorStack::reset() noexcept {
|
void AccumulatorStack::reset() noexcept {
|
||||||
accumulators[0].reset({});
|
psq_accumulators[0].reset({});
|
||||||
|
threat_accumulators[0].reset({});
|
||||||
size = 1;
|
size = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccumulatorStack::push(const DirtyPiece& dirtyPiece) noexcept {
|
void AccumulatorStack::push(const DirtyBoardData& dirtyBoardData) noexcept {
|
||||||
assert(size < accumulators.size());
|
assert(size < MaxSize);
|
||||||
accumulators[size].reset(dirtyPiece);
|
psq_accumulators[size].reset(dirtyBoardData.dp);
|
||||||
|
threat_accumulators[size].reset(dirtyBoardData.dts);
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,53 +138,71 @@ template<IndexType Dimensions>
|
|||||||
void AccumulatorStack::evaluate(const Position& pos,
|
void AccumulatorStack::evaluate(const Position& pos,
|
||||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
AccumulatorCaches::Cache<Dimensions>& cache) noexcept {
|
AccumulatorCaches::Cache<Dimensions>& cache) noexcept {
|
||||||
|
constexpr bool UseThreats = (Dimensions == TransformedFeatureDimensionsBig);
|
||||||
|
|
||||||
evaluate_side<WHITE>(pos, featureTransformer, cache);
|
evaluate_side<WHITE, PSQFeatureSet>(pos, featureTransformer, cache);
|
||||||
evaluate_side<BLACK>(pos, featureTransformer, cache);
|
|
||||||
|
if (UseThreats)
|
||||||
|
evaluate_side<WHITE, ThreatFeatureSet>(pos, featureTransformer, cache);
|
||||||
|
|
||||||
|
evaluate_side<BLACK, PSQFeatureSet>(pos, featureTransformer, cache);
|
||||||
|
|
||||||
|
if (UseThreats)
|
||||||
|
evaluate_side<BLACK, ThreatFeatureSet>(pos, featureTransformer, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, typename FeatureSet, IndexType Dimensions>
|
||||||
void AccumulatorStack::evaluate_side(const Position& pos,
|
void AccumulatorStack::evaluate_side(const Position& pos,
|
||||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
AccumulatorCaches::Cache<Dimensions>& cache) noexcept {
|
AccumulatorCaches::Cache<Dimensions>& cache) noexcept {
|
||||||
|
|
||||||
const auto last_usable_accum = find_last_usable_accumulator<Perspective, Dimensions>();
|
const auto last_usable_accum =
|
||||||
|
find_last_usable_accumulator<Perspective, FeatureSet, Dimensions>();
|
||||||
|
|
||||||
if ((accumulators[last_usable_accum].template acc<Dimensions>()).computed[Perspective])
|
if ((accumulators<FeatureSet>()[last_usable_accum].template acc<Dimensions>())
|
||||||
forward_update_incremental<Perspective>(pos, featureTransformer, last_usable_accum);
|
.computed[Perspective])
|
||||||
|
forward_update_incremental<Perspective, FeatureSet>(pos, featureTransformer,
|
||||||
|
last_usable_accum);
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
update_accumulator_refresh_cache<Perspective>(featureTransformer, pos, mut_latest(), cache);
|
if constexpr (std::is_same_v<FeatureSet, PSQFeatureSet>)
|
||||||
backward_update_incremental<Perspective>(pos, featureTransformer, last_usable_accum);
|
update_accumulator_refresh_cache<Perspective>(featureTransformer, pos,
|
||||||
|
mut_latest<PSQFeatureSet>(), cache);
|
||||||
|
else
|
||||||
|
update_threats_accumulator_full<Perspective>(featureTransformer, pos,
|
||||||
|
mut_latest<ThreatFeatureSet>());
|
||||||
|
|
||||||
|
backward_update_incremental<Perspective, FeatureSet>(pos, featureTransformer,
|
||||||
|
last_usable_accum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the earliest usable accumulator, this can either be a computed accumulator or the accumulator
|
// Find the earliest usable accumulator, this can either be a computed accumulator or the accumulator
|
||||||
// state just before a change that requires full refresh.
|
// state just before a change that requires full refresh.
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, typename FeatureSet, IndexType Dimensions>
|
||||||
std::size_t AccumulatorStack::find_last_usable_accumulator() const noexcept {
|
std::size_t AccumulatorStack::find_last_usable_accumulator() const noexcept {
|
||||||
|
|
||||||
for (std::size_t curr_idx = size - 1; curr_idx > 0; curr_idx--)
|
for (std::size_t curr_idx = size - 1; curr_idx > 0; curr_idx--)
|
||||||
{
|
{
|
||||||
if ((accumulators[curr_idx].template acc<Dimensions>()).computed[Perspective])
|
if ((accumulators<FeatureSet>()[curr_idx].template acc<Dimensions>()).computed[Perspective])
|
||||||
return curr_idx;
|
return curr_idx;
|
||||||
|
|
||||||
if (FeatureSet::requires_refresh(accumulators[curr_idx].dirtyPiece, Perspective))
|
if (FeatureSet::requires_refresh(accumulators<FeatureSet>()[curr_idx].diff, Perspective))
|
||||||
return curr_idx;
|
return curr_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, typename FeatureSet, IndexType Dimensions>
|
||||||
void AccumulatorStack::forward_update_incremental(
|
void AccumulatorStack::forward_update_incremental(
|
||||||
const Position& pos,
|
const Position& pos,
|
||||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
const std::size_t begin) noexcept {
|
const std::size_t begin) noexcept {
|
||||||
|
|
||||||
assert(begin < accumulators.size());
|
assert(begin < accumulators<FeatureSet>().size());
|
||||||
assert((accumulators[begin].acc<Dimensions>()).computed[Perspective]);
|
assert((accumulators<FeatureSet>()[begin].template acc<Dimensions>()).computed[Perspective]);
|
||||||
|
|
||||||
const Square ksq = pos.square<KING>(Perspective);
|
const Square ksq = pos.square<KING>(Perspective);
|
||||||
|
|
||||||
@@ -143,45 +210,65 @@ void AccumulatorStack::forward_update_incremental(
|
|||||||
{
|
{
|
||||||
if (next + 1 < size)
|
if (next + 1 < size)
|
||||||
{
|
{
|
||||||
DirtyPiece& dp1 = accumulators[next].dirtyPiece;
|
DirtyPiece& dp1 = mut_accumulators<PSQFeatureSet>()[next].diff;
|
||||||
DirtyPiece& dp2 = accumulators[next + 1].dirtyPiece;
|
DirtyPiece& dp2 = mut_accumulators<PSQFeatureSet>()[next + 1].diff;
|
||||||
|
|
||||||
if (dp1.to != SQ_NONE && dp1.to == dp2.remove_sq)
|
auto& accumulators = mut_accumulators<FeatureSet>();
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<FeatureSet, ThreatFeatureSet>)
|
||||||
{
|
{
|
||||||
const Square captureSq = dp1.to;
|
if (dp2.remove_sq != SQ_NONE
|
||||||
dp1.to = dp2.remove_sq = SQ_NONE;
|
&& (accumulators[next].diff.threateningSqs & square_bb(dp2.remove_sq)))
|
||||||
double_inc_update<Perspective>(featureTransformer, ksq, accumulators[next],
|
{
|
||||||
accumulators[next + 1], accumulators[next - 1]);
|
double_inc_update<Perspective>(featureTransformer, ksq, accumulators[next],
|
||||||
dp1.to = dp2.remove_sq = captureSq;
|
accumulators[next + 1], accumulators[next - 1],
|
||||||
|
dp2);
|
||||||
|
next++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
next++;
|
if constexpr (std::is_same_v<FeatureSet, PSQFeatureSet>)
|
||||||
continue;
|
{
|
||||||
|
if (dp1.to != SQ_NONE && dp1.to == dp2.remove_sq)
|
||||||
|
{
|
||||||
|
const Square captureSq = dp1.to;
|
||||||
|
dp1.to = dp2.remove_sq = SQ_NONE;
|
||||||
|
double_inc_update<Perspective>(featureTransformer, ksq, accumulators[next],
|
||||||
|
accumulators[next + 1], accumulators[next - 1]);
|
||||||
|
dp1.to = dp2.remove_sq = captureSq;
|
||||||
|
next++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update_accumulator_incremental<Perspective, true>(
|
|
||||||
featureTransformer, ksq, accumulators[next], accumulators[next - 1]);
|
update_accumulator_incremental<Perspective, true>(featureTransformer, ksq,
|
||||||
|
mut_accumulators<FeatureSet>()[next],
|
||||||
|
accumulators<FeatureSet>()[next - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert((latest().acc<Dimensions>()).computed[Perspective]);
|
assert((latest<PSQFeatureSet>().acc<Dimensions>()).computed[Perspective]);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, typename FeatureSet, IndexType Dimensions>
|
||||||
void AccumulatorStack::backward_update_incremental(
|
void AccumulatorStack::backward_update_incremental(
|
||||||
const Position& pos,
|
const Position& pos,
|
||||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
const std::size_t end) noexcept {
|
const std::size_t end) noexcept {
|
||||||
|
|
||||||
assert(end < accumulators.size());
|
assert(end < accumulators<FeatureSet>().size());
|
||||||
assert(end < size);
|
assert(end < size);
|
||||||
assert((latest().acc<Dimensions>()).computed[Perspective]);
|
assert((latest<FeatureSet>().template acc<Dimensions>()).computed[Perspective]);
|
||||||
|
|
||||||
const Square ksq = pos.square<KING>(Perspective);
|
const Square ksq = pos.square<KING>(Perspective);
|
||||||
|
|
||||||
for (std::int64_t next = std::int64_t(size) - 2; next >= std::int64_t(end); next--)
|
for (std::int64_t next = std::int64_t(size) - 2; next >= std::int64_t(end); next--)
|
||||||
update_accumulator_incremental<Perspective, false>(
|
update_accumulator_incremental<Perspective, false>(featureTransformer, ksq,
|
||||||
featureTransformer, ksq, accumulators[next], accumulators[next + 1]);
|
mut_accumulators<FeatureSet>()[next],
|
||||||
|
accumulators<FeatureSet>()[next + 1]);
|
||||||
|
|
||||||
assert((accumulators[end].acc<Dimensions>()).computed[Perspective]);
|
assert((accumulators<FeatureSet>()[end].template acc<Dimensions>()).computed[Perspective]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiations
|
// Explicit template instantiations
|
||||||
@@ -214,15 +301,15 @@ void fused_row_reduce(const ElementType* in, ElementType* out, const Ts* const..
|
|||||||
vecIn[i], reinterpret_cast<const typename VectorWrapper::type*>(rows)[i]...);
|
vecIn[i], reinterpret_cast<const typename VectorWrapper::type*>(rows)[i]...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<typename FeatureSet, Color Perspective, IndexType Dimensions>
|
||||||
struct AccumulatorUpdateContext {
|
struct AccumulatorUpdateContext {
|
||||||
const FeatureTransformer<Dimensions>& featureTransformer;
|
const FeatureTransformer<Dimensions>& featureTransformer;
|
||||||
const AccumulatorState& from;
|
const AccumulatorState<FeatureSet>& from;
|
||||||
AccumulatorState& to;
|
AccumulatorState<FeatureSet>& to;
|
||||||
|
|
||||||
AccumulatorUpdateContext(const FeatureTransformer<Dimensions>& ft,
|
AccumulatorUpdateContext(const FeatureTransformer<Dimensions>& ft,
|
||||||
const AccumulatorState& accF,
|
const AccumulatorState<FeatureSet>& accF,
|
||||||
AccumulatorState& accT) noexcept :
|
AccumulatorState<FeatureSet>& accT) noexcept :
|
||||||
featureTransformer{ft},
|
featureTransformer{ft},
|
||||||
from{accF},
|
from{accF},
|
||||||
to{accT} {}
|
to{accT} {}
|
||||||
@@ -240,40 +327,169 @@ struct AccumulatorUpdateContext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fused_row_reduce<Vec16Wrapper, Dimensions, ops...>(
|
fused_row_reduce<Vec16Wrapper, Dimensions, ops...>(
|
||||||
(from.acc<Dimensions>()).accumulation[Perspective],
|
(from.template acc<Dimensions>()).accumulation[Perspective],
|
||||||
(to.acc<Dimensions>()).accumulation[Perspective], to_weight_vector(indices)...);
|
(to.template acc<Dimensions>()).accumulation[Perspective], to_weight_vector(indices)...);
|
||||||
|
|
||||||
fused_row_reduce<Vec32Wrapper, PSQTBuckets, ops...>(
|
fused_row_reduce<Vec32Wrapper, PSQTBuckets, ops...>(
|
||||||
(from.acc<Dimensions>()).psqtAccumulation[Perspective],
|
(from.template acc<Dimensions>()).psqtAccumulation[Perspective],
|
||||||
(to.acc<Dimensions>()).psqtAccumulation[Perspective], to_psqt_weight_vector(indices)...);
|
(to.template acc<Dimensions>()).psqtAccumulation[Perspective],
|
||||||
|
to_psqt_weight_vector(indices)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(typename FeatureSet::IndexList added, typename FeatureSet::IndexList removed) {
|
||||||
|
const auto fromAcc = from.template acc<Dimensions>().accumulation[Perspective];
|
||||||
|
const auto toAcc = to.template acc<Dimensions>().accumulation[Perspective];
|
||||||
|
|
||||||
|
const auto fromPsqtAcc = from.template acc<Dimensions>().psqtAccumulation[Perspective];
|
||||||
|
const auto toPsqtAcc = to.template acc<Dimensions>().psqtAccumulation[Perspective];
|
||||||
|
|
||||||
|
#ifdef VECTOR
|
||||||
|
using Tiling = SIMDTiling<Dimensions, Dimensions, PSQTBuckets>;
|
||||||
|
vec_t acc[Tiling::NumRegs];
|
||||||
|
psqt_vec_t psqt[Tiling::NumPsqtRegs];
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < Dimensions / Tiling::TileHeight; ++j)
|
||||||
|
{
|
||||||
|
auto* fromTile = reinterpret_cast<const vec_t*>(&fromAcc[j * Tiling::TileHeight]);
|
||||||
|
auto* toTile = reinterpret_cast<vec_t*>(&toAcc[j * Tiling::TileHeight]);
|
||||||
|
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
|
||||||
|
acc[k] = fromTile[k];
|
||||||
|
|
||||||
|
for (IndexType i = 0; i < removed.size(); ++i)
|
||||||
|
{
|
||||||
|
IndexType index = removed[i];
|
||||||
|
const IndexType offset = Dimensions * index + j * Tiling::TileHeight;
|
||||||
|
auto* column =
|
||||||
|
reinterpret_cast<const vec_i8_t*>(&featureTransformer.threatWeights[offset]);
|
||||||
|
|
||||||
|
#ifdef USE_NEON
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; k += 2)
|
||||||
|
{
|
||||||
|
acc[k] = vec_sub_16(acc[k], vmovl_s8(vget_low_s8(column[k / 2])));
|
||||||
|
acc[k + 1] = vec_sub_16(acc[k + 1], vmovl_high_s8(column[k / 2]));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
|
||||||
|
acc[k] = vec_sub_16(acc[k], vec_convert_8_16(column[k]));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IndexType i = 0; i < added.size(); ++i)
|
||||||
|
{
|
||||||
|
IndexType index = added[i];
|
||||||
|
const IndexType offset = Dimensions * index + j * Tiling::TileHeight;
|
||||||
|
auto* column =
|
||||||
|
reinterpret_cast<const vec_i8_t*>(&featureTransformer.threatWeights[offset]);
|
||||||
|
|
||||||
|
#ifdef USE_NEON
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; k += 2)
|
||||||
|
{
|
||||||
|
acc[k] = vec_add_16(acc[k], vmovl_s8(vget_low_s8(column[k / 2])));
|
||||||
|
acc[k + 1] = vec_add_16(acc[k + 1], vmovl_high_s8(column[k / 2]));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
|
||||||
|
acc[k] = vec_add_16(acc[k], vec_convert_8_16(column[k]));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; k++)
|
||||||
|
vec_store(&toTile[k], acc[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < PSQTBuckets / Tiling::PsqtTileHeight; ++j)
|
||||||
|
{
|
||||||
|
auto* fromTilePsqt =
|
||||||
|
reinterpret_cast<const psqt_vec_t*>(&fromPsqtAcc[j * Tiling::PsqtTileHeight]);
|
||||||
|
auto* toTilePsqt =
|
||||||
|
reinterpret_cast<psqt_vec_t*>(&toPsqtAcc[j * Tiling::PsqtTileHeight]);
|
||||||
|
|
||||||
|
for (IndexType k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||||
|
psqt[k] = fromTilePsqt[k];
|
||||||
|
|
||||||
|
for (IndexType i = 0; i < removed.size(); ++i)
|
||||||
|
{
|
||||||
|
IndexType index = removed[i];
|
||||||
|
const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight;
|
||||||
|
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(
|
||||||
|
&featureTransformer.threatPsqtWeights[offset]);
|
||||||
|
|
||||||
|
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||||
|
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IndexType i = 0; i < added.size(); ++i)
|
||||||
|
{
|
||||||
|
IndexType index = added[i];
|
||||||
|
const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight;
|
||||||
|
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(
|
||||||
|
&featureTransformer.threatPsqtWeights[offset]);
|
||||||
|
|
||||||
|
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||||
|
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IndexType k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||||
|
vec_store_psqt(&toTilePsqt[k], psqt[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
std::copy_n(fromAcc, Dimensions, toAcc);
|
||||||
|
std::copy_n(fromPsqtAcc, PSQTBuckets, toPsqtAcc);
|
||||||
|
|
||||||
|
for (const auto index : removed)
|
||||||
|
{
|
||||||
|
const IndexType offset = Dimensions * index;
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < Dimensions; ++j)
|
||||||
|
toAcc[j] -= featureTransformer.threatWeights[offset + j];
|
||||||
|
|
||||||
|
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||||
|
toPsqtAcc[k] -= featureTransformer.threatPsqtWeights[index * PSQTBuckets + k];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto index : added)
|
||||||
|
{
|
||||||
|
const IndexType offset = Dimensions * index;
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < Dimensions; ++j)
|
||||||
|
toAcc[j] += featureTransformer.threatWeights[offset + j];
|
||||||
|
|
||||||
|
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||||
|
toPsqtAcc[k] += featureTransformer.threatPsqtWeights[index * PSQTBuckets + k];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, typename FeatureSet, IndexType Dimensions>
|
||||||
auto make_accumulator_update_context(const FeatureTransformer<Dimensions>& featureTransformer,
|
auto make_accumulator_update_context(const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
const AccumulatorState& accumulatorFrom,
|
const AccumulatorState<FeatureSet>& accumulatorFrom,
|
||||||
AccumulatorState& accumulatorTo) noexcept {
|
AccumulatorState<FeatureSet>& accumulatorTo) noexcept {
|
||||||
return AccumulatorUpdateContext<Perspective, Dimensions>{featureTransformer, accumulatorFrom,
|
return AccumulatorUpdateContext<FeatureSet, Perspective, Dimensions>{
|
||||||
accumulatorTo};
|
featureTransformer, accumulatorFrom, accumulatorTo};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Color Perspective, IndexType TransformedFeatureDimensions>
|
template<Color Perspective, IndexType TransformedFeatureDimensions>
|
||||||
void double_inc_update(const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
void double_inc_update(const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
||||||
const Square ksq,
|
const Square ksq,
|
||||||
AccumulatorState& middle_state,
|
AccumulatorState<PSQFeatureSet>& middle_state,
|
||||||
AccumulatorState& target_state,
|
AccumulatorState<PSQFeatureSet>& target_state,
|
||||||
const AccumulatorState& computed) {
|
const AccumulatorState<PSQFeatureSet>& computed) {
|
||||||
|
|
||||||
assert(computed.acc<TransformedFeatureDimensions>().computed[Perspective]);
|
assert(computed.acc<TransformedFeatureDimensions>().computed[Perspective]);
|
||||||
assert(!middle_state.acc<TransformedFeatureDimensions>().computed[Perspective]);
|
assert(!middle_state.acc<TransformedFeatureDimensions>().computed[Perspective]);
|
||||||
assert(!target_state.acc<TransformedFeatureDimensions>().computed[Perspective]);
|
assert(!target_state.acc<TransformedFeatureDimensions>().computed[Perspective]);
|
||||||
|
|
||||||
FeatureSet::IndexList removed, added;
|
PSQFeatureSet::IndexList removed, added;
|
||||||
FeatureSet::append_changed_indices<Perspective>(ksq, middle_state.dirtyPiece, removed, added);
|
PSQFeatureSet::append_changed_indices<Perspective>(ksq, middle_state.diff, removed, added);
|
||||||
// you can't capture a piece that was just involved in castling since the rook ends up
|
// you can't capture a piece that was just involved in castling since the rook ends up
|
||||||
// in a square that the king passed
|
// in a square that the king passed
|
||||||
assert(added.size() < 2);
|
assert(added.size() < 2);
|
||||||
FeatureSet::append_changed_indices<Perspective>(ksq, target_state.dirtyPiece, removed, added);
|
PSQFeatureSet::append_changed_indices<Perspective>(ksq, target_state.diff, removed, added);
|
||||||
|
|
||||||
assert(added.size() == 1);
|
assert(added.size() == 1);
|
||||||
assert(removed.size() == 2 || removed.size() == 3);
|
assert(removed.size() == 2 || removed.size() == 3);
|
||||||
@@ -300,15 +516,48 @@ void double_inc_update(const FeatureTransformer<TransformedFeatureDimensions>& f
|
|||||||
target_state.acc<TransformedFeatureDimensions>().computed[Perspective] = true;
|
target_state.acc<TransformedFeatureDimensions>().computed[Perspective] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Color Perspective, bool Forward, IndexType TransformedFeatureDimensions>
|
template<Color Perspective, IndexType TransformedFeatureDimensions>
|
||||||
|
void double_inc_update(const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
||||||
|
const Square ksq,
|
||||||
|
AccumulatorState<ThreatFeatureSet>& middle_state,
|
||||||
|
AccumulatorState<ThreatFeatureSet>& target_state,
|
||||||
|
const AccumulatorState<ThreatFeatureSet>& computed,
|
||||||
|
const DirtyPiece& dp2) {
|
||||||
|
|
||||||
|
assert(computed.acc<TransformedFeatureDimensions>().computed[Perspective]);
|
||||||
|
assert(!middle_state.acc<TransformedFeatureDimensions>().computed[Perspective]);
|
||||||
|
assert(!target_state.acc<TransformedFeatureDimensions>().computed[Perspective]);
|
||||||
|
|
||||||
|
ThreatFeatureSet::FusedUpdateData fusedData;
|
||||||
|
|
||||||
|
fusedData.dp2removed = dp2.remove_sq;
|
||||||
|
|
||||||
|
ThreatFeatureSet::IndexList removed, added;
|
||||||
|
ThreatFeatureSet::append_changed_indices<Perspective>(ksq, middle_state.diff, removed, added,
|
||||||
|
&fusedData, true);
|
||||||
|
ThreatFeatureSet::append_changed_indices<Perspective>(ksq, target_state.diff, removed, added,
|
||||||
|
&fusedData, false);
|
||||||
|
|
||||||
|
auto updateContext =
|
||||||
|
make_accumulator_update_context<Perspective>(featureTransformer, computed, target_state);
|
||||||
|
|
||||||
|
updateContext.apply(added, removed);
|
||||||
|
|
||||||
|
target_state.acc<TransformedFeatureDimensions>().computed[Perspective] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<Color Perspective,
|
||||||
|
bool Forward,
|
||||||
|
typename FeatureSet,
|
||||||
|
IndexType TransformedFeatureDimensions>
|
||||||
void update_accumulator_incremental(
|
void update_accumulator_incremental(
|
||||||
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
||||||
const Square ksq,
|
const Square ksq,
|
||||||
AccumulatorState& target_state,
|
AccumulatorState<FeatureSet>& target_state,
|
||||||
const AccumulatorState& computed) {
|
const AccumulatorState<FeatureSet>& computed) {
|
||||||
|
|
||||||
assert((computed.acc<TransformedFeatureDimensions>()).computed[Perspective]);
|
assert((computed.template acc<TransformedFeatureDimensions>()).computed[Perspective]);
|
||||||
assert(!(target_state.acc<TransformedFeatureDimensions>()).computed[Perspective]);
|
assert(!(target_state.template acc<TransformedFeatureDimensions>()).computed[Perspective]);
|
||||||
|
|
||||||
// The size must be enough to contain the largest possible update.
|
// The size must be enough to contain the largest possible update.
|
||||||
// That might depend on the feature set and generally relies on the
|
// That might depend on the feature set and generally relies on the
|
||||||
@@ -316,50 +565,56 @@ void update_accumulator_incremental(
|
|||||||
// updates with more added/removed features than MaxActiveDimensions.
|
// updates with more added/removed features than MaxActiveDimensions.
|
||||||
// In this case, the maximum size of both feature addition and removal
|
// In this case, the maximum size of both feature addition and removal
|
||||||
// is 2, since we are incrementally updating one move at a time.
|
// is 2, since we are incrementally updating one move at a time.
|
||||||
FeatureSet::IndexList removed, added;
|
typename FeatureSet::IndexList removed, added;
|
||||||
if constexpr (Forward)
|
if constexpr (Forward)
|
||||||
FeatureSet::append_changed_indices<Perspective>(ksq, target_state.dirtyPiece, removed,
|
FeatureSet::template append_changed_indices<Perspective>(ksq, target_state.diff, removed,
|
||||||
added);
|
added);
|
||||||
else
|
else
|
||||||
FeatureSet::append_changed_indices<Perspective>(ksq, computed.dirtyPiece, added, removed);
|
FeatureSet::template append_changed_indices<Perspective>(ksq, computed.diff, added,
|
||||||
|
removed);
|
||||||
assert(added.size() == 1 || added.size() == 2);
|
|
||||||
assert(removed.size() == 1 || removed.size() == 2);
|
|
||||||
assert((Forward && added.size() <= removed.size())
|
|
||||||
|| (!Forward && added.size() >= removed.size()));
|
|
||||||
|
|
||||||
// Workaround compiler warning for uninitialized variables, replicated on
|
|
||||||
// profile builds on windows with gcc 14.2.0.
|
|
||||||
// TODO remove once unneeded
|
|
||||||
sf_assume(added.size() == 1 || added.size() == 2);
|
|
||||||
sf_assume(removed.size() == 1 || removed.size() == 2);
|
|
||||||
|
|
||||||
auto updateContext =
|
auto updateContext =
|
||||||
make_accumulator_update_context<Perspective>(featureTransformer, computed, target_state);
|
make_accumulator_update_context<Perspective>(featureTransformer, computed, target_state);
|
||||||
|
|
||||||
if ((Forward && removed.size() == 1) || (!Forward && added.size() == 1))
|
if constexpr (std::is_same_v<FeatureSet, ThreatFeatureSet>)
|
||||||
{
|
updateContext.apply(added, removed);
|
||||||
assert(added.size() == 1 && removed.size() == 1);
|
|
||||||
updateContext.template apply<Add, Sub>(added[0], removed[0]);
|
|
||||||
}
|
|
||||||
else if (Forward && added.size() == 1)
|
|
||||||
{
|
|
||||||
assert(removed.size() == 2);
|
|
||||||
updateContext.template apply<Add, Sub, Sub>(added[0], removed[0], removed[1]);
|
|
||||||
}
|
|
||||||
else if (!Forward && removed.size() == 1)
|
|
||||||
{
|
|
||||||
assert(added.size() == 2);
|
|
||||||
updateContext.template apply<Add, Add, Sub>(added[0], added[1], removed[0]);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert(added.size() == 2 && removed.size() == 2);
|
assert(added.size() == 1 || added.size() == 2);
|
||||||
updateContext.template apply<Add, Add, Sub, Sub>(added[0], added[1], removed[0],
|
assert(removed.size() == 1 || removed.size() == 2);
|
||||||
removed[1]);
|
assert((Forward && added.size() <= removed.size())
|
||||||
|
|| (!Forward && added.size() >= removed.size()));
|
||||||
|
|
||||||
|
// Workaround compiler warning for uninitialized variables, replicated
|
||||||
|
// on profile builds on windows with gcc 14.2.0.
|
||||||
|
// TODO remove once unneeded
|
||||||
|
sf_assume(added.size() == 1 || added.size() == 2);
|
||||||
|
sf_assume(removed.size() == 1 || removed.size() == 2);
|
||||||
|
|
||||||
|
if ((Forward && removed.size() == 1) || (!Forward && added.size() == 1))
|
||||||
|
{
|
||||||
|
assert(added.size() == 1 && removed.size() == 1);
|
||||||
|
updateContext.template apply<Add, Sub>(added[0], removed[0]);
|
||||||
|
}
|
||||||
|
else if (Forward && added.size() == 1)
|
||||||
|
{
|
||||||
|
assert(removed.size() == 2);
|
||||||
|
updateContext.template apply<Add, Sub, Sub>(added[0], removed[0], removed[1]);
|
||||||
|
}
|
||||||
|
else if (!Forward && removed.size() == 1)
|
||||||
|
{
|
||||||
|
assert(added.size() == 2);
|
||||||
|
updateContext.template apply<Add, Add, Sub>(added[0], added[1], removed[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(added.size() == 2 && removed.size() == 2);
|
||||||
|
updateContext.template apply<Add, Add, Sub, Sub>(added[0], added[1], removed[0],
|
||||||
|
removed[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(target_state.acc<TransformedFeatureDimensions>()).computed[Perspective] = true;
|
(target_state.template acc<TransformedFeatureDimensions>()).computed[Perspective] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitboard get_changed_pieces(const Piece old[SQUARE_NB], const Piece new_[SQUARE_NB]) {
|
Bitboard get_changed_pieces(const Piece old[SQUARE_NB], const Piece new_[SQUARE_NB]) {
|
||||||
@@ -388,32 +643,32 @@ Bitboard get_changed_pieces(const Piece old[SQUARE_NB], const Piece new_[SQUARE_
|
|||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, IndexType Dimensions>
|
||||||
void update_accumulator_refresh_cache(const FeatureTransformer<Dimensions>& featureTransformer,
|
void update_accumulator_refresh_cache(const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
const Position& pos,
|
const Position& pos,
|
||||||
AccumulatorState& accumulatorState,
|
AccumulatorState<PSQFeatureSet>& accumulatorState,
|
||||||
AccumulatorCaches::Cache<Dimensions>& cache) {
|
AccumulatorCaches::Cache<Dimensions>& cache) {
|
||||||
|
|
||||||
using Tiling [[maybe_unused]] = SIMDTiling<Dimensions, Dimensions, PSQTBuckets>;
|
using Tiling [[maybe_unused]] = SIMDTiling<Dimensions, Dimensions, PSQTBuckets>;
|
||||||
|
|
||||||
const Square ksq = pos.square<KING>(Perspective);
|
const Square ksq = pos.square<KING>(Perspective);
|
||||||
auto& entry = cache[ksq][Perspective];
|
auto& entry = cache[ksq][Perspective];
|
||||||
FeatureSet::IndexList removed, added;
|
PSQFeatureSet::IndexList removed, added;
|
||||||
|
|
||||||
const Bitboard changed_bb = get_changed_pieces(entry.pieces, pos.piece_array());
|
const Bitboard changed_bb = get_changed_pieces(entry.pieces, pos.piece_array().data());
|
||||||
Bitboard removed_bb = changed_bb & entry.pieceBB;
|
Bitboard removed_bb = changed_bb & entry.pieceBB;
|
||||||
Bitboard added_bb = changed_bb & pos.pieces();
|
Bitboard added_bb = changed_bb & pos.pieces();
|
||||||
|
|
||||||
while (removed_bb)
|
while (removed_bb)
|
||||||
{
|
{
|
||||||
Square sq = pop_lsb(removed_bb);
|
Square sq = pop_lsb(removed_bb);
|
||||||
removed.push_back(FeatureSet::make_index<Perspective>(sq, entry.pieces[sq], ksq));
|
removed.push_back(PSQFeatureSet::make_index<Perspective>(sq, entry.pieces[sq], ksq));
|
||||||
}
|
}
|
||||||
while (added_bb)
|
while (added_bb)
|
||||||
{
|
{
|
||||||
Square sq = pop_lsb(added_bb);
|
Square sq = pop_lsb(added_bb);
|
||||||
added.push_back(FeatureSet::make_index<Perspective>(sq, pos.piece_on(sq), ksq));
|
added.push_back(PSQFeatureSet::make_index<Perspective>(sq, pos.piece_on(sq), ksq));
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.pieceBB = pos.pieces();
|
entry.pieceBB = pos.pieces();
|
||||||
std::copy_n(pos.piece_array(), SQUARE_NB, entry.pieces);
|
std::copy_n(pos.piece_array().begin(), SQUARE_NB, entry.pieces);
|
||||||
|
|
||||||
auto& accumulator = accumulatorState.acc<Dimensions>();
|
auto& accumulator = accumulatorState.acc<Dimensions>();
|
||||||
accumulator.computed[Perspective] = true;
|
accumulator.computed[Perspective] = true;
|
||||||
@@ -530,14 +785,110 @@ void update_accumulator_refresh_cache(const FeatureTransformer<Dimensions>& feat
|
|||||||
// The accumulator of the refresh entry has been updated.
|
// The accumulator of the refresh entry has been updated.
|
||||||
// Now copy its content to the actual accumulator we were refreshing.
|
// Now copy its content to the actual accumulator we were refreshing.
|
||||||
|
|
||||||
std::memcpy(accumulator.accumulation[Perspective], entry.accumulation,
|
std::memcpy(accumulator.accumulation[Perspective], entry.accumulation.data(),
|
||||||
sizeof(BiasType) * Dimensions);
|
sizeof(BiasType) * Dimensions);
|
||||||
|
|
||||||
std::memcpy(accumulator.psqtAccumulation[Perspective], entry.psqtAccumulation,
|
std::memcpy(accumulator.psqtAccumulation[Perspective], entry.psqtAccumulation.data(),
|
||||||
sizeof(int32_t) * PSQTBuckets);
|
sizeof(int32_t) * PSQTBuckets);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<Color Perspective, IndexType Dimensions>
|
||||||
|
void update_threats_accumulator_full(const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
|
const Position& pos,
|
||||||
|
AccumulatorState<ThreatFeatureSet>& accumulatorState) {
|
||||||
|
using Tiling [[maybe_unused]] = SIMDTiling<Dimensions, Dimensions, PSQTBuckets>;
|
||||||
|
|
||||||
|
ThreatFeatureSet::IndexList active;
|
||||||
|
ThreatFeatureSet::append_active_indices<Perspective>(pos, active);
|
||||||
|
|
||||||
|
auto& accumulator = accumulatorState.acc<Dimensions>();
|
||||||
|
accumulator.computed[Perspective] = true;
|
||||||
|
|
||||||
|
#ifdef VECTOR
|
||||||
|
vec_t acc[Tiling::NumRegs];
|
||||||
|
psqt_vec_t psqt[Tiling::NumPsqtRegs];
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < Dimensions / Tiling::TileHeight; ++j)
|
||||||
|
{
|
||||||
|
auto* accTile =
|
||||||
|
reinterpret_cast<vec_t*>(&accumulator.accumulation[Perspective][j * Tiling::TileHeight]);
|
||||||
|
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
|
||||||
|
acc[k] = vec_zero();
|
||||||
|
|
||||||
|
IndexType i = 0;
|
||||||
|
|
||||||
|
for (; i < active.size(); ++i)
|
||||||
|
{
|
||||||
|
IndexType index = active[i];
|
||||||
|
const IndexType offset = Dimensions * index + j * Tiling::TileHeight;
|
||||||
|
auto* column =
|
||||||
|
reinterpret_cast<const vec_i8_t*>(&featureTransformer.threatWeights[offset]);
|
||||||
|
|
||||||
|
#ifdef USE_NEON
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; k += 2)
|
||||||
|
{
|
||||||
|
acc[k] = vec_add_16(acc[k], vmovl_s8(vget_low_s8(column[k / 2])));
|
||||||
|
acc[k + 1] = vec_add_16(acc[k + 1], vmovl_high_s8(column[k / 2]));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
|
||||||
|
acc[k] = vec_add_16(acc[k], vec_convert_8_16(column[k]));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IndexType k = 0; k < Tiling::NumRegs; k++)
|
||||||
|
vec_store(&accTile[k], acc[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < PSQTBuckets / Tiling::PsqtTileHeight; ++j)
|
||||||
|
{
|
||||||
|
auto* accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||||
|
&accumulator.psqtAccumulation[Perspective][j * Tiling::PsqtTileHeight]);
|
||||||
|
|
||||||
|
for (IndexType k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||||
|
psqt[k] = vec_zero_psqt();
|
||||||
|
|
||||||
|
for (IndexType i = 0; i < active.size(); ++i)
|
||||||
|
{
|
||||||
|
IndexType index = active[i];
|
||||||
|
const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight;
|
||||||
|
auto* columnPsqt =
|
||||||
|
reinterpret_cast<const psqt_vec_t*>(&featureTransformer.threatPsqtWeights[offset]);
|
||||||
|
|
||||||
|
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||||
|
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IndexType k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||||
|
vec_store_psqt(&accTilePsqt[k], psqt[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < Dimensions; ++j)
|
||||||
|
accumulator.accumulation[Perspective][j] = 0;
|
||||||
|
|
||||||
|
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||||
|
accumulator.psqtAccumulation[Perspective][k] = 0;
|
||||||
|
|
||||||
|
for (const auto index : active)
|
||||||
|
{
|
||||||
|
const IndexType offset = Dimensions * index;
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < Dimensions; ++j)
|
||||||
|
accumulator.accumulation[Perspective][j] +=
|
||||||
|
featureTransformer.threatWeights[offset + j];
|
||||||
|
|
||||||
|
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||||
|
accumulator.psqtAccumulation[Perspective][k] +=
|
||||||
|
featureTransformer.threatPsqtWeights[index * PSQTBuckets + k];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-18
@@ -68,16 +68,16 @@ struct AccumulatorCaches {
|
|||||||
struct alignas(CacheLineSize) Cache {
|
struct alignas(CacheLineSize) Cache {
|
||||||
|
|
||||||
struct alignas(CacheLineSize) Entry {
|
struct alignas(CacheLineSize) Entry {
|
||||||
BiasType accumulation[Size];
|
std::array<BiasType, Size> accumulation;
|
||||||
PSQTWeightType psqtAccumulation[PSQTBuckets];
|
std::array<PSQTWeightType, PSQTBuckets> psqtAccumulation;
|
||||||
Piece pieces[SQUARE_NB];
|
Piece pieces[SQUARE_NB];
|
||||||
Bitboard pieceBB;
|
Bitboard pieceBB;
|
||||||
|
|
||||||
// To initialize a refresh entry, we set all its bitboards empty,
|
// To initialize a refresh entry, we set all its bitboards empty,
|
||||||
// so we put the biases in the accumulation, without any weights on top
|
// so we put the biases in the accumulation, without any weights on top
|
||||||
void clear(const BiasType* biases) {
|
void clear(const std::array<BiasType, Size>& biases) {
|
||||||
|
|
||||||
std::memcpy(accumulation, biases, sizeof(accumulation));
|
accumulation = biases;
|
||||||
std::memset((uint8_t*) this + offsetof(Entry, psqtAccumulation), 0,
|
std::memset((uint8_t*) this + offsetof(Entry, psqtAccumulation), 0,
|
||||||
sizeof(Entry) - offsetof(Entry, psqtAccumulation));
|
sizeof(Entry) - offsetof(Entry, psqtAccumulation));
|
||||||
}
|
}
|
||||||
@@ -106,10 +106,11 @@ struct AccumulatorCaches {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename FeatureSet>
|
||||||
struct AccumulatorState {
|
struct AccumulatorState {
|
||||||
Accumulator<TransformedFeatureDimensionsBig> accumulatorBig;
|
Accumulator<TransformedFeatureDimensionsBig> accumulatorBig;
|
||||||
Accumulator<TransformedFeatureDimensionsSmall> accumulatorSmall;
|
Accumulator<TransformedFeatureDimensionsSmall> accumulatorSmall;
|
||||||
DirtyPiece dirtyPiece;
|
typename FeatureSet::DiffType diff;
|
||||||
|
|
||||||
template<IndexType Size>
|
template<IndexType Size>
|
||||||
auto& acc() noexcept {
|
auto& acc() noexcept {
|
||||||
@@ -135,16 +136,22 @@ struct AccumulatorState {
|
|||||||
return accumulatorSmall;
|
return accumulatorSmall;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset(const DirtyPiece& dp) noexcept;
|
void reset(const typename FeatureSet::DiffType& dp) noexcept {
|
||||||
|
diff = dp;
|
||||||
|
accumulatorBig.computed.fill(false);
|
||||||
|
accumulatorSmall.computed.fill(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class AccumulatorStack {
|
class AccumulatorStack {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] const AccumulatorState& latest() const noexcept;
|
static constexpr std::size_t MaxSize = MAX_PLY + 1;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] const AccumulatorState<T>& latest() const noexcept;
|
||||||
|
|
||||||
void reset() noexcept;
|
void reset() noexcept;
|
||||||
void push(const DirtyPiece& dirtyPiece) noexcept;
|
void push(const DirtyBoardData& dirtyBoardData) noexcept;
|
||||||
void pop() noexcept;
|
void pop() noexcept;
|
||||||
|
|
||||||
template<IndexType Dimensions>
|
template<IndexType Dimensions>
|
||||||
@@ -153,28 +160,36 @@ class AccumulatorStack {
|
|||||||
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
|
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] AccumulatorState& mut_latest() noexcept;
|
template<typename T>
|
||||||
|
[[nodiscard]] AccumulatorState<T>& mut_latest() noexcept;
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<typename T>
|
||||||
|
[[nodiscard]] const std::array<AccumulatorState<T>, MaxSize>& accumulators() const noexcept;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] std::array<AccumulatorState<T>, MaxSize>& mut_accumulators() noexcept;
|
||||||
|
|
||||||
|
template<Color Perspective, typename FeatureSet, IndexType Dimensions>
|
||||||
void evaluate_side(const Position& pos,
|
void evaluate_side(const Position& pos,
|
||||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
|
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, typename FeatureSet, IndexType Dimensions>
|
||||||
[[nodiscard]] std::size_t find_last_usable_accumulator() const noexcept;
|
[[nodiscard]] std::size_t find_last_usable_accumulator() const noexcept;
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, typename FeatureSet, IndexType Dimensions>
|
||||||
void forward_update_incremental(const Position& pos,
|
void forward_update_incremental(const Position& pos,
|
||||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
const std::size_t begin) noexcept;
|
const std::size_t begin) noexcept;
|
||||||
|
|
||||||
template<Color Perspective, IndexType Dimensions>
|
template<Color Perspective, typename FeatureSet, IndexType Dimensions>
|
||||||
void backward_update_incremental(const Position& pos,
|
void backward_update_incremental(const Position& pos,
|
||||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||||
const std::size_t end) noexcept;
|
const std::size_t end) noexcept;
|
||||||
|
|
||||||
std::array<AccumulatorState, MAX_PLY + 1> accumulators;
|
std::array<AccumulatorState<PSQFeatureSet>, MaxSize> psq_accumulators;
|
||||||
std::size_t size = 1;
|
std::array<AccumulatorState<ThreatFeatureSet>, MaxSize> threat_accumulators;
|
||||||
|
std::size_t size = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Stockfish::Eval::NNUE
|
} // namespace Stockfish::Eval::NNUE
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
|
||||||
#include "features/half_ka_v2_hm.h"
|
#include "features/half_ka_v2_hm.h"
|
||||||
|
#include "features/full_threats.h"
|
||||||
#include "layers/affine_transform.h"
|
#include "layers/affine_transform.h"
|
||||||
#include "layers/affine_transform_sparse_input.h"
|
#include "layers/affine_transform_sparse_input.h"
|
||||||
#include "layers/clipped_relu.h"
|
#include "layers/clipped_relu.h"
|
||||||
@@ -35,10 +36,11 @@
|
|||||||
namespace Stockfish::Eval::NNUE {
|
namespace Stockfish::Eval::NNUE {
|
||||||
|
|
||||||
// Input features used in evaluation function
|
// Input features used in evaluation function
|
||||||
using FeatureSet = Features::HalfKAv2_hm;
|
using ThreatFeatureSet = Features::FullThreats;
|
||||||
|
using PSQFeatureSet = Features::HalfKAv2_hm;
|
||||||
|
|
||||||
// Number of input feature dimensions after conversion
|
// Number of input feature dimensions after conversion
|
||||||
constexpr IndexType TransformedFeatureDimensionsBig = 3072;
|
constexpr IndexType TransformedFeatureDimensionsBig = 1024;
|
||||||
constexpr int L2Big = 15;
|
constexpr int L2Big = 15;
|
||||||
constexpr int L3Big = 32;
|
constexpr int L3Big = 32;
|
||||||
|
|
||||||
|
|||||||
+12
-11
@@ -48,10 +48,11 @@
|
|||||||
|
|
||||||
namespace Stockfish::Eval::NNUE {
|
namespace Stockfish::Eval::NNUE {
|
||||||
|
|
||||||
using BiasType = std::int16_t;
|
using BiasType = std::int16_t;
|
||||||
using WeightType = std::int16_t;
|
using ThreatWeightType = std::int8_t;
|
||||||
using PSQTWeightType = std::int32_t;
|
using WeightType = std::int16_t;
|
||||||
using IndexType = std::uint32_t;
|
using PSQTWeightType = std::int32_t;
|
||||||
|
using IndexType = std::uint32_t;
|
||||||
|
|
||||||
// Version of the evaluation file
|
// Version of the evaluation file
|
||||||
constexpr std::uint32_t Version = 0x7AF32F20u;
|
constexpr std::uint32_t Version = 0x7AF32F20u;
|
||||||
@@ -172,8 +173,8 @@ inline void write_little_endian(std::ostream& stream, const IntType* values, std
|
|||||||
// Read N signed integers from the stream s, putting them in the array out.
|
// Read N signed integers from the stream s, putting them in the array out.
|
||||||
// The stream is assumed to be compressed using the signed LEB128 format.
|
// The stream is assumed to be compressed using the signed LEB128 format.
|
||||||
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
|
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
|
||||||
template<typename IntType>
|
template<typename IntType, std::size_t Count>
|
||||||
inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) {
|
inline void read_leb_128(std::istream& stream, std::array<IntType, Count>& out) {
|
||||||
|
|
||||||
// Check the presence of our LEB128 magic string
|
// Check the presence of our LEB128 magic string
|
||||||
char leb128MagicString[Leb128MagicStringSize];
|
char leb128MagicString[Leb128MagicStringSize];
|
||||||
@@ -188,7 +189,7 @@ inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count)
|
|||||||
auto bytes_left = read_little_endian<std::uint32_t>(stream);
|
auto bytes_left = read_little_endian<std::uint32_t>(stream);
|
||||||
|
|
||||||
std::uint32_t buf_pos = BUF_SIZE;
|
std::uint32_t buf_pos = BUF_SIZE;
|
||||||
for (std::size_t i = 0; i < count; ++i)
|
for (std::size_t i = 0; i < Count; ++i)
|
||||||
{
|
{
|
||||||
IntType result = 0;
|
IntType result = 0;
|
||||||
size_t shift = 0;
|
size_t shift = 0;
|
||||||
@@ -223,8 +224,8 @@ inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count)
|
|||||||
// This takes N integers from array values, compresses them with
|
// This takes N integers from array values, compresses them with
|
||||||
// the LEB128 algorithm and writes the result on the stream s.
|
// the LEB128 algorithm and writes the result on the stream s.
|
||||||
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
|
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
|
||||||
template<typename IntType>
|
template<typename IntType, std::size_t Count>
|
||||||
inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) {
|
inline void write_leb_128(std::ostream& stream, const std::array<IntType, Count>& values) {
|
||||||
|
|
||||||
// Write our LEB128 magic string
|
// Write our LEB128 magic string
|
||||||
stream.write(Leb128MagicString, Leb128MagicStringSize);
|
stream.write(Leb128MagicString, Leb128MagicStringSize);
|
||||||
@@ -232,7 +233,7 @@ inline void write_leb_128(std::ostream& stream, const IntType* values, std::size
|
|||||||
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
|
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
|
||||||
|
|
||||||
std::uint32_t byte_count = 0;
|
std::uint32_t byte_count = 0;
|
||||||
for (std::size_t i = 0; i < count; ++i)
|
for (std::size_t i = 0; i < Count; ++i)
|
||||||
{
|
{
|
||||||
IntType value = values[i];
|
IntType value = values[i];
|
||||||
std::uint8_t byte;
|
std::uint8_t byte;
|
||||||
@@ -264,7 +265,7 @@ inline void write_leb_128(std::ostream& stream, const IntType* values, std::size
|
|||||||
flush();
|
flush();
|
||||||
};
|
};
|
||||||
|
|
||||||
for (std::size_t i = 0; i < count; ++i)
|
for (std::size_t i = 0; i < Count; ++i)
|
||||||
{
|
{
|
||||||
IntType value = values[i];
|
IntType value = values[i];
|
||||||
while (true)
|
while (true)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
#include "../position.h"
|
#include "../position.h"
|
||||||
#include "../types.h"
|
#include "../types.h"
|
||||||
@@ -48,7 +49,7 @@ invert_permutation(const std::array<std::size_t, Len>& order) {
|
|||||||
// Divide a byte region of size TotalSize to chunks of size
|
// Divide a byte region of size TotalSize to chunks of size
|
||||||
// BlockSize, and permute the blocks by a given order
|
// BlockSize, and permute the blocks by a given order
|
||||||
template<std::size_t BlockSize, typename T, std::size_t N, std::size_t OrderSize>
|
template<std::size_t BlockSize, typename T, std::size_t N, std::size_t OrderSize>
|
||||||
void permute(T (&data)[N], const std::array<std::size_t, OrderSize>& order) {
|
void permute(std::array<T, N>& data, const std::array<std::size_t, OrderSize>& order) {
|
||||||
constexpr std::size_t TotalSize = N * sizeof(T);
|
constexpr std::size_t TotalSize = N * sizeof(T);
|
||||||
|
|
||||||
static_assert(TotalSize % (BlockSize * OrderSize) == 0,
|
static_assert(TotalSize % (BlockSize * OrderSize) == 0,
|
||||||
@@ -58,7 +59,7 @@ void permute(T (&data)[N], const std::array<std::size_t, OrderSize>& order) {
|
|||||||
|
|
||||||
std::array<std::byte, ProcessChunkSize> buffer{};
|
std::array<std::byte, ProcessChunkSize> buffer{};
|
||||||
|
|
||||||
std::byte* const bytes = reinterpret_cast<std::byte*>(data);
|
std::byte* const bytes = reinterpret_cast<std::byte*>(data.data());
|
||||||
|
|
||||||
for (std::size_t i = 0; i < TotalSize; i += ProcessChunkSize)
|
for (std::size_t i = 0; i < TotalSize; i += ProcessChunkSize)
|
||||||
{
|
{
|
||||||
@@ -79,7 +80,8 @@ void permute(T (&data)[N], const std::array<std::size_t, OrderSize>& order) {
|
|||||||
// Input feature converter
|
// Input feature converter
|
||||||
template<IndexType TransformedFeatureDimensions>
|
template<IndexType TransformedFeatureDimensions>
|
||||||
class FeatureTransformer {
|
class FeatureTransformer {
|
||||||
|
static constexpr bool UseThreats =
|
||||||
|
(TransformedFeatureDimensions == TransformedFeatureDimensionsBig);
|
||||||
// Number of output dimensions for one side
|
// Number of output dimensions for one side
|
||||||
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
|
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
|
||||||
|
|
||||||
@@ -88,7 +90,10 @@ class FeatureTransformer {
|
|||||||
using OutputType = TransformedFeatureType;
|
using OutputType = TransformedFeatureType;
|
||||||
|
|
||||||
// Number of input/output dimensions
|
// Number of input/output dimensions
|
||||||
static constexpr IndexType InputDimensions = FeatureSet::Dimensions;
|
static constexpr IndexType InputDimensions = PSQFeatureSet::Dimensions;
|
||||||
|
static constexpr IndexType ThreatInputDimensions = ThreatFeatureSet::Dimensions;
|
||||||
|
static constexpr IndexType TotalInputDimensions =
|
||||||
|
InputDimensions + (UseThreats ? ThreatInputDimensions : 0);
|
||||||
static constexpr IndexType OutputDimensions = HalfDimensions;
|
static constexpr IndexType OutputDimensions = HalfDimensions;
|
||||||
|
|
||||||
// Size of forward propagation buffer
|
// Size of forward propagation buffer
|
||||||
@@ -119,17 +124,24 @@ class FeatureTransformer {
|
|||||||
|
|
||||||
// Hash value embedded in the evaluation file
|
// Hash value embedded in the evaluation file
|
||||||
static constexpr std::uint32_t get_hash_value() {
|
static constexpr std::uint32_t get_hash_value() {
|
||||||
return FeatureSet::HashValue ^ (OutputDimensions * 2);
|
return (UseThreats ? ThreatFeatureSet::HashValue : PSQFeatureSet::HashValue)
|
||||||
|
^ (OutputDimensions * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void permute_weights() {
|
void permute_weights() {
|
||||||
permute<16>(biases, PackusEpi16Order);
|
permute<16>(biases, PackusEpi16Order);
|
||||||
permute<16>(weights, PackusEpi16Order);
|
permute<16>(weights, PackusEpi16Order);
|
||||||
|
|
||||||
|
if (UseThreats)
|
||||||
|
permute<8>(threatWeights, PackusEpi16Order);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unpermute_weights() {
|
void unpermute_weights() {
|
||||||
permute<16>(biases, InversePackusEpi16Order);
|
permute<16>(biases, InversePackusEpi16Order);
|
||||||
permute<16>(weights, InversePackusEpi16Order);
|
permute<16>(weights, InversePackusEpi16Order);
|
||||||
|
|
||||||
|
if (UseThreats)
|
||||||
|
permute<8>(threatWeights, InversePackusEpi16Order);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void scale_weights(bool read) {
|
inline void scale_weights(bool read) {
|
||||||
@@ -145,14 +157,51 @@ class FeatureTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read network parameters
|
// Read network parameters
|
||||||
|
// TODO: This is ugly. Currently LEB128 on the entire L1 necessitates
|
||||||
|
// reading the weights into a combined array, and then splitting.
|
||||||
bool read_parameters(std::istream& stream) {
|
bool read_parameters(std::istream& stream) {
|
||||||
|
read_leb_128<BiasType>(stream, biases);
|
||||||
|
|
||||||
read_leb_128<BiasType>(stream, biases, HalfDimensions);
|
if (UseThreats)
|
||||||
read_leb_128<WeightType>(stream, weights, HalfDimensions * InputDimensions);
|
{
|
||||||
read_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
auto combinedWeights =
|
||||||
|
std::make_unique<std::array<WeightType, HalfDimensions * TotalInputDimensions>>();
|
||||||
|
auto combinedPsqtWeights =
|
||||||
|
std::make_unique<std::array<PSQTWeightType, TotalInputDimensions * PSQTBuckets>>();
|
||||||
|
|
||||||
|
read_leb_128<WeightType>(stream, *combinedWeights);
|
||||||
|
|
||||||
|
std::copy(combinedWeights->begin(),
|
||||||
|
combinedWeights->begin() + ThreatInputDimensions * HalfDimensions,
|
||||||
|
std::begin(threatWeights));
|
||||||
|
|
||||||
|
std::copy(combinedWeights->begin() + ThreatInputDimensions * HalfDimensions,
|
||||||
|
combinedWeights->begin()
|
||||||
|
+ (ThreatInputDimensions + InputDimensions) * HalfDimensions,
|
||||||
|
std::begin(weights));
|
||||||
|
|
||||||
|
read_leb_128<PSQTWeightType>(stream, *combinedPsqtWeights);
|
||||||
|
|
||||||
|
std::copy(combinedPsqtWeights->begin(),
|
||||||
|
combinedPsqtWeights->begin() + ThreatInputDimensions * PSQTBuckets,
|
||||||
|
std::begin(threatPsqtWeights));
|
||||||
|
|
||||||
|
std::copy(combinedPsqtWeights->begin() + ThreatInputDimensions * PSQTBuckets,
|
||||||
|
combinedPsqtWeights->begin()
|
||||||
|
+ (ThreatInputDimensions + InputDimensions) * PSQTBuckets,
|
||||||
|
std::begin(psqtWeights));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
read_leb_128<WeightType>(stream, weights);
|
||||||
|
read_leb_128<PSQTWeightType>(stream, psqtWeights);
|
||||||
|
}
|
||||||
|
|
||||||
permute_weights();
|
permute_weights();
|
||||||
scale_weights(true);
|
|
||||||
|
if (!UseThreats)
|
||||||
|
scale_weights(true);
|
||||||
|
|
||||||
return !stream.fail();
|
return !stream.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,11 +210,44 @@ class FeatureTransformer {
|
|||||||
std::unique_ptr<FeatureTransformer> copy = std::make_unique<FeatureTransformer>(*this);
|
std::unique_ptr<FeatureTransformer> copy = std::make_unique<FeatureTransformer>(*this);
|
||||||
|
|
||||||
copy->unpermute_weights();
|
copy->unpermute_weights();
|
||||||
copy->scale_weights(false);
|
|
||||||
|
|
||||||
write_leb_128<BiasType>(stream, copy->biases, HalfDimensions);
|
if (!UseThreats)
|
||||||
write_leb_128<WeightType>(stream, copy->weights, HalfDimensions * InputDimensions);
|
copy->scale_weights(false);
|
||||||
write_leb_128<PSQTWeightType>(stream, copy->psqtWeights, PSQTBuckets * InputDimensions);
|
|
||||||
|
write_leb_128<BiasType>(stream, copy->biases);
|
||||||
|
|
||||||
|
if (UseThreats)
|
||||||
|
{
|
||||||
|
auto combinedWeights =
|
||||||
|
std::make_unique<std::array<WeightType, HalfDimensions * TotalInputDimensions>>();
|
||||||
|
auto combinedPsqtWeights =
|
||||||
|
std::make_unique<std::array<PSQTWeightType, TotalInputDimensions * PSQTBuckets>>();
|
||||||
|
|
||||||
|
std::copy(std::begin(copy->threatWeights),
|
||||||
|
std::begin(copy->threatWeights) + ThreatInputDimensions * HalfDimensions,
|
||||||
|
combinedWeights->begin());
|
||||||
|
|
||||||
|
std::copy(std::begin(copy->weights),
|
||||||
|
std::begin(copy->weights) + InputDimensions * HalfDimensions,
|
||||||
|
combinedWeights->begin() + ThreatInputDimensions * HalfDimensions);
|
||||||
|
|
||||||
|
write_leb_128<WeightType>(stream, *combinedWeights);
|
||||||
|
|
||||||
|
std::copy(std::begin(copy->threatPsqtWeights),
|
||||||
|
std::begin(copy->threatPsqtWeights) + ThreatInputDimensions * PSQTBuckets,
|
||||||
|
combinedPsqtWeights->begin());
|
||||||
|
|
||||||
|
std::copy(std::begin(copy->psqtWeights),
|
||||||
|
std::begin(copy->psqtWeights) + InputDimensions * PSQTBuckets,
|
||||||
|
combinedPsqtWeights->begin() + ThreatInputDimensions * PSQTBuckets);
|
||||||
|
|
||||||
|
write_leb_128<PSQTWeightType>(stream, *combinedPsqtWeights);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_leb_128<WeightType>(stream, copy->weights);
|
||||||
|
write_leb_128<PSQTWeightType>(stream, copy->psqtWeights);
|
||||||
|
}
|
||||||
|
|
||||||
return !stream.fail();
|
return !stream.fail();
|
||||||
}
|
}
|
||||||
@@ -187,17 +269,29 @@ class FeatureTransformer {
|
|||||||
int bucket) const {
|
int bucket) const {
|
||||||
|
|
||||||
using namespace SIMD;
|
using namespace SIMD;
|
||||||
|
|
||||||
accumulatorStack.evaluate(pos, *this, *cache);
|
accumulatorStack.evaluate(pos, *this, *cache);
|
||||||
const auto& accumulatorState = accumulatorStack.latest();
|
const auto& accumulatorState = accumulatorStack.latest<PSQFeatureSet>();
|
||||||
|
const auto& threatAccumulatorState = accumulatorStack.latest<ThreatFeatureSet>();
|
||||||
|
|
||||||
const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
|
const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
|
||||||
const auto& psqtAccumulation = (accumulatorState.acc<HalfDimensions>()).psqtAccumulation;
|
const auto& psqtAccumulation = (accumulatorState.acc<HalfDimensions>()).psqtAccumulation;
|
||||||
const auto psqt =
|
auto psqt =
|
||||||
(psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket])
|
(psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]);
|
||||||
/ 2;
|
|
||||||
|
if (UseThreats)
|
||||||
|
{
|
||||||
|
const auto& threatPsqtAccumulation =
|
||||||
|
(threatAccumulatorState.acc<HalfDimensions>()).psqtAccumulation;
|
||||||
|
psqt = (psqt + threatPsqtAccumulation[perspectives[0]][bucket]
|
||||||
|
- threatPsqtAccumulation[perspectives[1]][bucket])
|
||||||
|
/ 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
psqt /= 2;
|
||||||
|
|
||||||
const auto& accumulation = (accumulatorState.acc<HalfDimensions>()).accumulation;
|
const auto& accumulation = (accumulatorState.acc<HalfDimensions>()).accumulation;
|
||||||
|
const auto& threatAccumulation =
|
||||||
|
(threatAccumulatorState.acc<HalfDimensions>()).accumulation;
|
||||||
|
|
||||||
for (IndexType p = 0; p < 2; ++p)
|
for (IndexType p = 0; p < 2; ++p)
|
||||||
{
|
{
|
||||||
@@ -210,7 +304,7 @@ class FeatureTransformer {
|
|||||||
constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize;
|
constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize;
|
||||||
|
|
||||||
const vec_t Zero = vec_zero();
|
const vec_t Zero = vec_zero();
|
||||||
const vec_t One = vec_set_16(127 * 2);
|
const vec_t One = vec_set_16(UseThreats ? 255 : 127 * 2);
|
||||||
|
|
||||||
const vec_t* in0 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][0]));
|
const vec_t* in0 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][0]));
|
||||||
const vec_t* in1 =
|
const vec_t* in1 =
|
||||||
@@ -276,20 +370,48 @@ class FeatureTransformer {
|
|||||||
#else
|
#else
|
||||||
6;
|
6;
|
||||||
#endif
|
#endif
|
||||||
|
if (UseThreats)
|
||||||
for (IndexType j = 0; j < NumOutputChunks; ++j)
|
|
||||||
{
|
{
|
||||||
const vec_t sum0a =
|
const vec_t* tin0 =
|
||||||
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero), shift);
|
reinterpret_cast<const vec_t*>(&(threatAccumulation[perspectives[p]][0]));
|
||||||
const vec_t sum0b =
|
const vec_t* tin1 = reinterpret_cast<const vec_t*>(
|
||||||
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero), shift);
|
&(threatAccumulation[perspectives[p]][HalfDimensions / 2]));
|
||||||
const vec_t sum1a = vec_min_16(in1[j * 2 + 0], One);
|
for (IndexType j = 0; j < NumOutputChunks; ++j)
|
||||||
const vec_t sum1b = vec_min_16(in1[j * 2 + 1], One);
|
{
|
||||||
|
const vec_t acc0a = vec_add_16(in0[j * 2 + 0], tin0[j * 2 + 0]);
|
||||||
|
const vec_t acc0b = vec_add_16(in0[j * 2 + 1], tin0[j * 2 + 1]);
|
||||||
|
const vec_t acc1a = vec_add_16(in1[j * 2 + 0], tin1[j * 2 + 0]);
|
||||||
|
const vec_t acc1b = vec_add_16(in1[j * 2 + 1], tin1[j * 2 + 1]);
|
||||||
|
|
||||||
const vec_t pa = vec_mulhi_16(sum0a, sum1a);
|
const vec_t sum0a =
|
||||||
const vec_t pb = vec_mulhi_16(sum0b, sum1b);
|
vec_slli_16(vec_max_16(vec_min_16(acc0a, One), Zero), shift);
|
||||||
|
const vec_t sum0b =
|
||||||
|
vec_slli_16(vec_max_16(vec_min_16(acc0b, One), Zero), shift);
|
||||||
|
const vec_t sum1a = vec_min_16(acc1a, One);
|
||||||
|
const vec_t sum1b = vec_min_16(acc1b, One);
|
||||||
|
|
||||||
out[j] = vec_packus_16(pa, pb);
|
const vec_t pa = vec_mulhi_16(sum0a, sum1a);
|
||||||
|
const vec_t pb = vec_mulhi_16(sum0b, sum1b);
|
||||||
|
|
||||||
|
out[j] = vec_packus_16(pa, pb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (IndexType j = 0; j < NumOutputChunks; ++j)
|
||||||
|
{
|
||||||
|
const vec_t sum0a =
|
||||||
|
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero), shift);
|
||||||
|
const vec_t sum0b =
|
||||||
|
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero), shift);
|
||||||
|
const vec_t sum1a = vec_min_16(in1[j * 2 + 0], One);
|
||||||
|
const vec_t sum1b = vec_min_16(in1[j * 2 + 1], One);
|
||||||
|
|
||||||
|
const vec_t pa = vec_mulhi_16(sum0a, sum1a);
|
||||||
|
const vec_t pb = vec_mulhi_16(sum0b, sum1b);
|
||||||
|
|
||||||
|
out[j] = vec_packus_16(pa, pb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -299,8 +421,21 @@ class FeatureTransformer {
|
|||||||
BiasType sum0 = accumulation[static_cast<int>(perspectives[p])][j + 0];
|
BiasType sum0 = accumulation[static_cast<int>(perspectives[p])][j + 0];
|
||||||
BiasType sum1 =
|
BiasType sum1 =
|
||||||
accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
||||||
sum0 = std::clamp<BiasType>(sum0, 0, 127 * 2);
|
|
||||||
sum1 = std::clamp<BiasType>(sum1, 0, 127 * 2);
|
if (UseThreats)
|
||||||
|
{
|
||||||
|
BiasType sum0t = threatAccumulation[static_cast<int>(perspectives[p])][j + 0];
|
||||||
|
BiasType sum1t =
|
||||||
|
threatAccumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
||||||
|
sum0 = std::clamp<BiasType>(sum0 + sum0t, 0, 255);
|
||||||
|
sum1 = std::clamp<BiasType>(sum1 + sum1t, 0, 255);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sum0 = std::clamp<BiasType>(sum0, 0, 127 * 2);
|
||||||
|
sum1 = std::clamp<BiasType>(sum1, 0, 127 * 2);
|
||||||
|
}
|
||||||
|
|
||||||
output[offset + j] = static_cast<OutputType>(unsigned(sum0 * sum1) / 512);
|
output[offset + j] = static_cast<OutputType>(unsigned(sum0 * sum1) / 512);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,9 +445,15 @@ class FeatureTransformer {
|
|||||||
return psqt;
|
return psqt;
|
||||||
} // end of function transform()
|
} // end of function transform()
|
||||||
|
|
||||||
alignas(CacheLineSize) BiasType biases[HalfDimensions];
|
alignas(CacheLineSize) std::array<BiasType, HalfDimensions> biases;
|
||||||
alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions];
|
alignas(CacheLineSize) std::array<WeightType, HalfDimensions * InputDimensions> weights;
|
||||||
alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets];
|
alignas(CacheLineSize)
|
||||||
|
std::array<ThreatWeightType,
|
||||||
|
UseThreats ? HalfDimensions * ThreatInputDimensions : 0> threatWeights;
|
||||||
|
alignas(CacheLineSize) std::array<PSQTWeightType, InputDimensions * PSQTBuckets> psqtWeights;
|
||||||
|
alignas(CacheLineSize)
|
||||||
|
std::array<PSQTWeightType,
|
||||||
|
UseThreats ? ThreatInputDimensions * PSQTBuckets : 0> threatPsqtWeights;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Stockfish::Eval::NNUE
|
} // namespace Stockfish::Eval::NNUE
|
||||||
|
|||||||
+30
-2
@@ -47,11 +47,13 @@ namespace Stockfish::Eval::NNUE::SIMD {
|
|||||||
|
|
||||||
#ifdef USE_AVX512
|
#ifdef USE_AVX512
|
||||||
using vec_t = __m512i;
|
using vec_t = __m512i;
|
||||||
|
using vec_i8_t = __m256i;
|
||||||
using vec128_t = __m128i;
|
using vec128_t = __m128i;
|
||||||
using psqt_vec_t = __m256i;
|
using psqt_vec_t = __m256i;
|
||||||
using vec_uint_t = __m512i;
|
using vec_uint_t = __m512i;
|
||||||
#define vec_load(a) _mm512_load_si512(a)
|
#define vec_load(a) _mm512_load_si512(a)
|
||||||
#define vec_store(a, b) _mm512_store_si512(a, b)
|
#define vec_store(a, b) _mm512_store_si512(a, b)
|
||||||
|
#define vec_convert_8_16(a) _mm512_cvtepi8_epi16(a)
|
||||||
#define vec_add_16(a, b) _mm512_add_epi16(a, b)
|
#define vec_add_16(a, b) _mm512_add_epi16(a, b)
|
||||||
#define vec_sub_16(a, b) _mm512_sub_epi16(a, b)
|
#define vec_sub_16(a, b) _mm512_sub_epi16(a, b)
|
||||||
#define vec_mulhi_16(a, b) _mm512_mulhi_epi16(a, b)
|
#define vec_mulhi_16(a, b) _mm512_mulhi_epi16(a, b)
|
||||||
@@ -82,11 +84,13 @@ using vec_uint_t = __m512i;
|
|||||||
|
|
||||||
#elif USE_AVX2
|
#elif USE_AVX2
|
||||||
using vec_t = __m256i;
|
using vec_t = __m256i;
|
||||||
|
using vec_i8_t = __m128i;
|
||||||
using vec128_t = __m128i;
|
using vec128_t = __m128i;
|
||||||
using psqt_vec_t = __m256i;
|
using psqt_vec_t = __m256i;
|
||||||
using vec_uint_t = __m256i;
|
using vec_uint_t = __m256i;
|
||||||
#define vec_load(a) _mm256_load_si256(a)
|
#define vec_load(a) _mm256_load_si256(a)
|
||||||
#define vec_store(a, b) _mm256_store_si256(a, b)
|
#define vec_store(a, b) _mm256_store_si256(a, b)
|
||||||
|
#define vec_convert_8_16(a) _mm256_cvtepi8_epi16(a)
|
||||||
#define vec_add_16(a, b) _mm256_add_epi16(a, b)
|
#define vec_add_16(a, b) _mm256_add_epi16(a, b)
|
||||||
#define vec_sub_16(a, b) _mm256_sub_epi16(a, b)
|
#define vec_sub_16(a, b) _mm256_sub_epi16(a, b)
|
||||||
#define vec_mulhi_16(a, b) _mm256_mulhi_epi16(a, b)
|
#define vec_mulhi_16(a, b) _mm256_mulhi_epi16(a, b)
|
||||||
@@ -119,11 +123,12 @@ using vec_uint_t = __m256i;
|
|||||||
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
|
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
|
||||||
#define vec128_add(a, b) _mm_add_epi16(a, b)
|
#define vec128_add(a, b) _mm_add_epi16(a, b)
|
||||||
|
|
||||||
#define NumRegistersSIMD 16
|
#define NumRegistersSIMD 12
|
||||||
#define MaxChunkSize 32
|
#define MaxChunkSize 32
|
||||||
|
|
||||||
#elif USE_SSE2
|
#elif USE_SSE2
|
||||||
using vec_t = __m128i;
|
using vec_t = __m128i;
|
||||||
|
using vec_i8_t = std::uint64_t; // for the correct size -- will be loaded into an xmm reg
|
||||||
using vec128_t = __m128i;
|
using vec128_t = __m128i;
|
||||||
using psqt_vec_t = __m128i;
|
using psqt_vec_t = __m128i;
|
||||||
using vec_uint_t = __m128i;
|
using vec_uint_t = __m128i;
|
||||||
@@ -149,17 +154,35 @@ using vec_uint_t = __m128i;
|
|||||||
_mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128())))
|
_mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128())))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __i386__
|
||||||
|
inline __m128i _mm_cvtsi64_si128(int64_t val) {
|
||||||
|
return _mm_loadl_epi64(reinterpret_cast<const __m128i*>(&val));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SSE41
|
||||||
|
#define vec_convert_8_16(a) _mm_cvtepi8_epi16(_mm_cvtsi64_si128(static_cast<int64_t>(a)))
|
||||||
|
#else
|
||||||
|
// Credit: Yoshie2000
|
||||||
|
inline __m128i vec_convert_8_16(uint64_t x) {
|
||||||
|
__m128i v8 = _mm_cvtsi64_si128(static_cast<int64_t>(x));
|
||||||
|
__m128i sign = _mm_cmpgt_epi8(_mm_setzero_si128(), v8);
|
||||||
|
return _mm_unpacklo_epi8(v8, sign);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define vec128_zero _mm_setzero_si128()
|
#define vec128_zero _mm_setzero_si128()
|
||||||
#define vec128_set_16(a) _mm_set1_epi16(a)
|
#define vec128_set_16(a) _mm_set1_epi16(a)
|
||||||
#define vec128_load(a) _mm_load_si128(a)
|
#define vec128_load(a) _mm_load_si128(a)
|
||||||
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
|
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
|
||||||
#define vec128_add(a, b) _mm_add_epi16(a, b)
|
#define vec128_add(a, b) _mm_add_epi16(a, b)
|
||||||
|
|
||||||
#define NumRegistersSIMD (Is64Bit ? 16 : 8)
|
#define NumRegistersSIMD (Is64Bit ? 12 : 6)
|
||||||
#define MaxChunkSize 16
|
#define MaxChunkSize 16
|
||||||
|
|
||||||
#elif USE_NEON
|
#elif USE_NEON
|
||||||
using vec_t = int16x8_t;
|
using vec_t = int16x8_t;
|
||||||
|
using vec_i8_t = int8x16_t;
|
||||||
using psqt_vec_t = int32x4_t;
|
using psqt_vec_t = int32x4_t;
|
||||||
using vec128_t = uint16x8_t;
|
using vec128_t = uint16x8_t;
|
||||||
using vec_uint_t = uint32x4_t;
|
using vec_uint_t = uint32x4_t;
|
||||||
@@ -191,6 +214,11 @@ static constexpr std::uint32_t Mask[4] = {1, 2, 4, 8};
|
|||||||
#define NumRegistersSIMD 16
|
#define NumRegistersSIMD 16
|
||||||
#define MaxChunkSize 16
|
#define MaxChunkSize 16
|
||||||
|
|
||||||
|
#ifndef __aarch64__
|
||||||
|
// Single instruction doesn't exist on 32-bit ARM
|
||||||
|
inline int8x16_t vmovl_high_s8(int8x16_t val) { return vmovl_s8(vget_high_s8(val)); }
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#undef VECTOR
|
#undef VECTOR
|
||||||
|
|
||||||
|
|||||||
+137
-20
@@ -48,6 +48,7 @@ Key psq[PIECE_NB][SQUARE_NB];
|
|||||||
Key enpassant[FILE_NB];
|
Key enpassant[FILE_NB];
|
||||||
Key castling[CASTLING_RIGHT_NB];
|
Key castling[CASTLING_RIGHT_NB];
|
||||||
Key side, noPawns;
|
Key side, noPawns;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -687,10 +688,10 @@ bool Position::gives_check(Move m) const {
|
|||||||
// moves should be filtered out before this function is called.
|
// moves should be filtered out before this function is called.
|
||||||
// If a pointer to the TT table is passed, the entry for the new position
|
// If a pointer to the TT table is passed, the entry for the new position
|
||||||
// will be prefetched
|
// will be prefetched
|
||||||
DirtyPiece Position::do_move(Move m,
|
DirtyBoardData Position::do_move(Move m,
|
||||||
StateInfo& newSt,
|
StateInfo& newSt,
|
||||||
bool givesCheck,
|
bool givesCheck,
|
||||||
const TranspositionTable* tt = nullptr) {
|
const TranspositionTable* tt = nullptr) {
|
||||||
|
|
||||||
assert(m.is_ok());
|
assert(m.is_ok());
|
||||||
assert(&newSt != st);
|
assert(&newSt != st);
|
||||||
@@ -724,6 +725,10 @@ DirtyPiece Position::do_move(Move m,
|
|||||||
dp.from = from;
|
dp.from = from;
|
||||||
dp.to = to;
|
dp.to = to;
|
||||||
dp.add_sq = SQ_NONE;
|
dp.add_sq = SQ_NONE;
|
||||||
|
DirtyThreats dts;
|
||||||
|
dts.us = us;
|
||||||
|
dts.prevKsq = square<KING>(us);
|
||||||
|
dts.threatenedSqs = dts.threateningSqs = 0;
|
||||||
|
|
||||||
assert(color_of(pc) == us);
|
assert(color_of(pc) == us);
|
||||||
assert(captured == NO_PIECE || color_of(captured) == (m.type_of() != CASTLING ? them : us));
|
assert(captured == NO_PIECE || color_of(captured) == (m.type_of() != CASTLING ? them : us));
|
||||||
@@ -735,7 +740,7 @@ DirtyPiece Position::do_move(Move m,
|
|||||||
assert(captured == make_piece(us, ROOK));
|
assert(captured == make_piece(us, ROOK));
|
||||||
|
|
||||||
Square rfrom, rto;
|
Square rfrom, rto;
|
||||||
do_castling<true>(us, from, to, rfrom, rto, &dp);
|
do_castling<true>(us, from, to, rfrom, rto, &dts, &dp);
|
||||||
|
|
||||||
k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
||||||
st->nonPawnKey[us] ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
st->nonPawnKey[us] ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
||||||
@@ -758,6 +763,9 @@ DirtyPiece Position::do_move(Move m,
|
|||||||
assert(relative_rank(us, to) == RANK_6);
|
assert(relative_rank(us, to) == RANK_6);
|
||||||
assert(piece_on(to) == NO_PIECE);
|
assert(piece_on(to) == NO_PIECE);
|
||||||
assert(piece_on(capsq) == make_piece(them, PAWN));
|
assert(piece_on(capsq) == make_piece(them, PAWN));
|
||||||
|
|
||||||
|
// Update board and piece lists in ep case, normal captures are updated later
|
||||||
|
remove_piece(capsq, &dts);
|
||||||
}
|
}
|
||||||
|
|
||||||
st->pawnKey ^= Zobrist::psq[captured][capsq];
|
st->pawnKey ^= Zobrist::psq[captured][capsq];
|
||||||
@@ -774,11 +782,9 @@ DirtyPiece Position::do_move(Move m,
|
|||||||
dp.remove_pc = captured;
|
dp.remove_pc = captured;
|
||||||
dp.remove_sq = capsq;
|
dp.remove_sq = capsq;
|
||||||
|
|
||||||
// Update board and piece lists
|
|
||||||
remove_piece(capsq);
|
|
||||||
|
|
||||||
k ^= Zobrist::psq[captured][capsq];
|
k ^= Zobrist::psq[captured][capsq];
|
||||||
st->materialKey ^= Zobrist::psq[captured][8 + pieceCount[captured]];
|
st->materialKey ^=
|
||||||
|
Zobrist::psq[captured][8 + pieceCount[captured] - (m.type_of() != EN_PASSANT)];
|
||||||
|
|
||||||
// Reset rule 50 counter
|
// Reset rule 50 counter
|
||||||
st->rule50 = 0;
|
st->rule50 = 0;
|
||||||
@@ -806,7 +812,15 @@ DirtyPiece Position::do_move(Move m,
|
|||||||
|
|
||||||
// Move the piece. The tricky Chess960 castling is handled earlier
|
// Move the piece. The tricky Chess960 castling is handled earlier
|
||||||
if (m.type_of() != CASTLING)
|
if (m.type_of() != CASTLING)
|
||||||
move_piece(from, to);
|
{
|
||||||
|
if (captured && m.type_of() != EN_PASSANT)
|
||||||
|
{
|
||||||
|
remove_piece(from, &dts);
|
||||||
|
swap_piece(to, pc, &dts);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
move_piece(from, to, &dts);
|
||||||
|
}
|
||||||
|
|
||||||
// If the moving piece is a pawn do some special extra work
|
// If the moving piece is a pawn do some special extra work
|
||||||
if (type_of(pc) == PAWN)
|
if (type_of(pc) == PAWN)
|
||||||
@@ -823,8 +837,7 @@ DirtyPiece Position::do_move(Move m,
|
|||||||
assert(relative_rank(us, to) == RANK_8);
|
assert(relative_rank(us, to) == RANK_8);
|
||||||
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
|
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
|
||||||
|
|
||||||
remove_piece(to);
|
swap_piece(to, promotion, &dts);
|
||||||
put_piece(promotion, to);
|
|
||||||
|
|
||||||
dp.add_pc = promotion;
|
dp.add_pc = promotion;
|
||||||
dp.add_sq = to;
|
dp.add_sq = to;
|
||||||
@@ -949,13 +962,16 @@ DirtyPiece Position::do_move(Move m,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dts.ksq = square<KING>(us);
|
||||||
|
|
||||||
assert(pos_is_ok());
|
assert(pos_is_ok());
|
||||||
|
|
||||||
assert(dp.pc != NO_PIECE);
|
assert(dp.pc != NO_PIECE);
|
||||||
assert(!(bool(captured) || m.type_of() == CASTLING) ^ (dp.remove_sq != SQ_NONE));
|
assert(!(bool(captured) || m.type_of() == CASTLING) ^ (dp.remove_sq != SQ_NONE));
|
||||||
assert(dp.from != SQ_NONE);
|
assert(dp.from != SQ_NONE);
|
||||||
assert(!(dp.add_sq != SQ_NONE) ^ (m.type_of() == PROMOTION || m.type_of() == CASTLING));
|
assert(!(dp.add_sq != SQ_NONE) ^ (m.type_of() == PROMOTION || m.type_of() == CASTLING));
|
||||||
return dp;
|
|
||||||
|
return {dp, dts};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1021,12 +1037,113 @@ void Position::undo_move(Move m) {
|
|||||||
assert(pos_is_ok());
|
assert(pos_is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<bool PutPiece>
|
||||||
|
inline void add_dirty_threat(
|
||||||
|
DirtyThreats* const dts, Piece pc, Piece threatened, Square s, Square threatenedSq) {
|
||||||
|
if (PutPiece)
|
||||||
|
{
|
||||||
|
dts->threatenedSqs |= square_bb(threatenedSq);
|
||||||
|
dts->threateningSqs |= square_bb(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
dts->list.push_back({pc, threatened, s, threatenedSq, PutPiece});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool PutPiece, bool ComputeRay>
|
||||||
|
void Position::update_piece_threats(Piece pc, Square s, DirtyThreats* const dts) {
|
||||||
|
// Add newly threatened pieces
|
||||||
|
Bitboard occupied = pieces();
|
||||||
|
|
||||||
|
Bitboard rAttacks = attacks_bb<ROOK>(s, occupied);
|
||||||
|
Bitboard bAttacks = attacks_bb<BISHOP>(s, occupied);
|
||||||
|
Bitboard qAttacks = rAttacks | bAttacks;
|
||||||
|
|
||||||
|
Bitboard threatened;
|
||||||
|
|
||||||
|
switch (type_of(pc))
|
||||||
|
{
|
||||||
|
case PAWN :
|
||||||
|
threatened = PseudoAttacks[color_of(pc)][s];
|
||||||
|
break;
|
||||||
|
case BISHOP :
|
||||||
|
threatened = bAttacks;
|
||||||
|
break;
|
||||||
|
case ROOK :
|
||||||
|
threatened = rAttacks;
|
||||||
|
break;
|
||||||
|
case QUEEN :
|
||||||
|
threatened = qAttacks;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default :
|
||||||
|
threatened = PseudoAttacks[type_of(pc)][s];
|
||||||
|
}
|
||||||
|
|
||||||
|
threatened &= occupied;
|
||||||
|
|
||||||
|
while (threatened)
|
||||||
|
{
|
||||||
|
Square threatened_sq = pop_lsb(threatened);
|
||||||
|
Piece threatened_pc = piece_on(threatened_sq);
|
||||||
|
|
||||||
|
assert(threatened_sq != s);
|
||||||
|
assert(threatened_pc);
|
||||||
|
|
||||||
|
add_dirty_threat<PutPiece>(dts, pc, threatened_pc, s, threatened_sq);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitboard sliders = (pieces(ROOK, QUEEN) & rAttacks) | (pieces(BISHOP, QUEEN) & bAttacks);
|
||||||
|
|
||||||
|
Bitboard incoming_threats = (attacks_bb<KNIGHT>(s, occupied) & pieces(KNIGHT))
|
||||||
|
| (attacks_bb<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
|
||||||
|
| (attacks_bb<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
|
||||||
|
| (attacks_bb<KING>(s, occupied) & pieces(KING));
|
||||||
|
|
||||||
|
while (sliders)
|
||||||
|
{
|
||||||
|
Square slider_sq = pop_lsb(sliders);
|
||||||
|
Piece slider = piece_on(slider_sq);
|
||||||
|
|
||||||
|
Bitboard ray = RayPassBB[slider_sq][s] & ~BetweenBB[slider_sq][s];
|
||||||
|
threatened = ray & qAttacks & occupied;
|
||||||
|
|
||||||
|
assert(!more_than_one(threatened));
|
||||||
|
if (ComputeRay && threatened)
|
||||||
|
{
|
||||||
|
Square threatened_sq = lsb(threatened);
|
||||||
|
|
||||||
|
Piece threatened_pc = piece_on(threatened_sq);
|
||||||
|
add_dirty_threat<!PutPiece>(dts, slider, threatened_pc, slider_sq, threatened_sq);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_dirty_threat<PutPiece>(dts, slider, pc, slider_sq, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add threats of sliders that were already threatening s,
|
||||||
|
// sliders are already handled in the loop above
|
||||||
|
|
||||||
|
while (incoming_threats)
|
||||||
|
{
|
||||||
|
Square src_sq = pop_lsb(incoming_threats);
|
||||||
|
Piece src_pc = piece_on(src_sq);
|
||||||
|
|
||||||
|
assert(src_sq != s);
|
||||||
|
assert(src_pc != NO_PIECE);
|
||||||
|
|
||||||
|
add_dirty_threat<PutPiece>(dts, src_pc, pc, src_sq, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper used to do/undo a castling move. This is a bit
|
// Helper used to do/undo a castling move. This is a bit
|
||||||
// tricky in Chess960 where from/to squares can overlap.
|
// tricky in Chess960 where from/to squares can overlap.
|
||||||
template<bool Do>
|
template<bool Do>
|
||||||
void Position::do_castling(
|
void Position::do_castling(Color us,
|
||||||
Color us, Square from, Square& to, Square& rfrom, Square& rto, DirtyPiece* const dp) {
|
Square from,
|
||||||
|
Square& to,
|
||||||
|
Square& rfrom,
|
||||||
|
Square& rto,
|
||||||
|
DirtyThreats* const dts,
|
||||||
|
DirtyPiece* const dp) {
|
||||||
|
|
||||||
bool kingSide = to > from;
|
bool kingSide = to > from;
|
||||||
rfrom = to; // Castling is encoded as "king captures friendly rook"
|
rfrom = to; // Castling is encoded as "king captures friendly rook"
|
||||||
@@ -1044,12 +1161,12 @@ void Position::do_castling(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove both pieces first since squares could overlap in Chess960
|
// Remove both pieces first since squares could overlap in Chess960
|
||||||
remove_piece(Do ? from : to);
|
remove_piece(Do ? from : to, dts);
|
||||||
remove_piece(Do ? rfrom : rto);
|
remove_piece(Do ? rfrom : rto, dts);
|
||||||
board[Do ? from : to] = board[Do ? rfrom : rto] =
|
board[Do ? from : to] = board[Do ? rfrom : rto] =
|
||||||
NO_PIECE; // remove_piece does not do this for us
|
NO_PIECE; // remove_piece does not do this for us
|
||||||
put_piece(make_piece(us, KING), Do ? to : from);
|
put_piece(make_piece(us, KING), Do ? to : from, dts);
|
||||||
put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
|
put_piece(make_piece(us, ROOK), Do ? rto : rfrom, dts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1349,7 +1466,7 @@ bool Position::pos_is_ok() const {
|
|||||||
|
|
||||||
for (Piece pc : Pieces)
|
for (Piece pc : Pieces)
|
||||||
if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
|
if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
|
||||||
|| pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
|
|| pieceCount[pc] != std::count(board.begin(), board.end(), pc))
|
||||||
assert(0 && "pos_is_ok: Pieces");
|
assert(0 && "pos_is_ok: Pieces");
|
||||||
|
|
||||||
for (Color c : {WHITE, BLACK})
|
for (Color c : {WHITE, BLACK})
|
||||||
|
|||||||
+60
-29
@@ -19,6 +19,7 @@
|
|||||||
#ifndef POSITION_H_INCLUDED
|
#ifndef POSITION_H_INCLUDED
|
||||||
#define POSITION_H_INCLUDED
|
#define POSITION_H_INCLUDED
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
@@ -91,11 +92,11 @@ class Position {
|
|||||||
Bitboard pieces(PieceTypes... pts) const;
|
Bitboard pieces(PieceTypes... pts) const;
|
||||||
Bitboard pieces(Color c) const;
|
Bitboard pieces(Color c) const;
|
||||||
template<typename... PieceTypes>
|
template<typename... PieceTypes>
|
||||||
Bitboard pieces(Color c, PieceTypes... pts) const;
|
Bitboard pieces(Color c, PieceTypes... pts) const;
|
||||||
Piece piece_on(Square s) const;
|
Piece piece_on(Square s) const;
|
||||||
const Piece* piece_array() const;
|
const std::array<Piece, SQUARE_NB>& piece_array() const;
|
||||||
Square ep_square() const;
|
Square ep_square() const;
|
||||||
bool empty(Square s) const;
|
bool empty(Square s) const;
|
||||||
template<PieceType Pt>
|
template<PieceType Pt>
|
||||||
int count(Color c) const;
|
int count(Color c) const;
|
||||||
template<PieceType Pt>
|
template<PieceType Pt>
|
||||||
@@ -132,11 +133,11 @@ class Position {
|
|||||||
Piece captured_piece() const;
|
Piece captured_piece() const;
|
||||||
|
|
||||||
// Doing and undoing moves
|
// Doing and undoing moves
|
||||||
void do_move(Move m, StateInfo& newSt, const TranspositionTable* tt);
|
void do_move(Move m, StateInfo& newSt, const TranspositionTable* tt);
|
||||||
DirtyPiece do_move(Move m, StateInfo& newSt, bool givesCheck, const TranspositionTable* tt);
|
DirtyBoardData do_move(Move m, StateInfo& newSt, bool givesCheck, const TranspositionTable* tt);
|
||||||
void undo_move(Move m);
|
void undo_move(Move m);
|
||||||
void do_null_move(StateInfo& newSt, const TranspositionTable& tt);
|
void do_null_move(StateInfo& newSt, const TranspositionTable& tt);
|
||||||
void undo_null_move();
|
void undo_null_move();
|
||||||
|
|
||||||
// Static Exchange Evaluation
|
// Static Exchange Evaluation
|
||||||
bool see_ge(Move m, int threshold = 0) const;
|
bool see_ge(Move m, int threshold = 0) const;
|
||||||
@@ -166,8 +167,9 @@ class Position {
|
|||||||
|
|
||||||
StateInfo* state() const;
|
StateInfo* state() const;
|
||||||
|
|
||||||
void put_piece(Piece pc, Square s);
|
void put_piece(Piece pc, Square s, DirtyThreats* const dts = nullptr);
|
||||||
void remove_piece(Square s);
|
void remove_piece(Square s, DirtyThreats* const dts = nullptr);
|
||||||
|
void swap_piece(Square s, Piece pc, DirtyThreats* const dts = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Initialization helpers (used while setting up a position)
|
// Initialization helpers (used while setting up a position)
|
||||||
@@ -176,20 +178,24 @@ class Position {
|
|||||||
void set_check_info() const;
|
void set_check_info() const;
|
||||||
|
|
||||||
// Other helpers
|
// Other helpers
|
||||||
void move_piece(Square from, Square to);
|
template<bool PutPiece, bool ComputeRay = true>
|
||||||
|
void update_piece_threats(Piece pc, Square s, DirtyThreats* const dts);
|
||||||
|
void move_piece(Square from, Square to, DirtyThreats* const dts = nullptr);
|
||||||
template<bool Do>
|
template<bool Do>
|
||||||
void do_castling(Color us,
|
void do_castling(Color us,
|
||||||
Square from,
|
Square from,
|
||||||
Square& to,
|
Square& to,
|
||||||
Square& rfrom,
|
Square& rfrom,
|
||||||
Square& rto,
|
Square& rto,
|
||||||
DirtyPiece* const dp = nullptr);
|
DirtyThreats* const dts = nullptr,
|
||||||
|
DirtyPiece* const dp = nullptr);
|
||||||
Key adjust_key50(Key k) const;
|
Key adjust_key50(Key k) const;
|
||||||
|
|
||||||
// Data members
|
// Data members
|
||||||
Piece board[SQUARE_NB];
|
std::array<Piece, SQUARE_NB> board;
|
||||||
Bitboard byTypeBB[PIECE_TYPE_NB];
|
std::array<Bitboard, PIECE_TYPE_NB> byTypeBB;
|
||||||
Bitboard byColorBB[COLOR_NB];
|
std::array<Bitboard, COLOR_NB> byColorBB;
|
||||||
|
|
||||||
int pieceCount[PIECE_NB];
|
int pieceCount[PIECE_NB];
|
||||||
int castlingRightsMask[SQUARE_NB];
|
int castlingRightsMask[SQUARE_NB];
|
||||||
Square castlingRookSquare[CASTLING_RIGHT_NB];
|
Square castlingRookSquare[CASTLING_RIGHT_NB];
|
||||||
@@ -209,7 +215,7 @@ inline Piece Position::piece_on(Square s) const {
|
|||||||
return board[s];
|
return board[s];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Piece* Position::piece_array() const { return board; }
|
inline const std::array<Piece, SQUARE_NB>& Position::piece_array() const { return board; }
|
||||||
|
|
||||||
inline bool Position::empty(Square s) const { return piece_on(s) == NO_PIECE; }
|
inline bool Position::empty(Square s) const { return piece_on(s) == NO_PIECE; }
|
||||||
|
|
||||||
@@ -326,18 +332,23 @@ inline bool Position::capture_stage(Move m) const {
|
|||||||
|
|
||||||
inline Piece Position::captured_piece() const { return st->capturedPiece; }
|
inline Piece Position::captured_piece() const { return st->capturedPiece; }
|
||||||
|
|
||||||
inline void Position::put_piece(Piece pc, Square s) {
|
inline void Position::put_piece(Piece pc, Square s, DirtyThreats* const dts) {
|
||||||
|
|
||||||
board[s] = pc;
|
board[s] = pc;
|
||||||
byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
|
byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
|
||||||
byColorBB[color_of(pc)] |= s;
|
byColorBB[color_of(pc)] |= s;
|
||||||
pieceCount[pc]++;
|
pieceCount[pc]++;
|
||||||
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
|
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
|
||||||
|
|
||||||
|
if (dts)
|
||||||
|
update_piece_threats<true>(pc, s, dts);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Position::remove_piece(Square s) {
|
inline void Position::remove_piece(Square s, DirtyThreats* const dts) {
|
||||||
|
|
||||||
Piece pc = board[s];
|
Piece pc = board[s];
|
||||||
|
|
||||||
|
if (dts)
|
||||||
|
update_piece_threats<false>(pc, s, dts);
|
||||||
|
|
||||||
byTypeBB[ALL_PIECES] ^= s;
|
byTypeBB[ALL_PIECES] ^= s;
|
||||||
byTypeBB[type_of(pc)] ^= s;
|
byTypeBB[type_of(pc)] ^= s;
|
||||||
byColorBB[color_of(pc)] ^= s;
|
byColorBB[color_of(pc)] ^= s;
|
||||||
@@ -346,15 +357,35 @@ inline void Position::remove_piece(Square s) {
|
|||||||
pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
|
pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Position::move_piece(Square from, Square to) {
|
inline void Position::move_piece(Square from, Square to, DirtyThreats* const dts) {
|
||||||
|
|
||||||
Piece pc = board[from];
|
Piece pc = board[from];
|
||||||
Bitboard fromTo = from | to;
|
Bitboard fromTo = from | to;
|
||||||
|
|
||||||
|
if (dts)
|
||||||
|
update_piece_threats<false>(pc, from, dts);
|
||||||
|
|
||||||
byTypeBB[ALL_PIECES] ^= fromTo;
|
byTypeBB[ALL_PIECES] ^= fromTo;
|
||||||
byTypeBB[type_of(pc)] ^= fromTo;
|
byTypeBB[type_of(pc)] ^= fromTo;
|
||||||
byColorBB[color_of(pc)] ^= fromTo;
|
byColorBB[color_of(pc)] ^= fromTo;
|
||||||
board[from] = NO_PIECE;
|
board[from] = NO_PIECE;
|
||||||
board[to] = pc;
|
board[to] = pc;
|
||||||
|
|
||||||
|
if (dts)
|
||||||
|
update_piece_threats<true>(pc, to, dts);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Position::swap_piece(Square s, Piece pc, DirtyThreats* const dts) {
|
||||||
|
Piece old = board[s];
|
||||||
|
|
||||||
|
remove_piece(s);
|
||||||
|
|
||||||
|
if (dts)
|
||||||
|
update_piece_threats<false, false>(old, s, dts);
|
||||||
|
|
||||||
|
put_piece(pc, s);
|
||||||
|
|
||||||
|
if (dts)
|
||||||
|
update_piece_threats<true, false>(pc, s, dts);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) {
|
inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) {
|
||||||
|
|||||||
+11
-6
@@ -46,6 +46,7 @@
|
|||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "timeman.h"
|
#include "timeman.h"
|
||||||
#include "tt.h"
|
#include "tt.h"
|
||||||
|
#include "types.h"
|
||||||
#include "uci.h"
|
#include "uci.h"
|
||||||
#include "ucioption.h"
|
#include "ucioption.h"
|
||||||
|
|
||||||
@@ -527,15 +528,19 @@ void Search::Worker::do_move(Position& pos, const Move move, StateInfo& st, Stac
|
|||||||
|
|
||||||
void Search::Worker::do_move(
|
void Search::Worker::do_move(
|
||||||
Position& pos, const Move move, StateInfo& st, const bool givesCheck, Stack* const ss) {
|
Position& pos, const Move move, StateInfo& st, const bool givesCheck, Stack* const ss) {
|
||||||
bool capture = pos.capture_stage(move);
|
bool capture = pos.capture_stage(move);
|
||||||
DirtyPiece dp = pos.do_move(move, st, givesCheck, &tt);
|
|
||||||
nodes.fetch_add(1, std::memory_order_relaxed);
|
nodes.fetch_add(1, std::memory_order_relaxed);
|
||||||
accumulatorStack.push(dp);
|
|
||||||
|
DirtyBoardData dirtyBoardData = pos.do_move(move, st, givesCheck, &tt);
|
||||||
|
accumulatorStack.push(dirtyBoardData);
|
||||||
|
|
||||||
if (ss != nullptr)
|
if (ss != nullptr)
|
||||||
{
|
{
|
||||||
ss->currentMove = move;
|
ss->currentMove = move;
|
||||||
ss->continuationHistory = &continuationHistory[ss->inCheck][capture][dp.pc][move.to_sq()];
|
ss->continuationHistory =
|
||||||
ss->continuationCorrectionHistory = &continuationCorrectionHistory[dp.pc][move.to_sq()];
|
&continuationHistory[ss->inCheck][capture][dirtyBoardData.dp.pc][move.to_sq()];
|
||||||
|
ss->continuationCorrectionHistory =
|
||||||
|
&continuationCorrectionHistory[dirtyBoardData.dp.pc][move.to_sq()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+43
@@ -40,6 +40,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
// Disable some silly and noisy warnings from MSVC compiler
|
// Disable some silly and noisy warnings from MSVC compiler
|
||||||
@@ -290,6 +291,48 @@ struct DirtyPiece {
|
|||||||
Piece remove_pc, add_pc;
|
Piece remove_pc, add_pc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Keep track of what threats change on the board (used by NNUE)
|
||||||
|
struct DirtyThreat {
|
||||||
|
DirtyThreat() { /* don't initialize data */ }
|
||||||
|
DirtyThreat(Piece pc, Piece threatened_pc, Square pc_sq, Square threatened_sq, bool add) {
|
||||||
|
data = (add << 28) | (pc << 20) | (threatened_pc << 16) | (threatened_sq << 8) | (pc_sq);
|
||||||
|
}
|
||||||
|
|
||||||
|
Piece pc() const { return static_cast<Piece>(data >> 20 & 0xf); }
|
||||||
|
Piece threatened_pc() const { return static_cast<Piece>(data >> 16 & 0xf); }
|
||||||
|
Square threatened_sq() const { return static_cast<Square>(data >> 8 & 0xff); }
|
||||||
|
Square pc_sq() const { return static_cast<Square>(data & 0xff); }
|
||||||
|
bool add() const {
|
||||||
|
uint32_t b = data >> 28;
|
||||||
|
sf_assume(b == 0 || b == 1);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
using DirtyThreatList = ValueList<DirtyThreat, 80>;
|
||||||
|
|
||||||
|
// A piece can be involved in at most 8 outgoing attacks and 16 incoming attacks.
|
||||||
|
// Moving a piece also can reveal at most 8 discovered attacks.
|
||||||
|
// This implies that a non-castling move can change at most (8 + 16) * 3 + 8 = 80 features.
|
||||||
|
// By similar logic, a castling move can change at most (5 + 1 + 3 + 9) * 2 = 36 features.
|
||||||
|
// Thus, 80 should work as an upper bound.
|
||||||
|
|
||||||
|
struct DirtyThreats {
|
||||||
|
DirtyThreatList list;
|
||||||
|
Color us;
|
||||||
|
Square prevKsq, ksq;
|
||||||
|
|
||||||
|
Bitboard threatenedSqs, threateningSqs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DirtyBoardData {
|
||||||
|
DirtyPiece dp;
|
||||||
|
DirtyThreats dts;
|
||||||
|
};
|
||||||
|
|
||||||
#define ENABLE_INCR_OPERATORS_ON(T) \
|
#define ENABLE_INCR_OPERATORS_ON(T) \
|
||||||
constexpr T& operator++(T& d) { return d = T(int(d) + 1); } \
|
constexpr T& operator++(T& d) { return d = T(int(d) + 1); } \
|
||||||
constexpr T& operator--(T& d) { return d = T(int(d) - 1); }
|
constexpr T& operator--(T& d) { return d = T(int(d) - 1); }
|
||||||
|
|||||||
Reference in New Issue
Block a user