From 0f3f5d85fb5c9f75199f27fbf7a725ff3e8bb4dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 26 Feb 2021 10:13:37 +0100 Subject: [PATCH 001/282] Introduce DistanceFromPV We introduce a metric for each internal node in search, called DistanceFromPV. This distance indicated how far the current node is from the principal variation. We then use this distance to search the nodes which are close to the PV a little deeper (up to 4 plies deeper than the PV): this improves the quality of the search at these nodes and bring better updates for the PV during search. STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 54936 W: 5047 L: 4850 D: 45039 Ptnml(0-2): 183, 3907, 19075, 4136, 167 https://tests.stockfishchess.org/tests/view/6037b88e7f517a561bc4a392 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 49608 W: 1880 L: 1703 D: 46025 Ptnml(0-2): 22, 1514, 21555, 1691, 22 https://tests.stockfishchess.org/tests/view/6038271b7f517a561bc4a3cb Closes https://github.com/official-stockfish/Stockfish/pull/3369 Bench: 5037279 --- src/search.cpp | 23 +++++++++++++++-------- src/search.h | 1 + 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c33bc914..b3bd2f03 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -616,6 +616,7 @@ namespace { moveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; + ss->distanceFromPv = (PvNode ? 0 : ss->distanceFromPv); // Check for the available remaining time if (thisThread == Threads.main()) @@ -1175,8 +1176,12 @@ moves_loop: // When in check, search starts from here // Step 15. Make the move pos.do_move(move, st, givesCheck); - // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be - // re-searched at full depth. + (ss+1)->distanceFromPv = ss->distanceFromPv + moveCount - 1; + + // Step 16. Late moves reduction / extension (LMR, ~200 Elo) + // We use various heuristics for the sons of a node after the first son has + // been searched. In general we would like to reduce them, but there are many + // cases where we extend a son if it has good chances to be "interesting". if ( depth >= 3 && moveCount > 1 + 2 * rootNode && ( !captureOrPromotion @@ -1258,8 +1263,8 @@ moves_loop: // When in check, search starts from here r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - // If we are not in check use statScore, if we are in check - // use sum of main history and first continuation history with an offset + // If we are not in check use statScore, but if we are in check we use + // the sum of main history and first continuation history with an offset. if (ss->inCheck) r -= (thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] - 3833) / 16384; @@ -1267,18 +1272,20 @@ moves_loop: // When in check, search starts from here r -= ss->statScore / 14790; } - Depth d = std::clamp(newDepth - r, 1, newDepth); + // In general we want to cap the LMR depth search at newDepth. But for nodes + // close to the principal variation the cap is at (newDepth + 1), which will + // allow these nodes to be searched deeper than the pv (up to 4 plies deeper). + Depth d = std::clamp(newDepth - r, 1, newDepth + ((ss+1)->distanceFromPv <= 4)); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - doFullDepthSearch = value > alpha && d != newDepth; - + // If the son is reduced and fails high it will be re-searched at full depth + doFullDepthSearch = value > alpha && d < newDepth; didLMR = true; } else { doFullDepthSearch = !PvNode || moveCount > 1; - didLMR = false; } diff --git a/src/search.h b/src/search.h index 3bf3e9ae..5e51c18e 100644 --- a/src/search.h +++ b/src/search.h @@ -47,6 +47,7 @@ struct Stack { Value staticEval; int statScore; int moveCount; + int distanceFromPv; bool inCheck; bool ttPv; bool ttHit; From 9b1274aba3cad440b925283fe7407954743ade78 Mon Sep 17 00:00:00 2001 From: Antoine Champion Date: Sat, 30 Jan 2021 09:50:04 +0100 Subject: [PATCH 002/282] Clean functions returning by const values The codebase contains multiple functions returning by const-value. This patch is a small cleanup making those function returns by value instead, removing the const specifier. closes https://github.com/official-stockfish/Stockfish/pull/3328 No functional change --- AUTHORS | 1 + src/bitboard.cpp | 2 +- src/bitboard.h | 2 +- src/misc.cpp | 4 ++-- src/misc.h | 4 ++-- src/position.cpp | 2 +- src/position.h | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index 5f21c048..3ef7d1b1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,6 +24,7 @@ Ali AlZhrani (Cooffe) Andrew Grant (AndyGrant) Andrey Neporada (nepal) Andy Duplain +Antoine Champion (antoinechampion) Aram Tumanian (atumanian) Arjun Temurnikar Auguste Pop diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 841aa0b6..8146ce97 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -55,7 +55,7 @@ inline Bitboard safe_destination(Square s, int step) { /// Bitboards::pretty() returns an ASCII representation of a bitboard suitable /// to be printed to standard output. Useful for debugging. -const std::string Bitboards::pretty(Bitboard b) { +std::string Bitboards::pretty(Bitboard b) { std::string s = "+---+---+---+---+---+---+---+---+\n"; diff --git a/src/bitboard.h b/src/bitboard.h index 95591fc4..c9555b6b 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -33,7 +33,7 @@ bool probe(Square wksq, Square wpsq, Square bksq, Color us); namespace Bitboards { void init(); -const std::string pretty(Bitboard b); +std::string pretty(Bitboard b); } diff --git a/src/misc.cpp b/src/misc.cpp index 48e20a39..fe920140 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -138,7 +138,7 @@ public: /// the program was compiled) or "Stockfish ", depending on whether /// Version is empty. -const string engine_info(bool to_uci) { +string engine_info(bool to_uci) { const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); string month, day, year; @@ -161,7 +161,7 @@ const string engine_info(bool to_uci) { /// compiler_info() returns a string trying to describe the compiler we use -const std::string compiler_info() { +std::string compiler_info() { #define stringify2(x) #x #define stringify(x) stringify2(x) diff --git a/src/misc.h b/src/misc.h index 7b551ade..7eb75bc7 100644 --- a/src/misc.h +++ b/src/misc.h @@ -28,8 +28,8 @@ #include "types.h" -const std::string engine_info(bool to_uci = false); -const std::string compiler_info(); +std::string engine_info(bool to_uci = false); +std::string compiler_info(); void prefetch(void* addr); void start_logger(const std::string& fname); void* std_aligned_alloc(size_t alignment, size_t size); diff --git a/src/position.cpp b/src/position.cpp index 2eb30ca0..8f9b7eee 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -408,7 +408,7 @@ Position& Position::set(const string& code, Color c, StateInfo* si) { /// Position::fen() returns a FEN representation of the position. In case of /// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. -const string Position::fen() const { +string Position::fen() const { int emptyCnt; std::ostringstream ss; diff --git a/src/position.h b/src/position.h index 3624e29e..e9803756 100644 --- a/src/position.h +++ b/src/position.h @@ -87,7 +87,7 @@ public: // FEN string input/output Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); Position& set(const std::string& code, Color c, StateInfo* si); - const std::string fen() const; + std::string fen() const; // Position representation Bitboard pieces(PieceType pt) const; From 7ffae17f85709e49672a0e98e136b66aea067b2c Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Fri, 26 Feb 2021 10:02:13 +0100 Subject: [PATCH 003/282] Add Stockfish namespace. fixes #3350 and is a small cleanup that might make it easier to use SF in separate projects, like a NNUE trainer or similar. closes https://github.com/official-stockfish/Stockfish/pull/3370 No functional change. --- src/benchmark.cpp | 4 ++++ src/bitbase.cpp | 6 ++++-- src/bitboard.cpp | 6 ++++-- src/bitboard.h | 8 ++++++-- src/endgame.cpp | 4 ++++ src/endgame.h | 3 +++ src/evaluate.cpp | 6 +++++- src/evaluate.h | 4 ++++ src/main.cpp | 2 ++ src/material.cpp | 4 ++++ src/material.h | 4 ++-- src/misc.cpp | 4 ++++ src/misc.h | 4 ++++ src/movegen.cpp | 4 ++++ src/movegen.h | 4 ++++ src/movepick.cpp | 4 ++++ src/movepick.h | 4 ++++ src/nnue/architectures/halfkp_256x2-32-32.h | 4 ++-- src/nnue/evaluate_nnue.cpp | 4 ++-- src/nnue/evaluate_nnue.h | 4 ++-- src/nnue/features/feature_set.h | 4 ++-- src/nnue/features/features_common.h | 4 ++-- src/nnue/features/half_kp.cpp | 4 ++-- src/nnue/features/half_kp.h | 4 ++-- src/nnue/features/index_list.h | 4 ++-- src/nnue/layers/affine_transform.h | 4 ++-- src/nnue/layers/clipped_relu.h | 4 ++-- src/nnue/layers/input_slice.h | 4 ++-- src/nnue/nnue_accumulator.h | 4 ++-- src/nnue/nnue_architecture.h | 4 ++-- src/nnue/nnue_common.h | 4 ++-- src/nnue/nnue_feature_transformer.h | 4 ++-- src/pawns.cpp | 4 ++++ src/pawns.h | 4 ++-- src/position.cpp | 4 ++++ src/position.h | 3 +++ src/psqt.cpp | 3 +++ src/psqt.h | 4 ++-- src/search.cpp | 6 +++++- src/search.h | 4 ++++ src/syzygy/tbprobe.cpp | 8 ++++++-- src/syzygy/tbprobe.h | 4 ++-- src/thread.cpp | 4 ++++ src/thread.h | 3 +++ src/thread_win32_osx.h | 8 ++++++++ src/timeman.cpp | 4 ++++ src/timeman.h | 4 ++++ src/tt.cpp | 4 ++++ src/tt.h | 4 ++++ src/tune.cpp | 8 ++++++++ src/tune.h | 4 ++++ src/types.h | 4 ++++ src/uci.cpp | 4 ++++ src/uci.h | 4 ++++ src/ucioption.cpp | 4 ++++ tests/instrumented.sh | 18 +++++++++--------- 56 files changed, 199 insertions(+), 57 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 7cb04382..7945a453 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -92,6 +92,8 @@ const vector Defaults = { } // namespace +namespace Stockfish { + /// setup_bench() builds a list of UCI commands to be run by bench. There /// are five parameters: TT size in MB, number of search threads that /// should be used, the limit value spent for each position, a file name @@ -168,3 +170,5 @@ vector setup_bench(const Position& current, istream& is) { return list; } + +} // namespace Stockfish diff --git a/src/bitbase.cpp b/src/bitbase.cpp index b640eabb..ece9ec72 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -23,6 +23,8 @@ #include "bitboard.h" #include "types.h" +namespace Stockfish { + namespace { // There are 24 possible pawn squares: files A to D and ranks from 2 to 7. @@ -66,7 +68,6 @@ namespace { } // namespace - bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) { assert(file_of(wpsq) <= FILE_D); @@ -96,7 +97,6 @@ void Bitbases::init() { KPKBitbase.set(idx); } - namespace { KPKPosition::KPKPosition(unsigned idx) { @@ -168,3 +168,5 @@ namespace { } } // namespace + +} // namespace Stockfish diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 8146ce97..a2021449 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -22,6 +22,8 @@ #include "bitboard.h" #include "misc.h" +namespace Stockfish { + uint8_t PopCnt16[1 << 16]; uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; @@ -42,7 +44,6 @@ namespace { } - /// safe_destination() returns the bitboard of target square for the given step /// from the given square. If the step is off the board, returns empty bitboard. @@ -111,7 +112,6 @@ void Bitboards::init() { } } - namespace { Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { @@ -211,3 +211,5 @@ namespace { } } } + +} // namespace Stockfish diff --git a/src/bitboard.h b/src/bitboard.h index c9555b6b..e14fe0df 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -23,19 +23,21 @@ #include "types.h" +namespace Stockfish { + namespace Bitbases { void init(); bool probe(Square wksq, Square wpsq, Square bksq, Color us); -} +} // namespace Stockfish::Bitbases namespace Bitboards { void init(); std::string pretty(Bitboard b); -} +} // namespace Stockfish::Bitboards constexpr Bitboard AllSquares = ~Bitboard(0); constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; @@ -430,4 +432,6 @@ inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } +} // namespace Stockfish + #endif // #ifndef BITBOARD_H_INCLUDED diff --git a/src/endgame.cpp b/src/endgame.cpp index 1489a36b..a44d3a1c 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -22,6 +22,8 @@ #include "endgame.h" #include "movegen.h" +namespace Stockfish { + namespace { // Used to drive the king towards the edge of the board @@ -741,3 +743,5 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // it's probably at least a draw even with the pawn. return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; } + +} // namespace Stockfish diff --git a/src/endgame.h b/src/endgame.h index 860cc863..146111b9 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -28,6 +28,7 @@ #include "position.h" #include "types.h" +namespace Stockfish { /// EndgameCode lists all supported endgame functions by corresponding codes @@ -120,4 +121,6 @@ namespace Endgames { } } +} // namespace Stockfish + #endif // #ifndef ENDGAME_H_INCLUDED diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d55ef695..d43b8fa7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -54,7 +54,9 @@ using namespace std; -using namespace Eval::NNUE; +using namespace Stockfish::Eval::NNUE; + +namespace Stockfish { namespace Eval { @@ -1146,3 +1148,5 @@ std::string Eval::trace(const Position& pos) { return ss.str(); } + +} // namespace Stockfish diff --git a/src/evaluate.h b/src/evaluate.h index 8beca2d0..6210bd58 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -23,6 +23,8 @@ #include "types.h" +namespace Stockfish { + class Position; namespace Eval { @@ -49,4 +51,6 @@ namespace Eval { } // namespace Eval +} // namespace Stockfish + #endif // #ifndef EVALUATE_H_INCLUDED diff --git a/src/main.cpp b/src/main.cpp index ef662468..62e0ed52 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,8 @@ #include "tt.h" #include "uci.h" +using namespace Stockfish; + int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; diff --git a/src/material.cpp b/src/material.cpp index e76641d1..84d7a4bd 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -24,6 +24,8 @@ using namespace std; +namespace Stockfish { + namespace { #define S(mg, eg) make_score(mg, eg) @@ -223,3 +225,5 @@ Entry* probe(const Position& pos) { } } // namespace Material + +} // namespace Stockfish diff --git a/src/material.h b/src/material.h index be26425f..26535a53 100644 --- a/src/material.h +++ b/src/material.h @@ -24,7 +24,7 @@ #include "position.h" #include "types.h" -namespace Material { +namespace Stockfish::Material { /// Material::Entry contains various information about a material configuration. /// It contains a material imbalance evaluation, a function pointer to a special @@ -66,6 +66,6 @@ typedef HashTable Table; Entry* probe(const Position& pos); -} // namespace Material +} // namespace Stockfish::Material #endif // #ifndef MATERIAL_H_INCLUDED diff --git a/src/misc.cpp b/src/misc.cpp index fe920140..834e909b 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -61,6 +61,8 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); using namespace std; +namespace Stockfish { + namespace { /// Version number. If Version is left empty, then compile date in the format @@ -626,3 +628,5 @@ void init(int argc, char* argv[]) { } // namespace CommandLine + +} // namespace Stockfish diff --git a/src/misc.h b/src/misc.h index 7eb75bc7..f834e470 100644 --- a/src/misc.h +++ b/src/misc.h @@ -28,6 +28,8 @@ #include "types.h" +namespace Stockfish { + std::string engine_info(bool to_uci = false); std::string compiler_info(); void prefetch(void* addr); @@ -143,4 +145,6 @@ namespace CommandLine { extern std::string workingDirectory; // path of the working directory } +} // namespace Stockfish + #endif // #ifndef MISC_H_INCLUDED diff --git a/src/movegen.cpp b/src/movegen.cpp index c9d6a90d..51df6d07 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -21,6 +21,8 @@ #include "movegen.h" #include "position.h" +namespace Stockfish { + namespace { template @@ -362,3 +364,5 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { return moveList; } + +} // namespace Stockfish diff --git a/src/movegen.h b/src/movegen.h index 85887a64..3f895f05 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -23,6 +23,8 @@ #include "types.h" +namespace Stockfish { + class Position; enum GenType { @@ -70,4 +72,6 @@ private: ExtMove moveList[MAX_MOVES], *last; }; +} // namespace Stockfish + #endif // #ifndef MOVEGEN_H_INCLUDED diff --git a/src/movepick.cpp b/src/movepick.cpp index 0ceeb8ea..4ff4cff4 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -20,6 +20,8 @@ #include "movepick.h" +namespace Stockfish { + namespace { enum Stages { @@ -263,3 +265,5 @@ top: assert(false); return MOVE_NONE; // Silence warning } + +} // namespace Stockfish diff --git a/src/movepick.h b/src/movepick.h index ea599cda..c76d4957 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -27,6 +27,8 @@ #include "position.h" #include "types.h" +namespace Stockfish { + /// StatsEntry stores the stat table value. It is usually a number but could /// be a move or even a nested history. We use a class instead of naked value /// to directly call history update operator<<() on the entry so to use stats @@ -156,4 +158,6 @@ private: ExtMove moves[MAX_MOVES]; }; +} // namespace Stockfish + #endif // #ifndef MOVEPICK_H_INCLUDED diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h index a0fe2e0a..a6768204 100644 --- a/src/nnue/architectures/halfkp_256x2-32-32.h +++ b/src/nnue/architectures/halfkp_256x2-32-32.h @@ -28,7 +28,7 @@ #include "../layers/affine_transform.h" #include "../layers/clipped_relu.h" -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // Input features used in evaluation function using RawFeatures = Features::FeatureSet< @@ -49,6 +49,6 @@ using OutputLayer = AffineTransform; using Network = Layers::OutputLayer; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index fb4a5021..5416f13e 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -29,7 +29,7 @@ #include "evaluate_nnue.h" -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // Input feature converter LargePagePtr feature_transformer; @@ -141,4 +141,4 @@ namespace Eval::NNUE { return ReadParameters(stream); } -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index c30d7c01..24aa6cc0 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -25,7 +25,7 @@ #include -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // Hash value of evaluation function structure constexpr std::uint32_t kHashValue = @@ -54,6 +54,6 @@ namespace Eval::NNUE { template using LargePagePtr = std::unique_ptr>; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index 77d2220f..a3fea9c0 100644 --- a/src/nnue/features/feature_set.h +++ b/src/nnue/features/feature_set.h @@ -24,7 +24,7 @@ #include "features_common.h" #include -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { // Class template that represents a list of values template @@ -64,6 +64,6 @@ namespace Eval::NNUE::Features { }; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features #endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h index b0073b8b..118ec953 100644 --- a/src/nnue/features/features_common.h +++ b/src/nnue/features/features_common.h @@ -24,7 +24,7 @@ #include "../../evaluate.h" #include "../nnue_common.h" -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { class IndexList; @@ -40,6 +40,6 @@ namespace Eval::NNUE::Features { kFriend // side to move }; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features #endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index b52a45f2..ac6317e7 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -21,7 +21,7 @@ #include "half_kp.h" #include "index_list.h" -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { // Orient a square according to perspective (rotates by 180 for black) inline Square orient(Color perspective, Square s) { @@ -65,4 +65,4 @@ namespace Eval::NNUE::Features { template class HalfKP; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index d203dc22..2461acb7 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -24,7 +24,7 @@ #include "../../evaluate.h" #include "features_common.h" -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { // Feature HalfKP: Combination of the position of own king // and the position of pieces other than kings @@ -54,6 +54,6 @@ namespace Eval::NNUE::Features { IndexList* removed, IndexList* added); }; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features #endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h index ca3ebee5..9f03993b 100644 --- a/src/nnue/features/index_list.h +++ b/src/nnue/features/index_list.h @@ -24,7 +24,7 @@ #include "../../position.h" #include "../nnue_architecture.h" -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { // Class template used for feature index list template @@ -59,6 +59,6 @@ namespace Eval::NNUE::Features { : public ValueList { }; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features #endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index adf152ee..d2713c5a 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -24,7 +24,7 @@ #include #include "../nnue_common.h" -namespace Eval::NNUE::Layers { +namespace Stockfish::Eval::NNUE::Layers { // Affine transformation layer template @@ -459,6 +459,6 @@ namespace Eval::NNUE::Layers { #endif }; -} // namespace Eval::NNUE::Layers +} // namespace Stockfish::Eval::NNUE::Layers #endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 3ed41ee5..a10e3e48 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -23,7 +23,7 @@ #include "../nnue_common.h" -namespace Eval::NNUE::Layers { +namespace Stockfish::Eval::NNUE::Layers { // Clipped ReLU template @@ -161,6 +161,6 @@ namespace Eval::NNUE::Layers { PreviousLayer previous_layer_; }; -} // namespace Eval::NNUE::Layers +} // namespace Stockfish::Eval::NNUE::Layers #endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index efdf0725..43b06eec 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -23,7 +23,7 @@ #include "../nnue_common.h" -namespace Eval::NNUE::Layers { +namespace Stockfish::Eval::NNUE::Layers { // Input layer template @@ -63,6 +63,6 @@ class InputSlice { private: }; -} // namespace Layers +} // namespace Stockfish::Eval::NNUE::Layers #endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 6b4390f9..55fafa13 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -23,7 +23,7 @@ #include "nnue_architecture.h" -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // The accumulator of a StateInfo without parent is set to the INIT state enum AccumulatorState { EMPTY, COMPUTED, INIT }; @@ -35,6 +35,6 @@ namespace Eval::NNUE { AccumulatorState state[2]; }; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // NNUE_ACCUMULATOR_H_INCLUDED diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index ad5be006..1680368e 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -24,7 +24,7 @@ // Defines the network structure #include "architectures/halfkp_256x2-32-32.h" -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, ""); static_assert(Network::kOutputDimensions == 1, ""); @@ -33,6 +33,6 @@ namespace Eval::NNUE { // Trigger for full calculation instead of difference calculation constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 33e58745..09a152a5 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -43,7 +43,7 @@ #include #endif -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // Version of the evaluation file constexpr std::uint32_t kVersion = 0x7AF32F16u; @@ -127,6 +127,6 @@ namespace Eval::NNUE { return result; } -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2641321e..1e0b0e6d 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -27,7 +27,7 @@ #include // std::memset() -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // If vector instructions are enabled, we update and refresh the // accumulator tile by tile such that each tile fits in the CPU's @@ -412,6 +412,6 @@ namespace Eval::NNUE { WeightType weights_[kHalfDimensions * kInputDimensions]; }; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED diff --git a/src/pawns.cpp b/src/pawns.cpp index cd4d4e45..9a0610a0 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -24,6 +24,8 @@ #include "position.h" #include "thread.h" +namespace Stockfish { + namespace { #define V Value @@ -298,3 +300,5 @@ template Score Entry::do_king_safety(const Position& pos); template Score Entry::do_king_safety(const Position& pos); } // namespace Pawns + +} // namespace Stockfish diff --git a/src/pawns.h b/src/pawns.h index 888bf990..124619d6 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -23,7 +23,7 @@ #include "position.h" #include "types.h" -namespace Pawns { +namespace Stockfish::Pawns { /// Pawns::Entry contains various information about a pawn structure. A lookup /// to the pawn hash table (performed by calling the probe function) returns a @@ -65,6 +65,6 @@ typedef HashTable Table; Entry* probe(const Position& pos); -} // namespace Pawns +} // namespace Stockfish::Pawns #endif // #ifndef PAWNS_H_INCLUDED diff --git a/src/position.cpp b/src/position.cpp index 8f9b7eee..17b165b9 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -34,6 +34,8 @@ using std::string; +namespace Stockfish { + namespace Zobrist { Key psq[PIECE_NB][SQUARE_NB]; @@ -1338,3 +1340,5 @@ bool Position::pos_is_ok() const { return true; } + +} // namespace Stockfish diff --git a/src/position.h b/src/position.h index e9803756..4ab3761f 100644 --- a/src/position.h +++ b/src/position.h @@ -31,6 +31,7 @@ #include "nnue/nnue_accumulator.h" +namespace Stockfish { /// StateInfo struct stores information needed to restore a Position object to /// its previous state when we retract a move. Whenever a move is made on the @@ -423,4 +424,6 @@ inline StateInfo* Position::state() const { return st; } +} // namespace Stockfish + #endif // #ifndef POSITION_H_INCLUDED diff --git a/src/psqt.cpp b/src/psqt.cpp index cfade295..33a3e00c 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -24,6 +24,7 @@ #include "bitboard.h" #include "types.h" +namespace Stockfish { namespace { @@ -126,3 +127,5 @@ void init() { } } // namespace PSQT + +} // namespace Stockfish diff --git a/src/psqt.h b/src/psqt.h index 8b4fd6eb..7abb1483 100644 --- a/src/psqt.h +++ b/src/psqt.h @@ -24,7 +24,7 @@ #include "types.h" -namespace PSQT +namespace Stockfish::PSQT { extern Score psq[PIECE_NB][SQUARE_NB]; @@ -32,7 +32,7 @@ extern Score psq[PIECE_NB][SQUARE_NB]; // Fill psqt array from a set of internally linked parameters extern void init(); -} // namespace PSQT +} // namespace Stockfish::PSQT #endif // PSQT_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index b3bd2f03..4e2ec095 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -35,6 +35,8 @@ #include "uci.h" #include "syzygy/tbprobe.h" +namespace Stockfish { + namespace Search { LimitsType Limits; @@ -422,7 +424,7 @@ void Thread::search() { while (true) { Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); - bestValue = ::search(rootPos, ss, alpha, beta, adjustedDepth, false); + bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the @@ -2034,3 +2036,5 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { m.tbRank = 0; } } + +} // namespace Stockfish diff --git a/src/search.h b/src/search.h index 5e51c18e..6f9fbd05 100644 --- a/src/search.h +++ b/src/search.h @@ -25,6 +25,8 @@ #include "movepick.h" #include "types.h" +namespace Stockfish { + class Position; namespace Search { @@ -107,4 +109,6 @@ void clear(); } // namespace Search +} // namespace Stockfish + #endif // #ifndef SEARCH_H_INCLUDED diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 115815e1..5cfd38e5 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -50,9 +50,11 @@ #include #endif -using namespace Tablebases; +using namespace Stockfish::Tablebases; -int Tablebases::MaxCardinality; +int Stockfish::Tablebases::MaxCardinality; + +namespace Stockfish { namespace { @@ -1610,3 +1612,5 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { return true; } + +} // namespace Stockfish diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index cefd39ce..56734af9 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -23,7 +23,7 @@ #include "../search.h" -namespace Tablebases { +namespace Stockfish::Tablebases { enum WDLScore { WDLLoss = -2, // Loss @@ -73,6 +73,6 @@ inline std::ostream& operator<<(std::ostream& os, const ProbeState v) { return os; } -} +} // namespace Stockfish::Tablebases #endif diff --git a/src/thread.cpp b/src/thread.cpp index a12c0bcc..3ef73dfa 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -26,6 +26,8 @@ #include "syzygy/tbprobe.h" #include "tt.h" +namespace Stockfish { + ThreadPool Threads; // Global object @@ -258,3 +260,5 @@ void ThreadPool::wait_for_search_finished() const { if (th != front()) th->wait_for_search_finished(); } + +} // namespace Stockfish diff --git a/src/thread.h b/src/thread.h index 585f2088..2b3dea0d 100644 --- a/src/thread.h +++ b/src/thread.h @@ -32,6 +32,7 @@ #include "search.h" #include "thread_win32_osx.h" +namespace Stockfish { /// Thread class keeps together all the thread-related stuff. We use /// per-thread pawn and material hash tables so that once we get a @@ -128,4 +129,6 @@ private: extern ThreadPool Threads; +} // namespace Stockfish + #endif // #ifndef THREAD_H_INCLUDED diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index a0e4d199..a21674cc 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -31,6 +31,8 @@ #include +namespace Stockfish { + static const size_t TH_STACK_SIZE = 8 * 1024 * 1024; template > @@ -57,10 +59,16 @@ public: void join() { pthread_join(thread, NULL); } }; +} // namespace Stockfish + #else // Default case: use STL classes +namespace Stockfish { + typedef std::thread NativeThread; +} // namespace Stockfish + #endif #endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED diff --git a/src/timeman.cpp b/src/timeman.cpp index fc4fbaac..f742d1e4 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -24,6 +24,8 @@ #include "timeman.h" #include "uci.h" +namespace Stockfish { + TimeManagement Time; // Our global time management object @@ -95,3 +97,5 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { if (Options["Ponder"]) optimumTime += optimumTime / 4; } + +} // namespace Stockfish diff --git a/src/timeman.h b/src/timeman.h index 55a68de4..b1878d65 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -23,6 +23,8 @@ #include "search.h" #include "thread.h" +namespace Stockfish { + /// The TimeManagement class computes the optimal time to think depending on /// the maximum available time, the game move number and other parameters. @@ -44,4 +46,6 @@ private: extern TimeManagement Time; +} // namespace Stockfish + #endif // #ifndef TIMEMAN_H_INCLUDED diff --git a/src/tt.cpp b/src/tt.cpp index cb5af5c8..1f495ca9 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -26,6 +26,8 @@ #include "tt.h" #include "uci.h" +namespace Stockfish { + TranspositionTable TT; // Our global transposition table /// TTEntry::save() populates the TTEntry with a new node's data, possibly @@ -156,3 +158,5 @@ int TranspositionTable::hashfull() const { return cnt / ClusterSize; } + +} // namespace Stockfish diff --git a/src/tt.h b/src/tt.h index a750b6c4..d915d92e 100644 --- a/src/tt.h +++ b/src/tt.h @@ -22,6 +22,8 @@ #include "misc.h" #include "types.h" +namespace Stockfish { + /// TTEntry struct is the 10 bytes transposition table entry, defined as below: /// /// key 16 bit @@ -100,4 +102,6 @@ private: extern TranspositionTable TT; +} // namespace Stockfish + #endif // #ifndef TT_H_INCLUDED diff --git a/src/tune.cpp b/src/tune.cpp index 424bdac8..d9618efc 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -26,6 +26,8 @@ using std::string; +namespace Stockfish { + bool Tune::update_on_last; const UCI::Option* LastOption = nullptr; BoolConditions Conditions; @@ -126,6 +128,8 @@ void BoolConditions::set() { sync_cout << binary[i] << sync_endl; } +} // namespace Stockfish + // Init options with tuning session results instead of default values. Useful to // get correct bench signature after a tuning session or to test tuned values. @@ -138,7 +142,11 @@ void BoolConditions::set() { #include +namespace Stockfish { + void Tune::read_results() { /* ...insert your values here... */ } + +} // namespace Stockfish diff --git a/src/tune.h b/src/tune.h index c2cd0c97..c904c09d 100644 --- a/src/tune.h +++ b/src/tune.h @@ -24,6 +24,8 @@ #include #include +namespace Stockfish { + typedef std::pair Range; // Option's min-max values typedef Range (RangeFun) (int); @@ -190,4 +192,6 @@ public: #define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \ TUNE(Conditions, set_conditions) +} // namespace Stockfish + #endif // #ifndef TUNE_H_INCLUDED diff --git a/src/types.h b/src/types.h index d270384e..efebce1a 100644 --- a/src/types.h +++ b/src/types.h @@ -83,6 +83,8 @@ # define pext(b, m) 0 #endif +namespace Stockfish { + #ifdef USE_POPCNT constexpr bool HasPopCnt = true; #else @@ -482,6 +484,8 @@ constexpr Key make_key(uint64_t seed) { return seed * 6364136223846793005ULL + 1442695040888963407ULL; } +} // namespace Stockfish + #endif // #ifndef TYPES_H_INCLUDED #include "tune.h" // Global visibility to tuning setup diff --git a/src/uci.cpp b/src/uci.cpp index b3017e91..47a8824e 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -34,6 +34,8 @@ using namespace std; +namespace Stockfish { + extern vector setup_bench(const Position&, istream&); namespace { @@ -369,3 +371,5 @@ Move UCI::to_move(const Position& pos, string& str) { return MOVE_NONE; } + +} // namespace Stockfish diff --git a/src/uci.h b/src/uci.h index edcfcade..d3160109 100644 --- a/src/uci.h +++ b/src/uci.h @@ -24,6 +24,8 @@ #include "types.h" +namespace Stockfish { + class Position; namespace UCI { @@ -78,4 +80,6 @@ Move to_move(const Position& pos, std::string& str); extern UCI::OptionsMap Options; +} // namespace Stockfish + #endif // #ifndef UCI_H_INCLUDED diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 03f377b3..d59c0100 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -31,6 +31,8 @@ using std::string; +namespace Stockfish { + UCI::OptionsMap Options; // Global object namespace UCI { @@ -190,3 +192,5 @@ Option& Option::operator=(const string& v) { } } // namespace UCI + +} // namespace Stockfish diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 03e9c9de..bfb50e94 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -39,16 +39,16 @@ case $1 in threads="2" cat << EOF > tsan.supp -race:TTEntry::move -race:TTEntry::depth -race:TTEntry::bound -race:TTEntry::save -race:TTEntry::value -race:TTEntry::eval -race:TTEntry::is_pv +race:Stockfish::TTEntry::move +race:Stockfish::TTEntry::depth +race:Stockfish::TTEntry::bound +race:Stockfish::TTEntry::save +race:Stockfish::TTEntry::value +race:Stockfish::TTEntry::eval +race:Stockfish::TTEntry::is_pv -race:TranspositionTable::probe -race:TranspositionTable::hashfull +race:Stockfish::TranspositionTable::probe +race:Stockfish::TranspositionTable::hashfull EOF From d4b864ff126e4a5784c9d7f636be057c247738f1 Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Wed, 3 Mar 2021 22:30:23 +0800 Subject: [PATCH 004/282] Do not try to use large pages on 32 bit Windows. verified to work on windows XP. fixes #3379 closes https://github.com/official-stockfish/Stockfish/pull/3380 No functional change. --- src/misc.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 834e909b..7600fc11 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -362,7 +362,7 @@ void std_aligned_free(void* ptr) { /// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. #if defined(_WIN32) - +#if defined(_WIN64) static void* aligned_large_pages_alloc_win(size_t allocSize) { HANDLE hProcessToken { }; @@ -407,15 +407,20 @@ static void* aligned_large_pages_alloc_win(size_t allocSize) { return mem; } +#endif void* aligned_large_pages_alloc(size_t allocSize) { +#if defined(_WIN64) // Try to allocate large pages void* mem = aligned_large_pages_alloc_win(allocSize); // Fall back to regular, page aligned, allocation if necessary if (!mem) mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +#else + void* mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +#endif return mem; } From 5346f1c6c72e46d66bb4c21259f8c06096c63034 Mon Sep 17 00:00:00 2001 From: mattginsberg Date: Sun, 28 Feb 2021 07:59:07 -0800 Subject: [PATCH 005/282] Deal with commented lines in UCI input commands starting with '#' as the first character will be ignored closes https://github.com/official-stockfish/Stockfish/pull/3378 No functional change --- src/uci.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uci.cpp b/src/uci.cpp index 47a8824e..051ff2e0 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -277,7 +277,7 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; - else + else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; } while (token != "quit" && argc == 1); // Command line args are one-shot From b74274628c052cc910e36202b88bc5f81724d78c Mon Sep 17 00:00:00 2001 From: bmc4 Date: Fri, 5 Mar 2021 08:57:43 -0300 Subject: [PATCH 006/282] Use Bitboard over Square in movegen It uses pos.checkers() on target when movegen is the type of EVASION. It simplify the code. And it's also expected a slightly speed up, because Bitboard is more direct when doing bitwise. Passed STC: LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 28176 W: 2506 L: 2437 D: 23233 Ptnml(0-2): 80, 1904, 10063, 1949, 92 https://tests.stockfishchess.org/tests/view/60421d18ddcba5f0627bb6a9 Passed LTC: LLR: 2.93 (-2.94,2.94) {-0.75,0.25} Total: 9704 W: 402 L: 341 D: 8961 Ptnml(0-2): 3, 279, 4230, 334, 6 https://tests.stockfishchess.org/tests/view/60422823ddcba5f0627bb6ae closes https://github.com/official-stockfish/Stockfish/pull/3383 No functional change --- src/movegen.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 51df6d07..c5d76afa 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -223,11 +223,8 @@ namespace { target = ~pos.pieces(); break; case EVASIONS: - { - Square checksq = lsb(pos.checkers()); - target = between_bb(pos.square(Us), checksq) | checksq; + target = between_bb(pos.square(Us), lsb(pos.checkers())) | pos.checkers(); break; - } case NON_EVASIONS: target = ~pos.pieces(Us); break; From f3b296c2e2061951d366edfbd5287f336e865553 Mon Sep 17 00:00:00 2001 From: Topologist Date: Mon, 8 Mar 2021 19:46:41 +0100 Subject: [PATCH 007/282] Change advanced pawn push threshold A pawn push is now considered to be "advanced" if the relative destination rank is > 6 (previously it was > 5). This affects the search heuristic. Also remove an assert concerning en passant moves in qsearch(). STC: LLR: 2.97 (-2.94,2.94) {-0.25,1.25} Total: 46744 W: 4224 L: 4040 D: 38480 Ptnml(0-2): 165, 3206, 16451, 3380, 170 https://tests.stockfishchess.org/tests/view/604746082433018de7a3872e LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 107840 W: 4198 L: 3892 D: 99750 Ptnml(0-2): 58, 3472, 46557, 3772, 61 https://tests.stockfishchess.org/tests/view/60475eae2433018de7a38737 Closes https://github.com/official-stockfish/Stockfish/pull/3389 Bench: 4796780 --- AUTHORS | 1 + src/position.h | 2 +- src/search.cpp | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 3ef7d1b1..662cc6ec 100644 --- a/AUTHORS +++ b/AUTHORS @@ -168,6 +168,7 @@ Sergio Vieri (sergiovieri) sf-x Shane Booth (shane31) Shawn Varghese (xXH4CKST3RXx) +Siad Daboul (Topologist) Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) Steinar Gunderson (sesse) diff --git a/src/position.h b/src/position.h index 4ab3761f..a7654aa1 100644 --- a/src/position.h +++ b/src/position.h @@ -312,7 +312,7 @@ inline bool Position::pawn_passed(Color c, Square s) const { inline bool Position::advanced_pawn_push(Move m) const { return type_of(moved_piece(m)) == PAWN - && relative_rank(sideToMove, to_sq(m)) > RANK_5; + && relative_rank(sideToMove, to_sq(m)) > RANK_6; } inline int Position::pawns_on_same_color_squares(Color c, Square s) const { diff --git a/src/search.cpp b/src/search.cpp index 4e2ec095..fa592a85 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1575,15 +1575,13 @@ moves_loop: // When in check, search starts from here moveCount++; - // Futility pruning + // Futility pruning and moveCount pruning if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck && futilityBase > -VALUE_KNOWN_WIN && !pos.advanced_pawn_push(move)) { - assert(type_of(move) != EN_PASSANT); // Due to !pos.advanced_pawn_push - // moveCount pruning if (moveCount > 2) continue; From 939395729c78dd43816826ffdb0a61f33a833e9f Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 16 Mar 2021 20:51:31 +0100 Subject: [PATCH 008/282] Introduce least_significant_square_bb() Introducing least_significant_square_bb(). It is a function that returns a value equal to square_bb(lsb(bb)), but it uses fewer instruction. It should speed up more on older processors like armv7-a Clang. Passed STC: LLR: 2.93 (-2.94,2.94) {-0.25,1.25} Total: 213200 W: 19171 L: 18753 D: 175276 Ptnml(0-2): 680, 14513, 75831, 14861, 715 https://tests.stockfishchess.org/tests/view/604bc7632433018de7a38982 Closes https://github.com/official-stockfish/Stockfish/pull/3391 No functional change --- src/bitboard.h | 7 +++++++ src/position.cpp | 10 +++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index e14fe0df..1b6af3ea 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -414,6 +414,13 @@ inline Square msb(Bitboard b) { #endif +/// least_significant_square_bb() returns the bitboard of the least significant +/// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)). + +inline Bitboard least_significant_square_bb(Bitboard b) { + assert(b); + return b & -b; +} /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard diff --git a/src/position.cpp b/src/position.cpp index 17b165b9..f4739413 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1109,7 +1109,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = PawnValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } @@ -1118,7 +1118,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = KnightValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); } else if ((bb = stmAttackers & pieces(BISHOP))) @@ -1126,7 +1126,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = BishopValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } @@ -1135,7 +1135,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = RookValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); } @@ -1144,7 +1144,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = QueenValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) | (attacks_bb(to, occupied) & pieces(ROOK , QUEEN)); } From 4b509559fbabe8a41cb8387c71d07bb1c7b78d6f Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 15 Mar 2021 19:52:45 -0300 Subject: [PATCH 009/282] Simplify move generation (1/2) STC: LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 29792 W: 2611 L: 2545 D: 24636 Ptnml(0-2): 94, 1982, 10659, 2086, 75 https://tests.stockfishchess.org/tests/view/604fe5b62433018de7a38ba8 LTC: LLR: 2.92 (-2.94,2.94) {-0.75,0.25} Total: 22040 W: 826 L: 777 D: 20437 Ptnml(0-2): 8, 646, 9664, 693, 9 https://tests.stockfishchess.org/tests/view/604fec892433018de7a38bac Closes https://github.com/official-stockfish/Stockfish/pull/3399 No functional change --- src/movegen.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index c5d76afa..d8c4370a 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -182,17 +182,12 @@ namespace { Bitboard bb = piecesToMove & pos.pieces(Pt); - if (!bb) - return moveList; - - [[maybe_unused]] const Bitboard checkSquares = pos.check_squares(Pt); - while (bb) { Square from = pop_lsb(&bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; if constexpr (Checks) - b &= checkSquares; + b &= pos.check_squares(Pt); while (b) *moveList++ = make_move(from, pop_lsb(&b)); From 830f597134bc942554283833623f12aa970bcad6 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 16 Mar 2021 12:21:24 -0300 Subject: [PATCH 010/282] Simplify move generation (2/2) STC: LLR: 2.97 (-2.94,2.94) {-1.25,0.25} Total: 39352 W: 3551 L: 3493 D: 32308 Ptnml(0-2): 143, 2695, 13928, 2781, 129 https://tests.stockfishchess.org/tests/view/6050007a2433018de7a38bbb LTC: LLR: 2.96 (-2.94,2.94) {-0.75,0.25} Total: 44944 W: 1629 L: 1596 D: 41719 Ptnml(0-2): 22, 1319, 19762, 1342, 27 https://tests.stockfishchess.org/tests/view/60500e892433018de7a38bc4 Closes https://github.com/official-stockfish/Stockfish/pull/3399 No functional change --- src/movegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index d8c4370a..5dbc37ce 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -63,7 +63,7 @@ namespace { Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; - Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target: + Bitboard enemies = (Type == EVASIONS ? pos.checkers(): Type == CAPTURES ? target : pos.pieces(Them)); // Single and double pawn pushes, no promotions From d58e83695f1dbe5bb75ca9e5d6775757b5fd5f7a Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Thu, 11 Mar 2021 14:51:20 +0300 Subject: [PATCH 011/282] Remove advanced_pawn_push() Continuation of work by @topologist: we now do futility pruning and movecount pruning in qsearch() for pawn pushes up to the 7th rank. So the condition to avoid the pruning is if the move is a promotion or not. This allows to get rid of the advanced_pawn_push() function in position.h alltogether. Passed STC https://tests.stockfishchess.org/tests/view/6048c5842433018de7a387e6 LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 34424 W: 3081 L: 3015 D: 28328 Ptnml(0-2): 110, 2442, 12052, 2488, 120 Passed LTC https://tests.stockfishchess.org/tests/view/6048f7d22433018de7a387f0 LLR: 2.94 (-2.94,2.94) {-0.75,0.25} Total: 142024 W: 5170 L: 5202 D: 131652 Ptnml(0-2): 50, 4678, 61613, 4596, 75 Closes https://github.com/official-stockfish/Stockfish/pull/3390 Bench: 4339126 --- src/position.h | 6 ------ src/search.cpp | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/position.h b/src/position.h index a7654aa1..d470ef90 100644 --- a/src/position.h +++ b/src/position.h @@ -128,7 +128,6 @@ public: bool capture(Move m) const; bool capture_or_promotion(Move m) const; bool gives_check(Move m) const; - bool advanced_pawn_push(Move m) const; Piece moved_piece(Move m) const; Piece captured_piece() const; @@ -310,11 +309,6 @@ inline bool Position::pawn_passed(Color c, Square s) const { return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); } -inline bool Position::advanced_pawn_push(Move m) const { - return type_of(moved_piece(m)) == PAWN - && relative_rank(sideToMove, to_sq(m)) > RANK_6; -} - inline int Position::pawns_on_same_color_squares(Color c, Square s) const { return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares)); } diff --git a/src/search.cpp b/src/search.cpp index fa592a85..af7f801f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1579,7 +1579,7 @@ moves_loop: // When in check, search starts from here if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck && futilityBase > -VALUE_KNOWN_WIN - && !pos.advanced_pawn_push(move)) + && type_of(move) != PROMOTION) { if (moveCount > 2) From 50890616591443ab06faa0927747bf14c8d450e3 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 15 Mar 2021 16:06:42 -0300 Subject: [PATCH 012/282] Change definition of between_bb() We remark that in current master, most of our use cases for between_bb() can be optimized if the second parameter of the function is added to the segment. So we change the definition of between_bb(s1, s2) such that it excludes s1 but includes s2. We also use a precomputed array for between_bb() for another small speed gain (see https://tests.stockfishchess.org/tests/view/604d09f72433018de7a389fb). Passed STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 18736 W: 1746 L: 1607 D: 15383 Ptnml(0-2): 61, 1226, 6644, 1387, 50 https://tests.stockfishchess.org/tests/view/60428c84ddcba5f0627bb6e4 Yellow LTC: LTC: LLR: -3.00 (-2.94,2.94) {0.25,1.25} Total: 39144 W: 1431 L: 1413 D: 36300 Ptnml(0-2): 13, 1176, 17184, 1178, 21 https://tests.stockfishchess.org/tests/view/605128702433018de7a38ca1 Closes https://github.com/official-stockfish/Stockfish/pull/3397 --------- Verified for correctness by running perft on the following position: ./stockfish position fen 4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21 go perft 6 Nodes searched: 6136386434 -------- No functional change --- src/bitboard.cpp | 9 ++++++++- src/bitboard.h | 20 ++++++++++++-------- src/movegen.cpp | 6 +++--- src/position.cpp | 8 ++++---- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index a2021449..2da4d728 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -29,6 +29,7 @@ uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; Bitboard SquareBB[SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB]; +Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; @@ -107,8 +108,14 @@ void Bitboards::init() { for (PieceType pt : { BISHOP, ROOK }) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) + { if (PseudoAttacks[pt][s1] & s2) - 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] = (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1))); + } + BetweenBB[s1][s2] |= s2; + } } } diff --git a/src/bitboard.h b/src/bitboard.h index 1b6af3ea..70835e8e 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -75,6 +75,7 @@ extern uint8_t PopCnt16[1 << 16]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern Bitboard SquareBB[SQUARE_NB]; +extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; @@ -215,19 +216,22 @@ inline Bitboard line_bb(Square s1, Square s2) { } -/// between_bb() returns a bitboard representing squares that are linearly -/// between the two given squares (excluding the given squares). If the given -/// squares are not on a same file/rank/diagonal, we return 0. For instance, -/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6. +/// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open +/// segment between the squares s1 and s2 (excluding s1 but including s2). If the +/// given squares are not on a same file/rank/diagonal, it returns s2. For instance, +/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but +/// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick +/// allows to generate non-king evasion moves faster: the defending piece must either +/// interpose itself to cover the check or capture the checking piece. inline Bitboard between_bb(Square s1, Square s2) { - Bitboard b = line_bb(s1, s2) & ((AllSquares << s1) ^ (AllSquares << s2)); - return b & (b - 1); //exclude lsb + assert(is_ok(s1) && is_ok(s2)); + return BetweenBB[s1][s2]; } -/// forward_ranks_bb() returns a bitboard representing the squares on the ranks -/// in front of the given one, from the point of view of the given color. For instance, +/// forward_ranks_bb() returns a bitboard representing the squares on the ranks in +/// front of the given one, from the point of view of the given color. For instance, /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. constexpr Bitboard forward_ranks_bb(Color c, Square s) { diff --git a/src/movegen.cpp b/src/movegen.cpp index 5dbc37ce..742dbf40 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -158,7 +158,7 @@ namespace { { assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6)); - // An en passant capture cannot resolve a discovered check. + // An en passant capture cannot resolve a discovered check if (Type == EVASIONS && (target & (pos.ep_square() + Up))) return moveList; @@ -218,7 +218,7 @@ namespace { target = ~pos.pieces(); break; case EVASIONS: - target = between_bb(pos.square(Us), lsb(pos.checkers())) | pos.checkers(); + target = between_bb(pos.square(Us), lsb(pos.checkers())); break; case NON_EVASIONS: target = ~pos.pieces(Us); @@ -329,7 +329,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { if (more_than_one(pos.checkers())) return moveList; // Double check, only a king move can save the day - // Generate blocking evasions or captures of the checking piece + // Generate blocking interpositions or captures of the checking piece return us == WHITE ? generate_all(pos, moveList) : generate_all(pos, moveList); } diff --git a/src/position.cpp b/src/position.cpp index f4739413..772e4545 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -320,7 +320,7 @@ void Position::set_castling_right(Color c, Square rfrom) { Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1); Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1); - castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto) + castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto)) & ~(kfrom | rfrom); } @@ -613,8 +613,8 @@ bool Position::pseudo_legal(const Move m) const { if (more_than_one(checkers())) return false; - // Our move must be a blocking evasion or a capture of the checking piece - if (!((between_bb(lsb(checkers()), square(us)) | checkers()) & to)) + // Our move must be a blocking interposition or a capture of the checking piece + if (!(between_bb(square(us), lsb(checkers())) & to)) return false; } // In case of king moves under check we have to remove king so as to catch @@ -1218,7 +1218,7 @@ bool Position::has_game_cycle(int ply) const { Square s1 = from_sq(move); Square s2 = to_sq(move); - if (!(between_bb(s1, s2) & pieces())) + if (!((between_bb(s1, s2) ^ s2) & pieces())) { if (ply > i) return true; From ace9632c6713e346ccca73b3e2edc046c1f5527c Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 15 Mar 2021 19:05:01 +0100 Subject: [PATCH 013/282] Add a specific FRC correction from classical to NNUE our net currently is not trained on FRC games, and so doesn't know about the important pattern of a bishop that is cornered in FRC. This patch introduces a term we have in the classical evaluation for this case, and adds it to the NNUE eval. Since fishtest doesn't support FRC right now, the patch was tested locally at STC conditions, starting from the book of FRC starting positions. Score of master vs patch: 993 - 2226 - 6781 [0.438] 10000 Which corresponds to approximately 40 Elo The patch passes non-regression testing for traditional chess (where it adds one branch). passed STC: https://tests.stockfishchess.org/tests/view/604fa2532433018de7a38b67 LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 30560 W: 2701 L: 2636 D: 25223 Ptnml(0-2): 88, 2056, 10921, 2133, 82 passed STC also in an earlier version: https://tests.stockfishchess.org/tests/view/604f61282433018de7a38b4d closes https://github.com/official-stockfish/Stockfish/pull/3398 No functional change --- src/evaluate.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d43b8fa7..bb4f9ef7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1038,6 +1038,45 @@ make_v: return v; } + // specifically correct for cornered bishops to fix FRC with NNUE. + Value fix_FRC(const Position& pos) { + + Value bAdjust = Value(0); + + constexpr Value p1=Value(209), p2=Value(136), p3=Value(148); + + Color Us = pos.side_to_move(); + if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_A1)) + && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_B2))) + { + bAdjust -= !pos.empty(relative_square(Us,SQ_B3)) ? p1 + : pos.piece_on(relative_square(Us,SQ_C3)) == make_piece(Us, PAWN) ? p2 + : p3; + } + if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_H1)) + && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_G2))) + { + bAdjust -= !pos.empty(relative_square(Us,SQ_G3)) ? p1 + : pos.piece_on(relative_square(Us,SQ_F3)) == make_piece(Us, PAWN) ? p2 + : p3; + } + if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_A8)) + && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_B7))) + { + bAdjust += !pos.empty(relative_square(Us,SQ_B6)) ? p1 + : pos.piece_on(relative_square(Us,SQ_C6)) == make_piece(~Us, PAWN) ? p2 + : p3; + } + if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_H8)) + && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_G7))) + { + bAdjust += !pos.empty(relative_square(Us,SQ_G6)) ? p1 + : pos.piece_on(relative_square(Us,SQ_F6)) == make_piece(~Us, PAWN) ? p2 + : p3; + } + return bAdjust; + } + } // namespace @@ -1055,7 +1094,12 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&](){ int mat = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); - return NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo; + Value nnueValue = NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo; + + if (pos.is_chess960()) + nnueValue += fix_FRC(pos); + + return nnueValue; }; // If there is PSQ imbalance use classical eval, with small probability if it is small From ec42154ef2569a58dae2164e328d5bbffcb2aee9 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Sat, 13 Mar 2021 17:40:07 +0100 Subject: [PATCH 014/282] Use reference instead of pointer for pop_lsb() signature This patch changes the pop_lsb() signature from Square pop_lsb(Bitboard*) to Square pop_lsb(Bitboard&). This is more idomatic for C++ style signatures. Passed a non-regression STC test: LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 21280 W: 1928 L: 1847 D: 17505 Ptnml(0-2): 71, 1427, 7558, 1518, 66 https://tests.stockfishchess.org/tests/view/6053a1e22433018de7a38e2f We have verified that the generated binary is identical on gcc-10. Closes https://github.com/official-stockfish/Stockfish/pull/3404 No functional change. --- src/bitbase.cpp | 4 ++-- src/bitboard.h | 8 ++++---- src/evaluate.cpp | 8 ++++---- src/movegen.cpp | 30 +++++++++++++++--------------- src/nnue/features/half_kp.cpp | 2 +- src/pawns.cpp | 4 ++-- src/position.cpp | 6 +++--- src/syzygy/tbprobe.cpp | 4 ++-- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index ece9ec72..10aab6d9 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -150,8 +150,8 @@ namespace { Bitboard b = attacks_bb(ksq[stm]); while (b) - r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(&b), psq)] - : db[index(WHITE, pop_lsb(&b), ksq[WHITE], psq)]; + r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(b), psq)] + : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)]; if (stm == WHITE) { diff --git a/src/bitboard.h b/src/bitboard.h index 70835e8e..c663f4bd 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -428,10 +428,10 @@ inline Bitboard least_significant_square_bb(Bitboard b) { /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard -inline Square pop_lsb(Bitboard* b) { - assert(*b); - const Square s = lsb(*b); - *b &= *b - 1; +inline Square pop_lsb(Bitboard& b) { + assert(b); + const Square s = lsb(b); + b &= b - 1; return s; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index bb4f9ef7..d4138cfa 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -397,7 +397,7 @@ namespace { attackedBy[Us][Pt] = 0; while (b1) { - Square s = pop_lsb(&b1); + Square s = pop_lsb(b1); // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN)) @@ -658,11 +658,11 @@ namespace { { b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]); while (b) - score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))]; + score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(b)))]; b = weak & attackedBy[Us][ROOK]; while (b) - score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))]; + score += ThreatByRook[type_of(pos.piece_on(pop_lsb(b)))]; if (weak & attackedBy[Us][KING]) score += ThreatByKing; @@ -760,7 +760,7 @@ namespace { while (b) { - Square s = pop_lsb(&b); + Square s = pop_lsb(b); assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up))); diff --git a/src/movegen.cpp b/src/movegen.cpp index 742dbf40..8b043f42 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -102,13 +102,13 @@ namespace { while (b1) { - Square to = pop_lsb(&b1); + Square to = pop_lsb(b1); *moveList++ = make_move(to - Up, to); } while (b2) { - Square to = pop_lsb(&b2); + Square to = pop_lsb(b2); *moveList++ = make_move(to - Up - Up, to); } } @@ -127,13 +127,13 @@ namespace { Bitboard b3 = shift(pawnsOn7) & emptySquares; while (b1) - moveList = make_promotions(moveList, pop_lsb(&b1), ksq); + moveList = make_promotions(moveList, pop_lsb(b1), ksq); while (b2) - moveList = make_promotions(moveList, pop_lsb(&b2), ksq); + moveList = make_promotions(moveList, pop_lsb(b2), ksq); while (b3) - moveList = make_promotions(moveList, pop_lsb(&b3), ksq); + moveList = make_promotions(moveList, pop_lsb(b3), ksq); } // Standard and en passant captures @@ -144,13 +144,13 @@ namespace { while (b1) { - Square to = pop_lsb(&b1); + Square to = pop_lsb(b1); *moveList++ = make_move(to - UpRight, to); } while (b2) { - Square to = pop_lsb(&b2); + Square to = pop_lsb(b2); *moveList++ = make_move(to - UpLeft, to); } @@ -167,7 +167,7 @@ namespace { assert(b1); while (b1) - *moveList++ = make(pop_lsb(&b1), pos.ep_square()); + *moveList++ = make(pop_lsb(b1), pos.ep_square()); } } @@ -183,14 +183,14 @@ namespace { Bitboard bb = piecesToMove & pos.pieces(Pt); while (bb) { - Square from = pop_lsb(&bb); + Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; if constexpr (Checks) b &= pos.check_squares(Pt); while (b) - *moveList++ = make_move(from, pop_lsb(&b)); + *moveList++ = make_move(from, pop_lsb(b)); } return moveList; @@ -236,7 +236,7 @@ namespace { Square ksq = pos.square(Us); Bitboard b = attacks_bb(ksq) & target; while (b) - *moveList++ = make_move(ksq, pop_lsb(&b)); + *moveList++ = make_move(ksq, pop_lsb(b)); if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) @@ -286,7 +286,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { while (dc) { - Square from = pop_lsb(&dc); + Square from = pop_lsb(dc); PieceType pt = type_of(pos.piece_on(from)); Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); @@ -295,7 +295,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { b &= ~attacks_bb(pos.square(~us)); while (b) - *moveList++ = make_move(from, pop_lsb(&b)); + *moveList++ = make_move(from, pop_lsb(b)); } return us == WHITE ? generate_all(pos, moveList) @@ -319,12 +319,12 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // the king evasions in order to skip known illegal moves, which avoids any // useless legality checks later on. while (sliders) - sliderAttacks |= line_bb(ksq, pop_lsb(&sliders)) & ~pos.checkers(); + sliderAttacks |= line_bb(ksq, pop_lsb(sliders)) & ~pos.checkers(); // Generate evasions for king, capture and non capture moves Bitboard b = attacks_bb(ksq) & ~pos.pieces(us) & ~sliderAttacks; while (b) - *moveList++ = make_move(ksq, pop_lsb(&b)); + *moveList++ = make_move(ksq, pop_lsb(b)); if (more_than_one(pos.checkers())) return moveList; // Double check, only a king move can save the day diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index ac6317e7..3a25a91d 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -41,7 +41,7 @@ namespace Stockfish::Eval::NNUE::Features { Square ksq = orient(perspective, pos.square(perspective)); Bitboard bb = pos.pieces() & ~pos.pieces(KING); while (bb) { - Square s = pop_lsb(&bb); + Square s = pop_lsb(bb); active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); } } diff --git a/src/pawns.cpp b/src/pawns.cpp index 9a0610a0..81255813 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -110,7 +110,7 @@ namespace { // Loop through all pawns of the current color and score each pawn while (b) { - s = pop_lsb(&b); + s = pop_lsb(b); assert(pos.piece_on(s) == make_piece(Us, PAWN)); @@ -290,7 +290,7 @@ Score Entry::do_king_safety(const Position& pos) { if (pawns & attacks_bb(ksq)) minPawnDist = 1; else while (pawns) - minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); + minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(pawns))); return shelter - make_score(0, 16 * minPawnDist); } diff --git a/src/position.cpp b/src/position.cpp index 772e4545..6c5a11b2 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -73,7 +73,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { << std::setfill(' ') << std::dec << "\nCheckers: "; for (Bitboard b = pos.checkers(); b; ) - os << UCI::square(pop_lsb(&b)) << " "; + os << UCI::square(pop_lsb(b)) << " "; if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) @@ -359,7 +359,7 @@ void Position::set_state(StateInfo* si) const { for (Bitboard b = pieces(); b; ) { - Square s = pop_lsb(&b); + Square s = pop_lsb(b); Piece pc = piece_on(s); si->key ^= Zobrist::psq[pc][s]; @@ -476,7 +476,7 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners while (snipers) { - Square sniperSq = pop_lsb(&snipers); + Square sniperSq = pop_lsb(snipers); Bitboard b = between_bb(s, sniperSq) & occupancy; if (b && !more_than_one(b)) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 5cfd38e5..0500dd5a 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -711,7 +711,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu leadPawns = b = pos.pieces(color_of(pc), PAWN); do - squares[size++] = pop_lsb(&b) ^ flipSquares; + squares[size++] = pop_lsb(b) ^ flipSquares; while (b); leadPawnsCnt = size; @@ -731,7 +731,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // directly map them to the correct color and square. b = pos.pieces() ^ leadPawns; do { - Square s = pop_lsb(&b); + Square s = pop_lsb(b); squares[size] = s ^ flipSquares; pieces[size++] = Piece(pos.piece_on(s) ^ flipColor); } while (b); From 83eac08e7562d93787f75eccd4b7781c4bd45dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 19 Mar 2021 19:43:25 +0100 Subject: [PATCH 015/282] Small cleanups (march 2021) With help of @BM123499, @mstembera, @gvreuls, @noobpwnftw and @Fanael Thanks! Closes https://github.com/official-stockfish/Stockfish/pull/3405 No functional change --- AUTHORS | 2 +- src/bitbase.cpp | 4 +- src/evaluate.cpp | 92 +++++++++++++++++------------- src/material.cpp | 2 +- src/misc.cpp | 22 +++---- src/movegen.cpp | 3 +- src/nnue/features/half_kp.cpp | 3 +- src/nnue/layers/affine_transform.h | 8 +-- src/pawns.cpp | 3 +- src/search.cpp | 33 ++++++----- src/syzygy/tbprobe.cpp | 10 ++-- src/thread.cpp | 6 +- 12 files changed, 107 insertions(+), 81 deletions(-) diff --git a/AUTHORS b/AUTHORS index 662cc6ec..11de282b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of August 4, 2020 +# List of authors for Stockfish, as of March 24, 2021 # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 10aab6d9..27bf4095 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -150,8 +150,8 @@ namespace { Bitboard b = attacks_bb(ksq[stm]); while (b) - r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(b), psq)] - : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)]; + r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)] + : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)]; if (stm == WHITE) { diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d4138cfa..ca86a435 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -54,7 +54,6 @@ using namespace std; -using namespace Stockfish::Eval::NNUE; namespace Stockfish { @@ -396,7 +395,8 @@ namespace { attackedBy[Us][Pt] = 0; - while (b1) { + while (b1) + { Square s = pop_lsb(b1); // Find attacked squares, including x-ray attacks for bishops and rooks @@ -1038,46 +1038,51 @@ make_v: return v; } - // specifically correct for cornered bishops to fix FRC with NNUE. + + /// Fisher Random Chess: correction for cornered bishops, to fix chess960 play with NNUE + Value fix_FRC(const Position& pos) { - Value bAdjust = Value(0); + constexpr Bitboard Corners = 1ULL << SQ_A1 | 1ULL << SQ_H1 | 1ULL << SQ_A8 | 1ULL << SQ_H8; - constexpr Value p1=Value(209), p2=Value(136), p3=Value(148); + if (!(pos.pieces(BISHOP) & Corners)) + return VALUE_ZERO; - Color Us = pos.side_to_move(); - if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_A1)) - && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_B2))) - { - bAdjust -= !pos.empty(relative_square(Us,SQ_B3)) ? p1 - : pos.piece_on(relative_square(Us,SQ_C3)) == make_piece(Us, PAWN) ? p2 - : p3; - } - if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_H1)) - && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_G2))) - { - bAdjust -= !pos.empty(relative_square(Us,SQ_G3)) ? p1 - : pos.piece_on(relative_square(Us,SQ_F3)) == make_piece(Us, PAWN) ? p2 - : p3; - } - if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_A8)) - && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_B7))) - { - bAdjust += !pos.empty(relative_square(Us,SQ_B6)) ? p1 - : pos.piece_on(relative_square(Us,SQ_C6)) == make_piece(~Us, PAWN) ? p2 - : p3; - } - if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_H8)) - && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_G7))) - { - bAdjust += !pos.empty(relative_square(Us,SQ_G6)) ? p1 - : pos.piece_on(relative_square(Us,SQ_F6)) == make_piece(~Us, PAWN) ? p2 - : p3; - } - return bAdjust; + constexpr int penalty1 = -209; + constexpr int penalty2 = -136; + constexpr int penalty3 = -148; + + int correction = 0; + + if ( pos.piece_on(SQ_A1) == W_BISHOP + && pos.piece_on(SQ_B2) == W_PAWN) + correction += !pos.empty(SQ_B3) ? penalty1 + : pos.piece_on(SQ_C3) == W_PAWN ? penalty2 + : penalty3; + + if ( pos.piece_on(SQ_H1) == W_BISHOP + && pos.piece_on(SQ_G2) == W_PAWN) + correction += !pos.empty(SQ_G3) ? penalty1 + : pos.piece_on(SQ_F3) == W_PAWN ? penalty2 + : penalty3; + + if ( pos.piece_on(SQ_A8) == B_BISHOP + && pos.piece_on(SQ_B7) == B_PAWN) + correction += !pos.empty(SQ_B6) ? -penalty1 + : pos.piece_on(SQ_C6) == B_PAWN ? -penalty2 + : -penalty3; + + if ( pos.piece_on(SQ_H8) == B_BISHOP + && pos.piece_on(SQ_G7) == B_PAWN) + correction += !pos.empty(SQ_G6) ? -penalty1 + : pos.piece_on(SQ_F6) == B_PAWN ? -penalty2 + : -penalty3; + + return pos.side_to_move() == WHITE ? Value(correction) + : -Value(correction); } -} // namespace +} // namespace Eval /// evaluate() is the evaluator for the outer world. It returns a static @@ -1092,14 +1097,19 @@ Value Eval::evaluate(const Position& pos) { else { // Scale and shift NNUE for compatibility with search and classical evaluation - auto adjusted_NNUE = [&](){ - int mat = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); - Value nnueValue = NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo; + auto adjusted_NNUE = [&]() + { + int material = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); + int scale = 641 + + material / 32 + - 4 * pos.rule50_count(); + + Value nnue = NNUE::evaluate(pos) * scale / 1024 + Tempo; if (pos.is_chess960()) - nnueValue += fix_FRC(pos); + nnue += fix_FRC(pos); - return nnueValue; + return nnue; }; // If there is PSQ imbalance use classical eval, with small probability if it is small diff --git a/src/material.cpp b/src/material.cpp index 84d7a4bd..9d17af20 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -74,7 +74,7 @@ namespace { bool is_KBPsK(const Position& pos, Color us) { return pos.non_pawn_material(us) == BishopValueMg - && pos.count(us) >= 1; + && pos.count(us) >= 1; } bool is_KQKRPs(const Position& pos, Color us) { diff --git a/src/misc.cpp b/src/misc.cpp index 7600fc11..918dc7a9 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -362,8 +362,12 @@ void std_aligned_free(void* ptr) { /// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. #if defined(_WIN32) -#if defined(_WIN64) -static void* aligned_large_pages_alloc_win(size_t allocSize) { + +static void* aligned_large_pages_alloc_windows(size_t allocSize) { + + #if !defined(_WIN64) + return nullptr; + #else HANDLE hProcessToken { }; LUID luid { }; @@ -406,21 +410,18 @@ static void* aligned_large_pages_alloc_win(size_t allocSize) { CloseHandle(hProcessToken); return mem; + + #endif } -#endif void* aligned_large_pages_alloc(size_t allocSize) { -#if defined(_WIN64) // Try to allocate large pages - void* mem = aligned_large_pages_alloc_win(allocSize); + void* mem = aligned_large_pages_alloc_windows(allocSize); // Fall back to regular, page aligned, allocation if necessary if (!mem) mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); -#else - void* mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); -#endif return mem; } @@ -456,8 +457,9 @@ void aligned_large_pages_free(void* mem) { if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) { DWORD err = GetLastError(); - std::cerr << "Failed to free transposition table. Error code: 0x" << - std::hex << err << std::dec << std::endl; + std::cerr << "Failed to free large page memory. Error code: 0x" + << std::hex << err + << std::dec << std::endl; exit(EXIT_FAILURE); } } diff --git a/src/movegen.cpp b/src/movegen.cpp index 8b043f42..1e42f99e 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -182,7 +182,8 @@ namespace { Bitboard bb = piecesToMove & pos.pieces(Pt); - while (bb) { + while (bb) + { Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 3a25a91d..5a440b92 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -40,7 +40,8 @@ namespace Stockfish::Eval::NNUE::Features { Square ksq = orient(perspective, pos.square(perspective)); Bitboard bb = pos.pieces() & ~pos.pieces(KING); - while (bb) { + while (bb) + { Square s = pop_lsb(bb); active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); } diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index d2713c5a..1faa180d 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -96,7 +96,7 @@ namespace Stockfish::Eval::NNUE::Layers { IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2; sum[w[idx] < 0] += w[idx]; } - for (int sign : {-1, 1}) + for (int sign : { -1, 1 }) while (sign * sum[sign == -1] > 258) { int maxK = 0, maxW = 0; @@ -234,9 +234,9 @@ namespace Stockfish::Eval::NNUE::Layers { __m128i product1 = _mm_maddubs_epi16(a1, b1); __m128i product2 = _mm_maddubs_epi16(a2, b2); __m128i product3 = _mm_maddubs_epi16(a3, b3); - product0 = _mm_adds_epi16(product0, product1); - product2 = _mm_adds_epi16(product2, product3); - product0 = _mm_adds_epi16(product0, product2); + product0 = _mm_add_epi16(product0, product1); + product2 = _mm_add_epi16(product2, product3); + product0 = _mm_add_epi16(product0, product2); product0 = _mm_madd_epi16(product0, kOnes128); acc = _mm_add_epi32(acc, product0); }; diff --git a/src/pawns.cpp b/src/pawns.cpp index 81255813..70fb6f23 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -109,7 +109,8 @@ namespace { e->blockedCount += popcount(shift(ourPawns) & (theirPawns | doubleAttackThem)); // Loop through all pawns of the current color and score each pawn - while (b) { + while (b) + { s = pop_lsb(b); assert(pos.piece_on(s) == make_piece(Us, PAWN)); diff --git a/src/search.cpp b/src/search.cpp index af7f801f..ff8764d8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -612,12 +612,12 @@ namespace { // Step 1. Initialize node Thread* thisThread = pos.this_thread(); - ss->inCheck = pos.checkers(); - priorCapture = pos.captured_piece(); - Color us = pos.side_to_move(); - moveCount = captureCount = quietCount = ss->moveCount = 0; - bestValue = -VALUE_INFINITE; - maxValue = VALUE_INFINITE; + ss->inCheck = pos.checkers(); + priorCapture = pos.captured_piece(); + Color us = pos.side_to_move(); + moveCount = captureCount = quietCount = ss->moveCount = 0; + bestValue = -VALUE_INFINITE; + maxValue = VALUE_INFINITE; ss->distanceFromPv = (PvNode ? 0 : ss->distanceFromPv); // Check for the available remaining time @@ -917,6 +917,7 @@ namespace { return probCutBeta; assert(probCutBeta < VALUE_INFINITE); + MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); int probCutCount = 0; bool ttPv = ss->ttPv; @@ -1121,6 +1122,7 @@ moves_loop: // When in check, search starts from here { Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2; Depth singularDepth = (depth - 1 + 3 * formerPv) / 2; + ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); ss->excludedMove = MOVE_NONE; @@ -1205,15 +1207,19 @@ moves_loop: // When in check, search starts from here // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~10 Elo) - if (ss->ttPv && !likelyFailLow) + if ( ss->ttPv + && !likelyFailLow) r -= 2; // Increase reduction at root and non-PV nodes when the best move does not change frequently - if ((rootNode || !PvNode) && thisThread->rootDepth > 10 && thisThread->bestMoveChanges <= 2) + if ( (rootNode || !PvNode) + && thisThread->rootDepth > 10 + && thisThread->bestMoveChanges <= 2) r++; // More reductions for late moves if position was not in previous PV - if (moveCountPruning && !formerPv) + if ( moveCountPruning + && !formerPv) r++; // Decrease reduction if opponent's move count is high (~5 Elo) @@ -1226,7 +1232,7 @@ moves_loop: // When in check, search starts from here if (captureOrPromotion) { - // Unless giving check, this capture is likely bad + // Increase reduction for non-checking captures likely to be bad if ( !givesCheck && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha) r++; @@ -1246,7 +1252,7 @@ moves_loop: // When in check, search starts from here // Decrease reduction for moves that escape a capture. Filter out // castling moves, because they are coded as "king captures rook" and - // hence break make_move(). (~2 Elo) + // hence break reverse_move() (~2 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(reverse_move(move))) r -= 2 + ss->ttPv - (type_of(movedPiece) == PAWN); @@ -1408,8 +1414,9 @@ moves_loop: // When in check, search starts from here assert(moveCount || !ss->inCheck || excludedMove || !MoveList(pos).size()); if (!moveCount) - bestValue = excludedMove ? alpha - : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; + bestValue = excludedMove ? alpha : + ss->inCheck ? mated_in(ss->ply) + : VALUE_DRAW; // If there is a move which produces search value greater than alpha we update stats of searched moves else if (bestMove) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 0500dd5a..831c8259 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -192,7 +192,8 @@ public: std::stringstream ss(Paths); std::string path; - while (std::getline(ss, path, SepChar)) { + while (std::getline(ss, path, SepChar)) + { fname = path + "/" + f; std::ifstream::open(fname); if (is_open()) @@ -567,7 +568,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) { int buf64Size = 64; Sym sym; - while (true) { + while (true) + { int len = 0; // This is the symbol length - d->min_sym_len // Now get the symbol length. For any symbol s64 of length l right-padded @@ -605,8 +607,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // We binary-search for our value recursively expanding into the left and // right child symbols until we reach a leaf node where symlen[sym] + 1 == 1 // that will store the value we need. - while (d->symlen[sym]) { - + while (d->symlen[sym]) + { Sym left = d->btree[sym].get(); // If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and diff --git a/src/thread.cpp b/src/thread.cpp index 3ef73dfa..da8e1d05 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -128,14 +128,16 @@ void Thread::idle_loop() { void ThreadPool::set(size_t requested) { - if (size() > 0) { // destroy any existing thread(s) + if (size() > 0) // destroy any existing thread(s) + { main()->wait_for_search_finished(); while (size() > 0) delete back(), pop_back(); } - if (requested > 0) { // create new thread(s) + if (requested > 0) // create new thread(s) + { push_back(new MainThread(0)); while (size() < requested) From f28303d214645e37ee13b5c285d33142dd71bcb1 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 27 Feb 2021 11:52:18 +0100 Subject: [PATCH 016/282] Allow using Intel SDE for PGO builds. The software development emulator (SDE) allows to run binaries compiled for architectures not supported by the actual CPU. This is useful to do PGO builds for newer architectures. The SDE can currently be obtained from https://software.intel.com/content/www/us/en/develop/articles/intel-software-development-emulator.html This patch introduces a new optional makefile argument SDE_PATH. If not empty it should contain the path to the sde executable closes https://github.com/official-stockfish/Stockfish/pull/3373 No functional change. --- src/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index eb32758f..cdd2007f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -31,7 +31,11 @@ PREFIX = /usr/local BINDIR = $(PREFIX)/bin ### Built-in benchmark for pgo-builds -PGOBENCH = ./$(EXE) bench +ifeq ($(SDE_PATH),) + PGOBENCH = ./$(EXE) bench +else + PGOBENCH = $(SDE_PATH) -- ./$(EXE) bench +endif ### Source and object files SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ From 62a0b65ff886f8f4895d854705c0c870e6a2a834 Mon Sep 17 00:00:00 2001 From: mstembera Date: Thu, 25 Mar 2021 13:33:05 -0700 Subject: [PATCH 017/282] Simplify and unify FRC cornered bishop. tested locally as fishtest doesn't support FRC: STC NNUE 9646 - 9647 - 20707 [0.500] 40000 -0.0 +/- 2.4, LOS: 49.7 %, DrawRatio: 51.8 % STC classical 9678 - 9609 - 20713 [0.501] 40000 0.6 +/- 2.4, LOS: 69.0 %, DrawRatio: 51.8 % and verified independently: Score of master vs patch: 6463 - 6580 - 34957 [0.499] 48000 closes https://github.com/official-stockfish/Stockfish/pull/3413 bench: 4321677 --- src/evaluate.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ca86a435..74f2e39b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -260,7 +260,8 @@ namespace { constexpr Score UncontestedOutpost = S( 1, 10); constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopXRayPawns = S( 4, 5); - constexpr Score CorneredBishop = S( 50, 50); + constexpr Value CorneredBishopV = Value(50); + constexpr Score CorneredBishop = S(CorneredBishopV, CorneredBishopV); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); constexpr Score KnightOnQueen = S( 16, 11); @@ -477,9 +478,8 @@ namespace { { Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); if (pos.piece_on(s + d) == make_piece(Us, PAWN)) - score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 - : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2 - : CorneredBishop; + score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 + : CorneredBishop * 3; } } } @@ -1048,35 +1048,27 @@ make_v: if (!(pos.pieces(BISHOP) & Corners)) return VALUE_ZERO; - constexpr int penalty1 = -209; - constexpr int penalty2 = -136; - constexpr int penalty3 = -148; - int correction = 0; if ( pos.piece_on(SQ_A1) == W_BISHOP && pos.piece_on(SQ_B2) == W_PAWN) - correction += !pos.empty(SQ_B3) ? penalty1 - : pos.piece_on(SQ_C3) == W_PAWN ? penalty2 - : penalty3; + correction += !pos.empty(SQ_B3) ? -CorneredBishopV * 4 + : -CorneredBishopV * 3; if ( pos.piece_on(SQ_H1) == W_BISHOP && pos.piece_on(SQ_G2) == W_PAWN) - correction += !pos.empty(SQ_G3) ? penalty1 - : pos.piece_on(SQ_F3) == W_PAWN ? penalty2 - : penalty3; + correction += !pos.empty(SQ_G3) ? -CorneredBishopV * 4 + : -CorneredBishopV * 3; if ( pos.piece_on(SQ_A8) == B_BISHOP && pos.piece_on(SQ_B7) == B_PAWN) - correction += !pos.empty(SQ_B6) ? -penalty1 - : pos.piece_on(SQ_C6) == B_PAWN ? -penalty2 - : -penalty3; + correction += !pos.empty(SQ_B6) ? CorneredBishopV * 4 + : CorneredBishopV * 3; if ( pos.piece_on(SQ_H8) == B_BISHOP && pos.piece_on(SQ_G7) == B_PAWN) - correction += !pos.empty(SQ_G6) ? -penalty1 - : pos.piece_on(SQ_F6) == B_PAWN ? -penalty2 - : -penalty3; + correction += !pos.empty(SQ_G6) ? CorneredBishopV * 4 + : CorneredBishopV * 3; return pos.side_to_move() == WHITE ? Value(correction) : -Value(correction); From c489df6f5b5629a135af3b8222fa1ef607ec1526 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 30 Mar 2021 00:42:45 -0300 Subject: [PATCH 018/282] Simplify King Evasion Simplify away the removal of some illegal `KING`-evasion moves during move generation. Verified for correctness by running perft on the following positions: ``` ./stockfish bench 16 1 6 default perft Nodes searched: 71608931810 ./stockfish position fen 4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21 go perft 6 Nodes searched: 6136386434 ``` Passed STC: LLR: 2.94 (-2.94,2.94) {-1.00,0.20} Total: 16072 W: 1473 L: 1349 D: 13250 Ptnml(0-2): 57, 1047, 5710, 1159, 63 https://tests.stockfishchess.org/tests/view/60629e7ef183b42957b423b1 Passed LTC: LLR: 2.94 (-2.94,2.94) {-0.70,0.20} Total: 59064 W: 2214 L: 2177 D: 54673 Ptnml(0-2): 26, 1944, 25556, 1979, 27 https://tests.stockfishchess.org/tests/view/6062dce4f183b42957b423de closes https://github.com/official-stockfish/Stockfish/pull/3415 No functional change --- src/movegen.cpp | 12 ++---------- src/position.cpp | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 1e42f99e..50496136 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -313,17 +313,9 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { Color us = pos.side_to_move(); Square ksq = pos.square(us); - Bitboard sliderAttacks = 0; - Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN); - // Find all the squares attacked by slider checkers. We will remove them from - // the king evasions in order to skip known illegal moves, which avoids any - // useless legality checks later on. - while (sliders) - sliderAttacks |= line_bb(ksq, pop_lsb(sliders)) & ~pos.checkers(); - - // Generate evasions for king, capture and non capture moves - Bitboard b = attacks_bb(ksq) & ~pos.pieces(us) & ~sliderAttacks; + // Generate evasions for king + Bitboard b = attacks_bb(ksq) & ~pos.pieces(us); while (b) *moveList++ = make_move(ksq, pop_lsb(b)); diff --git a/src/position.cpp b/src/position.cpp index 6c5a11b2..ec356ace 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -544,7 +544,7 @@ bool Position::legal(Move m) const { // If the moving piece is a king, check whether the destination square is // attacked by the opponent. if (type_of(piece_on(from)) == KING) - return !(attackers_to(to) & pieces(~us)); + return !(attackers_to(to, pieces() ^ from) & pieces(~us)); // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. From b862c8d4be2cf6245360a8072ab910a67eb5bc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 24 Mar 2021 21:55:49 +0100 Subject: [PATCH 019/282] Small clean-up Bench: 4321677 --- AUTHORS | 2 +- README.md | 10 +++++----- src/bitboard.cpp | 2 +- src/bitboard.h | 3 +++ src/evaluate.cpp | 27 ++++++++++++++------------- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/AUTHORS b/AUTHORS index 11de282b..c12b98a0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of March 24, 2021 +# List of authors for Stockfish, as of March 31, 2021 # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) diff --git a/README.md b/README.md index fdc6fd61..67fb5fa0 100644 --- a/README.md +++ b/README.md @@ -242,9 +242,9 @@ When not using the Makefile to compile (for instance, with Microsoft MSVC) you need to manually set/unset some switches in the compiler command line; see file *types.h* for a quick reference. -When reporting an issue or a bug, please tell us which version and -compiler you used to create your executable. These informations can -be found by typing the following commands in a console: +When reporting an issue or a bug, please tell us which Stockfish version +and which compiler you used to create your executable. This information +can be found by typing the following command in a console: ``` ./stockfish compiler @@ -252,8 +252,8 @@ be found by typing the following commands in a console: ## Understanding the code base and participating in the project -Stockfish's improvement over the last couple of years has been a great -community effort. There are a few ways to help contribute to its growth. +Stockfish's improvement over the last decade has been a great community +effort. There are a few ways to help contribute to its growth. ### Donating hardware diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 2da4d728..6b84b51e 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -130,7 +130,7 @@ namespace { for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) { Square s = sq; - while(safe_destination(s, d) && !(occupied & s)) + while (safe_destination(s, d) && !(occupied & s)) attacks |= (s += d); } diff --git a/src/bitboard.h b/src/bitboard.h index c663f4bd..b29f3e24 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -212,6 +212,7 @@ constexpr Bitboard adjacent_files_bb(Square s) { inline Bitboard line_bb(Square s1, Square s2) { assert(is_ok(s1) && is_ok(s2)); + return LineBB[s1][s2]; } @@ -225,7 +226,9 @@ inline Bitboard line_bb(Square s1, Square s2) { /// interpose itself to cover the check or capture the checking piece. inline Bitboard between_bb(Square s1, Square s2) { + assert(is_ok(s1) && is_ok(s2)); + return BetweenBB[s1][s2]; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 74f2e39b..437e753a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -256,12 +256,12 @@ namespace { S(0, 0), S(3, 44), S(37, 68), S(42, 60), S(0, 39), S(58, 43) }; + constexpr Value CorneredBishop = Value(50); + // Assorted bonuses and penalties constexpr Score UncontestedOutpost = S( 1, 10); constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopXRayPawns = S( 4, 5); - constexpr Value CorneredBishopV = Value(50); - constexpr Score CorneredBishop = S(CorneredBishopV, CorneredBishopV); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); constexpr Score KnightOnQueen = S( 16, 11); @@ -478,8 +478,8 @@ namespace { { Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); if (pos.piece_on(s + d) == make_piece(Us, PAWN)) - score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 - : CorneredBishop * 3; + score -= !pos.empty(s + d + pawn_push(Us)) ? 4 * make_score(CorneredBishop, CorneredBishop) + : 3 * make_score(CorneredBishop, CorneredBishop); } } } @@ -1052,23 +1052,23 @@ make_v: if ( pos.piece_on(SQ_A1) == W_BISHOP && pos.piece_on(SQ_B2) == W_PAWN) - correction += !pos.empty(SQ_B3) ? -CorneredBishopV * 4 - : -CorneredBishopV * 3; + correction += !pos.empty(SQ_B3) ? -CorneredBishop * 4 + : -CorneredBishop * 3; if ( pos.piece_on(SQ_H1) == W_BISHOP && pos.piece_on(SQ_G2) == W_PAWN) - correction += !pos.empty(SQ_G3) ? -CorneredBishopV * 4 - : -CorneredBishopV * 3; + correction += !pos.empty(SQ_G3) ? -CorneredBishop * 4 + : -CorneredBishop * 3; if ( pos.piece_on(SQ_A8) == B_BISHOP && pos.piece_on(SQ_B7) == B_PAWN) - correction += !pos.empty(SQ_B6) ? CorneredBishopV * 4 - : CorneredBishopV * 3; + correction += !pos.empty(SQ_B6) ? CorneredBishop * 4 + : CorneredBishop * 3; if ( pos.piece_on(SQ_H8) == B_BISHOP && pos.piece_on(SQ_G7) == B_PAWN) - correction += !pos.empty(SQ_G6) ? CorneredBishopV * 4 - : CorneredBishopV * 3; + correction += !pos.empty(SQ_G6) ? CorneredBishop * 4 + : CorneredBishop * 3; return pos.side_to_move() == WHITE ? Value(correction) : -Value(correction); @@ -1119,7 +1119,8 @@ Value Eval::evaluate(const Position& pos) { // If the classical eval is small and imbalance large, use NNUE nevertheless. // For the case of opposite colored bishops, switch to NNUE eval with // small probability if the classical eval is less than the threshold. - if ( largePsq && !strongClassical + if ( largePsq + && !strongClassical && ( abs(v) * 16 < NNUEThreshold2 * r50 || ( pos.opposite_bishops() && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 From f40913f7f699d9d875e44929608b45947421ad03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 4 Apr 2021 10:51:45 +0200 Subject: [PATCH 020/282] Keep more pawns This patch increases the weight of pawns in the scale factor applied to the output of the NNUE evaluation. This has the effect that Stockfish will try a little bit harder to keep more pawns in position where the engine has the advantage, and exchange more pawns in bad positions. STC: LLR: 2.93 (-2.94,2.94) {-0.20,1.10} Total: 42552 W: 3858 L: 3668 D: 35026 Ptnml(0-2): 152, 2956, 14876, 3134, 158 https://tests.stockfishchess.org/tests/view/606a06dd2b2df919fd5f0504 LTC: LLR: 2.95 (-2.94,2.94) {0.20,0.90} Total: 44328 W: 1703 L: 1531 D: 41094 Ptnml(0-2): 20, 1373, 19207, 1543, 21 https://tests.stockfishchess.org/tests/view/606aa4ec2b2df919fd5f053e Closes https://github.com/official-stockfish/Stockfish/pull/3420 Bench: 4310076 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 437e753a..789e2859 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,8 +1091,8 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&]() { - int material = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); - int scale = 641 + int material = pos.non_pawn_material() + 4 * PawnValueMg * pos.count(); + int scale = 580 + material / 32 - 4 * pos.rule50_count(); From 3dfda1b28e14d1f3c2422fed5f203e305f58af28 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 6 Apr 2021 16:51:05 +0300 Subject: [PATCH 021/282] Replace distanceFromPv with a better logic This patch removes the recently introduced distanceFromPv logic, and replaces it with following logic: if reduction of moves with low movecount is really negative, we search them deeper than the first move. passed STC: LLR: 2.95 (-2.94,2.94) {-0.20,1.10} Total: 153008 W: 13913 L: 13579 D: 125516 Ptnml(0-2): 547, 10811, 53470, 11113, 563 https://tests.stockfishchess.org/tests/view/6069c9d02b2df919fd5f04d2 passed LTC: LLR: 2.94 (-2.94,2.94) {0.20,0.90} Total: 101920 W: 3964 L: 3699 D: 94257 Ptnml(0-2): 55, 3279, 44019, 3560, 47 https://tests.stockfishchess.org/tests/view/606a99fd2b2df919fd5f0532 Closes https://github.com/official-stockfish/Stockfish/pull/3421 Bench: 4191632 --- src/search.cpp | 11 ++++------- src/search.h | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ff8764d8..f23db4c1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -618,7 +618,6 @@ namespace { moveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; - ss->distanceFromPv = (PvNode ? 0 : ss->distanceFromPv); // Check for the available remaining time if (thisThread == Threads.main()) @@ -1180,8 +1179,6 @@ moves_loop: // When in check, search starts from here // Step 15. Make the move pos.do_move(move, st, givesCheck); - (ss+1)->distanceFromPv = ss->distanceFromPv + moveCount - 1; - // Step 16. Late moves reduction / extension (LMR, ~200 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many @@ -1280,10 +1277,10 @@ moves_loop: // When in check, search starts from here r -= ss->statScore / 14790; } - // In general we want to cap the LMR depth search at newDepth. But for nodes - // close to the principal variation the cap is at (newDepth + 1), which will - // allow these nodes to be searched deeper than the pv (up to 4 plies deeper). - Depth d = std::clamp(newDepth - r, 1, newDepth + ((ss+1)->distanceFromPv <= 4)); + // In general we want to cap the LMR depth search at newDepth. But if + // reductions are really negative and movecount is low, we allow this move + // to be searched deeper than the first move. + Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && moveCount <= 5)); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); diff --git a/src/search.h b/src/search.h index 6f9fbd05..811b2e2a 100644 --- a/src/search.h +++ b/src/search.h @@ -49,7 +49,6 @@ struct Stack { Value staticEval; int statScore; int moveCount; - int distanceFromPv; bool inCheck; bool ttPv; bool ttHit; From 79bb28281ce5e9b40b2662915a96e9072ddad82e Mon Sep 17 00:00:00 2001 From: bmc4 Date: Fri, 2 Apr 2021 09:30:14 -0300 Subject: [PATCH 022/282] Merge all move generators Merging `generate` and `generate` into `generate_all()`. STC: LLR: 2.94 (-2.94,2.94) {-1.00,0.20} Total: 161800 W: 14585 L: 14624 D: 132591 Ptnml(0-2): 577, 11681, 56451, 11586, 605 https://tests.stockfishchess.org/tests/view/606532732b2df919fd5f026d LTC: LLR: 2.98 (-2.94,2.94) {-0.70,0.20} Total: 188504 W: 6906 L: 6961 D: 174637 Ptnml(0-2): 87, 6272, 81610, 6175, 108 https://tests.stockfishchess.org/tests/view/6065b0772b2df919fd5f02ae ------------ Verified for correctness of `EVASIONS` by running perft: ``` ./stockfish b3nch 16 1 6 default perft (replace 3 by e in b3nch) Nodes searched : 71608931810 ``` Also tested for correctness on Chess960 with a similar code shown here: https://github.com/official-stockfish/Stockfish/pull/3418#issuecomment-816630295 ``` ./stockfish b3nch 16 1 6 fischer.txt perft Nodes searched : 506736009395 ``` ------------ Closes https://github.com/official-stockfish/Stockfish/pull/3418 No functional change --- src/movegen.cpp | 99 ++++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 72 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 50496136..c3c149b7 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -175,19 +175,19 @@ namespace { } - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) { + template + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); - Bitboard bb = piecesToMove & pos.pieces(Pt); + Bitboard bb = pos.pieces(Us, Pt); while (bb) { Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; - if constexpr (Checks) + if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) b &= pos.check_squares(Pt); while (b) @@ -204,10 +204,11 @@ namespace { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations - Bitboard target, piecesToMove = pos.pieces(Us); - - if(Type == QUIET_CHECKS) - piecesToMove &= ~pos.blockers_for_king(~Us); + const Square ksq = pos.square(Us); + Bitboard target; + + if (Type == EVASIONS && more_than_one(pos.checkers())) + goto kingMoves; // Double check, only a king move can save the day switch (Type) { @@ -219,7 +220,7 @@ namespace { target = ~pos.pieces(); break; case EVASIONS: - target = between_bb(pos.square(Us), lsb(pos.checkers())); + target = between_bb(ksq, lsb(pos.checkers())); break; case NON_EVASIONS: target = ~pos.pieces(Us); @@ -227,19 +228,22 @@ namespace { } moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, piecesToMove, target); - moveList = generate_moves(pos, moveList, piecesToMove, target); - moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target); - moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); - if (Type != QUIET_CHECKS && Type != EVASIONS) +kingMoves: + if (!Checks || pos.blockers_for_king(~Us) & ksq) { - Square ksq = pos.square(Us); - Bitboard b = attacks_bb(ksq) & target; + Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); + if (Checks) + b &= ~attacks_bb(pos.square(~Us)); + while (b) *moveList++ = make_move(ksq, pop_lsb(b)); - if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) + if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) if (!pos.castling_impeded(cr) && pos.can_castle(cr)) *moveList++ = make(ksq, pos.castling_rook_square(cr)); @@ -253,6 +257,8 @@ namespace { /// Generates all pseudo-legal captures plus queen and checking knight promotions /// Generates all pseudo-legal non-captures and underpromotions (except checking knight) +/// Generates all pseudo-legal check evasions when the side to move is in check +/// Generates all pseudo-legal non-captures giving check, except castling /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. @@ -260,8 +266,8 @@ namespace { template ExtMove* generate(const Position& pos, ExtMove* moveList) { - static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()"); - assert(!pos.checkers()); + static_assert(Type != LEGAL, "Unsupported type in generate()"); + assert((Type == EVASIONS) == (bool)pos.checkers()); Color us = pos.side_to_move(); @@ -272,62 +278,11 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // Explicit template instantiations template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); +template ExtMove* generate(const Position&, ExtMove*); +template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -/// generate generates all pseudo-legal non-captures giving check, -/// except castling. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(!pos.checkers()); - - Color us = pos.side_to_move(); - Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN); - - while (dc) - { - Square from = pop_lsb(dc); - PieceType pt = type_of(pos.piece_on(from)); - - Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); - - if (pt == KING) - b &= ~attacks_bb(pos.square(~us)); - - while (b) - *moveList++ = make_move(from, pop_lsb(b)); - } - - return us == WHITE ? generate_all(pos, moveList) - : generate_all(pos, moveList); -} - - -/// generate generates all pseudo-legal check evasions when the side -/// to move is in check. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(pos.checkers()); - - Color us = pos.side_to_move(); - Square ksq = pos.square(us); - - // Generate evasions for king - Bitboard b = attacks_bb(ksq) & ~pos.pieces(us); - while (b) - *moveList++ = make_move(ksq, pop_lsb(b)); - - if (more_than_one(pos.checkers())) - return moveList; // Double check, only a king move can save the day - - // Generate blocking interpositions or captures of the checking piece - return us == WHITE ? generate_all(pos, moveList) - : generate_all(pos, moveList); -} - - /// generate generates all the legal moves in the given position template<> From 4889cf22bb2985d6d0babb8da93698e5d7641304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 15 Apr 2021 11:18:38 +0200 Subject: [PATCH 023/282] Revert previous patch Revert the previous patch about move generation, as it unexpectedly changed the bench. Better to take the time to understand the issue. Bench: 4191632 --- src/movegen.cpp | 99 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 27 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index c3c149b7..50496136 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -175,19 +175,19 @@ namespace { } - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { + template + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); - Bitboard bb = pos.pieces(Us, Pt); + Bitboard bb = piecesToMove & pos.pieces(Pt); while (bb) { Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; - if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) + if constexpr (Checks) b &= pos.check_squares(Pt); while (b) @@ -204,11 +204,10 @@ namespace { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations - const Square ksq = pos.square(Us); - Bitboard target; - - if (Type == EVASIONS && more_than_one(pos.checkers())) - goto kingMoves; // Double check, only a king move can save the day + Bitboard target, piecesToMove = pos.pieces(Us); + + if(Type == QUIET_CHECKS) + piecesToMove &= ~pos.blockers_for_king(~Us); switch (Type) { @@ -220,7 +219,7 @@ namespace { target = ~pos.pieces(); break; case EVASIONS: - target = between_bb(ksq, lsb(pos.checkers())); + target = between_bb(pos.square(Us), lsb(pos.checkers())); break; case NON_EVASIONS: target = ~pos.pieces(Us); @@ -228,22 +227,19 @@ namespace { } moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, piecesToMove, target); + moveList = generate_moves(pos, moveList, piecesToMove, target); + moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target); + moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target); -kingMoves: - if (!Checks || pos.blockers_for_king(~Us) & ksq) + if (Type != QUIET_CHECKS && Type != EVASIONS) { - Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); - if (Checks) - b &= ~attacks_bb(pos.square(~Us)); - + Square ksq = pos.square(Us); + Bitboard b = attacks_bb(ksq) & target; while (b) *moveList++ = make_move(ksq, pop_lsb(b)); - if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) + if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) if (!pos.castling_impeded(cr) && pos.can_castle(cr)) *moveList++ = make(ksq, pos.castling_rook_square(cr)); @@ -257,8 +253,6 @@ kingMoves: /// Generates all pseudo-legal captures plus queen and checking knight promotions /// Generates all pseudo-legal non-captures and underpromotions (except checking knight) -/// Generates all pseudo-legal check evasions when the side to move is in check -/// Generates all pseudo-legal non-captures giving check, except castling /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. @@ -266,8 +260,8 @@ kingMoves: template ExtMove* generate(const Position& pos, ExtMove* moveList) { - static_assert(Type != LEGAL, "Unsupported type in generate()"); - assert((Type == EVASIONS) == (bool)pos.checkers()); + static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()"); + assert(!pos.checkers()); Color us = pos.side_to_move(); @@ -278,11 +272,62 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // Explicit template instantiations template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); +/// generate generates all pseudo-legal non-captures giving check, +/// except castling. Returns a pointer to the end of the move list. +template<> +ExtMove* generate(const Position& pos, ExtMove* moveList) { + + assert(!pos.checkers()); + + Color us = pos.side_to_move(); + Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN); + + while (dc) + { + Square from = pop_lsb(dc); + PieceType pt = type_of(pos.piece_on(from)); + + Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); + + if (pt == KING) + b &= ~attacks_bb(pos.square(~us)); + + while (b) + *moveList++ = make_move(from, pop_lsb(b)); + } + + return us == WHITE ? generate_all(pos, moveList) + : generate_all(pos, moveList); +} + + +/// generate generates all pseudo-legal check evasions when the side +/// to move is in check. Returns a pointer to the end of the move list. +template<> +ExtMove* generate(const Position& pos, ExtMove* moveList) { + + assert(pos.checkers()); + + Color us = pos.side_to_move(); + Square ksq = pos.square(us); + + // Generate evasions for king + Bitboard b = attacks_bb(ksq) & ~pos.pieces(us); + while (b) + *moveList++ = make_move(ksq, pop_lsb(b)); + + if (more_than_one(pos.checkers())) + return moveList; // Double check, only a king move can save the day + + // Generate blocking interpositions or captures of the checking piece + return us == WHITE ? generate_all(pos, moveList) + : generate_all(pos, moveList); +} + + /// generate generates all the legal moves in the given position template<> From 14d162d9f4f3544acf617de46a38a69878003e31 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 14 Apr 2021 21:25:18 +0300 Subject: [PATCH 024/282] Simplification: last capture extension The code for last capture extension can be removed in current master. Passed STC LLR: 2.95 (-2.94,2.94) {-1.00,0.20} Total: 85024 W: 7754 L: 7707 D: 69563 Ptnml(0-2): 293, 5991, 29914, 6004, 310 https://tests.stockfishchess.org/tests/view/607690f1814175337896068f Passed LTC LLR: 2.96 (-2.94,2.94) {-0.70,0.20} Total: 39880 W: 1503 L: 1453 D: 36924 Ptnml(0-2): 17, 1281, 17293, 1333, 16 https://tests.stockfishchess.org/tests/view/6076ccbe814175337896069e Closes https://github.com/official-stockfish/Stockfish/pull/3430 Bench: 4202264 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f23db4c1..c9ee47fe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1158,11 +1158,6 @@ moves_loop: // When in check, search starts from here && (pos.is_discovered_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; - // Last captures extension - else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg - && pos.non_pawn_material() <= 2 * RookValueMg) - extension = 1; - // Add extension to new depth newDepth += extension; From 255514fb29f5a7a2c95863c9b074e36209b841c1 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 13 Apr 2021 20:02:28 +0200 Subject: [PATCH 025/282] Documentation patch: AppendChangedIndices Clarify the assumptions on the position passed to the AppendChangedIndices(). Closes https://github.com/official-stockfish/Stockfish/pull/3428 No functional change --- src/nnue/features/half_kp.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 5a440b92..8e6907ae 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -47,7 +47,25 @@ namespace Stockfish::Eval::NNUE::Features { } } - // Get a list of indices for recently changed features + + // AppendChangedIndices() : get a list of indices for recently changed features + + // IMPORTANT: The `pos` in this function is pretty much useless as it + // is not always the position the features are updated to. The feature + // transformer code right now can update multiple accumulators per move, + // but since Stockfish only keeps the full state of the current leaf + // search position it is not possible to always pass here the position for + // which the accumulator is being updated. Therefore the only thing that + // can be reliably extracted from `pos` is the king square for the king + // of the `perspective` color (note: not even the other king's square will + // match reality in all cases, this is also the reason why `dp` is passed + // as a parameter and not extracted from pos.state()). This is of particular + // problem for future nets with other feature sets, where updating the active + // feature might require more information from the intermediate positions. In + // this case the only easy solution is to remove the multiple updates from + // the feature transformer update code and only update the accumulator for + // the current leaf position (the position after the move). + template void HalfKP::AppendChangedIndices( const Position& pos, const DirtyPiece& dp, Color perspective, From a7ab92ec25c91e8413630c52cfc2db6b4ecacf0c Mon Sep 17 00:00:00 2001 From: dsmsgms Date: Mon, 12 Apr 2021 12:06:22 -0300 Subject: [PATCH 026/282] Use classical eval for Bishop vs Pawns NNUE evaluation is incapable of recognizing trivially drawn bishop endgames (the wrong-colored rook pawn), which are in fact ubiquitous and stock standard in chess analysis. Switching off NNUE evaluation in KBPs vs KPs endgames is a measure that stops Stockfish from trading down to a drawn version of these endings when we presumably have advantage. The patch is able to edge over master in endgame positions. Patch tested for Elo gain with the "endgame.epd" book, and verified for non-regression with our usual book (see the pull request for details). STC: LLR: 2.93 (-2.94,2.94) {-0.20,1.10} Total: 33232 W: 6655 L: 6497 D: 20080 Ptnml(0-2): 4, 2342, 11769, 2494, 7 https://tests.stockfishchess.org/tests/view/6074a52981417533789605b8 LTC: LLR: 2.93 (-2.94,2.94) {0.20,0.90} Total: 159056 W: 29799 L: 29378 D: 99879 Ptnml(0-2): 7, 9004, 61085, 9425, 7 https://tests.stockfishchess.org/tests/view/6074c39a81417533789605ca Closes https://github.com/official-stockfish/Stockfish/pull/3427 Bench: 4503918 blah --- src/evaluate.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 789e2859..ba3de70b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1104,23 +1104,26 @@ Value Eval::evaluate(const Position& pos) { return nnue; }; - // If there is PSQ imbalance use classical eval, with small probability if it is small + // If there is PSQ imbalance we use the classical eval. We also introduce + // a small probability of using the classical eval when PSQ imbalance is small. Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); // Use classical evaluation for really low piece endgames. - // The most critical case is a bishop + A/H file pawn vs naked king draw. - bool strongClassical = pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2; + // One critical case is the draw for bishop + A/H file pawn vs naked king. + bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg + || (pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2); - v = classical || strongClassical ? Evaluation(pos).value() : adjusted_NNUE(); + v = classical || lowPieceEndgame ? Evaluation(pos).value() + : adjusted_NNUE(); // If the classical eval is small and imbalance large, use NNUE nevertheless. - // For the case of opposite colored bishops, switch to NNUE eval with - // small probability if the classical eval is less than the threshold. - if ( largePsq - && !strongClassical + // For the case of opposite colored bishops, switch to NNUE eval with small + // probability if the classical eval is less than the threshold. + if ( largePsq + && !lowPieceEndgame && ( abs(v) * 16 < NNUEThreshold2 * r50 || ( pos.opposite_bishops() && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 From fbbd4adc3c01460faa3cc8f91771ab9b0ef718ca Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Mon, 19 Apr 2021 19:50:19 +0200 Subject: [PATCH 027/282] Unify naming convention of the NNUE code matches the rest of the stockfish code base closes https://github.com/official-stockfish/Stockfish/pull/3437 No functional change --- src/nnue/architectures/halfkp_256x2-32-32.h | 6 +- src/nnue/evaluate_nnue.cpp | 64 ++--- src/nnue/evaluate_nnue.h | 4 +- src/nnue/features/feature_set.h | 14 +- src/nnue/features/features_common.h | 4 +- src/nnue/features/half_kp.cpp | 10 +- src/nnue/features/half_kp.h | 22 +- src/nnue/features/index_list.h | 2 +- src/nnue/layers/affine_transform.h | 252 ++++++++++---------- src/nnue/layers/clipped_relu.h | 96 ++++---- src/nnue/layers/input_slice.h | 24 +- src/nnue/nnue_accumulator.h | 4 +- src/nnue/nnue_architecture.h | 6 +- src/nnue/nnue_common.h | 35 ++- src/nnue/nnue_feature_transformer.h | 179 +++++++------- src/position.cpp | 4 +- src/search.cpp | 8 +- 17 files changed, 364 insertions(+), 370 deletions(-) diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h index a6768204..5f6cc7f3 100644 --- a/src/nnue/architectures/halfkp_256x2-32-32.h +++ b/src/nnue/architectures/halfkp_256x2-32-32.h @@ -32,15 +32,15 @@ namespace Stockfish::Eval::NNUE { // Input features used in evaluation function using RawFeatures = Features::FeatureSet< - Features::HalfKP>; + Features::HalfKP>; // Number of input feature dimensions after conversion -constexpr IndexType kTransformedFeatureDimensions = 256; +constexpr IndexType TransformedFeatureDimensions = 256; namespace Layers { // Define network structure -using InputLayer = InputSlice; +using InputLayer = InputSlice; using HiddenLayer1 = ClippedReLU>; using HiddenLayer2 = ClippedReLU>; using OutputLayer = AffineTransform; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 5416f13e..0e539611 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -32,7 +32,7 @@ namespace Stockfish::Eval::NNUE { // Input feature converter - LargePagePtr feature_transformer; + LargePagePtr featureTransformer; // Evaluation function AlignedPtr network; @@ -44,14 +44,14 @@ namespace Stockfish::Eval::NNUE { // Initialize the evaluation function parameters template - void Initialize(AlignedPtr& pointer) { + void initialize(AlignedPtr& pointer) { pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); std::memset(pointer.get(), 0, sizeof(T)); } template - void Initialize(LargePagePtr& pointer) { + void initialize(LargePagePtr& pointer) { static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); pointer.reset(reinterpret_cast(aligned_large_pages_alloc(sizeof(T)))); @@ -60,46 +60,46 @@ namespace Stockfish::Eval::NNUE { // Read evaluation function parameters template - bool ReadParameters(std::istream& stream, T& reference) { + bool read_parameters(std::istream& stream, T& reference) { std::uint32_t header; header = read_little_endian(stream); - if (!stream || header != T::GetHashValue()) return false; - return reference.ReadParameters(stream); + if (!stream || header != T::get_hash_value()) return false; + return reference.read_parameters(stream); } } // namespace Detail // Initialize the evaluation function parameters - void Initialize() { + void initialize() { - Detail::Initialize(feature_transformer); - Detail::Initialize(network); + Detail::initialize(featureTransformer); + Detail::initialize(network); } // Read network header - bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture) + bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* architecture) { std::uint32_t version, size; version = read_little_endian(stream); - *hash_value = read_little_endian(stream); + *hashValue = read_little_endian(stream); size = read_little_endian(stream); - if (!stream || version != kVersion) return false; + if (!stream || version != Version) return false; architecture->resize(size); stream.read(&(*architecture)[0], size); return !stream.fail(); } // Read network parameters - bool ReadParameters(std::istream& stream) { + bool read_parameters(std::istream& stream) { - std::uint32_t hash_value; + std::uint32_t hashValue; std::string architecture; - if (!ReadHeader(stream, &hash_value, &architecture)) return false; - if (hash_value != kHashValue) return false; - if (!Detail::ReadParameters(stream, *feature_transformer)) return false; - if (!Detail::ReadParameters(stream, *network)) return false; + if (!read_header(stream, &hashValue, &architecture)) return false; + if (hashValue != HashValue) return false; + if (!Detail::read_parameters(stream, *featureTransformer)) return false; + if (!Detail::read_parameters(stream, *network)) return false; return stream && stream.peek() == std::ios::traits_type::eof(); } @@ -109,36 +109,36 @@ namespace Stockfish::Eval::NNUE { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. - constexpr uint64_t alignment = kCacheLineSize; + constexpr uint64_t alignment = CacheLineSize; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType transformed_features_unaligned[ - FeatureTransformer::kBufferSize + alignment / sizeof(TransformedFeatureType)]; - char buffer_unaligned[Network::kBufferSize + alignment]; + TransformedFeatureType transformedFeaturesUnaligned[ + FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; + char bufferUnaligned[Network::BufferSize + alignment]; - auto* transformed_features = align_ptr_up(&transformed_features_unaligned[0]); - auto* buffer = align_ptr_up(&buffer_unaligned[0]); + auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); + auto* buffer = align_ptr_up(&bufferUnaligned[0]); #else alignas(alignment) - TransformedFeatureType transformed_features[FeatureTransformer::kBufferSize]; - alignas(alignment) char buffer[Network::kBufferSize]; + TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + alignas(alignment) char buffer[Network::BufferSize]; #endif - ASSERT_ALIGNED(transformed_features, alignment); + ASSERT_ALIGNED(transformedFeatures, alignment); ASSERT_ALIGNED(buffer, alignment); - feature_transformer->Transform(pos, transformed_features); - const auto output = network->Propagate(transformed_features, buffer); + featureTransformer->transform(pos, transformedFeatures); + const auto output = network->propagate(transformedFeatures, buffer); - return static_cast(output[0] / FV_SCALE); + return static_cast(output[0] / OutputScale); } // Load eval, from a file stream or a memory stream bool load_eval(std::string name, std::istream& stream) { - Initialize(); + initialize(); fileName = name; - return ReadParameters(stream); + return read_parameters(stream); } } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 24aa6cc0..c7fa4a96 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -28,8 +28,8 @@ namespace Stockfish::Eval::NNUE { // Hash value of evaluation function structure - constexpr std::uint32_t kHashValue = - FeatureTransformer::GetHashValue() ^ Network::GetHashValue(); + constexpr std::uint32_t HashValue = + FeatureTransformer::get_hash_value() ^ Network::get_hash_value(); // Deleter for automating release of memory area template diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index a3fea9c0..d09f9b94 100644 --- a/src/nnue/features/feature_set.h +++ b/src/nnue/features/feature_set.h @@ -36,7 +36,7 @@ namespace Stockfish::Eval::NNUE::Features { return value == First || CompileTimeList::Contains(value); } static constexpr std::array - kValues = {{First, Remaining...}}; + Values = {{First, Remaining...}}; }; // Base class of feature set @@ -51,16 +51,16 @@ namespace Stockfish::Eval::NNUE::Features { public: // Hash value embedded in the evaluation file - static constexpr std::uint32_t kHashValue = FeatureType::kHashValue; + static constexpr std::uint32_t HashValue = FeatureType::HashValue; // Number of feature dimensions - static constexpr IndexType kDimensions = FeatureType::kDimensions; + static constexpr IndexType Dimensions = FeatureType::Dimensions; // Maximum number of simultaneously active features - static constexpr IndexType kMaxActiveDimensions = - FeatureType::kMaxActiveDimensions; + static constexpr IndexType MaxActiveDimensions = + FeatureType::MaxActiveDimensions; // Trigger for full calculation instead of difference calculation using SortedTriggerSet = - CompileTimeList; - static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues; + CompileTimeList; + static constexpr auto RefreshTriggers = SortedTriggerSet::Values; }; diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h index 118ec953..9584cac8 100644 --- a/src/nnue/features/features_common.h +++ b/src/nnue/features/features_common.h @@ -33,11 +33,11 @@ namespace Stockfish::Eval::NNUE::Features { // Trigger to perform full calculations instead of difference only enum class TriggerEvent { - kFriendKingMoved // calculate full evaluation when own king moves + FriendKingMoved // calculate full evaluation when own king moves }; enum class Side { - kFriend // side to move + Friend // side to move }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 8e6907ae..5c7538de 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -30,12 +30,12 @@ namespace Stockfish::Eval::NNUE::Features { // Index of a feature for a given king position and another piece on some square inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) { - return IndexType(orient(perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq); + return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); } // Get a list of indices for active features template - void HalfKP::AppendActiveIndices( + void HalfKP::append_active_indices( const Position& pos, Color perspective, IndexList* active) { Square ksq = orient(perspective, pos.square(perspective)); @@ -48,7 +48,7 @@ namespace Stockfish::Eval::NNUE::Features { } - // AppendChangedIndices() : get a list of indices for recently changed features + // append_changed_indices() : get a list of indices for recently changed features // IMPORTANT: The `pos` in this function is pretty much useless as it // is not always the position the features are updated to. The feature @@ -67,7 +67,7 @@ namespace Stockfish::Eval::NNUE::Features { // the current leaf position (the position after the move). template - void HalfKP::AppendChangedIndices( + void HalfKP::append_changed_indices( const Position& pos, const DirtyPiece& dp, Color perspective, IndexList* removed, IndexList* added) { @@ -82,6 +82,6 @@ namespace Stockfish::Eval::NNUE::Features { } } - template class HalfKP; + template class HalfKP; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index 2461acb7..14efb089 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -33,25 +33,25 @@ namespace Stockfish::Eval::NNUE::Features { public: // Feature name - static constexpr const char* kName = "HalfKP(Friend)"; + static constexpr const char* Name = "HalfKP(Friend)"; // Hash value embedded in the evaluation file - static constexpr std::uint32_t kHashValue = - 0x5D69D5B9u ^ (AssociatedKing == Side::kFriend); + static constexpr std::uint32_t HashValue = + 0x5D69D5B9u ^ (AssociatedKing == Side::Friend); // Number of feature dimensions - static constexpr IndexType kDimensions = - static_cast(SQUARE_NB) * static_cast(PS_END); + static constexpr IndexType Dimensions = + static_cast(SQUARE_NB) * static_cast(PS_NB); // Maximum number of simultaneously active features - static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count + static constexpr IndexType MaxActiveDimensions = 30; // Kings don't count // Trigger for full calculation instead of difference calculation - static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved; + static constexpr TriggerEvent RefreshTrigger = TriggerEvent::FriendKingMoved; // Get a list of indices for active features - static void AppendActiveIndices(const Position& pos, Color perspective, - IndexList* active); + static void append_active_indices(const Position& pos, Color perspective, + IndexList* active); // Get a list of indices for recently changed features - static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added); + static void append_changed_indices(const Position& pos, const DirtyPiece& dp, Color perspective, + IndexList* removed, IndexList* added); }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h index 9f03993b..edf0add1 100644 --- a/src/nnue/features/index_list.h +++ b/src/nnue/features/index_list.h @@ -56,7 +56,7 @@ namespace Stockfish::Eval::NNUE::Features { //Type of feature index list class IndexList - : public ValueList { + : public ValueList { }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 1faa180d..424fad56 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -27,7 +27,7 @@ namespace Stockfish::Eval::NNUE::Layers { // Affine transformation layer - template + template class AffineTransform { public: // Input/output type @@ -36,64 +36,64 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType kInputDimensions = - PreviousLayer::kOutputDimensions; - static constexpr IndexType kOutputDimensions = OutputDimensions; - static constexpr IndexType kPaddedInputDimensions = - CeilToMultiple(kInputDimensions, kMaxSimdWidth); + static constexpr IndexType InputDimensions = + PreviousLayer::OutputDimensions; + static constexpr IndexType OutputDimensions = OutDims; + static constexpr IndexType PaddedInputDimensions = + ceil_to_multiple(InputDimensions, MaxSimdWidth); #if defined (USE_AVX512) - static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 2; + static constexpr const IndexType OutputSimdWidth = SimdWidth / 2; #elif defined (USE_SSSE3) - static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 4; + static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; #endif // Size of forward propagation buffer used in this layer - static constexpr std::size_t kSelfBufferSize = - CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize); + static constexpr std::size_t SelfBufferSize = + ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t kBufferSize = - PreviousLayer::kBufferSize + kSelfBufferSize; + static constexpr std::size_t BufferSize = + PreviousLayer::BufferSize + SelfBufferSize; // Hash value embedded in the evaluation file - static constexpr std::uint32_t GetHashValue() { - std::uint32_t hash_value = 0xCC03DAE4u; - hash_value += kOutputDimensions; - hash_value ^= PreviousLayer::GetHashValue() >> 1; - hash_value ^= PreviousLayer::GetHashValue() << 31; - return hash_value; + static constexpr std::uint32_t get_hash_value() { + std::uint32_t hashValue = 0xCC03DAE4u; + hashValue += OutputDimensions; + hashValue ^= PreviousLayer::get_hash_value() >> 1; + hashValue ^= PreviousLayer::get_hash_value() << 31; + return hashValue; } - // Read network parameters - bool ReadParameters(std::istream& stream) { - if (!previous_layer_.ReadParameters(stream)) return false; - for (std::size_t i = 0; i < kOutputDimensions; ++i) - biases_[i] = read_little_endian(stream); - for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i) + // Read network parameters + bool read_parameters(std::istream& stream) { + if (!previousLayer.read_parameters(stream)) return false; + for (std::size_t i = 0; i < OutputDimensions; ++i) + biases[i] = read_little_endian(stream); + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) #if !defined (USE_SSSE3) - weights_[i] = read_little_endian(stream); + weights[i] = read_little_endian(stream); #else - weights_[ - (i / 4) % (kPaddedInputDimensions / 4) * kOutputDimensions * 4 + - i / kPaddedInputDimensions * 4 + + weights[ + (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + + i / PaddedInputDimensions * 4 + i % 4 ] = read_little_endian(stream); // Determine if eights of weight and input products can be summed using 16bits // without saturation. We assume worst case combinations of 0 and 127 for all inputs. - if (kOutputDimensions > 1 && !stream.fail()) + if (OutputDimensions > 1 && !stream.fail()) { canSaturate16.count = 0; #if !defined(USE_VNNI) - for (IndexType i = 0; i < kPaddedInputDimensions; i += 16) - for (IndexType j = 0; j < kOutputDimensions; ++j) + for (IndexType i = 0; i < PaddedInputDimensions; i += 16) + for (IndexType j = 0; j < OutputDimensions; ++j) for (int x = 0; x < 2; ++x) { - WeightType* w = &weights_[i * kOutputDimensions + j * 4 + x * 2]; + WeightType* w = &weights[i * OutputDimensions + j * 4 + x * 2]; int sum[2] = {0, 0}; for (int k = 0; k < 8; ++k) { - IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2; + IndexType idx = k / 2 * OutputDimensions * 4 + k % 2; sum[w[idx] < 0] += w[idx]; } for (int sign : { -1, 1 }) @@ -102,12 +102,12 @@ namespace Stockfish::Eval::NNUE::Layers { int maxK = 0, maxW = 0; for (int k = 0; k < 8; ++k) { - IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2; + IndexType idx = k / 2 * OutputDimensions * 4 + k % 2; if (maxW < sign * w[idx]) maxK = k, maxW = sign * w[idx]; } - IndexType idx = maxK / 2 * kOutputDimensions * 4 + maxK % 2; + IndexType idx = maxK / 2 * OutputDimensions * 4 + maxK % 2; sum[sign == -1] -= w[idx]; canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx]); w[idx] = 0; @@ -126,14 +126,14 @@ namespace Stockfish::Eval::NNUE::Layers { } // Forward propagation - const OutputType* Propagate( - const TransformedFeatureType* transformed_features, char* buffer) const { - const auto input = previous_layer_.Propagate( - transformed_features, buffer + kSelfBufferSize); + const OutputType* propagate( + const TransformedFeatureType* transformedFeatures, char* buffer) const { + const auto input = previousLayer.propagate( + transformedFeatures, buffer + SelfBufferSize); #if defined (USE_AVX512) - [[maybe_unused]] const __m512i kOnes512 = _mm512_set1_epi16(1); + [[maybe_unused]] const __m512i Ones512 = _mm512_set1_epi16(1); [[maybe_unused]] auto m512_hadd = [](__m512i sum, int bias) -> int { return _mm512_reduce_add_epi32(sum) + bias; @@ -144,7 +144,7 @@ namespace Stockfish::Eval::NNUE::Layers { acc = _mm512_dpbusd_epi32(acc, a, b); #else __m512i product0 = _mm512_maddubs_epi16(a, b); - product0 = _mm512_madd_epi16(product0, kOnes512); + product0 = _mm512_madd_epi16(product0, Ones512); acc = _mm512_add_epi32(acc, product0); #endif }; @@ -164,7 +164,7 @@ namespace Stockfish::Eval::NNUE::Layers { product0 = _mm512_add_epi16(product0, product1); product2 = _mm512_add_epi16(product2, product3); product0 = _mm512_add_epi16(product0, product2); - product0 = _mm512_madd_epi16(product0, kOnes512); + product0 = _mm512_madd_epi16(product0, Ones512); acc = _mm512_add_epi32(acc, product0); #endif }; @@ -172,7 +172,7 @@ namespace Stockfish::Eval::NNUE::Layers { #endif #if defined (USE_AVX2) - [[maybe_unused]] const __m256i kOnes256 = _mm256_set1_epi16(1); + [[maybe_unused]] const __m256i Ones256 = _mm256_set1_epi16(1); [[maybe_unused]] auto m256_hadd = [](__m256i sum, int bias) -> int { __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); @@ -186,7 +186,7 @@ namespace Stockfish::Eval::NNUE::Layers { acc = _mm256_dpbusd_epi32(acc, a, b); #else __m256i product0 = _mm256_maddubs_epi16(a, b); - product0 = _mm256_madd_epi16(product0, kOnes256); + product0 = _mm256_madd_epi16(product0, Ones256); acc = _mm256_add_epi32(acc, product0); #endif }; @@ -206,7 +206,7 @@ namespace Stockfish::Eval::NNUE::Layers { product0 = _mm256_add_epi16(product0, product1); product2 = _mm256_add_epi16(product2, product3); product0 = _mm256_add_epi16(product0, product2); - product0 = _mm256_madd_epi16(product0, kOnes256); + product0 = _mm256_madd_epi16(product0, Ones256); acc = _mm256_add_epi32(acc, product0); #endif }; @@ -214,7 +214,7 @@ namespace Stockfish::Eval::NNUE::Layers { #endif #if defined (USE_SSSE3) - [[maybe_unused]] const __m128i kOnes128 = _mm_set1_epi16(1); + [[maybe_unused]] const __m128i Ones128 = _mm_set1_epi16(1); [[maybe_unused]] auto m128_hadd = [](__m128i sum, int bias) -> int { sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC @@ -224,7 +224,7 @@ namespace Stockfish::Eval::NNUE::Layers { [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { __m128i product0 = _mm_maddubs_epi16(a, b); - product0 = _mm_madd_epi16(product0, kOnes128); + product0 = _mm_madd_epi16(product0, Ones128); acc = _mm_add_epi32(acc, product0); }; @@ -237,7 +237,7 @@ namespace Stockfish::Eval::NNUE::Layers { product0 = _mm_add_epi16(product0, product1); product2 = _mm_add_epi16(product2, product3); product0 = _mm_add_epi16(product0, product2); - product0 = _mm_madd_epi16(product0, kOnes128); + product0 = _mm_madd_epi16(product0, Ones128); acc = _mm_add_epi32(acc, product0); }; @@ -269,71 +269,71 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined (USE_SSSE3) const auto output = reinterpret_cast(buffer); - const auto input_vector = reinterpret_cast(input); + const auto inputVector = reinterpret_cast(input); - static_assert(kOutputDimensions % kOutputSimdWidth == 0 || kOutputDimensions == 1); + static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); - // kOutputDimensions is either 1 or a multiple of kSimdWidth + // OutputDimensions is either 1 or a multiple of SimdWidth // because then it is also an input dimension. - if constexpr (kOutputDimensions % kOutputSimdWidth == 0) + if constexpr (OutputDimensions % OutputSimdWidth == 0) { - constexpr IndexType kNumChunks = kPaddedInputDimensions / 4; + constexpr IndexType NumChunks = PaddedInputDimensions / 4; const auto input32 = reinterpret_cast(input); vec_t* outptr = reinterpret_cast(output); - std::memcpy(output, biases_, kOutputDimensions * sizeof(OutputType)); + std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); - for (int i = 0; i < (int)kNumChunks - 3; i += 4) + for (int i = 0; i < (int)NumChunks - 3; i += 4) { const vec_t in0 = vec_set_32(input32[i + 0]); const vec_t in1 = vec_set_32(input32[i + 1]); const vec_t in2 = vec_set_32(input32[i + 2]); const vec_t in3 = vec_set_32(input32[i + 3]); - const auto col0 = reinterpret_cast(&weights_[(i + 0) * kOutputDimensions * 4]); - const auto col1 = reinterpret_cast(&weights_[(i + 1) * kOutputDimensions * 4]); - const auto col2 = reinterpret_cast(&weights_[(i + 2) * kOutputDimensions * 4]); - const auto col3 = reinterpret_cast(&weights_[(i + 3) * kOutputDimensions * 4]); - for (int j = 0; j * kOutputSimdWidth < kOutputDimensions; ++j) + const auto col0 = reinterpret_cast(&weights[(i + 0) * OutputDimensions * 4]); + const auto col1 = reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); + const auto col2 = reinterpret_cast(&weights[(i + 2) * OutputDimensions * 4]); + const auto col3 = reinterpret_cast(&weights[(i + 3) * OutputDimensions * 4]); + for (int j = 0; j * OutputSimdWidth < OutputDimensions; ++j) vec_add_dpbusd_32x4(outptr[j], in0, col0[j], in1, col1[j], in2, col2[j], in3, col3[j]); } for (int i = 0; i < canSaturate16.count; ++i) output[canSaturate16.ids[i].out] += input[canSaturate16.ids[i].in] * canSaturate16.ids[i].w; } - else if constexpr (kOutputDimensions == 1) + else if constexpr (OutputDimensions == 1) { #if defined (USE_AVX512) - if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) != 0) + if constexpr (PaddedInputDimensions % (SimdWidth * 2) != 0) { - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const auto input_vector256 = reinterpret_cast(input); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + const auto inputVector256 = reinterpret_cast(input); __m256i sum0 = _mm256_setzero_si256(); - const auto row0 = reinterpret_cast(&weights_[0]); + const auto row0 = reinterpret_cast(&weights[0]); - for (int j = 0; j < (int)kNumChunks; ++j) + for (int j = 0; j < (int)NumChunks; ++j) { - const __m256i in = input_vector256[j]; + const __m256i in = inputVector256[j]; m256_add_dpbusd_epi32(sum0, in, row0[j]); } - output[0] = m256_hadd(sum0, biases_[0]); + output[0] = m256_hadd(sum0, biases[0]); } else #endif { #if defined (USE_AVX512) - constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2); + constexpr IndexType NumChunks = PaddedInputDimensions / (SimdWidth * 2); #else - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; #endif vec_t sum0 = vec_setzero(); - const auto row0 = reinterpret_cast(&weights_[0]); + const auto row0 = reinterpret_cast(&weights[0]); - for (int j = 0; j < (int)kNumChunks; ++j) + for (int j = 0; j < (int)NumChunks; ++j) { - const vec_t in = input_vector[j]; + const vec_t in = inputVector[j]; vec_add_dpbusd_32(sum0, in, row0[j]); } - output[0] = vec_hadd(sum0, biases_[0]); + output[0] = vec_hadd(sum0, biases[0]); } } @@ -344,80 +344,80 @@ namespace Stockfish::Eval::NNUE::Layers { auto output = reinterpret_cast(buffer); #if defined(USE_SSE2) - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const __m128i kZeros = _mm_setzero_si128(); - const auto input_vector = reinterpret_cast(input); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + const __m128i Zeros = _mm_setzero_si128(); + const auto inputVector = reinterpret_cast(input); #elif defined(USE_MMX) - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const __m64 kZeros = _mm_setzero_si64(); - const auto input_vector = reinterpret_cast(input); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + const __m64 Zeros = _mm_setzero_si64(); + const auto inputVector = reinterpret_cast(input); #elif defined(USE_NEON) - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const auto input_vector = reinterpret_cast(input); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + const auto inputVector = reinterpret_cast(input); #endif - for (IndexType i = 0; i < kOutputDimensions; ++i) { - const IndexType offset = i * kPaddedInputDimensions; + for (IndexType i = 0; i < OutputDimensions; ++i) { + const IndexType offset = i * PaddedInputDimensions; #if defined(USE_SSE2) - __m128i sum_lo = _mm_cvtsi32_si128(biases_[i]); - __m128i sum_hi = kZeros; - const auto row = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + __m128i sumLo = _mm_cvtsi32_si128(biases[i]); + __m128i sumHi = Zeros; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { __m128i row_j = _mm_load_si128(&row[j]); - __m128i input_j = _mm_load_si128(&input_vector[j]); - __m128i extended_row_lo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); - __m128i extended_row_hi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); - __m128i extended_input_lo = _mm_unpacklo_epi8(input_j, kZeros); - __m128i extended_input_hi = _mm_unpackhi_epi8(input_j, kZeros); - __m128i product_lo = _mm_madd_epi16(extended_row_lo, extended_input_lo); - __m128i product_hi = _mm_madd_epi16(extended_row_hi, extended_input_hi); - sum_lo = _mm_add_epi32(sum_lo, product_lo); - sum_hi = _mm_add_epi32(sum_hi, product_hi); + __m128i input_j = _mm_load_si128(&inputVector[j]); + __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); + __m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); + __m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros); + __m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros); + __m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo); + __m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi); + sumLo = _mm_add_epi32(sumLo, productLo); + sumHi = _mm_add_epi32(sumHi, productHi); } - __m128i sum = _mm_add_epi32(sum_lo, sum_hi); - __m128i sum_high_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); - sum = _mm_add_epi32(sum, sum_high_64); + __m128i sum = _mm_add_epi32(sumLo, sumHi); + __m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sumHigh_64); __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2)); sum = _mm_add_epi32(sum, sum_second_32); output[i] = _mm_cvtsi128_si32(sum); #elif defined(USE_MMX) - __m64 sum_lo = _mm_cvtsi32_si64(biases_[i]); - __m64 sum_hi = kZeros; - const auto row = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + __m64 sumLo = _mm_cvtsi32_si64(biases[i]); + __m64 sumHi = Zeros; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { __m64 row_j = row[j]; - __m64 input_j = input_vector[j]; - __m64 extended_row_lo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); - __m64 extended_row_hi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); - __m64 extended_input_lo = _mm_unpacklo_pi8(input_j, kZeros); - __m64 extended_input_hi = _mm_unpackhi_pi8(input_j, kZeros); - __m64 product_lo = _mm_madd_pi16(extended_row_lo, extended_input_lo); - __m64 product_hi = _mm_madd_pi16(extended_row_hi, extended_input_hi); - sum_lo = _mm_add_pi32(sum_lo, product_lo); - sum_hi = _mm_add_pi32(sum_hi, product_hi); + __m64 input_j = inputVector[j]; + __m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); + __m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); + __m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros); + __m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros); + __m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo); + __m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi); + sumLo = _mm_add_pi32(sumLo, productLo); + sumHi = _mm_add_pi32(sumHi, productHi); } - __m64 sum = _mm_add_pi32(sum_lo, sum_hi); + __m64 sum = _mm_add_pi32(sumLo, sumHi); sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); output[i] = _mm_cvtsi64_si32(sum); #elif defined(USE_NEON) - int32x4_t sum = {biases_[i]}; - const auto row = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { - int16x8_t product = vmull_s8(input_vector[j * 2], row[j * 2]); - product = vmlal_s8(product, input_vector[j * 2 + 1], row[j * 2 + 1]); + int32x4_t sum = {biases[i]}; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { + int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]); + product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); sum = vpadalq_s16(sum, product); } output[i] = sum[0] + sum[1] + sum[2] + sum[3]; #else - OutputType sum = biases_[i]; - for (IndexType j = 0; j < kInputDimensions; ++j) { - sum += weights_[offset + j] * input[j]; + OutputType sum = biases[i]; + for (IndexType j = 0; j < InputDimensions; ++j) { + sum += weights[offset + j] * input[j]; } output[i] = sum; #endif @@ -436,10 +436,10 @@ namespace Stockfish::Eval::NNUE::Layers { using BiasType = OutputType; using WeightType = std::int8_t; - PreviousLayer previous_layer_; + PreviousLayer previousLayer; - alignas(kCacheLineSize) BiasType biases_[kOutputDimensions]; - alignas(kCacheLineSize) WeightType weights_[kOutputDimensions * kPaddedInputDimensions]; + alignas(CacheLineSize) BiasType biases[OutputDimensions]; + alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; #if defined (USE_SSSE3) struct CanSaturate { int count; @@ -447,7 +447,7 @@ namespace Stockfish::Eval::NNUE::Layers { uint16_t out; uint16_t in; int8_t w; - } ids[kPaddedInputDimensions * kOutputDimensions * 3 / 4]; + } ids[PaddedInputDimensions * OutputDimensions * 3 / 4]; void add(int i, int j, int8_t w) { ids[count].out = i; diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index a10e3e48..00809c50 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -35,130 +35,130 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType kInputDimensions = - PreviousLayer::kOutputDimensions; - static constexpr IndexType kOutputDimensions = kInputDimensions; + static constexpr IndexType InputDimensions = + PreviousLayer::OutputDimensions; + static constexpr IndexType OutputDimensions = InputDimensions; // Size of forward propagation buffer used in this layer - static constexpr std::size_t kSelfBufferSize = - CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize); + static constexpr std::size_t SelfBufferSize = + ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t kBufferSize = - PreviousLayer::kBufferSize + kSelfBufferSize; + static constexpr std::size_t BufferSize = + PreviousLayer::BufferSize + SelfBufferSize; // Hash value embedded in the evaluation file - static constexpr std::uint32_t GetHashValue() { - std::uint32_t hash_value = 0x538D24C7u; - hash_value += PreviousLayer::GetHashValue(); - return hash_value; + static constexpr std::uint32_t get_hash_value() { + std::uint32_t hashValue = 0x538D24C7u; + hashValue += PreviousLayer::get_hash_value(); + return hashValue; } // Read network parameters - bool ReadParameters(std::istream& stream) { - return previous_layer_.ReadParameters(stream); + bool read_parameters(std::istream& stream) { + return previousLayer.read_parameters(stream); } // Forward propagation - const OutputType* Propagate( - const TransformedFeatureType* transformed_features, char* buffer) const { - const auto input = previous_layer_.Propagate( - transformed_features, buffer + kSelfBufferSize); + const OutputType* propagate( + const TransformedFeatureType* transformedFeatures, char* buffer) const { + const auto input = previousLayer.propagate( + transformedFeatures, buffer + SelfBufferSize); const auto output = reinterpret_cast(buffer); #if defined(USE_AVX2) - constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; - const __m256i kZero = _mm256_setzero_si256(); - const __m256i kOffsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); + constexpr IndexType NumChunks = InputDimensions / SimdWidth; + const __m256i Zero = _mm256_setzero_si256(); + const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m256i*>(output); - for (IndexType i = 0; i < kNumChunks; ++i) { + for (IndexType i = 0; i < NumChunks; ++i) { const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( _mm256_load_si256(&in[i * 4 + 0]), - _mm256_load_si256(&in[i * 4 + 1])), kWeightScaleBits); + _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( _mm256_load_si256(&in[i * 4 + 2]), - _mm256_load_si256(&in[i * 4 + 3])), kWeightScaleBits); + _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( - _mm256_packs_epi16(words0, words1), kZero), kOffsets)); + _mm256_packs_epi16(words0, words1), Zero), Offsets)); } - constexpr IndexType kStart = kNumChunks * kSimdWidth; + constexpr IndexType Start = NumChunks * SimdWidth; #elif defined(USE_SSE2) - constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; + constexpr IndexType NumChunks = InputDimensions / SimdWidth; #ifdef USE_SSE41 - const __m128i kZero = _mm_setzero_si128(); + const __m128i Zero = _mm_setzero_si128(); #else const __m128i k0x80s = _mm_set1_epi8(-128); #endif const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m128i*>(output); - for (IndexType i = 0; i < kNumChunks; ++i) { + for (IndexType i = 0; i < NumChunks; ++i) { const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( _mm_load_si128(&in[i * 4 + 0]), - _mm_load_si128(&in[i * 4 + 1])), kWeightScaleBits); + _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( _mm_load_si128(&in[i * 4 + 2]), - _mm_load_si128(&in[i * 4 + 3])), kWeightScaleBits); + _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); const __m128i packedbytes = _mm_packs_epi16(words0, words1); _mm_store_si128(&out[i], #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, kZero) + _mm_max_epi8(packedbytes, Zero) #else _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) #endif ); } - constexpr IndexType kStart = kNumChunks * kSimdWidth; + constexpr IndexType Start = NumChunks * SimdWidth; #elif defined(USE_MMX) - constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; + constexpr IndexType NumChunks = InputDimensions / SimdWidth; const __m64 k0x80s = _mm_set1_pi8(-128); const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m64*>(output); - for (IndexType i = 0; i < kNumChunks; ++i) { + for (IndexType i = 0; i < NumChunks; ++i) { const __m64 words0 = _mm_srai_pi16( _mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]), - kWeightScaleBits); + WeightScaleBits); const __m64 words1 = _mm_srai_pi16( _mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]), - kWeightScaleBits); + WeightScaleBits); const __m64 packedbytes = _mm_packs_pi16(words0, words1); out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); } _mm_empty(); - constexpr IndexType kStart = kNumChunks * kSimdWidth; + constexpr IndexType Start = NumChunks * SimdWidth; #elif defined(USE_NEON) - constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2); - const int8x8_t kZero = {0}; + constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); + const int8x8_t Zero = {0}; const auto in = reinterpret_cast(input); const auto out = reinterpret_cast(output); - for (IndexType i = 0; i < kNumChunks; ++i) { + for (IndexType i = 0; i < NumChunks; ++i) { int16x8_t shifted; const auto pack = reinterpret_cast(&shifted); - pack[0] = vqshrn_n_s32(in[i * 2 + 0], kWeightScaleBits); - pack[1] = vqshrn_n_s32(in[i * 2 + 1], kWeightScaleBits); - out[i] = vmax_s8(vqmovn_s16(shifted), kZero); + pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits); + pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits); + out[i] = vmax_s8(vqmovn_s16(shifted), Zero); } - constexpr IndexType kStart = kNumChunks * (kSimdWidth / 2); + constexpr IndexType Start = NumChunks * (SimdWidth / 2); #else - constexpr IndexType kStart = 0; + constexpr IndexType Start = 0; #endif - for (IndexType i = kStart; i < kInputDimensions; ++i) { + for (IndexType i = Start; i < InputDimensions; ++i) { output[i] = static_cast( - std::max(0, std::min(127, input[i] >> kWeightScaleBits))); + std::max(0, std::min(127, input[i] >> WeightScaleBits))); } return output; } private: - PreviousLayer previous_layer_; + PreviousLayer previousLayer; }; } // namespace Stockfish::Eval::NNUE::Layers diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index 43b06eec..f113b911 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -26,38 +26,38 @@ namespace Stockfish::Eval::NNUE::Layers { // Input layer -template +template class InputSlice { public: // Need to maintain alignment - static_assert(Offset % kMaxSimdWidth == 0, ""); + static_assert(Offset % MaxSimdWidth == 0, ""); // Output type using OutputType = TransformedFeatureType; // Output dimensionality - static constexpr IndexType kOutputDimensions = OutputDimensions; + static constexpr IndexType OutputDimensions = OutDims; // Size of forward propagation buffer used from the input layer to this layer - static constexpr std::size_t kBufferSize = 0; + static constexpr std::size_t BufferSize = 0; // Hash value embedded in the evaluation file - static constexpr std::uint32_t GetHashValue() { - std::uint32_t hash_value = 0xEC42E90Du; - hash_value ^= kOutputDimensions ^ (Offset << 10); - return hash_value; + static constexpr std::uint32_t get_hash_value() { + std::uint32_t hashValue = 0xEC42E90Du; + hashValue ^= OutputDimensions ^ (Offset << 10); + return hashValue; } // Read network parameters - bool ReadParameters(std::istream& /*stream*/) { + bool read_parameters(std::istream& /*stream*/) { return true; } // Forward propagation - const OutputType* Propagate( - const TransformedFeatureType* transformed_features, + const OutputType* propagate( + const TransformedFeatureType* transformedFeatures, char* /*buffer*/) const { - return transformed_features + Offset; + return transformedFeatures + Offset; } private: diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 55fafa13..aeb5f2bd 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -29,9 +29,9 @@ namespace Stockfish::Eval::NNUE { enum AccumulatorState { EMPTY, COMPUTED, INIT }; // Class that holds the result of affine transformation of input features - struct alignas(kCacheLineSize) Accumulator { + struct alignas(CacheLineSize) Accumulator { std::int16_t - accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; + accumulation[2][RefreshTriggers.size()][TransformedFeatureDimensions]; AccumulatorState state[2]; }; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 1680368e..f59474df 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -26,12 +26,12 @@ namespace Stockfish::Eval::NNUE { - static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, ""); - static_assert(Network::kOutputDimensions == 1, ""); + static_assert(TransformedFeatureDimensions % MaxSimdWidth == 0, ""); + static_assert(Network::OutputDimensions == 1, ""); static_assert(std::is_same::value, ""); // Trigger for full calculation instead of difference calculation - constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers; + constexpr auto RefreshTriggers = RawFeatures::RefreshTriggers; } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 09a152a5..20eb27d4 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -46,30 +46,30 @@ namespace Stockfish::Eval::NNUE { // Version of the evaluation file - constexpr std::uint32_t kVersion = 0x7AF32F16u; + constexpr std::uint32_t Version = 0x7AF32F16u; // Constant used in evaluation value calculation - constexpr int FV_SCALE = 16; - constexpr int kWeightScaleBits = 6; + constexpr int OutputScale = 16; + constexpr int WeightScaleBits = 6; // Size of cache line (in bytes) - constexpr std::size_t kCacheLineSize = 64; + constexpr std::size_t CacheLineSize = 64; // SIMD width (in bytes) #if defined(USE_AVX2) - constexpr std::size_t kSimdWidth = 32; + constexpr std::size_t SimdWidth = 32; #elif defined(USE_SSE2) - constexpr std::size_t kSimdWidth = 16; + constexpr std::size_t SimdWidth = 16; #elif defined(USE_MMX) - constexpr std::size_t kSimdWidth = 8; + constexpr std::size_t SimdWidth = 8; #elif defined(USE_NEON) - constexpr std::size_t kSimdWidth = 16; + constexpr std::size_t SimdWidth = 16; #endif - constexpr std::size_t kMaxSimdWidth = 32; + constexpr std::size_t MaxSimdWidth = 32; // unique number for each piece type on each square enum { @@ -84,19 +84,16 @@ namespace Stockfish::Eval::NNUE { PS_B_ROOK = 7 * SQUARE_NB + 1, PS_W_QUEEN = 8 * SQUARE_NB + 1, PS_B_QUEEN = 9 * SQUARE_NB + 1, - PS_W_KING = 10 * SQUARE_NB + 1, - PS_END = PS_W_KING, // pieces without kings (pawns included) - PS_B_KING = 11 * SQUARE_NB + 1, - PS_END2 = 12 * SQUARE_NB + 1 + PS_NB = 10 * SQUARE_NB + 1 }; - constexpr uint32_t kpp_board_index[COLOR_NB][PIECE_NB] = { + constexpr uint32_t PieceSquareIndex[COLOR_NB][PIECE_NB] = { // convention: W - us, B - them // viewed from other side, W and B are reversed - { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE, - PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE }, - { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE, - PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE } + { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE, + PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE }, + { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE, + PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE } }; // Type of input feature after conversion @@ -105,7 +102,7 @@ namespace Stockfish::Eval::NNUE { // Round n up to be a multiple of base template - constexpr IntType CeilToMultiple(IntType n, IntType base) { + constexpr IntType ceil_to_multiple(IntType n, IntType base) { return (n + base - 1) / base * base; } diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 1e0b0e6d..de4b4937 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -40,7 +40,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) _mm512_store_si512(a,b) #define vec_add_16(a,b) _mm512_add_epi16(a,b) #define vec_sub_16(a,b) _mm512_sub_epi16(a,b) - static constexpr IndexType kNumRegs = 8; // only 8 are needed + static constexpr IndexType NumRegs = 8; // only 8 are needed #elif USE_AVX2 typedef __m256i vec_t; @@ -48,7 +48,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) _mm256_store_si256(a,b) #define vec_add_16(a,b) _mm256_add_epi16(a,b) #define vec_sub_16(a,b) _mm256_sub_epi16(a,b) - static constexpr IndexType kNumRegs = 16; + static constexpr IndexType NumRegs = 16; #elif USE_SSE2 typedef __m128i vec_t; @@ -56,7 +56,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_epi16(a,b) #define vec_sub_16(a,b) _mm_sub_epi16(a,b) - static constexpr IndexType kNumRegs = Is64Bit ? 16 : 8; + static constexpr IndexType NumRegs = Is64Bit ? 16 : 8; #elif USE_MMX typedef __m64 vec_t; @@ -64,7 +64,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_pi16(a,b) #define vec_sub_16(a,b) _mm_sub_pi16(a,b) - static constexpr IndexType kNumRegs = 8; + static constexpr IndexType NumRegs = 8; #elif USE_NEON typedef int16x8_t vec_t; @@ -72,7 +72,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) vaddq_s16(a,b) #define vec_sub_16(a,b) vsubq_s16(a,b) - static constexpr IndexType kNumRegs = 16; + static constexpr IndexType NumRegs = 16; #else #undef VECTOR @@ -84,11 +84,11 @@ namespace Stockfish::Eval::NNUE { private: // Number of output dimensions for one side - static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions; + static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; #ifdef VECTOR - static constexpr IndexType kTileHeight = kNumRegs * sizeof(vec_t) / 2; - static_assert(kHalfDimensions % kTileHeight == 0, "kTileHeight must divide kHalfDimensions"); + static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; + static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions"); #endif public: @@ -96,95 +96,92 @@ namespace Stockfish::Eval::NNUE { using OutputType = TransformedFeatureType; // Number of input/output dimensions - static constexpr IndexType kInputDimensions = RawFeatures::kDimensions; - static constexpr IndexType kOutputDimensions = kHalfDimensions * 2; + static constexpr IndexType InputDimensions = RawFeatures::Dimensions; + static constexpr IndexType OutputDimensions = HalfDimensions * 2; // Size of forward propagation buffer - static constexpr std::size_t kBufferSize = - kOutputDimensions * sizeof(OutputType); + static constexpr std::size_t BufferSize = + OutputDimensions * sizeof(OutputType); // Hash value embedded in the evaluation file - static constexpr std::uint32_t GetHashValue() { - - return RawFeatures::kHashValue ^ kOutputDimensions; + static constexpr std::uint32_t get_hash_value() { + return RawFeatures::HashValue ^ OutputDimensions; } // Read network parameters - bool ReadParameters(std::istream& stream) { - - for (std::size_t i = 0; i < kHalfDimensions; ++i) - biases_[i] = read_little_endian(stream); - for (std::size_t i = 0; i < kHalfDimensions * kInputDimensions; ++i) - weights_[i] = read_little_endian(stream); + bool read_parameters(std::istream& stream) { + for (std::size_t i = 0; i < HalfDimensions; ++i) + biases[i] = read_little_endian(stream); + for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) + weights[i] = read_little_endian(stream); return !stream.fail(); } // Convert input features - void Transform(const Position& pos, OutputType* output) const { - - UpdateAccumulator(pos, WHITE); - UpdateAccumulator(pos, BLACK); + void transform(const Position& pos, OutputType* output) const { + update_accumulator(pos, WHITE); + update_accumulator(pos, BLACK); const auto& accumulation = pos.state()->accumulator.accumulation; #if defined(USE_AVX512) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth * 2); - static_assert(kHalfDimensions % (kSimdWidth * 2) == 0); - const __m512i kControl = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); - const __m512i kZero = _mm512_setzero_si512(); + constexpr IndexType NumChunks = HalfDimensions / (SimdWidth * 2); + static_assert(HalfDimensions % (SimdWidth * 2) == 0); + const __m512i Control = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); + const __m512i Zero = _mm512_setzero_si512(); #elif defined(USE_AVX2) - constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; - constexpr int kControl = 0b11011000; - const __m256i kZero = _mm256_setzero_si256(); + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; + constexpr int Control = 0b11011000; + const __m256i Zero = _mm256_setzero_si256(); #elif defined(USE_SSE2) - constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; #ifdef USE_SSE41 - const __m128i kZero = _mm_setzero_si128(); + const __m128i Zero = _mm_setzero_si128(); #else const __m128i k0x80s = _mm_set1_epi8(-128); #endif #elif defined(USE_MMX) - constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; const __m64 k0x80s = _mm_set1_pi8(-128); #elif defined(USE_NEON) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - const int8x8_t kZero = {0}; + constexpr IndexType NumChunks = HalfDimensions / (SimdWidth / 2); + const int8x8_t Zero = {0}; #endif const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; for (IndexType p = 0; p < 2; ++p) { - const IndexType offset = kHalfDimensions * p; + const IndexType offset = HalfDimensions * p; #if defined(USE_AVX512) auto out = reinterpret_cast<__m512i*>(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { __m512i sum0 = _mm512_load_si512( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); __m512i sum1 = _mm512_load_si512( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); - _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(kControl, - _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), kZero))); + _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, + _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); } #elif defined(USE_AVX2) auto out = reinterpret_cast<__m256i*>(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { __m256i sum0 = _mm256_load_si256( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); __m256i sum1 = _mm256_load_si256( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( - _mm256_packs_epi16(sum0, sum1), kZero), kControl)); + _mm256_packs_epi16(sum0, sum1), Zero), Control)); } #elif defined(USE_SSE2) auto out = reinterpret_cast<__m128i*>(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { __m128i sum0 = _mm_load_si128(&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 0]); __m128i sum1 = _mm_load_si128(&reinterpret_cast( @@ -194,7 +191,7 @@ namespace Stockfish::Eval::NNUE { _mm_store_si128(&out[j], #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, kZero) + _mm_max_epi8(packedbytes, Zero) #else _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) #endif @@ -204,7 +201,7 @@ namespace Stockfish::Eval::NNUE { #elif defined(USE_MMX) auto out = reinterpret_cast<__m64*>(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { __m64 sum0 = *(&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 0]); __m64 sum1 = *(&reinterpret_cast( @@ -215,14 +212,14 @@ namespace Stockfish::Eval::NNUE { #elif defined(USE_NEON) const auto out = reinterpret_cast(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { int16x8_t sum = reinterpret_cast( accumulation[perspectives[p]][0])[j]; - out[j] = vmax_s8(vqmovn_s16(sum), kZero); + out[j] = vmax_s8(vqmovn_s16(sum), Zero); } #else - for (IndexType j = 0; j < kHalfDimensions; ++j) { + for (IndexType j = 0; j < HalfDimensions; ++j) { BiasType sum = accumulation[static_cast(perspectives[p])][0][j]; output[offset + j] = static_cast( std::max(0, std::min(127, sum))); @@ -236,12 +233,12 @@ namespace Stockfish::Eval::NNUE { } private: - void UpdateAccumulator(const Position& pos, const Color c) const { + void update_accumulator(const Position& pos, const Color c) const { #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array // is defined in the VECTOR code below, once in each branch - vec_t acc[kNumRegs]; + vec_t acc[NumRegs]; #endif // Look for a usable accumulator of an earlier position. We keep track @@ -254,8 +251,8 @@ namespace Stockfish::Eval::NNUE { // The first condition tests whether an incremental update is // possible at all: if this side's king has moved, it is not possible. static_assert(std::is_same_v>, - "Current code assumes that only kFriendlyKingMoved refresh trigger is being used."); + Features::CompileTimeList>, + "Current code assumes that only FriendlyKingMoved refresh trigger is being used."); if ( dp.piece[0] == make_piece(c, KING) || (gain -= dp.dirty_num + 1) < 0) break; @@ -273,13 +270,13 @@ namespace Stockfish::Eval::NNUE { // Gather all features to be updated. This code assumes HalfKP features // only and doesn't support refresh triggers. - static_assert(std::is_same_v>, + static_assert(std::is_same_v>, RawFeatures>); Features::IndexList removed[2], added[2]; - Features::HalfKP::AppendChangedIndices(pos, + Features::HalfKP::append_changed_indices(pos, next->dirtyPiece, c, &removed[0], &added[0]); for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) - Features::HalfKP::AppendChangedIndices(pos, + Features::HalfKP::append_changed_indices(pos, st2->dirtyPiece, c, &removed[1], &added[1]); // Mark the accumulators as computed. @@ -290,12 +287,12 @@ namespace Stockfish::Eval::NNUE { StateInfo *info[3] = { next, next == pos.state() ? nullptr : pos.state(), nullptr }; #ifdef VECTOR - for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { // Load accumulator auto accTile = reinterpret_cast( - &st->accumulator.accumulation[c][0][j * kTileHeight]); - for (IndexType k = 0; k < kNumRegs; ++k) + &st->accumulator.accumulation[c][0][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_load(&accTile[k]); for (IndexType i = 0; info[i]; ++i) @@ -303,25 +300,25 @@ namespace Stockfish::Eval::NNUE { // Difference calculation for the deactivated features for (const auto index : removed[i]) { - const IndexType offset = kHalfDimensions * index + j * kTileHeight; - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType k = 0; k < kNumRegs; ++k) + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_sub_16(acc[k], column[k]); } // Difference calculation for the activated features for (const auto index : added[i]) { - const IndexType offset = kHalfDimensions * index + j * kTileHeight; - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType k = 0; k < kNumRegs; ++k) + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_add_16(acc[k], column[k]); } // Store accumulator accTile = reinterpret_cast( - &info[i]->accumulator.accumulation[c][0][j * kTileHeight]); - for (IndexType k = 0; k < kNumRegs; ++k) + &info[i]->accumulator.accumulation[c][0][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) vec_store(&accTile[k], acc[k]); } } @@ -331,25 +328,25 @@ namespace Stockfish::Eval::NNUE { { std::memcpy(info[i]->accumulator.accumulation[c][0], st->accumulator.accumulation[c][0], - kHalfDimensions * sizeof(BiasType)); + HalfDimensions * sizeof(BiasType)); st = info[i]; // Difference calculation for the deactivated features for (const auto index : removed[i]) { - const IndexType offset = kHalfDimensions * index; + const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < kHalfDimensions; ++j) - st->accumulator.accumulation[c][0][j] -= weights_[offset + j]; + for (IndexType j = 0; j < HalfDimensions; ++j) + st->accumulator.accumulation[c][0][j] -= weights[offset + j]; } // Difference calculation for the activated features for (const auto index : added[i]) { - const IndexType offset = kHalfDimensions * index; + const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < kHalfDimensions; ++j) - st->accumulator.accumulation[c][0][j] += weights_[offset + j]; + for (IndexType j = 0; j < HalfDimensions; ++j) + st->accumulator.accumulation[c][0][j] += weights[offset + j]; } } #endif @@ -360,41 +357,41 @@ namespace Stockfish::Eval::NNUE { auto& accumulator = pos.state()->accumulator; accumulator.state[c] = COMPUTED; Features::IndexList active; - Features::HalfKP::AppendActiveIndices(pos, c, &active); + Features::HalfKP::append_active_indices(pos, c, &active); #ifdef VECTOR - for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { auto biasesTile = reinterpret_cast( - &biases_[j * kTileHeight]); - for (IndexType k = 0; k < kNumRegs; ++k) + &biases[j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = biasesTile[k]; for (const auto index : active) { - const IndexType offset = kHalfDimensions * index + j * kTileHeight; - auto column = reinterpret_cast(&weights_[offset]); + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); - for (unsigned k = 0; k < kNumRegs; ++k) + for (unsigned k = 0; k < NumRegs; ++k) acc[k] = vec_add_16(acc[k], column[k]); } auto accTile = reinterpret_cast( - &accumulator.accumulation[c][0][j * kTileHeight]); - for (unsigned k = 0; k < kNumRegs; k++) + &accumulator.accumulation[c][0][j * TileHeight]); + for (unsigned k = 0; k < NumRegs; k++) vec_store(&accTile[k], acc[k]); } #else - std::memcpy(accumulator.accumulation[c][0], biases_, - kHalfDimensions * sizeof(BiasType)); + std::memcpy(accumulator.accumulation[c][0], biases, + HalfDimensions * sizeof(BiasType)); for (const auto index : active) { - const IndexType offset = kHalfDimensions * index; + const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < kHalfDimensions; ++j) - accumulator.accumulation[c][0][j] += weights_[offset + j]; + for (IndexType j = 0; j < HalfDimensions; ++j) + accumulator.accumulation[c][0][j] += weights[offset + j]; } #endif } @@ -407,9 +404,9 @@ namespace Stockfish::Eval::NNUE { using BiasType = std::int16_t; using WeightType = std::int16_t; - alignas(kCacheLineSize) BiasType biases_[kHalfDimensions]; - alignas(kCacheLineSize) - WeightType weights_[kHalfDimensions * kInputDimensions]; + alignas(CacheLineSize) BiasType biases[HalfDimensions]; + alignas(CacheLineSize) + WeightType weights[HalfDimensions * InputDimensions]; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/position.cpp b/src/position.cpp index ec356ace..2b3be3f7 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -79,7 +79,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { && !pos.can_castle(ANY_CASTLING)) { StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); Position p; p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread()); @@ -1315,7 +1315,7 @@ bool Position::pos_is_ok() const { assert(0 && "pos_is_ok: Bitboards"); StateInfo si = *st; - ASSERT_ALIGNED(&si, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&si, Eval::NNUE::CacheLineSize); set_state(&si); if (std::memcmp(&si, st, sizeof(StateInfo))) diff --git a/src/search.cpp b/src/search.cpp index c9ee47fe..1d841023 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -165,7 +165,7 @@ namespace { uint64_t perft(Position& pos, Depth depth) { StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); uint64_t cnt, nodes = 0; const bool leaf = (depth == 2); @@ -597,7 +597,7 @@ namespace { Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64]; StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); TTEntry* tte; Key posKey; @@ -1458,7 +1458,7 @@ moves_loop: // When in check, search starts from here Move pv[MAX_PLY+1]; StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); TTEntry* tte; Key posKey; @@ -1964,7 +1964,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { bool RootMove::extract_ponder_from_tt(Position& pos) { StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); bool ttHit; From 32d781769db9f017405fe422bfdc702069fb0422 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 22 Apr 2021 14:07:37 -0300 Subject: [PATCH 028/282] Merge all move generators Merging `generate` and `generate` into `generate_all()`. verified to yield correct perft results, even though bench changes due to different order of generated moves. No regresion playing games: passed STC: LLR: 2.94 (-2.94,2.94) {-1.00,0.20} Total: 161800 W: 14585 L: 14624 D: 132591 Ptnml(0-2): 577, 11681, 56451, 11586, 605 https://tests.stockfishchess.org/tests/view/606532732b2df919fd5f026d passed LTC: LLR: 2.98 (-2.94,2.94) {-0.70,0.20} Total: 188504 W: 6906 L: 6961 D: 174637 Ptnml(0-2): 87, 6272, 81610, 6175, 108 https://tests.stockfishchess.org/tests/view/6065b0772b2df919fd5f02ae closes https://github.com/official-stockfish/Stockfish/pull/3418 Bench: 4536129 --- src/movegen.cpp | 119 +++++++++++++----------------------------------- 1 file changed, 31 insertions(+), 88 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 50496136..bd9d0b62 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -63,8 +63,8 @@ namespace { Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; - Bitboard enemies = (Type == EVASIONS ? pos.checkers(): - Type == CAPTURES ? target : pos.pieces(Them)); + Bitboard enemies = (Type == EVASIONS ? pos.checkers() + : Type == CAPTURES ? target : pos.pieces(Them)); // Single and double pawn pushes, no promotions if (Type != CAPTURES) @@ -175,19 +175,19 @@ namespace { } - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) { + template + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); - Bitboard bb = piecesToMove & pos.pieces(Pt); + Bitboard bb = pos.pieces(Us, Pt); while (bb) { Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; - if constexpr (Checks) + if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) b &= pos.check_squares(Pt); while (b) @@ -204,42 +204,34 @@ namespace { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations - Bitboard target, piecesToMove = pos.pieces(Us); + const Square ksq = pos.square(Us); + Bitboard target; - if(Type == QUIET_CHECKS) - piecesToMove &= ~pos.blockers_for_king(~Us); + if (Type == EVASIONS && more_than_one(pos.checkers())) + goto kingMoves; // Double check, only a king move can save the day - switch (Type) - { - case CAPTURES: - target = pos.pieces(~Us); - break; - case QUIETS: - case QUIET_CHECKS: - target = ~pos.pieces(); - break; - case EVASIONS: - target = between_bb(pos.square(Us), lsb(pos.checkers())); - break; - case NON_EVASIONS: - target = ~pos.pieces(Us); - break; - } + target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers())) + : Type == NON_EVASIONS ? ~pos.pieces( Us) + : Type == CAPTURES ? pos.pieces(~Us) + : ~pos.pieces( ); // QUIETS || QUIET_CHECKS moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, piecesToMove, target); - moveList = generate_moves(pos, moveList, piecesToMove, target); - moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target); - moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); - if (Type != QUIET_CHECKS && Type != EVASIONS) +kingMoves: + if (!Checks || pos.blockers_for_king(~Us) & ksq) { - Square ksq = pos.square(Us); - Bitboard b = attacks_bb(ksq) & target; + Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); + if (Checks) + b &= ~attacks_bb(pos.square(~Us)); + while (b) *moveList++ = make_move(ksq, pop_lsb(b)); - if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) + if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) if (!pos.castling_impeded(cr) && pos.can_castle(cr)) *moveList++ = make(ksq, pos.castling_rook_square(cr)); @@ -253,6 +245,8 @@ namespace { /// Generates all pseudo-legal captures plus queen and checking knight promotions /// Generates all pseudo-legal non-captures and underpromotions (except checking knight) +/// Generates all pseudo-legal check evasions when the side to move is in check +/// Generates all pseudo-legal non-captures giving check, except castling /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. @@ -260,8 +254,8 @@ namespace { template ExtMove* generate(const Position& pos, ExtMove* moveList) { - static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()"); - assert(!pos.checkers()); + static_assert(Type != LEGAL, "Unsupported type in generate()"); + assert((Type == EVASIONS) == (bool)pos.checkers()); Color us = pos.side_to_move(); @@ -272,62 +266,11 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // Explicit template instantiations template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); +template ExtMove* generate(const Position&, ExtMove*); +template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -/// generate generates all pseudo-legal non-captures giving check, -/// except castling. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(!pos.checkers()); - - Color us = pos.side_to_move(); - Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN); - - while (dc) - { - Square from = pop_lsb(dc); - PieceType pt = type_of(pos.piece_on(from)); - - Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); - - if (pt == KING) - b &= ~attacks_bb(pos.square(~us)); - - while (b) - *moveList++ = make_move(from, pop_lsb(b)); - } - - return us == WHITE ? generate_all(pos, moveList) - : generate_all(pos, moveList); -} - - -/// generate generates all pseudo-legal check evasions when the side -/// to move is in check. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(pos.checkers()); - - Color us = pos.side_to_move(); - Square ksq = pos.square(us); - - // Generate evasions for king - Bitboard b = attacks_bb(ksq) & ~pos.pieces(us); - while (b) - *moveList++ = make_move(ksq, pop_lsb(b)); - - if (more_than_one(pos.checkers())) - return moveList; // Double check, only a king move can save the day - - // Generate blocking interpositions or captures of the checking piece - return us == WHITE ? generate_all(pos, moveList) - : generate_all(pos, moveList); -} - - /// generate generates all the legal moves in the given position template<> From b748b46714d5f8e0acca0a042ede1fc95e4f5190 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 24 Apr 2021 15:08:11 +0200 Subject: [PATCH 029/282] Cleanup and simplify NNUE code. A lot of optimizations happend since the NNUE was introduced and since then some parts of the code were left unused. This got to the point where asserts were have to be made just to let people know that modifying something will not have any effects or may even break everything due to the assumptions being made. Removing these parts removes those inexisting "false dependencies". Additionally: * append_changed_indices now takes the king pos and stateinfo explicitly, no more misleading pos parameter * IndexList is removed in favor of a generic ValueList. Feature transformer just instantiates the type it needs. * The update cost and refresh requirement is deferred to the feature set once again, but now doesn't go through the whole FeatureSet machinery and just calls HalfKP directly. * accumulator no longer has a singular dimension. * The PS constants and the PieceSquareIndex array are made local to the HalfKP feature set because they are specific to it and DO differ for other feature sets. * A few names are changed to more descriptive Passed STC non-regression: https://tests.stockfishchess.org/tests/view/608421dd95e7f1852abd2790 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 180008 W: 16186 L: 16258 D: 147564 Ptnml(0-2): 587, 12593, 63725, 12503, 596 closes https://github.com/official-stockfish/Stockfish/pull/3441 No functional change --- src/misc.h | 43 ++++++++ src/nnue/architectures/halfkp_256x2-32-32.h | 54 ---------- src/nnue/features/feature_set.h | 69 ------------ src/nnue/features/features_common.h | 45 -------- src/nnue/features/half_kp.cpp | 65 ++++++------ src/nnue/features/half_kp.h | 75 ++++++++++--- src/nnue/features/index_list.h | 64 ------------ src/nnue/nnue_accumulator.h | 2 +- src/nnue/nnue_architecture.h | 30 +++++- src/nnue/nnue_common.h | 25 ----- src/nnue/nnue_feature_transformer.h | 110 ++++++++++---------- 11 files changed, 219 insertions(+), 363 deletions(-) delete mode 100644 src/nnue/architectures/halfkp_256x2-32-32.h delete mode 100644 src/nnue/features/feature_set.h delete mode 100644 src/nnue/features/features_common.h delete mode 100644 src/nnue/features/index_list.h diff --git a/src/misc.h b/src/misc.h index f834e470..59ca6e37 100644 --- a/src/misc.h +++ b/src/misc.h @@ -78,6 +78,49 @@ T* align_ptr_up(T* ptr) return reinterpret_cast(reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); } +template +class ValueListInserter { +public: + ValueListInserter(T* v, std::size_t& s) : + values(v), + size(&s) + { + } + + void push_back(const T& value) { values[(*size)++] = value; } +private: + T* values; + std::size_t* size; +}; + +template +class ValueList { + +public: + std::size_t size() const { return size_; } + void resize(std::size_t newSize) { size_ = newSize; } + void push_back(const T& value) { values_[size_++] = value; } + T& operator[](std::size_t index) { return values_[index]; } + T* begin() { return values_; } + T* end() { return values_ + size_; } + const T& operator[](std::size_t index) const { return values_[index]; } + const T* begin() const { return values_; } + const T* end() const { return values_ + size_; } + operator ValueListInserter() { return ValueListInserter(values_, size_); } + + void swap(ValueList& other) { + const std::size_t maxSize = std::max(size_, other.size_); + for (std::size_t i = 0; i < maxSize; ++i) { + std::swap(values_[i], other.values_[i]); + } + std::swap(size_, other.size_); + } + +private: + T values_[MaxSize]; + std::size_t size_ = 0; +}; + /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated /// to the public domain by Sebastiano Vigna (2014). diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h deleted file mode 100644 index 5f6cc7f3..00000000 --- a/src/nnue/architectures/halfkp_256x2-32-32.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 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 . -*/ - -// Definition of input features and network structure used in NNUE evaluation function - -#ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED -#define NNUE_HALFKP_256X2_32_32_H_INCLUDED - -#include "../features/feature_set.h" -#include "../features/half_kp.h" - -#include "../layers/input_slice.h" -#include "../layers/affine_transform.h" -#include "../layers/clipped_relu.h" - -namespace Stockfish::Eval::NNUE { - -// Input features used in evaluation function -using RawFeatures = Features::FeatureSet< - Features::HalfKP>; - -// Number of input feature dimensions after conversion -constexpr IndexType TransformedFeatureDimensions = 256; - -namespace Layers { - -// Define network structure -using InputLayer = InputSlice; -using HiddenLayer1 = ClippedReLU>; -using HiddenLayer2 = ClippedReLU>; -using OutputLayer = AffineTransform; - -} // namespace Layers - -using Network = Layers::OutputLayer; - -} // namespace Stockfish::Eval::NNUE - -#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h deleted file mode 100644 index d09f9b94..00000000 --- a/src/nnue/features/feature_set.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 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 . -*/ - -// A class template that represents the input feature set of the NNUE evaluation function - -#ifndef NNUE_FEATURE_SET_H_INCLUDED -#define NNUE_FEATURE_SET_H_INCLUDED - -#include "features_common.h" -#include - -namespace Stockfish::Eval::NNUE::Features { - - // Class template that represents a list of values - template - struct CompileTimeList; - - template - struct CompileTimeList { - static constexpr bool Contains(T value) { - return value == First || CompileTimeList::Contains(value); - } - static constexpr std::array - Values = {{First, Remaining...}}; - }; - - // Base class of feature set - template - class FeatureSetBase { - - }; - - // Class template that represents the feature set - template - class FeatureSet : public FeatureSetBase> { - - public: - // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = FeatureType::HashValue; - // Number of feature dimensions - static constexpr IndexType Dimensions = FeatureType::Dimensions; - // Maximum number of simultaneously active features - static constexpr IndexType MaxActiveDimensions = - FeatureType::MaxActiveDimensions; - // Trigger for full calculation instead of difference calculation - using SortedTriggerSet = - CompileTimeList; - static constexpr auto RefreshTriggers = SortedTriggerSet::Values; - - }; - -} // namespace Stockfish::Eval::NNUE::Features - -#endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h deleted file mode 100644 index 9584cac8..00000000 --- a/src/nnue/features/features_common.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 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 . -*/ - -//Common header of input features of NNUE evaluation function - -#ifndef NNUE_FEATURES_COMMON_H_INCLUDED -#define NNUE_FEATURES_COMMON_H_INCLUDED - -#include "../../evaluate.h" -#include "../nnue_common.h" - -namespace Stockfish::Eval::NNUE::Features { - - class IndexList; - - template - class FeatureSet; - - // Trigger to perform full calculations instead of difference only - enum class TriggerEvent { - FriendKingMoved // calculate full evaluation when own king moves - }; - - enum class Side { - Friend // side to move - }; - -} // namespace Stockfish::Eval::NNUE::Features - -#endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 5c7538de..aa1decee 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -19,69 +19,68 @@ //Definition of input features HalfKP of NNUE evaluation function #include "half_kp.h" -#include "index_list.h" + +#include "../../position.h" namespace Stockfish::Eval::NNUE::Features { // Orient a square according to perspective (rotates by 180 for black) - inline Square orient(Color perspective, Square s) { + inline Square HalfKP::orient(Color perspective, Square s) { return Square(int(s) ^ (bool(perspective) * 63)); } // Index of a feature for a given king position and another piece on some square - inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) { + inline IndexType HalfKP::make_index(Color perspective, Square s, Piece pc, Square ksq) { return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); } // Get a list of indices for active features - template - void HalfKP::append_active_indices( - const Position& pos, Color perspective, IndexList* active) { - + void HalfKP::append_active_indices( + const Position& pos, + Color perspective, + ValueListInserter active + ) { Square ksq = orient(perspective, pos.square(perspective)); Bitboard bb = pos.pieces() & ~pos.pieces(KING); while (bb) { Square s = pop_lsb(bb); - active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); + active.push_back(make_index(perspective, s, pos.piece_on(s), ksq)); } } // append_changed_indices() : get a list of indices for recently changed features - // IMPORTANT: The `pos` in this function is pretty much useless as it - // is not always the position the features are updated to. The feature - // transformer code right now can update multiple accumulators per move, - // but since Stockfish only keeps the full state of the current leaf - // search position it is not possible to always pass here the position for - // which the accumulator is being updated. Therefore the only thing that - // can be reliably extracted from `pos` is the king square for the king - // of the `perspective` color (note: not even the other king's square will - // match reality in all cases, this is also the reason why `dp` is passed - // as a parameter and not extracted from pos.state()). This is of particular - // problem for future nets with other feature sets, where updating the active - // feature might require more information from the intermediate positions. In - // this case the only easy solution is to remove the multiple updates from - // the feature transformer update code and only update the accumulator for - // the current leaf position (the position after the move). - - template - void HalfKP::append_changed_indices( - const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added) { - - Square ksq = orient(perspective, pos.square(perspective)); + void HalfKP::append_changed_indices( + Square ksq, + StateInfo* st, + Color perspective, + ValueListInserter removed, + ValueListInserter added + ) { + const auto& dp = st->dirtyPiece; + Square oriented_ksq = orient(perspective, ksq); for (int i = 0; i < dp.dirty_num; ++i) { Piece pc = dp.piece[i]; if (type_of(pc) == KING) continue; if (dp.from[i] != SQ_NONE) - removed->push_back(make_index(perspective, dp.from[i], pc, ksq)); + removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq)); if (dp.to[i] != SQ_NONE) - added->push_back(make_index(perspective, dp.to[i], pc, ksq)); + added.push_back(make_index(perspective, dp.to[i], pc, oriented_ksq)); } } - template class HalfKP; + int HalfKP::update_cost(StateInfo* st) { + return st->dirtyPiece.dirty_num; + } + + int HalfKP::refresh_cost(const Position& pos) { + return pos.count() - 2; + } + + bool HalfKP::requires_refresh(StateInfo* st, Color perspective) { + return st->dirtyPiece.piece[0] == make_piece(perspective, KING); + } } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index 14efb089..a09c221b 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -21,37 +21,88 @@ #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED #define NNUE_FEATURES_HALF_KP_H_INCLUDED +#include "../nnue_common.h" + #include "../../evaluate.h" -#include "features_common.h" +#include "../../misc.h" + +namespace Stockfish { + struct StateInfo; +} namespace Stockfish::Eval::NNUE::Features { // Feature HalfKP: Combination of the position of own king // and the position of pieces other than kings - template class HalfKP { + // unique number for each piece type on each square + enum { + PS_NONE = 0, + PS_W_PAWN = 1, + PS_B_PAWN = 1 * SQUARE_NB + 1, + PS_W_KNIGHT = 2 * SQUARE_NB + 1, + PS_B_KNIGHT = 3 * SQUARE_NB + 1, + PS_W_BISHOP = 4 * SQUARE_NB + 1, + PS_B_BISHOP = 5 * SQUARE_NB + 1, + PS_W_ROOK = 6 * SQUARE_NB + 1, + PS_B_ROOK = 7 * SQUARE_NB + 1, + PS_W_QUEEN = 8 * SQUARE_NB + 1, + PS_B_QUEEN = 9 * SQUARE_NB + 1, + PS_NB = 10 * SQUARE_NB + 1 + }; + + static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { + // convention: W - us, B - them + // viewed from other side, W and B are reversed + { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE, + PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE }, + { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE, + PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE } + }; + + // Orient a square according to perspective (rotates by 180 for black) + static Square orient(Color perspective, Square s); + + // Index of a feature for a given king position and another piece on some square + static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq); + public: // Feature name static constexpr const char* Name = "HalfKP(Friend)"; + // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = - 0x5D69D5B9u ^ (AssociatedKing == Side::Friend); + static constexpr std::uint32_t HashValue = 0x5D69D5B8u; + // Number of feature dimensions static constexpr IndexType Dimensions = static_cast(SQUARE_NB) * static_cast(PS_NB); - // Maximum number of simultaneously active features - static constexpr IndexType MaxActiveDimensions = 30; // Kings don't count - // Trigger for full calculation instead of difference calculation - static constexpr TriggerEvent RefreshTrigger = TriggerEvent::FriendKingMoved; + + // Maximum number of simultaneously active features. 30 because kins are not included. + static constexpr IndexType MaxActiveDimensions = 30; // Get a list of indices for active features - static void append_active_indices(const Position& pos, Color perspective, - IndexList* active); + static void append_active_indices( + const Position& pos, + Color perspective, + ValueListInserter active); // Get a list of indices for recently changed features - static void append_changed_indices(const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added); + static void append_changed_indices( + Square ksq, + StateInfo* st, + Color perspective, + ValueListInserter removed, + ValueListInserter added); + + // Returns the cost of updating one perspective, the most costly one. + // Assumes no refresh needed. + static int update_cost(StateInfo* st); + static int refresh_cost(const Position& pos); + + // Returns whether the change stored in this StateInfo means that + // a full accumulator refresh is required. + static bool requires_refresh(StateInfo* st, Color perspective); }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h deleted file mode 100644 index edf0add1..00000000 --- a/src/nnue/features/index_list.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 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 . -*/ - -// Definition of index list of input features - -#ifndef NNUE_FEATURES_INDEX_LIST_H_INCLUDED -#define NNUE_FEATURES_INDEX_LIST_H_INCLUDED - -#include "../../position.h" -#include "../nnue_architecture.h" - -namespace Stockfish::Eval::NNUE::Features { - - // Class template used for feature index list - template - class ValueList { - - public: - std::size_t size() const { return size_; } - void resize(std::size_t size) { size_ = size; } - void push_back(const T& value) { values_[size_++] = value; } - T& operator[](std::size_t index) { return values_[index]; } - T* begin() { return values_; } - T* end() { return values_ + size_; } - const T& operator[](std::size_t index) const { return values_[index]; } - const T* begin() const { return values_; } - const T* end() const { return values_ + size_; } - - void swap(ValueList& other) { - const std::size_t max_size = std::max(size_, other.size_); - for (std::size_t i = 0; i < max_size; ++i) { - std::swap(values_[i], other.values_[i]); - } - std::swap(size_, other.size_); - } - - private: - T values_[MaxSize]; - std::size_t size_ = 0; - }; - - //Type of feature index list - class IndexList - : public ValueList { - }; - -} // namespace Stockfish::Eval::NNUE::Features - -#endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index aeb5f2bd..72a151f8 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -31,7 +31,7 @@ namespace Stockfish::Eval::NNUE { // Class that holds the result of affine transformation of input features struct alignas(CacheLineSize) Accumulator { std::int16_t - accumulation[2][RefreshTriggers.size()][TransformedFeatureDimensions]; + accumulation[2][TransformedFeatureDimensions]; AccumulatorState state[2]; }; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index f59474df..55a01fbe 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -21,18 +21,38 @@ #ifndef NNUE_ARCHITECTURE_H_INCLUDED #define NNUE_ARCHITECTURE_H_INCLUDED -// Defines the network structure -#include "architectures/halfkp_256x2-32-32.h" +#include "nnue_common.h" + +#include "features/half_kp.h" + +#include "layers/input_slice.h" +#include "layers/affine_transform.h" +#include "layers/clipped_relu.h" namespace Stockfish::Eval::NNUE { + // Input features used in evaluation function + using FeatureSet = Features::HalfKP; + + // Number of input feature dimensions after conversion + constexpr IndexType TransformedFeatureDimensions = 256; + + namespace Layers { + + // Define network structure + using InputLayer = InputSlice; + using HiddenLayer1 = ClippedReLU>; + using HiddenLayer2 = ClippedReLU>; + using OutputLayer = AffineTransform; + + } // namespace Layers + + using Network = Layers::OutputLayer; + static_assert(TransformedFeatureDimensions % MaxSimdWidth == 0, ""); static_assert(Network::OutputDimensions == 1, ""); static_assert(std::is_same::value, ""); - // Trigger for full calculation instead of difference calculation - constexpr auto RefreshTriggers = RawFeatures::RefreshTriggers; - } // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 20eb27d4..8c54f9ba 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -71,31 +71,6 @@ namespace Stockfish::Eval::NNUE { constexpr std::size_t MaxSimdWidth = 32; - // unique number for each piece type on each square - enum { - PS_NONE = 0, - PS_W_PAWN = 1, - PS_B_PAWN = 1 * SQUARE_NB + 1, - PS_W_KNIGHT = 2 * SQUARE_NB + 1, - PS_B_KNIGHT = 3 * SQUARE_NB + 1, - PS_W_BISHOP = 4 * SQUARE_NB + 1, - PS_B_BISHOP = 5 * SQUARE_NB + 1, - PS_W_ROOK = 6 * SQUARE_NB + 1, - PS_B_ROOK = 7 * SQUARE_NB + 1, - PS_W_QUEEN = 8 * SQUARE_NB + 1, - PS_B_QUEEN = 9 * SQUARE_NB + 1, - PS_NB = 10 * SQUARE_NB + 1 - }; - - constexpr uint32_t PieceSquareIndex[COLOR_NB][PIECE_NB] = { - // convention: W - us, B - them - // viewed from other side, W and B are reversed - { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE }, - { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE } - }; - // Type of input feature after conversion using TransformedFeatureType = std::uint8_t; using IndexType = std::uint32_t; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index de4b4937..f4412749 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -23,7 +23,8 @@ #include "nnue_common.h" #include "nnue_architecture.h" -#include "features/index_list.h" + +#include "../misc.h" #include // std::memset() @@ -96,7 +97,7 @@ namespace Stockfish::Eval::NNUE { using OutputType = TransformedFeatureType; // Number of input/output dimensions - static constexpr IndexType InputDimensions = RawFeatures::Dimensions; + static constexpr IndexType InputDimensions = FeatureSet::Dimensions; static constexpr IndexType OutputDimensions = HalfDimensions * 2; // Size of forward propagation buffer @@ -105,7 +106,7 @@ namespace Stockfish::Eval::NNUE { // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value() { - return RawFeatures::HashValue ^ OutputDimensions; + return FeatureSet::HashValue ^ OutputDimensions; } // Read network parameters @@ -161,9 +162,9 @@ namespace Stockfish::Eval::NNUE { auto out = reinterpret_cast<__m512i*>(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { __m512i sum0 = _mm512_load_si512( - &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); + &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); __m512i sum1 = _mm512_load_si512( - &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); + &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); } @@ -172,9 +173,9 @@ namespace Stockfish::Eval::NNUE { auto out = reinterpret_cast<__m256i*>(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { __m256i sum0 = _mm256_load_si256( - &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); + &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); __m256i sum1 = _mm256_load_si256( - &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); + &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( _mm256_packs_epi16(sum0, sum1), Zero), Control)); } @@ -183,9 +184,9 @@ namespace Stockfish::Eval::NNUE { auto out = reinterpret_cast<__m128i*>(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { __m128i sum0 = _mm_load_si128(&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 0]); + accumulation[perspectives[p]])[j * 2 + 0]); __m128i sum1 = _mm_load_si128(&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 1]); + accumulation[perspectives[p]])[j * 2 + 1]); const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); _mm_store_si128(&out[j], @@ -203,9 +204,9 @@ namespace Stockfish::Eval::NNUE { auto out = reinterpret_cast<__m64*>(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { __m64 sum0 = *(&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 0]); + accumulation[perspectives[p]])[j * 2 + 0]); __m64 sum1 = *(&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 1]); + accumulation[perspectives[p]])[j * 2 + 1]); const __m64 packedbytes = _mm_packs_pi16(sum0, sum1); out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); } @@ -214,13 +215,13 @@ namespace Stockfish::Eval::NNUE { const auto out = reinterpret_cast(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { int16x8_t sum = reinterpret_cast( - accumulation[perspectives[p]][0])[j]; + accumulation[perspectives[p]])[j]; out[j] = vmax_s8(vqmovn_s16(sum), Zero); } #else for (IndexType j = 0; j < HalfDimensions; ++j) { - BiasType sum = accumulation[static_cast(perspectives[p])][0][j]; + BiasType sum = accumulation[static_cast(perspectives[p])][j]; output[offset + j] = static_cast( std::max(0, std::min(127, sum))); } @@ -233,7 +234,13 @@ namespace Stockfish::Eval::NNUE { } private: - void update_accumulator(const Position& pos, const Color c) const { + void update_accumulator(const Position& pos, const Color perspective) const { + + // The size must be enough to contain the largest possible update. + // That might depend on the feature set and generally relies on the + // feature set's update cost calculation to be correct and never + // allow updates with more added/removed features than MaxActiveDimensions. + using IndexList = ValueList; #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array @@ -244,23 +251,19 @@ namespace Stockfish::Eval::NNUE { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; - int gain = pos.count() - 2; - while (st->accumulator.state[c] == EMPTY) + int gain = FeatureSet::refresh_cost(pos); + while (st->accumulator.state[perspective] == EMPTY) { - auto& dp = st->dirtyPiece; - // The first condition tests whether an incremental update is - // possible at all: if this side's king has moved, it is not possible. - static_assert(std::is_same_v>, - "Current code assumes that only FriendlyKingMoved refresh trigger is being used."); - if ( dp.piece[0] == make_piece(c, KING) - || (gain -= dp.dirty_num + 1) < 0) + // This governs when a full feature refresh is needed and how many + // updates are better than just one full refresh. + if ( FeatureSet::requires_refresh(st, perspective) + || (gain -= FeatureSet::update_cost(st) + 1) < 0) break; next = st; st = st->previous; } - if (st->accumulator.state[c] == COMPUTED) + if (st->accumulator.state[perspective] == COMPUTED) { if (next == nullptr) return; @@ -268,34 +271,32 @@ namespace Stockfish::Eval::NNUE { // Update incrementally in two steps. First, we update the "next" // accumulator. Then, we update the current accumulator (pos.state()). - // Gather all features to be updated. This code assumes HalfKP features - // only and doesn't support refresh triggers. - static_assert(std::is_same_v>, - RawFeatures>); - Features::IndexList removed[2], added[2]; - Features::HalfKP::append_changed_indices(pos, - next->dirtyPiece, c, &removed[0], &added[0]); + // Gather all features to be updated. + const Square ksq = pos.square(perspective); + IndexList removed[2], added[2]; + FeatureSet::append_changed_indices( + ksq, next, perspective, removed[0], added[0]); for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) - Features::HalfKP::append_changed_indices(pos, - st2->dirtyPiece, c, &removed[1], &added[1]); + FeatureSet::append_changed_indices( + ksq, st2, perspective, removed[1], added[1]); // Mark the accumulators as computed. - next->accumulator.state[c] = COMPUTED; - pos.state()->accumulator.state[c] = COMPUTED; + next->accumulator.state[perspective] = COMPUTED; + pos.state()->accumulator.state[perspective] = COMPUTED; - // Now update the accumulators listed in info[], where the last element is a sentinel. - StateInfo *info[3] = + // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. + StateInfo *states_to_update[3] = { next, next == pos.state() ? nullptr : pos.state(), nullptr }; #ifdef VECTOR for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { // Load accumulator auto accTile = reinterpret_cast( - &st->accumulator.accumulation[c][0][j * TileHeight]); + &st->accumulator.accumulation[perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_load(&accTile[k]); - for (IndexType i = 0; info[i]; ++i) + for (IndexType i = 0; states_to_update[i]; ++i) { // Difference calculation for the deactivated features for (const auto index : removed[i]) @@ -317,19 +318,19 @@ namespace Stockfish::Eval::NNUE { // Store accumulator accTile = reinterpret_cast( - &info[i]->accumulator.accumulation[c][0][j * TileHeight]); + &states_to_update[i]->accumulator.accumulation[perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) vec_store(&accTile[k], acc[k]); } } #else - for (IndexType i = 0; info[i]; ++i) + for (IndexType i = 0; states_to_update[i]; ++i) { - std::memcpy(info[i]->accumulator.accumulation[c][0], - st->accumulator.accumulation[c][0], + std::memcpy(states_to_update[i]->accumulator.accumulation[perspective], + st->accumulator.accumulation[perspective], HalfDimensions * sizeof(BiasType)); - st = info[i]; + st = states_to_update[i]; // Difference calculation for the deactivated features for (const auto index : removed[i]) @@ -337,7 +338,7 @@ namespace Stockfish::Eval::NNUE { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[c][0][j] -= weights[offset + j]; + st->accumulator.accumulation[perspective][j] -= weights[offset + j]; } // Difference calculation for the activated features @@ -346,7 +347,7 @@ namespace Stockfish::Eval::NNUE { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[c][0][j] += weights[offset + j]; + st->accumulator.accumulation[perspective][j] += weights[offset + j]; } } #endif @@ -355,9 +356,9 @@ namespace Stockfish::Eval::NNUE { { // Refresh the accumulator auto& accumulator = pos.state()->accumulator; - accumulator.state[c] = COMPUTED; - Features::IndexList active; - Features::HalfKP::append_active_indices(pos, c, &active); + accumulator.state[perspective] = COMPUTED; + IndexList active; + FeatureSet::append_active_indices(pos, perspective, active); #ifdef VECTOR for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) @@ -377,13 +378,13 @@ namespace Stockfish::Eval::NNUE { } auto accTile = reinterpret_cast( - &accumulator.accumulation[c][0][j * TileHeight]); + &accumulator.accumulation[perspective][j * TileHeight]); for (unsigned k = 0; k < NumRegs; k++) vec_store(&accTile[k], acc[k]); } #else - std::memcpy(accumulator.accumulation[c][0], biases, + std::memcpy(accumulator.accumulation[perspective], biases, HalfDimensions * sizeof(BiasType)); for (const auto index : active) @@ -391,7 +392,7 @@ namespace Stockfish::Eval::NNUE { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - accumulator.accumulation[c][0][j] += weights[offset + j]; + accumulator.accumulation[perspective][j] += weights[offset + j]; } #endif } @@ -405,8 +406,7 @@ namespace Stockfish::Eval::NNUE { using WeightType = std::int16_t; alignas(CacheLineSize) BiasType biases[HalfDimensions]; - alignas(CacheLineSize) - WeightType weights[HalfDimensions * InputDimensions]; + alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions]; }; } // namespace Stockfish::Eval::NNUE From c0ff241464338f7e5bb815b4f72c2a95d12244a0 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 25 Apr 2021 07:09:35 +0200 Subject: [PATCH 030/282] Thread based reduction tweak. For PV nodes at the first two plies no reductions are done for each fourth thread. STC (8 threads): LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 53992 W: 3334 L: 3167 D: 47491 Ptnml(0-2): 64, 2713, 21285, 2860, 74 https://tests.stockfishchess.org/tests/view/6083b2d695e7f1852abd277a LTC (8 threads): LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 64888 W: 1888 L: 1725 D: 61275 Ptnml(0-2): 14, 1556, 29146, 1709, 19 https://tests.stockfishchess.org/tests/view/6084249595e7f1852abd2795 closes https://github.com/official-stockfish/Stockfish/pull/3443 No functional change (for one thread) --- src/search.cpp | 3 ++- src/thread.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1d841023..99c2b09f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1185,7 +1185,8 @@ moves_loop: // When in check, search starts from here || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode || (!PvNode && !formerPv && captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 3678) - || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024) + && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { Depth r = reduction(improving, depth, moveCount); diff --git a/src/thread.h b/src/thread.h index 2b3dea0d..4cf5dabb 100644 --- a/src/thread.h +++ b/src/thread.h @@ -55,6 +55,7 @@ public: void idle_loop(); void start_searching(); void wait_for_search_finished(); + int id() const { return idx; } Pawns::Table pawnsTable; Material::Table materialTable; From 33a858eaa1f792b3413384a3d0993dba36aca92e Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Sat, 24 Apr 2021 21:37:47 -0300 Subject: [PATCH 031/282] More extensions if SE search is very low. More extensions for non-PV nodes if value from singular extension search is significantly below singularBeta. Passed STC: LLR: 2.97 (-2.94,2.94) <-0.50,2.50> Total: 25064 W: 2334 L: 2162 D: 20568 Ptnml(0-2): 82, 1720, 8768, 1868, 94 https://tests.stockfishchess.org/tests/view/6084ba7995e7f1852abd27e3 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 67136 W: 2644 L: 2450 D: 62042 Ptnml(0-2): 46, 2134, 28990, 2376, 22 https://tests.stockfishchess.org/tests/view/6084d79195e7f1852abd27ee closes https://github.com/official-stockfish/Stockfish/pull/3445 Bench: 4075325 --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 99c2b09f..5542fc87 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1130,6 +1130,8 @@ moves_loop: // When in check, search starts from here { extension = 1; singularQuietLMR = !ttCapture; + if (!PvNode && value < singularBeta - 140) + extension = 2; } // Multi-cut pruning From 84b42b3ab328e3f1b9829d8f5d967e4d45a45a39 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 27 Apr 2021 19:59:46 -0300 Subject: [PATCH 032/282] Simplify pawn moves generator This patch simplifies QUIET_CHECKS pawn move generator by merging discovery check move generator with direct check move generator. It also simplifies emptySquares instantiation. In addition, I added a comment in generate_moves() to clarify Check branches. STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 112648 W: 9952 L: 9945 D: 92751 Ptnml(0-2): 369, 7682, 40195, 7729, 349 https://tests.stockfishchess.org/tests/view/6088226895e7f1852abd2978 LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 74656 W: 2797 L: 2765 D: 69094 Ptnml(0-2): 38, 2328, 32554, 2380, 28 https://tests.stockfishchess.org/tests/view/60884e5095e7f1852abd2994 closes https://github.com/official-stockfish/Stockfish/pull/3447 No functional change --- src/movegen.cpp | 43 ++++++++++++++----------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index bd9d0b62..be168450 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -58,19 +58,16 @@ namespace { constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); const Square ksq = pos.square(Them); - Bitboard emptySquares; + const Bitboard emptySquares = Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces(); + const Bitboard enemies = Type == EVASIONS ? pos.checkers() + : Type == CAPTURES ? target : pos.pieces(Them); Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; - Bitboard enemies = (Type == EVASIONS ? pos.checkers() - : Type == CAPTURES ? target : pos.pieces(Them)); - // Single and double pawn pushes, no promotions if (Type != CAPTURES) { - emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); - Bitboard b1 = shift(pawnsNotOn7) & emptySquares; Bitboard b2 = shift(b1 & TRank3BB) & emptySquares; @@ -82,22 +79,12 @@ namespace { if (Type == QUIET_CHECKS) { - b1 &= pawn_attacks_bb(Them, ksq); - b2 &= pawn_attacks_bb(Them, ksq); - - // Add pawn pushes which give discovered check. This is possible only - // if the pawn is not on the same file as the enemy king, because we - // don't generate captures. Note that a possible discovered check - // promotion has been already generated amongst the captures. - Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7; - if (dcCandidateQuiets) - { - Bitboard dc1 = shift(dcCandidateQuiets) & emptySquares & ~file_bb(ksq); - Bitboard dc2 = shift(dc1 & TRank3BB) & emptySquares; - - b1 |= dc1; - b2 |= dc2; - } + // To make a quiet check, you either make a direct check by pushing a pawn + // or push a blocker pawn that is not on the same file as the enemy king. + // Discovered check promotion has been already generated amongst the captures. + Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq); + b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns); + b2 &= pawn_attacks_bb(Them, ksq) | shift(dcCandidatePawns); } while (b1) @@ -116,16 +103,13 @@ namespace { // Promotions and underpromotions if (pawnsOn7) { - if (Type == CAPTURES) - emptySquares = ~pos.pieces(); - - if (Type == EVASIONS) - emptySquares &= target; - Bitboard b1 = shift(pawnsOn7) & enemies; Bitboard b2 = shift(pawnsOn7) & enemies; Bitboard b3 = shift(pawnsOn7) & emptySquares; + if (Type == EVASIONS) + b3 &= target; + while (b1) moveList = make_promotions(moveList, pop_lsb(b1), ksq); @@ -185,8 +169,9 @@ namespace { while (bb) { Square from = pop_lsb(bb); - Bitboard b = attacks_bb(from, pos.pieces()) & target; + + // To check, you either move freely a blocker or make a direct check. if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) b &= pos.check_squares(Pt); From 6ad4f485d37556f5e09baae5a939f44ac5b51c84 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sat, 24 Apr 2021 14:46:01 +0100 Subject: [PATCH 033/282] Change tempo with time and threads Introduce variable tempo for nnue depending on logarithm of estimated strength, where strength is the product of time and number of threads. The original idea here was that NNUE is best with a slightly different tempo value to classical, since its style of play is slightly different. It turns out that the best tempo for NNUE varies with strength of play, so a formula is used which gives about 19 for STC and 24 for LTC under current fishtest settings. STC 10+0.1: LLR: 2.94 (-2.94,2.94) {-0.20,1.10} Total: 120816 W: 11155 L: 10861 D: 98800 Ptnml(0-2): 406, 8728, 41933, 8848, 493 https://tests.stockfishchess.org/tests/view/60735b3a8141753378960534 LTC 60+0.6: LLR: 2.94 (-2.94,2.94) {0.20,0.90} Total: 35688 W: 1392 L: 1234 D: 33062 Ptnml(0-2): 23, 1079, 15473, 1255, 14 https://tests.stockfishchess.org/tests/view/6073ffbc814175337896057f Passed non-regression SMP test at LTC 20+0.2 (8 threads): LLR: 2.95 (-2.94,2.94) {-0.70,0.20} Total: 11008 W: 317 L: 267 D: 10424 Ptnml(0-2): 2, 245, 4962, 291, 4 https://tests.stockfishchess.org/tests/view/60749ea881417533789605a4 closes https://github.com/official-stockfish/Stockfish/pull/3426 Bench 4075325 --- src/evaluate.cpp | 3 ++- src/timeman.cpp | 8 ++++++++ src/timeman.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ba3de70b..0fb9abdf 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -33,6 +33,7 @@ #include "misc.h" #include "pawns.h" #include "thread.h" +#include "timeman.h" #include "uci.h" #include "incbin/incbin.h" @@ -1096,7 +1097,7 @@ Value Eval::evaluate(const Position& pos) { + material / 32 - 4 * pos.rule50_count(); - Value nnue = NNUE::evaluate(pos) * scale / 1024 + Tempo; + Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE; if (pos.is_chess960()) nnue += fix_FRC(pos); diff --git a/src/timeman.cpp b/src/timeman.cpp index f742d1e4..3236b6e9 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -94,6 +94,14 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { optimumTime = TimePoint(optScale * timeLeft); maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); + if (Stockfish::Search::Limits.use_time_management()) + { + int strength = std::log( std::max(1, int(optimumTime * Threads.size() / 10))) * 60; + tempoNNUE = std::clamp( (strength + 264) / 24, 18, 30); + } + else + tempoNNUE = 28; // default for no time given + if (Options["Ponder"]) optimumTime += optimumTime / 4; } diff --git a/src/timeman.h b/src/timeman.h index b1878d65..4ac0b4be 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -37,6 +37,7 @@ public: TimePoint(Threads.nodes_searched()) : now() - startTime; } int64_t availableNodes; // When in 'nodes as time' mode + int tempoNNUE; private: TimePoint startTime; From 33fadb5118a0ec3917ec3bbc255fafdf148627ec Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 29 Apr 2021 08:18:37 +0200 Subject: [PATCH 034/282] Add some more information on the UCI protocol Improve README.md: provide a link to the protocol, and document some non-standard options. fixes https://github.com/official-stockfish/Stockfish/issues/3446 closes https://github.com/official-stockfish/Stockfish/pull/3450 No functional change --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67fb5fa0..bc34ee13 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,14 @@ This distribution of Stockfish consists of the following files: * a file with the .nnue extension, storing the neural network for the NNUE evaluation. Binary distributions will have this file embedded. -## UCI options +## The UCI protocol and available options -Currently, Stockfish has the following UCI options: +The Universal Chess Interface (UCI) is a standard protocol used to communicate with a chess engine, +and is the recommended way to do so for typical graphical user interfaces (GUI) or chess tools. + +Stockfish implements most commands as described in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip) + +For users, the following UCI options, which can typically be set via a GUI, are available in Stockfish: * #### Threads The number of CPU threads used for searching a position. For best performance, set @@ -136,6 +141,24 @@ Currently, Stockfish has the following UCI options: * #### Debug Log File Write all communication to and from the engine into a text file. +For developers the following non-standard commands might be of interest, mainly useful for debugging: + + * #### compiler + Give information about the compiler and environment used for building a binary. + + * #### flip + Flips the side to move. + + * #### bench ttSize threads limit fenFile limitType evalType + Performs a standard benchmark using various options. The signature or standard node + count is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. + + * #### d + Display the current position, with ascii art and fen. + + * #### eval + Return the evaluation of the current position. + ## A note on classical evaluation versus NNUE evaluation Both approaches assign a value to a position that is used in alpha-beta (PVS) search From b1c8840f104d4d36b3f9a420b8b26a5e94c6dc18 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 1 May 2021 10:18:57 +0200 Subject: [PATCH 035/282] Simplify check extension Simplify check extension, as it seems not to bring any strength and thus is no longer needed. STC https://tests.stockfishchess.org/tests/view/608c18e995e7f1852abd2b81 LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 54544 W: 4891 L: 4815 D: 44838 Ptnml(0-2): 186, 3889, 19081, 3895, 221 LTC https://tests.stockfishchess.org/tests/view/608c6ab195e7f1852abd2bc6 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 51008 W: 1845 L: 1794 D: 47369 Ptnml(0-2): 31, 1591, 22206, 1648, 28 closes https://github.com/official-stockfish/Stockfish/pull/3452 bench: 3993071 --- src/position.h | 5 ----- src/search.cpp | 5 ----- 2 files changed, 10 deletions(-) diff --git a/src/position.h b/src/position.h index d470ef90..c226373b 100644 --- a/src/position.h +++ b/src/position.h @@ -115,7 +115,6 @@ public: Bitboard blockers_for_king(Color c) const; Bitboard check_squares(PieceType pt) const; Bitboard pinners(Color c) const; - bool is_discovered_check_on_king(Color c, Move m) const; // Attacks to/from a given square Bitboard attackers_to(Square s) const; @@ -301,10 +300,6 @@ inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; } -inline bool Position::is_discovered_check_on_king(Color c, Move m) const { - return st->blockersForKing[c] & from_sq(m); -} - inline bool Position::pawn_passed(Color c, Square s) const { return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); } diff --git a/src/search.cpp b/src/search.cpp index 5542fc87..fd833b81 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1155,11 +1155,6 @@ moves_loop: // When in check, search starts from here } } - // Check extension (~2 Elo) - else if ( givesCheck - && (pos.is_discovered_check_on_king(~us, move) || pos.see_ge(move))) - extension = 1; - // Add extension to new depth newDepth += extension; From ca250e969c9d235b2d8be1d5d088b8834aa374b9 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sun, 2 May 2021 18:50:09 +0200 Subject: [PATCH 036/282] Add an UCI level command "export_net". This command writes the embedded net to the file `EvalFileDefaultName`. If there is no embedded net the command does nothing. fixes #3453 closes https://github.com/official-stockfish/Stockfish/pull/3454 No functional change --- README.md | 16 ++++++++++------ src/evaluate.cpp | 11 +++++++++++ src/evaluate.h | 1 + src/uci.cpp | 1 + 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bc34ee13..013d4b32 100644 --- a/README.md +++ b/README.md @@ -143,22 +143,26 @@ For users, the following UCI options, which can typically be set via a GUI, are For developers the following non-standard commands might be of interest, mainly useful for debugging: - * #### compiler - Give information about the compiler and environment used for building a binary. - - * #### flip - Flips the side to move. - * #### bench ttSize threads limit fenFile limitType evalType Performs a standard benchmark using various options. The signature or standard node count is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. + * #### compiler + Give information about the compiler and environment used for building a binary. + * #### d Display the current position, with ascii art and fen. * #### eval Return the evaluation of the current position. + * #### export_net + If the binary contains an embedded net, save it in a file (named according to the default value of EvalFile). + + * #### flip + Flips the side to move. + + ## A note on classical evaluation versus NNUE evaluation Both approaches assign a value to a position that is used in alpha-beta (PVS) search diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0fb9abdf..f0784e8f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -47,7 +47,9 @@ // Note that this does not work in Microsoft Visual Studio. #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) INCBIN(EmbeddedNNUE, EvalFileDefaultName); + constexpr bool gHasEmbeddedNet = true; #else + constexpr bool gHasEmbeddedNet = false; const unsigned char gEmbeddedNNUEData[1] = {0x0}; const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1]; const unsigned int gEmbeddedNNUESize = 1; @@ -114,6 +116,15 @@ namespace Eval { } } + void NNUE::export_net() { + if constexpr (gHasEmbeddedNet) { + ofstream stream(EvalFileDefaultName, std::ios_base::binary); + stream.write(reinterpret_cast(gEmbeddedNNUEData), gEmbeddedNNUESize); + } else { + sync_cout << "No embedded network file." << sync_endl; + } + } + /// NNUE::verify() verifies that the last net used was loaded successfully void NNUE::verify() { diff --git a/src/evaluate.h b/src/evaluate.h index 6210bd58..b7525aab 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -45,6 +45,7 @@ namespace Eval { Value evaluate(const Position& pos); bool load_eval(std::string name, std::istream& stream); void init(); + void export_net(); void verify(); } // namespace NNUE diff --git a/src/uci.cpp b/src/uci.cpp index 051ff2e0..64bb7a7c 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -277,6 +277,7 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; + else if (token == "export_net") Eval::NNUE::export_net(); else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; From d777ea79fff0f651bd948c881946cd06bebd9381 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 5 May 2021 19:03:20 +0300 Subject: [PATCH 037/282] Cleanup of likelyFailLow logic This patch broadens and simplifies definition of PvNode that is likely to fail low. New definition can be described as following "If node was already researched at depth >= current depth and failed low there" which is more logical than the previous version and takes less space + allows to not recompute it every time during move loop. Passed simplification STC https://tests.stockfishchess.org/tests/view/609148bf95e7f1852abd2e82 LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 20128 W: 1865 L: 1751 D: 16512 Ptnml(0-2): 63, 1334, 7165, 1430, 72 Passed simplification LTC https://tests.stockfishchess.org/tests/view/6091691295e7f1852abd2e8b LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 95128 W: 3498 L: 3481 D: 88149 Ptnml(0-2): 41, 2956, 41549, 2981, 37 closes https://github.com/official-stockfish/Stockfish/pull/3455 Bench: 3933037 --- src/search.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fd833b81..bcd53f9e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1006,6 +1006,13 @@ moves_loop: // When in check, search starts from here value = bestValue; singularQuietLMR = moveCountPruning = false; + // Indicate PvNodes that will probably fail low if the node was searched + // at a depth equal or greater than the current depth, and the result of this search was a fail low. + bool likelyFailLow = PvNode + && ttMove + && (tte->bound() & BOUND_UPPER) + && tte->depth() >= depth; + // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); @@ -1044,14 +1051,6 @@ moves_loop: // When in check, search starts from here movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); - // Indicate PvNodes that will probably fail low if node was searched with non-PV search - // at depth equal or greater to current depth and result of this search was far below alpha - bool likelyFailLow = PvNode - && ttMove - && (tte->bound() & BOUND_UPPER) - && ttValue < alpha + 200 + 100 * depth - && tte->depth() >= depth; - // Calculate new depth for this move newDepth = depth - 1; From 58054fd0fa6294510fc8cf76b0ba9673d5094c10 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Fri, 7 May 2021 12:24:12 +0200 Subject: [PATCH 038/282] Exporting the currently loaded network file This PR adds an ability to export any currently loaded network. The export_net command now takes an optional filename parameter. If the loaded net is not the embedded net the filename parameter is required. Two changes were required to support this: * the "architecture" string, which is really just a some kind of description in the net, is now saved into netDescription on load and correctly saved on export. * the AffineTransform scrambles weights for some architectures and sparsifies them, such that retrieving the index is hard. This is solved by having a temporary scrambled<->unscrambled index lookup table when loading the network, and the actual index is saved for each individual weight that makes it to canSaturate16. This increases the size of the canSaturate16 entries by 6 bytes. closes https://github.com/official-stockfish/Stockfish/pull/3456 No functional change --- README.md | 18 +++++++---- src/evaluate.cpp | 25 ++++++++++----- src/evaluate.h | 4 ++- src/nnue/evaluate_nnue.cpp | 46 +++++++++++++++++++++++++--- src/nnue/layers/affine_transform.h | 47 +++++++++++++++++++++++++---- src/nnue/layers/clipped_relu.h | 5 +++ src/nnue/layers/input_slice.h | 5 +++ src/nnue/nnue_common.h | 18 +++++++++++ src/nnue/nnue_feature_transformer.h | 9 ++++++ src/uci.cpp | 9 +++++- 10 files changed, 159 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 013d4b32..8d5ce8d0 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,13 @@ This distribution of Stockfish consists of the following files: * Readme.md, the file you are currently reading. * Copying.txt, a text file containing the GNU General Public License version 3. - + * AUTHORS, a text file with the list of authors for the project * src, a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. - * a file with the .nnue extension, storing the neural network for the NNUE + * a file with the .nnue extension, storing the neural network for the NNUE evaluation. Binary distributions will have this file embedded. ## The UCI protocol and available options @@ -156,8 +156,14 @@ For developers the following non-standard commands might be of interest, mainly * #### eval Return the evaluation of the current position. - * #### export_net - If the binary contains an embedded net, save it in a file (named according to the default value of EvalFile). + * #### export_net [filename] + Exports the currently loaded network to a file. + If the currently loaded network is the embedded network and the filename + is not specified then the network is saved to the file matching the name + of the embedded network, as defined in evaluate.h. + If the currently loaded network is not the embedded network (some net set + through the UCI setoption) then the filename parameter is required and the + network is saved into that file. * #### flip Flips the side to move. @@ -189,7 +195,7 @@ Stockfish binary, but the default value of the EvalFile UCI option is the name o that is guaranteed to be compatible with that binary. 2) to use the NNUE evaluation, the additional data file with neural network parameters -needs to be available. Normally, this file is already embedded in the binary or it +needs to be available. Normally, this file is already embedded in the binary or it can be downloaded. The filename for the default (recommended) net can be found as the default value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue` (for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from @@ -202,7 +208,7 @@ replacing `[filename]` as needed. If the engine is searching a position that is not in the tablebases (e.g. a position with 8 pieces), it will access the tablebases during the search. -If the engine reports a very large score (typically 153.xx), this means +If the engine reports a very large score (typically 153.xx), this means it has found a winning line into a tablebase position. If the engine is given a position to search that is in the tablebases, it diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f0784e8f..c396e0f7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -47,9 +47,7 @@ // Note that this does not work in Microsoft Visual Studio. #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) INCBIN(EmbeddedNNUE, EvalFileDefaultName); - constexpr bool gHasEmbeddedNet = true; #else - constexpr bool gHasEmbeddedNet = false; const unsigned char gEmbeddedNNUEData[1] = {0x0}; const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1]; const unsigned int gEmbeddedNNUESize = 1; @@ -116,12 +114,23 @@ namespace Eval { } } - void NNUE::export_net() { - if constexpr (gHasEmbeddedNet) { - ofstream stream(EvalFileDefaultName, std::ios_base::binary); - stream.write(reinterpret_cast(gEmbeddedNNUEData), gEmbeddedNNUESize); + void NNUE::export_net(const std::optional& filename) { + std::string actualFilename; + if (filename.has_value()) { + actualFilename = filename.value(); } else { - sync_cout << "No embedded network file." << sync_endl; + if (eval_file_loaded != EvalFileDefaultName) { + sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl; + return; + } + actualFilename = EvalFileDefaultName; + } + + ofstream stream(actualFilename, std::ios_base::binary); + if (save_eval(stream)) { + sync_cout << "Network saved successfully to " << actualFilename << "." << sync_endl; + } else { + sync_cout << "Failed to export a net." << sync_endl; } } @@ -1128,7 +1137,7 @@ Value Eval::evaluate(const Position& pos) { bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg || (pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2); - v = classical || lowPieceEndgame ? Evaluation(pos).value() + v = classical || lowPieceEndgame ? Evaluation(pos).value() : adjusted_NNUE(); // If the classical eval is small and imbalance large, use NNUE nevertheless. diff --git a/src/evaluate.h b/src/evaluate.h index b7525aab..128a7cae 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -20,6 +20,7 @@ #define EVALUATE_H_INCLUDED #include +#include #include "types.h" @@ -44,8 +45,9 @@ namespace Eval { Value evaluate(const Position& pos); bool load_eval(std::string name, std::istream& stream); + bool save_eval(std::ostream& stream); void init(); - void export_net(); + void export_net(const std::optional& filename); void verify(); } // namespace NNUE diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 0e539611..e0d4b911 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -39,6 +39,7 @@ namespace Stockfish::Eval::NNUE { // Evaluation function file name std::string fileName; + std::string netDescription; namespace Detail { @@ -68,6 +69,14 @@ namespace Stockfish::Eval::NNUE { return reference.read_parameters(stream); } + // Write evaluation function parameters + template + bool write_parameters(std::ostream& stream, const T& reference) { + + write_little_endian(stream, T::get_hash_value()); + return reference.write_parameters(stream); + } + } // namespace Detail // Initialize the evaluation function parameters @@ -78,7 +87,7 @@ namespace Stockfish::Eval::NNUE { } // Read network header - bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* architecture) + bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc) { std::uint32_t version, size; @@ -86,8 +95,18 @@ namespace Stockfish::Eval::NNUE { *hashValue = read_little_endian(stream); size = read_little_endian(stream); if (!stream || version != Version) return false; - architecture->resize(size); - stream.read(&(*architecture)[0], size); + desc->resize(size); + stream.read(&(*desc)[0], size); + return !stream.fail(); + } + + // Write network header + bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc) + { + write_little_endian(stream, Version); + write_little_endian(stream, hashValue); + write_little_endian(stream, desc.size()); + stream.write(&desc[0], desc.size()); return !stream.fail(); } @@ -95,14 +114,22 @@ namespace Stockfish::Eval::NNUE { bool read_parameters(std::istream& stream) { std::uint32_t hashValue; - std::string architecture; - if (!read_header(stream, &hashValue, &architecture)) return false; + if (!read_header(stream, &hashValue, &netDescription)) return false; if (hashValue != HashValue) return false; if (!Detail::read_parameters(stream, *featureTransformer)) return false; if (!Detail::read_parameters(stream, *network)) return false; return stream && stream.peek() == std::ios::traits_type::eof(); } + // Write network parameters + bool write_parameters(std::ostream& stream) { + + if (!write_header(stream, HashValue, netDescription)) return false; + if (!Detail::write_parameters(stream, *featureTransformer)) return false; + if (!Detail::write_parameters(stream, *network)) return false; + return (bool)stream; + } + // Evaluation function. Perform differential calculation. Value evaluate(const Position& pos) { @@ -141,4 +168,13 @@ namespace Stockfish::Eval::NNUE { return read_parameters(stream); } + // Save eval, to a file stream or a memory stream + bool save_eval(std::ostream& stream) { + + if (fileName.empty()) + return false; + + return write_parameters(stream); + } + } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 424fad56..fc192691 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -69,15 +69,19 @@ namespace Stockfish::Eval::NNUE::Layers { if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) #if !defined (USE_SSSE3) + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) weights[i] = read_little_endian(stream); #else - weights[ + std::unique_ptr indexMap = std::make_unique(OutputDimensions * PaddedInputDimensions); + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) { + const uint32_t scrambledIdx = (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + i / PaddedInputDimensions * 4 + - i % 4 - ] = read_little_endian(stream); + i % 4; + weights[scrambledIdx] = read_little_endian(stream); + indexMap[scrambledIdx] = i; + } // Determine if eights of weight and input products can be summed using 16bits // without saturation. We assume worst case combinations of 0 and 127 for all inputs. @@ -109,7 +113,8 @@ namespace Stockfish::Eval::NNUE::Layers { IndexType idx = maxK / 2 * OutputDimensions * 4 + maxK % 2; sum[sign == -1] -= w[idx]; - canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx]); + const uint32_t scrambledIdx = idx + i * OutputDimensions + j * 4 + x * 2; + canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx], indexMap[scrambledIdx]); w[idx] = 0; } } @@ -125,6 +130,34 @@ namespace Stockfish::Eval::NNUE::Layers { return !stream.fail(); } + // Write network parameters + bool write_parameters(std::ostream& stream) const { + if (!previousLayer.write_parameters(stream)) return false; + for (std::size_t i = 0; i < OutputDimensions; ++i) + write_little_endian(stream, biases[i]); +#if !defined (USE_SSSE3) + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + write_little_endian(stream, weights[i]); +#else + std::unique_ptr unscrambledWeights = std::make_unique(OutputDimensions * PaddedInputDimensions); + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) { + unscrambledWeights[i] = + weights[ + (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + + i / PaddedInputDimensions * 4 + + i % 4 + ]; + } + for (int i = 0; i < canSaturate16.count; ++i) + unscrambledWeights[canSaturate16.ids[i].wIdx] = canSaturate16.ids[i].w; + + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + write_little_endian(stream, unscrambledWeights[i]); +#endif + + return !stream.fail(); + } + // Forward propagation const OutputType* propagate( const TransformedFeatureType* transformedFeatures, char* buffer) const { @@ -444,12 +477,14 @@ namespace Stockfish::Eval::NNUE::Layers { struct CanSaturate { int count; struct Entry { + uint32_t wIdx; uint16_t out; uint16_t in; int8_t w; } ids[PaddedInputDimensions * OutputDimensions * 3 / 4]; - void add(int i, int j, int8_t w) { + void add(int i, int j, int8_t w, uint32_t wIdx) { + ids[count].wIdx = wIdx; ids[count].out = i; ids[count].in = j; ids[count].w = w; diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 00809c50..f1ac2dfe 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -59,6 +59,11 @@ namespace Stockfish::Eval::NNUE::Layers { return previousLayer.read_parameters(stream); } + // Write network parameters + bool write_parameters(std::ostream& stream) const { + return previousLayer.write_parameters(stream); + } + // Forward propagation const OutputType* propagate( const TransformedFeatureType* transformedFeatures, char* buffer) const { diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index f113b911..bd4d7447 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -53,6 +53,11 @@ class InputSlice { return true; } + // Read network parameters + bool write_parameters(std::ostream& /*stream*/) const { + return true; + } + // Forward propagation const OutputType* propagate( const TransformedFeatureType* transformedFeatures, diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 8c54f9ba..d41e0237 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -99,6 +99,24 @@ namespace Stockfish::Eval::NNUE { return result; } + template + inline void write_little_endian(std::ostream& stream, IntType value) { + + std::uint8_t u[sizeof(IntType)]; + typename std::make_unsigned::type v = value; + + std::size_t i = 0; + // if constexpr to silence the warning about shift by 8 + if constexpr (sizeof(IntType) > 1) { + for (; i + 1 < sizeof(IntType); ++i) { + u[i] = v; + v >>= 8; + } + } + u[i] = v; + + stream.write(reinterpret_cast(u), sizeof(IntType)); + } } // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index f4412749..a4a8e98f 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -118,6 +118,15 @@ namespace Stockfish::Eval::NNUE { return !stream.fail(); } + // Write network parameters + bool write_parameters(std::ostream& stream) const { + for (std::size_t i = 0; i < HalfDimensions; ++i) + write_little_endian(stream, biases[i]); + for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) + write_little_endian(stream, weights[i]); + return !stream.fail(); + } + // Convert input features void transform(const Position& pos, OutputType* output) const { update_accumulator(pos, WHITE); diff --git a/src/uci.cpp b/src/uci.cpp index 64bb7a7c..bb17b8d7 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -277,7 +277,14 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; - else if (token == "export_net") Eval::NNUE::export_net(); + else if (token == "export_net") { + std::optional filename; + std::string f; + if (is >> skipws >> f) { + filename = f; + } + Eval::NNUE::export_net(filename); + } else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; From 602687801b73552fb8364bcc82191f932ddca8b7 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 11 May 2021 11:04:02 -0300 Subject: [PATCH 039/282] Simplify LMR as it seems not to bring any strength and thus is no longer needed. Tests for updating elo estimates: https://tests.stockfishchess.org/tests/view/6099ff123a33eb67a844f789 https://tests.stockfishchess.org/tests/view/60953e6695e7f1852abd305b Individual simplification tests: https://tests.stockfishchess.org/tests/view/6098cfc73a33eb67a844f6a1 https://tests.stockfishchess.org/tests/view/6095539495e7f1852abd308b LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 96984 W: 3624 L: 3608 D: 89752 Ptnml(0-2): 45, 3222, 41939, 3244, 42 https://tests.stockfishchess.org/tests/view/6099921a3a33eb67a844f74f closes https://github.com/official-stockfish/Stockfish/pull/3458 bench: 3836428 --- src/search.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index bcd53f9e..8f157b7e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1206,16 +1206,11 @@ moves_loop: // When in check, search starts from here && thisThread->bestMoveChanges <= 2) r++; - // More reductions for late moves if position was not in previous PV - if ( moveCountPruning - && !formerPv) - r++; - // Decrease reduction if opponent's move count is high (~5 Elo) if ((ss-1)->moveCount > 13) r--; - // Decrease reduction if ttMove has been singularly extended (~3 Elo) + // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) r--; @@ -1228,7 +1223,7 @@ moves_loop: // When in check, search starts from here } else { - // Increase reduction if ttMove is a capture (~5 Elo) + // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) r++; @@ -1239,13 +1234,6 @@ moves_loop: // When in check, search starts from here if (cutNode) r += 2; - // Decrease reduction for moves that escape a capture. Filter out - // castling moves, because they are coded as "king captures rook" and - // hence break reverse_move() (~2 Elo) - else if ( type_of(move) == NORMAL - && !pos.see_ge(reverse_move(move))) - r -= 2 + ss->ttPv - (type_of(movedPiece) == PAWN); - ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From a0e2debe3f1d14f84984a9a2c1482dc41f695548 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sun, 9 May 2021 16:33:41 -0300 Subject: [PATCH 040/282] Remove coordination between searching threads In summary, this revert #2204, as it seems not to bring any strength anymore, so it's no long needed. STC (5+0.05 @ 8 threads): LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 105728 W: 6406 L: 6393 D: 92929 Ptnml(0-2): 154, 5479, 41599, 5464, 168 https://tests.stockfishchess.org/tests/view/6096994095e7f1852abd3154 LTC (20+0.2 @ 8 threads): LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 26336 W: 774 L: 712 D: 24850 Ptnml(0-2): 9, 641, 11810, 695, 13 https://tests.stockfishchess.org/tests/view/6097c62995e7f1852abd31e8 closes https://github.com/official-stockfish/Stockfish/pull/3459 No functional change. --- src/search.cpp | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8f157b7e..3a0839d6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -102,49 +102,6 @@ namespace { Move best = MOVE_NONE; }; - // Breadcrumbs are used to mark nodes as being searched by a given thread - struct Breadcrumb { - std::atomic thread; - std::atomic key; - }; - std::array breadcrumbs; - - // ThreadHolding structure keeps track of which thread left breadcrumbs at the given - // node for potential reductions. A free node will be marked upon entering the moves - // loop by the constructor, and unmarked upon leaving that loop by the destructor. - struct ThreadHolding { - explicit ThreadHolding(Thread* thisThread, Key posKey, int ply) { - location = ply < 8 ? &breadcrumbs[posKey & (breadcrumbs.size() - 1)] : nullptr; - otherThread = false; - owning = false; - if (location) - { - // See if another already marked this location, if not, mark it ourselves - Thread* tmp = (*location).thread.load(std::memory_order_relaxed); - if (tmp == nullptr) - { - (*location).thread.store(thisThread, std::memory_order_relaxed); - (*location).key.store(posKey, std::memory_order_relaxed); - owning = true; - } - else if ( tmp != thisThread - && (*location).key.load(std::memory_order_relaxed) == posKey) - otherThread = true; - } - } - - ~ThreadHolding() { - if (owning) // Free the marked location - (*location).thread.store(nullptr, std::memory_order_relaxed); - } - - bool marked() { return otherThread; } - - private: - Breadcrumb* location; - bool otherThread, owning; - }; - template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); @@ -1013,9 +970,6 @@ moves_loop: // When in check, search starts from here && (tte->bound() & BOUND_UPPER) && tte->depth() >= depth; - // Mark this node as being searched - ThreadHolding th(thisThread, posKey, ss->ply); - // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) @@ -1190,10 +1144,6 @@ moves_loop: // When in check, search starts from here if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; - // Increase reduction if other threads are searching this position - if (th.marked()) - r++; - // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~10 Elo) if ( ss->ttPv From b62af7ac1e78c1b35103dfe6110201d0b810aee0 Mon Sep 17 00:00:00 2001 From: EntityFX Date: Mon, 12 Apr 2021 13:42:35 +0300 Subject: [PATCH 041/282] E2K: added support for MCST Elbrus 2000 CPU architecture e2k (Elbrus 2000) - this is a VLIW/EPIC architecture, the like Intel Itanium (IA-64) architecture. The architecture has half native / half software support for most Intel/AMD SIMD (e.g. MMX/SSE/SSE2/SSE3/SSSE3/SSE4.1/SSE4.2/AES/AVX/AVX2 & 3DNow!/SSE4a/XOP/FMA4) via intrinsics. https://en.wikipedia.org/wiki/Elbrus_2000 closes https://github.com/official-stockfish/Stockfish/pull/3425 No functional change --- AUTHORS | 1 + src/Makefile | 15 ++++++++++++++- src/misc.cpp | 14 +++++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index c12b98a0..69d682f1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -27,6 +27,7 @@ Andy Duplain Antoine Champion (antoinechampion) Aram Tumanian (atumanian) Arjun Temurnikar +Artem Solopiy (EntityFX) Auguste Pop Balint Pfliegel Ben Koshy (BKSpurgeon) diff --git a/src/Makefile b/src/Makefile index cdd2007f..71105bdb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -97,6 +97,7 @@ ifeq ($(ARCH), $(filter $(ARCH), \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \ x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 \ + e2k \ armv7 armv7-neon armv8 apple-silicon general-64 general-32)) SUPPORTED_ARCH=true else @@ -291,6 +292,17 @@ ifeq ($(ARCH),ppc-64) prefetch = yes endif +ifeq ($(findstring e2k,$(ARCH)),e2k) + arch = e2k + mmx = yes + bits = 64 + sse = yes + sse2 = yes + ssse3 = yes + sse41 = yes + popcnt = yes +endif + endif ### ========================================================================== @@ -514,7 +526,6 @@ ifeq ($(popcnt),yes) endif endif - ifeq ($(avx2),yes) CXXFLAGS += -DUSE_AVX2 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) @@ -682,6 +693,7 @@ help: @echo "armv7 > ARMv7 32-bit" @echo "armv7-neon > ARMv7 32-bit with popcnt and neon" @echo "armv8 > ARMv8 64-bit with popcnt and neon" + @echo "e2k > Elbrus 2000" @echo "apple-silicon > Apple silicon ARM64" @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" @@ -829,6 +841,7 @@ config-sanity: net @test "$(SUPPORTED_ARCH)" = "true" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ + test "$(arch)" = "e2k" || \ test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" diff --git a/src/misc.cpp b/src/misc.cpp index 918dc7a9..9e7b7e37 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -51,7 +51,7 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); #include #endif -#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) +#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) || defined(__e2k__) #define POSIXALIGNEDALLOC #include #endif @@ -192,6 +192,18 @@ std::string compiler_info() { compiler += "(version "; compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD); compiler += ")"; + #elif defined(__e2k__) && defined(__LCC__) + #define dot_ver2(n) \ + compiler += (char)'.'; \ + compiler += (char)('0' + (n) / 10); \ + compiler += (char)('0' + (n) % 10); + + compiler += "MCST LCC "; + compiler += "(version "; + compiler += std::to_string(__LCC__ / 100); + dot_ver2(__LCC__ % 100) + dot_ver2(__LCC_MINOR__) + compiler += ")"; #elif __GNUC__ compiler += "g++ (GNUC) "; compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); From 594e2ac9994f22cb7c5b74f3da3dbb512cbdb510 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Wed, 12 May 2021 09:38:26 -0300 Subject: [PATCH 042/282] Simplify LMR rule for non-checking captures We simplify away the complicated rule in LMR for "non-checking captures likely to be bad", as it seems not to bring any strength anymore. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 55256 W: 4972 L: 4897 D: 45387 Ptnml(0-2): 177, 3976, 19234, 4077, 164 https://tests.stockfishchess.org/tests/view/609adf3b3a33eb67a844f842 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 10344 W: 437 L: 353 D: 9554 Ptnml(0-2): 1, 322, 4449, 392, 8 https://tests.stockfishchess.org/tests/view/609b3dfa3a33eb67a844f88e -- While at it, we also update the Elo estimate of the previous rule in LMR (see https://tests.stockfishchess.org/tests/view/609af2a63a33eb67a844f867). closes https://github.com/official-stockfish/Stockfish/pull/3460 Bench: 3840688 --- src/search.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3a0839d6..d16d9cad 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1156,7 +1156,7 @@ moves_loop: // When in check, search starts from here && thisThread->bestMoveChanges <= 2) r++; - // Decrease reduction if opponent's move count is high (~5 Elo) + // Decrease reduction if opponent's move count is high (~1 Elo) if ((ss-1)->moveCount > 13) r--; @@ -1164,14 +1164,7 @@ moves_loop: // When in check, search starts from here if (singularQuietLMR) r--; - if (captureOrPromotion) - { - // Increase reduction for non-checking captures likely to be bad - if ( !givesCheck - && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha) - r++; - } - else + if (!captureOrPromotion) { // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) From bd756ee45ce1181a90ec149892ccf0e4cfe3639e Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Fri, 14 May 2021 17:35:32 +0200 Subject: [PATCH 043/282] Remove BoolConditions from tuning code Remove BoolConditions from tuning code, as the feature does not work and the code has not be touched in years. No functional change --- src/tune.cpp | 19 ------------------- src/tune.h | 34 ---------------------------------- 2 files changed, 53 deletions(-) diff --git a/src/tune.cpp b/src/tune.cpp index d9618efc..ac91b606 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -30,7 +30,6 @@ namespace Stockfish { bool Tune::update_on_last; const UCI::Option* LastOption = nullptr; -BoolConditions Conditions; static std::map TuneResults; string Tune::next(string& names, bool pop) { @@ -110,24 +109,6 @@ template<> void Tune::Entry::read_option() { template<> void Tune::Entry::init_option() {} template<> void Tune::Entry::read_option() { value(); } - -// Set binary conditions according to a probability that depends -// on the corresponding parameter value. - -void BoolConditions::set() { - - static PRNG rng(now()); - static bool startup = true; // To workaround fishtest bench - - for (size_t i = 0; i < binary.size(); i++) - binary[i] = !startup && (values[i] + int(rng.rand() % variance) > threshold); - - startup = false; - - for (size_t i = 0; i < binary.size(); i++) - sync_cout << binary[i] << sync_endl; -} - } // namespace Stockfish diff --git a/src/tune.h b/src/tune.h index c904c09d..b5c715b3 100644 --- a/src/tune.h +++ b/src/tune.h @@ -46,27 +46,6 @@ struct SetRange { #define SetDefaultRange SetRange(default_range) -/// BoolConditions struct is used to tune boolean conditions in the -/// code by toggling them on/off according to a probability that -/// depends on the value of a tuned integer parameter: for high -/// values of the parameter condition is always disabled, for low -/// values is always enabled, otherwise it is enabled with a given -/// probability that depnends on the parameter under tuning. - -struct BoolConditions { - void init(size_t size) { values.resize(size, defaultValue), binary.resize(size, 0); } - void set(); - - std::vector binary, values; - int defaultValue = 465, variance = 40, threshold = 500; - SetRange range = SetRange(0, 1000); -}; - -extern BoolConditions Conditions; - -inline void set_conditions() { Conditions.set(); } - - /// Tune class implements the 'magic' code that makes the setup of a fishtest /// tuning session as easy as it can be. Mainly you have just to remove const /// qualifiers from the variables you want to tune and flag them for tuning, so @@ -159,14 +138,6 @@ class Tune { return add(value, (next(names), std::move(names)), args...); } - // Template specialization for BoolConditions - template - int add(const SetRange& range, std::string&& names, BoolConditions& cond, Args&&... args) { - for (size_t size = cond.values.size(), i = 0; i < size; i++) - add(cond.range, next(names, i == size - 1) + "_" + std::to_string(i), cond.values[i]); - return add(range, std::move(names), args...); - } - std::vector> list; public: @@ -187,11 +158,6 @@ public: #define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true -// Some macro to tune toggling of boolean conditions -#define CONDITION(x) (Conditions.binary[__COUNTER__] || (x)) -#define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \ - TUNE(Conditions, set_conditions) - } // namespace Stockfish #endif // #ifndef TUNE_H_INCLUDED From 24b8b3098bc24ec576b7d03ffb72b2908e6c8c80 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 13 May 2021 11:12:56 -0300 Subject: [PATCH 044/282] Remove early return in Probcut code We simplify away early return in ProbCut, as it seems not to bring any strength anymore. STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 42632 W: 3705 L: 3617 D: 35310 Ptnml(0-2): 123, 2947, 15110, 2991, 145 https://tests.stockfishchess.org/tests/view/609c49da7746e3dc74ffae02 LTC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 35384 W: 1314 L: 1251 D: 32819 Ptnml(0-2): 11, 1130, 15355, 1177, 19 https://tests.stockfishchess.org/tests/view/609c71467746e3dc74ffae47 --- While at it, we also update the Elo estimate of ProbCut (see https://tests.stockfishchess.org/tests/view/609bfb597746e3dc74ffabe3). closes https://github.com/official-stockfish/Stockfish/pull/3462 bench: 3764662 --- src/search.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d16d9cad..788be984 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -847,7 +847,7 @@ namespace { probCutBeta = beta + 209 - 44 * improving; - // Step 9. ProbCut (~10 Elo) + // Step 9. ProbCut (~4 Elo) // If we have a good enough capture and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode @@ -862,15 +862,6 @@ namespace { && ttValue != VALUE_NONE && ttValue < probCutBeta)) { - // if ttMove is a capture and value from transposition table is good enough produce probCut - // cutoff without digging into actual probCut search - if ( ss->ttHit - && tte->depth() >= depth - 3 - && ttValue != VALUE_NONE - && ttValue >= probCutBeta - && ttMove - && pos.capture_or_promotion(ttMove)) - return probCutBeta; assert(probCutBeta < VALUE_INFINITE); From c82f6f56a65759461f417602059ad7c97b9451aa Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 13 May 2021 23:47:41 -0300 Subject: [PATCH 045/282] Simplify LMR rules for statScore We simplify two parts of LMR which seem not to bring strength anymore. --- Individual Tests: https://tests.stockfishchess.org/tests/view/609d1cc15085663412d0856a https://tests.stockfishchess.org/tests/view/609cb0cc7746e3dc74ffae8d https://tests.stockfishchess.org/tests/view/609d1c9f5085663412d08568 --- LTC: LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 84184 W: 3093 L: 3066 D: 78025 Ptnml(0-2): 47, 2755, 36458, 2788, 44 https://tests.stockfishchess.org/tests/view/609d84615085663412d08e2f --- While at it, we also update the Elo estimate of the previous rule in LMR, see: https://tests.stockfishchess.org/tests/view/609a933c3a33eb67a844f7ca https://tests.stockfishchess.org/tests/view/609a959c3a33eb67a844f7d5 https://tests.stockfishchess.org/tests/view/609afff73a33eb67a844f870 --- closes https://github.com/official-stockfish/Stockfish/pull/3464 Bench: 4156523 --- src/search.cpp | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 788be984..e03016b6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1131,12 +1131,12 @@ moves_loop: // When in check, search starts from here { Depth r = reduction(improving, depth, moveCount); - // Decrease reduction if the ttHit running average is large + // Decrease reduction if the ttHit running average is large (~0 Elo) if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; // Decrease reduction if position is or has been on the PV - // and node is not likely to fail low. (~10 Elo) + // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv && !likelyFailLow) r -= 2; @@ -1162,9 +1162,10 @@ moves_loop: // When in check, search starts from here r++; // Increase reduction at root if failing high - r += rootNode ? thisThread->failedHighCnt * thisThread->failedHighCnt * moveCount / 512 : 0; + if (rootNode) + r += thisThread->failedHighCnt * thisThread->failedHighCnt * moveCount / 512; - // Increase reduction for cut nodes (~10 Elo) + // Increase reduction for cut nodes (~3 Elo) if (cutNode) r += 2; @@ -1174,20 +1175,8 @@ moves_loop: // When in check, search starts from here + (*contHist[3])[movedPiece][to_sq(move)] - 4741; - // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -89 && (ss-1)->statScore < -116) - r--; - - else if ((ss-1)->statScore >= -112 && ss->statScore < -100) - r++; - // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - // If we are not in check use statScore, but if we are in check we use - // the sum of main history and first continuation history with an offset. - if (ss->inCheck) - r -= (thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] - 3833) / 16384; - else + if (!ss->inCheck) r -= ss->statScore / 14790; } From 61e1c66b7cb1dea9346a9b74e801e4da74ad7591 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 15 May 2021 00:55:45 +0300 Subject: [PATCH 046/282] Simplification for countermoves based pruning Simplify away two extra conditions in countermoves based pruning. These conditions (both of them) were introduced quite a long time ago via speculative LTCs and seem to no longer bring any benefit. passed STC https://tests.stockfishchess.org/tests/view/609e81f35085663412d08f31 LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 28488 W: 2487 L: 2382 D: 23619 Ptnml(0-2): 87, 1919, 10123, 2032, 83 passed LTC https://tests.stockfishchess.org/tests/view/609e9c085085663412d08f59 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 33176 W: 1219 L: 1155 D: 30802 Ptnml(0-2): 13, 1036, 14423, 1106, 10 closes https://github.com/official-stockfish/Stockfish/pull/3468 Bench: 4749514 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e03016b6..bf6ba6c4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1026,7 +1026,7 @@ moves_loop: // When in check, search starts from here else { // Countermoves based pruning (~20 Elo) - if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) + if ( lmrDepth < 4 && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) continue; From f90274d8ce1aad4ad0595aacbceb74b6cbe306a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 17 May 2021 09:13:34 +0200 Subject: [PATCH 047/282] Small clean-ups - Comment for Countemove pruning -> Continuation history - Fix comment in input_slice.h - Shorter lines in Makefile - Comment for scale factor - Fix comment for pinners in see_ge() - Change Thread.id() signature to size_t - Trailing space in reprosearch.sh - Add Douglas Matos Gomes to the AUTHORS file - Introduce comment for undo_null_move() - Use Stockfish coding style for export_net() - Change date in AUTHORS file closes https://github.com/official-stockfish/Stockfish/pull/3416 No functional change --- AUTHORS | 3 ++- src/Makefile | 6 ++---- src/evaluate.cpp | 28 ++++++++++++++++------------ src/nnue/layers/input_slice.h | 2 +- src/position.cpp | 9 ++++++--- src/search.cpp | 5 ++--- src/thread.h | 2 +- tests/reprosearch.sh | 2 +- 8 files changed, 31 insertions(+), 26 deletions(-) diff --git a/AUTHORS b/AUTHORS index 69d682f1..9042495f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of March 31, 2021 +# List of authors for Stockfish, as of May 17, 2021 # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) @@ -52,6 +52,7 @@ Dieter Dobbelaere (ddobbelaere) DiscanX Dominik Schlösser (domschl) double-beep +Douglas Matos Gomes (dsmsgms) Eduardo Cáceres (eduherminio) Eelco de Groot (KingDefender) Elvin Liu (solarlight2) diff --git a/src/Makefile b/src/Makefile index 71105bdb..660a13fb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -96,8 +96,7 @@ endif ifeq ($(ARCH), $(filter $(ARCH), \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \ x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ - x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 \ - e2k \ + x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ armv7 armv7-neon armv8 apple-silicon general-64 general-32)) SUPPORTED_ARCH=true else @@ -840,8 +839,7 @@ config-sanity: net @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(SUPPORTED_ARCH)" = "true" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ - test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ - test "$(arch)" = "e2k" || \ + test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \ test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c396e0f7..403d59dd 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,24 +114,28 @@ namespace Eval { } } + /// NNUE::export_net() exports the currently loaded network to a file void NNUE::export_net(const std::optional& filename) { std::string actualFilename; - if (filename.has_value()) { - actualFilename = filename.value(); - } else { - if (eval_file_loaded != EvalFileDefaultName) { - sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl; - return; - } - actualFilename = EvalFileDefaultName; + + if (filename.has_value()) + actualFilename = filename.value(); + else + { + if (eval_file_loaded != EvalFileDefaultName) + { + sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl; + return; + } + actualFilename = EvalFileDefaultName; } ofstream stream(actualFilename, std::ios_base::binary); - if (save_eval(stream)) { + + if (save_eval(stream)) sync_cout << "Network saved successfully to " << actualFilename << "." << sync_endl; - } else { + else sync_cout << "Failed to export a net." << sync_endl; - } } /// NNUE::verify() verifies that the last net used was loaded successfully @@ -927,7 +931,7 @@ namespace { Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; int sf = me->scale_factor(pos, strongSide); - // If scale factor is not already specific, scale down via general heuristics + // If scale factor is not already specific, scale up/down via general heuristics if (sf == SCALE_FACTOR_NORMAL) { if (pos.opposite_bishops()) diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index bd4d7447..b6bf1727 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -53,7 +53,7 @@ class InputSlice { return true; } - // Read network parameters + // Write network parameters bool write_parameters(std::ostream& /*stream*/) const { return true; } diff --git a/src/position.cpp b/src/position.cpp index 2b3be3f7..f1c36156 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -988,7 +988,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ } -/// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips +/// Position::do_null_move() is used to do a "null move": it flips /// the side to move without executing any move on the board. void Position::do_null_move(StateInfo& newSt) { @@ -1027,6 +1027,9 @@ void Position::do_null_move(StateInfo& newSt) { assert(pos_is_ok()); } + +/// Position::undo_null_move() must be used to undo a "null move" + void Position::undo_null_move() { assert(!checkers()); @@ -1092,8 +1095,8 @@ bool Position::see_ge(Move m, Value threshold) const { if (!(stmAttackers = attackers & pieces(stm))) break; - // Don't allow pinned pieces to attack (except the king) as long as - // there are pinners on their original square. + // Don't allow pinned pieces to attack as long as there are + // pinners on their original square. if (pinners(~stm) & occupied) stmAttackers &= ~blockers_for_king(stm); diff --git a/src/search.cpp b/src/search.cpp index bf6ba6c4..29b334ed 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -862,7 +862,6 @@ namespace { && ttValue != VALUE_NONE && ttValue < probCutBeta)) { - assert(probCutBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); @@ -1025,7 +1024,7 @@ moves_loop: // When in check, search starts from here } else { - // Countermoves based pruning (~20 Elo) + // Continuation history based pruning (~20 Elo) if ( lmrDepth < 4 && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) @@ -1528,7 +1527,7 @@ moves_loop: // When in check, search starts from here [pos.moved_piece(move)] [to_sq(move)]; - // CounterMove based pruning + // Continuation history based pruning if ( !captureOrPromotion && bestValue > VALUE_TB_LOSS_IN_MAX_PLY && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold diff --git a/src/thread.h b/src/thread.h index 4cf5dabb..5785fd25 100644 --- a/src/thread.h +++ b/src/thread.h @@ -55,7 +55,7 @@ public: void idle_loop(); void start_searching(); void wait_for_search_finished(); - int id() const { return idx; } + size_t id() const { return idx; } Pawns::Table pawnsTable; Material::Table materialTable; diff --git a/tests/reprosearch.sh b/tests/reprosearch.sh index 9fd847ff..c1167f7f 100755 --- a/tests/reprosearch.sh +++ b/tests/reprosearch.sh @@ -10,7 +10,7 @@ trap 'error ${LINENO}' ERR echo "reprosearch testing started" -# repeat two short games, separated by ucinewgame. +# repeat two short games, separated by ucinewgame. # with go nodes $nodes they should result in exactly # the same node count for each iteration. cat << EOF > repeat.exp From e8d64af1230fdac65bb0da246df3e7abe82e0838 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 18 May 2021 17:36:26 +0200 Subject: [PATCH 048/282] New NNUE architecture and net Introduces a new NNUE network architecture and associated network parameters, as obtained by a new pytorch trainer. The network is already very strong at short TC, without regression at longer TC, and has potential for further improvements. https://tests.stockfishchess.org/tests/view/60a159c65085663412d0921d TC: 10s+0.1s, 1 thread ELO: 21.74 +-3.4 (95%) LOS: 100.0% Total: 10000 W: 1559 L: 934 D: 7507 Ptnml(0-2): 38, 701, 2972, 1176, 113 https://tests.stockfishchess.org/tests/view/60a187005085663412d0925b TC: 60s+0.6s, 1 thread ELO: 5.85 +-1.7 (95%) LOS: 100.0% Total: 20000 W: 1381 L: 1044 D: 17575 Ptnml(0-2): 27, 885, 7864, 1172, 52 https://tests.stockfishchess.org/tests/view/60a2beede229097940a03806 TC: 20s+0.2s, 8 threads LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 34272 W: 1610 L: 1452 D: 31210 Ptnml(0-2): 30, 1285, 14350, 1439, 32 https://tests.stockfishchess.org/tests/view/60a2d687e229097940a03c72 TC: 60s+0.6s, 8 threads LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 45544 W: 1262 L: 1214 D: 43068 Ptnml(0-2): 12, 1129, 20442, 1177, 12 The network has been trained (by vondele) using the https://github.com/glinscott/nnue-pytorch/ trainer (started by glinscott), specifically the branch https://github.com/Sopel97/nnue-pytorch/tree/experiment_56. The data used are in 64 billion positions (193GB total) generated and scored with the current master net d8: https://drive.google.com/file/d/1hOOYSDKgOOp38ZmD0N4DV82TOLHzjUiF/view?usp=sharing d9: https://drive.google.com/file/d/1VlhnHL8f-20AXhGkILujnNXHwy9T-MQw/view?usp=sharing d10: https://drive.google.com/file/d/1ZC5upzBYMmMj1gMYCkt6rCxQG0GnO3Kk/view?usp=sharing fishtest_d9: https://drive.google.com/file/d/1GQHt0oNgKaHazwJFTRbXhlCN3FbUedFq/view?usp=sharing This network also contains a few architectural changes with respect to the current master: Size changed from 256x2-32-32-1 to 512x2-16-32-1 ~15-20% slower ~2x larger adds a special path for 16 valued ClippedReLU fixes affine transform code for 16 inputs/outputs, buy using InputDimensions instead of PaddedInputDimensions this is safe now because the inputs are processed in groups of 4 in the current affine transform code The feature set changed from HalfKP to HalfKAv2 Includes information about the kings like HalfKA Packs king features better, resulting in 8% size reduction compared to HalfKA The board is flipped for the black's perspective, instead of rotated like in the current master PSQT values for each feature the feature transformer now outputs a part that is fowarded directly to the output and allows learning piece values more directly than the previous network architecture. The effect is visible for high imbalance positions, where the current master network outputs evaluations skewed towards zero. 8 PSQT values per feature, chosen based on (popcount(pos.pieces()) - 1) / 4 initialized to classical material values on the start of the training 8 subnetworks (512x2->16->32->1), chosen based on (popcount(pos.pieces()) - 1) / 4 only one subnetwork is evaluated for any position, no or marginal speed loss A diagram of the network is available: https://user-images.githubusercontent.com/8037982/118656988-553a1700-b7eb-11eb-82ef-56a11cbebbf2.png A more complete description: https://github.com/glinscott/nnue-pytorch/blob/master/docs/nnue.md closes https://github.com/official-stockfish/Stockfish/pull/3474 Bench: 3806488 --- src/Makefile | 2 +- src/evaluate.cpp | 13 +- src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 24 ++-- .../features/{half_kp.cpp => half_ka_v2.cpp} | 25 ++-- src/nnue/features/{half_kp.h => half_ka_v2.h} | 51 +++---- src/nnue/layers/affine_transform.h | 116 ++++----------- src/nnue/layers/clipped_relu.h | 50 +++++-- src/nnue/nnue_accumulator.h | 4 +- src/nnue/nnue_architecture.h | 10 +- src/nnue/nnue_common.h | 2 +- src/nnue/nnue_feature_transformer.h | 133 +++++++++++++++++- src/search.cpp | 6 +- 13 files changed, 265 insertions(+), 173 deletions(-) rename src/nnue/features/{half_kp.cpp => half_ka_v2.cpp} (77%) rename src/nnue/features/{half_kp.h => half_ka_v2.h} (73%) diff --git a/src/Makefile b/src/Makefile index 660a13fb..066e7697 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,7 +41,7 @@ endif SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ - nnue/evaluate_nnue.cpp nnue/features/half_kp.cpp + nnue/evaluate_nnue.cpp nnue/features/half_ka_v2.cpp OBJS = $(notdir $(SRCS:.cpp=.o)) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 403d59dd..256bd994 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -120,7 +120,7 @@ namespace Eval { if (filename.has_value()) actualFilename = filename.value(); - else + else { if (eval_file_loaded != EvalFileDefaultName) { @@ -1116,10 +1116,8 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&]() { - int material = pos.non_pawn_material() + 4 * PawnValueMg * pos.count(); - int scale = 580 - + material / 32 - - 4 * pos.rule50_count(); + + int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE; @@ -1134,7 +1132,7 @@ Value Eval::evaluate(const Position& pos) { Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; - bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); + bool classical = largePsq; // Use classical evaluation for really low piece endgames. // One critical case is the draw for bishop + A/H file pawn vs naked king. @@ -1151,8 +1149,7 @@ Value Eval::evaluate(const Position& pos) { && !lowPieceEndgame && ( abs(v) * 16 < NNUEThreshold2 * r50 || ( pos.opposite_bishops() - && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 - && !(pos.this_thread()->nodes & 0xB)))) + && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50))) v = adjusted_NNUE(); } diff --git a/src/evaluate.h b/src/evaluate.h index 128a7cae..ee4c175b 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-62ef826d1a6d.nnue" + #define EvalFileDefaultName "nn-8a08400ed089.nnue" namespace NNUE { diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index e0d4b911..97cef814 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -35,7 +35,7 @@ namespace Stockfish::Eval::NNUE { LargePagePtr featureTransformer; // Evaluation function - AlignedPtr network; + AlignedPtr network[LayerStacks]; // Evaluation function file name std::string fileName; @@ -83,7 +83,8 @@ namespace Stockfish::Eval::NNUE { void initialize() { Detail::initialize(featureTransformer); - Detail::initialize(network); + for (std::size_t i = 0; i < LayerStacks; ++i) + Detail::initialize(network[i]); } // Read network header @@ -92,7 +93,7 @@ namespace Stockfish::Eval::NNUE { std::uint32_t version, size; version = read_little_endian(stream); - *hashValue = read_little_endian(stream); + *hashValue = read_little_endian(stream); size = read_little_endian(stream); if (!stream || version != Version) return false; desc->resize(size); @@ -117,7 +118,8 @@ namespace Stockfish::Eval::NNUE { if (!read_header(stream, &hashValue, &netDescription)) return false; if (hashValue != HashValue) return false; if (!Detail::read_parameters(stream, *featureTransformer)) return false; - if (!Detail::read_parameters(stream, *network)) return false; + for (std::size_t i = 0; i < LayerStacks; ++i) + if (!Detail::read_parameters(stream, *(network[i]))) return false; return stream && stream.peek() == std::ios::traits_type::eof(); } @@ -126,7 +128,8 @@ namespace Stockfish::Eval::NNUE { if (!write_header(stream, HashValue, netDescription)) return false; if (!Detail::write_parameters(stream, *featureTransformer)) return false; - if (!Detail::write_parameters(stream, *network)) return false; + for (std::size_t i = 0; i < LayerStacks; ++i) + if (!Detail::write_parameters(stream, *(network[i]))) return false; return (bool)stream; } @@ -154,10 +157,15 @@ namespace Stockfish::Eval::NNUE { ASSERT_ALIGNED(transformedFeatures, alignment); ASSERT_ALIGNED(buffer, alignment); - featureTransformer->transform(pos, transformedFeatures); - const auto output = network->propagate(transformedFeatures, buffer); + const std::size_t bucket = (pos.count() - 1) / 4; - return static_cast(output[0] / OutputScale); + const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket); + if (lazy) { + return static_cast(psqt / OutputScale); + } else { + const auto output = network[bucket]->propagate(transformedFeatures, buffer); + return static_cast((output[0] + psqt) / OutputScale); + } } // Load eval, from a file stream or a memory stream diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_ka_v2.cpp similarity index 77% rename from src/nnue/features/half_kp.cpp rename to src/nnue/features/half_ka_v2.cpp index aa1decee..57f43e50 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_ka_v2.cpp @@ -16,32 +16,32 @@ along with this program. If not, see . */ -//Definition of input features HalfKP of NNUE evaluation function +//Definition of input features HalfKAv2 of NNUE evaluation function -#include "half_kp.h" +#include "half_ka_v2.h" #include "../../position.h" namespace Stockfish::Eval::NNUE::Features { // Orient a square according to perspective (rotates by 180 for black) - inline Square HalfKP::orient(Color perspective, Square s) { - return Square(int(s) ^ (bool(perspective) * 63)); + inline Square HalfKAv2::orient(Color perspective, Square s) { + return Square(int(s) ^ (bool(perspective) * 56)); } // Index of a feature for a given king position and another piece on some square - inline IndexType HalfKP::make_index(Color perspective, Square s, Piece pc, Square ksq) { + inline IndexType HalfKAv2::make_index(Color perspective, Square s, Piece pc, Square ksq) { return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); } // Get a list of indices for active features - void HalfKP::append_active_indices( + void HalfKAv2::append_active_indices( const Position& pos, Color perspective, ValueListInserter active ) { Square ksq = orient(perspective, pos.square(perspective)); - Bitboard bb = pos.pieces() & ~pos.pieces(KING); + Bitboard bb = pos.pieces(); while (bb) { Square s = pop_lsb(bb); @@ -52,7 +52,7 @@ namespace Stockfish::Eval::NNUE::Features { // append_changed_indices() : get a list of indices for recently changed features - void HalfKP::append_changed_indices( + void HalfKAv2::append_changed_indices( Square ksq, StateInfo* st, Color perspective, @@ -63,7 +63,6 @@ namespace Stockfish::Eval::NNUE::Features { Square oriented_ksq = orient(perspective, ksq); for (int i = 0; i < dp.dirty_num; ++i) { Piece pc = dp.piece[i]; - if (type_of(pc) == KING) continue; if (dp.from[i] != SQ_NONE) removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq)); if (dp.to[i] != SQ_NONE) @@ -71,15 +70,15 @@ namespace Stockfish::Eval::NNUE::Features { } } - int HalfKP::update_cost(StateInfo* st) { + int HalfKAv2::update_cost(StateInfo* st) { return st->dirtyPiece.dirty_num; } - int HalfKP::refresh_cost(const Position& pos) { - return pos.count() - 2; + int HalfKAv2::refresh_cost(const Position& pos) { + return pos.count(); } - bool HalfKP::requires_refresh(StateInfo* st, Color perspective) { + bool HalfKAv2::requires_refresh(StateInfo* st, Color perspective) { return st->dirtyPiece.piece[0] == make_piece(perspective, KING); } diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_ka_v2.h similarity index 73% rename from src/nnue/features/half_kp.h rename to src/nnue/features/half_ka_v2.h index a09c221b..e4b2edd9 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_ka_v2.h @@ -18,8 +18,8 @@ //Definition of input features HalfKP of NNUE evaluation function -#ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED -#define NNUE_FEATURES_HALF_KP_H_INCLUDED +#ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED +#define NNUE_FEATURES_HALF_KA_V2_H_INCLUDED #include "../nnue_common.h" @@ -32,33 +32,34 @@ namespace Stockfish { namespace Stockfish::Eval::NNUE::Features { - // Feature HalfKP: Combination of the position of own king - // and the position of pieces other than kings - class HalfKP { + // Feature HalfKAv2: Combination of the position of own king + // and the position of pieces + class HalfKAv2 { // unique number for each piece type on each square enum { PS_NONE = 0, - PS_W_PAWN = 1, - PS_B_PAWN = 1 * SQUARE_NB + 1, - PS_W_KNIGHT = 2 * SQUARE_NB + 1, - PS_B_KNIGHT = 3 * SQUARE_NB + 1, - PS_W_BISHOP = 4 * SQUARE_NB + 1, - PS_B_BISHOP = 5 * SQUARE_NB + 1, - PS_W_ROOK = 6 * SQUARE_NB + 1, - PS_B_ROOK = 7 * SQUARE_NB + 1, - PS_W_QUEEN = 8 * SQUARE_NB + 1, - PS_B_QUEEN = 9 * SQUARE_NB + 1, - PS_NB = 10 * SQUARE_NB + 1 + PS_W_PAWN = 0, + PS_B_PAWN = 1 * SQUARE_NB, + PS_W_KNIGHT = 2 * SQUARE_NB, + PS_B_KNIGHT = 3 * SQUARE_NB, + PS_W_BISHOP = 4 * SQUARE_NB, + PS_B_BISHOP = 5 * SQUARE_NB, + PS_W_ROOK = 6 * SQUARE_NB, + PS_B_ROOK = 7 * SQUARE_NB, + PS_W_QUEEN = 8 * SQUARE_NB, + PS_B_QUEEN = 9 * SQUARE_NB, + PS_KING = 10 * SQUARE_NB, + PS_NB = 11 * SQUARE_NB }; static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { // convention: W - us, B - them // viewed from other side, W and B are reversed - { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE }, - { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE } + { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE, + PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE }, + { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, + PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE } }; // Orient a square according to perspective (rotates by 180 for black) @@ -69,17 +70,17 @@ namespace Stockfish::Eval::NNUE::Features { public: // Feature name - static constexpr const char* Name = "HalfKP(Friend)"; + static constexpr const char* Name = "HalfKAv2(Friend)"; // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = 0x5D69D5B8u; + static constexpr std::uint32_t HashValue = 0x5f234cb8u; // Number of feature dimensions static constexpr IndexType Dimensions = static_cast(SQUARE_NB) * static_cast(PS_NB); - // Maximum number of simultaneously active features. 30 because kins are not included. - static constexpr IndexType MaxActiveDimensions = 30; + // Maximum number of simultaneously active features. + static constexpr IndexType MaxActiveDimensions = 32; // Get a list of indices for active features static void append_active_indices( @@ -107,4 +108,4 @@ namespace Stockfish::Eval::NNUE::Features { } // namespace Stockfish::Eval::NNUE::Features -#endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED +#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index fc192691..9a3b778e 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -69,62 +69,15 @@ namespace Stockfish::Eval::NNUE::Layers { if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); -#if !defined (USE_SSSE3) for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) +#if !defined (USE_SSSE3) weights[i] = read_little_endian(stream); #else - std::unique_ptr indexMap = std::make_unique(OutputDimensions * PaddedInputDimensions); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) { - const uint32_t scrambledIdx = + weights[ (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + i / PaddedInputDimensions * 4 + - i % 4; - weights[scrambledIdx] = read_little_endian(stream); - indexMap[scrambledIdx] = i; - } - - // Determine if eights of weight and input products can be summed using 16bits - // without saturation. We assume worst case combinations of 0 and 127 for all inputs. - if (OutputDimensions > 1 && !stream.fail()) - { - canSaturate16.count = 0; -#if !defined(USE_VNNI) - for (IndexType i = 0; i < PaddedInputDimensions; i += 16) - for (IndexType j = 0; j < OutputDimensions; ++j) - for (int x = 0; x < 2; ++x) - { - WeightType* w = &weights[i * OutputDimensions + j * 4 + x * 2]; - int sum[2] = {0, 0}; - for (int k = 0; k < 8; ++k) - { - IndexType idx = k / 2 * OutputDimensions * 4 + k % 2; - sum[w[idx] < 0] += w[idx]; - } - for (int sign : { -1, 1 }) - while (sign * sum[sign == -1] > 258) - { - int maxK = 0, maxW = 0; - for (int k = 0; k < 8; ++k) - { - IndexType idx = k / 2 * OutputDimensions * 4 + k % 2; - if (maxW < sign * w[idx]) - maxK = k, maxW = sign * w[idx]; - } - - IndexType idx = maxK / 2 * OutputDimensions * 4 + maxK % 2; - sum[sign == -1] -= w[idx]; - const uint32_t scrambledIdx = idx + i * OutputDimensions + j * 4 + x * 2; - canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx], indexMap[scrambledIdx]); - w[idx] = 0; - } - } - - // Non functional optimization for faster more linear access - std::sort(canSaturate16.ids, canSaturate16.ids + canSaturate16.count, - [](const typename CanSaturate::Entry& e1, const typename CanSaturate::Entry& e2) - { return e1.in == e2.in ? e1.out < e2.out : e1.in < e2.in; }); -#endif - } + i % 4 + ] = read_little_endian(stream); #endif return !stream.fail(); @@ -148,8 +101,6 @@ namespace Stockfish::Eval::NNUE::Layers { i % 4 ]; } - for (int i = 0; i < canSaturate16.count; ++i) - unscrambledWeights[canSaturate16.ids[i].wIdx] = canSaturate16.ids[i].w; for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) write_little_endian(stream, unscrambledWeights[i]); @@ -194,11 +145,11 @@ namespace Stockfish::Eval::NNUE::Layers { __m512i product1 = _mm512_maddubs_epi16(a1, b1); __m512i product2 = _mm512_maddubs_epi16(a2, b2); __m512i product3 = _mm512_maddubs_epi16(a3, b3); - product0 = _mm512_add_epi16(product0, product1); - product2 = _mm512_add_epi16(product2, product3); - product0 = _mm512_add_epi16(product0, product2); + product0 = _mm512_adds_epi16(product0, product1); product0 = _mm512_madd_epi16(product0, Ones512); - acc = _mm512_add_epi32(acc, product0); + product2 = _mm512_adds_epi16(product2, product3); + product2 = _mm512_madd_epi16(product2, Ones512); + acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product2)); #endif }; @@ -236,11 +187,11 @@ namespace Stockfish::Eval::NNUE::Layers { __m256i product1 = _mm256_maddubs_epi16(a1, b1); __m256i product2 = _mm256_maddubs_epi16(a2, b2); __m256i product3 = _mm256_maddubs_epi16(a3, b3); - product0 = _mm256_add_epi16(product0, product1); - product2 = _mm256_add_epi16(product2, product3); - product0 = _mm256_add_epi16(product0, product2); + product0 = _mm256_adds_epi16(product0, product1); product0 = _mm256_madd_epi16(product0, Ones256); - acc = _mm256_add_epi32(acc, product0); + product2 = _mm256_adds_epi16(product2, product3); + product2 = _mm256_madd_epi16(product2, Ones256); + acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product2)); #endif }; @@ -267,11 +218,11 @@ namespace Stockfish::Eval::NNUE::Layers { __m128i product1 = _mm_maddubs_epi16(a1, b1); __m128i product2 = _mm_maddubs_epi16(a2, b2); __m128i product3 = _mm_maddubs_epi16(a3, b3); - product0 = _mm_add_epi16(product0, product1); - product2 = _mm_add_epi16(product2, product3); - product0 = _mm_add_epi16(product0, product2); + product0 = _mm_adds_epi16(product0, product1); product0 = _mm_madd_epi16(product0, Ones128); - acc = _mm_add_epi32(acc, product0); + product2 = _mm_adds_epi16(product2, product3); + product2 = _mm_madd_epi16(product2, Ones128); + acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product2)); }; #endif @@ -300,6 +251,8 @@ namespace Stockfish::Eval::NNUE::Layers { #endif #if defined (USE_SSSE3) + // Different layout, we process 4 inputs at a time, always. + static_assert(InputDimensions % 4 == 0); const auto output = reinterpret_cast(buffer); const auto inputVector = reinterpret_cast(input); @@ -310,7 +263,7 @@ namespace Stockfish::Eval::NNUE::Layers { // because then it is also an input dimension. if constexpr (OutputDimensions % OutputSimdWidth == 0) { - constexpr IndexType NumChunks = PaddedInputDimensions / 4; + constexpr IndexType NumChunks = InputDimensions / 4; const auto input32 = reinterpret_cast(input); vec_t* outptr = reinterpret_cast(output); @@ -329,8 +282,6 @@ namespace Stockfish::Eval::NNUE::Layers { for (int j = 0; j * OutputSimdWidth < OutputDimensions; ++j) vec_add_dpbusd_32x4(outptr[j], in0, col0[j], in1, col1[j], in2, col2[j], in3, col3[j]); } - for (int i = 0; i < canSaturate16.count; ++i) - output[canSaturate16.ids[i].out] += input[canSaturate16.ids[i].in] * canSaturate16.ids[i].w; } else if constexpr (OutputDimensions == 1) { @@ -377,17 +328,21 @@ namespace Stockfish::Eval::NNUE::Layers { auto output = reinterpret_cast(buffer); #if defined(USE_SSE2) - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + // At least a multiple of 16, with SSE2. + static_assert(InputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = InputDimensions / SimdWidth; const __m128i Zeros = _mm_setzero_si128(); const auto inputVector = reinterpret_cast(input); #elif defined(USE_MMX) - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + static_assert(InputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = InputDimensions / SimdWidth; const __m64 Zeros = _mm_setzero_si64(); const auto inputVector = reinterpret_cast(input); #elif defined(USE_NEON) - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + static_assert(InputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = InputDimensions / SimdWidth; const auto inputVector = reinterpret_cast(input); #endif @@ -473,25 +428,6 @@ namespace Stockfish::Eval::NNUE::Layers { alignas(CacheLineSize) BiasType biases[OutputDimensions]; alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; -#if defined (USE_SSSE3) - struct CanSaturate { - int count; - struct Entry { - uint32_t wIdx; - uint16_t out; - uint16_t in; - int8_t w; - } ids[PaddedInputDimensions * OutputDimensions * 3 / 4]; - - void add(int i, int j, int8_t w, uint32_t wIdx) { - ids[count].wIdx = wIdx; - ids[count].out = i; - ids[count].in = j; - ids[count].w = w; - ++count; - } - } canSaturate16; -#endif }; } // namespace Stockfish::Eval::NNUE::Layers diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index f1ac2dfe..65455df4 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -72,22 +72,42 @@ namespace Stockfish::Eval::NNUE::Layers { const auto output = reinterpret_cast(buffer); #if defined(USE_AVX2) - constexpr IndexType NumChunks = InputDimensions / SimdWidth; - const __m256i Zero = _mm256_setzero_si256(); - const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); - const auto in = reinterpret_cast(input); - const auto out = reinterpret_cast<__m256i*>(output); - for (IndexType i = 0; i < NumChunks; ++i) { - const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_load_si256(&in[i * 4 + 0]), - _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); - const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_load_si256(&in[i * 4 + 2]), - _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); - _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( - _mm256_packs_epi16(words0, words1), Zero), Offsets)); + if constexpr (InputDimensions % SimdWidth == 0) { + constexpr IndexType NumChunks = InputDimensions / SimdWidth; + const __m256i Zero = _mm256_setzero_si256(); + const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m256i*>(output); + for (IndexType i = 0; i < NumChunks; ++i) { + const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( + _mm256_load_si256(&in[i * 4 + 0]), + _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); + const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( + _mm256_load_si256(&in[i * 4 + 2]), + _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); + _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( + _mm256_packs_epi16(words0, words1), Zero), Offsets)); + } + } else { + constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); + const __m128i Zero = _mm_setzero_si128(); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m128i*>(output); + for (IndexType i = 0; i < NumChunks; ++i) { + const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 0]), + _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); + const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 2]), + _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); + const __m128i packedbytes = _mm_packs_epi16(words0, words1); + _mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero)); + } } - constexpr IndexType Start = NumChunks * SimdWidth; + constexpr IndexType Start = + InputDimensions % SimdWidth == 0 + ? InputDimensions / SimdWidth * SimdWidth + : InputDimensions / (SimdWidth / 2) * (SimdWidth / 2); #elif defined(USE_SSE2) constexpr IndexType NumChunks = InputDimensions / SimdWidth; diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 72a151f8..e24902c4 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -30,8 +30,8 @@ namespace Stockfish::Eval::NNUE { // Class that holds the result of affine transformation of input features struct alignas(CacheLineSize) Accumulator { - std::int16_t - accumulation[2][TransformedFeatureDimensions]; + std::int16_t accumulation[2][TransformedFeatureDimensions]; + std::int32_t psqtAccumulation[2][PSQTBuckets]; AccumulatorState state[2]; }; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 55a01fbe..879a39cd 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -23,7 +23,7 @@ #include "nnue_common.h" -#include "features/half_kp.h" +#include "features/half_ka_v2.h" #include "layers/input_slice.h" #include "layers/affine_transform.h" @@ -32,16 +32,18 @@ namespace Stockfish::Eval::NNUE { // Input features used in evaluation function - using FeatureSet = Features::HalfKP; + using FeatureSet = Features::HalfKAv2; // Number of input feature dimensions after conversion - constexpr IndexType TransformedFeatureDimensions = 256; + constexpr IndexType TransformedFeatureDimensions = 512; + constexpr IndexType PSQTBuckets = 8; + constexpr IndexType LayerStacks = 8; namespace Layers { // Define network structure using InputLayer = InputSlice; - using HiddenLayer1 = ClippedReLU>; + using HiddenLayer1 = ClippedReLU>; using HiddenLayer2 = ClippedReLU>; using OutputLayer = AffineTransform; diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index d41e0237..dc700061 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -46,7 +46,7 @@ namespace Stockfish::Eval::NNUE { // Version of the evaluation file - constexpr std::uint32_t Version = 0x7AF32F16u; + constexpr std::uint32_t Version = 0x7AF32F20u; // Constant used in evaluation value calculation constexpr int OutputScale = 16; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index a4a8e98f..2c0a0c6d 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -35,45 +35,82 @@ namespace Stockfish::Eval::NNUE { // vector registers. #define VECTOR + static_assert(PSQTBuckets == 8, "Assumed by the current choice of constants."); + #ifdef USE_AVX512 typedef __m512i vec_t; + typedef __m256i psqt_vec_t; #define vec_load(a) _mm512_load_si512(a) #define vec_store(a,b) _mm512_store_si512(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_load_psqt(a) _mm256_load_si256(a) + #define vec_store_psqt(a,b) _mm256_store_si256(a,b) + #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) + #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) + #define vec_zero_psqt() _mm256_setzero_si256() static constexpr IndexType NumRegs = 8; // only 8 are needed + static constexpr IndexType NumPsqtRegs = 1; #elif USE_AVX2 typedef __m256i vec_t; + typedef __m256i psqt_vec_t; #define vec_load(a) _mm256_load_si256(a) #define vec_store(a,b) _mm256_store_si256(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_load_psqt(a) _mm256_load_si256(a) + #define vec_store_psqt(a,b) _mm256_store_si256(a,b) + #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) + #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) + #define vec_zero_psqt() _mm256_setzero_si256() static constexpr IndexType NumRegs = 16; + static constexpr IndexType NumPsqtRegs = 1; #elif USE_SSE2 typedef __m128i vec_t; + typedef __m128i psqt_vec_t; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_epi16(a,b) #define vec_sub_16(a,b) _mm_sub_epi16(a,b) + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a,b) *(a)=(b) + #define vec_add_psqt_32(a,b) _mm_add_epi32(a,b) + #define vec_sub_psqt_32(a,b) _mm_sub_epi32(a,b) + #define vec_zero_psqt() _mm_setzero_si128() static constexpr IndexType NumRegs = Is64Bit ? 16 : 8; + static constexpr IndexType NumPsqtRegs = 2; #elif USE_MMX typedef __m64 vec_t; + typedef std::int32_t psqt_vec_t; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_pi16(a,b) #define vec_sub_16(a,b) _mm_sub_pi16(a,b) + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a,b) *(a)=(b) + #define vec_add_psqt_32(a,b) a+b + #define vec_sub_psqt_32(a,b) a-b + #define vec_zero_psqt() 0 static constexpr IndexType NumRegs = 8; + static constexpr IndexType NumPsqtRegs = 8; #elif USE_NEON typedef int16x8_t vec_t; + typedef int32x4_t psqt_vec_t; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) vaddq_s16(a,b) #define vec_sub_16(a,b) vsubq_s16(a,b) + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a,b) *(a)=(b) + #define vec_add_psqt_32(a,b) vaddq_s32(a,b) + #define vec_sub_psqt_32(a,b) vsubq_s32(a,b) + #define vec_zero_psqt() psqt_vec_t{0} static constexpr IndexType NumRegs = 16; + static constexpr IndexType NumPsqtRegs = 2; #else #undef VECTOR @@ -87,9 +124,13 @@ namespace Stockfish::Eval::NNUE { // Number of output dimensions for one side static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; + static constexpr int LazyThreshold = 1400; + #ifdef VECTOR static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; + static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4; static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions"); + static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets"); #endif public: @@ -115,6 +156,8 @@ namespace Stockfish::Eval::NNUE { biases[i] = read_little_endian(stream); for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) weights[i] = read_little_endian(stream); + for (std::size_t i = 0; i < PSQTBuckets * InputDimensions; ++i) + psqtWeights[i] = read_little_endian(stream); return !stream.fail(); } @@ -128,11 +171,21 @@ namespace Stockfish::Eval::NNUE { } // Convert input features - void transform(const Position& pos, OutputType* output) const { + std::pair transform(const Position& pos, OutputType* output, int bucket) const { update_accumulator(pos, WHITE); update_accumulator(pos, BLACK); + const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; const auto& accumulation = pos.state()->accumulator.accumulation; + const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation; + + const auto psqt = ( + psqtAccumulation[static_cast(perspectives[0])][bucket] + - psqtAccumulation[static_cast(perspectives[1])][bucket] + ) / 2; + + if (abs(psqt) > LazyThreshold * OutputScale) + return { psqt, true }; #if defined(USE_AVX512) constexpr IndexType NumChunks = HalfDimensions / (SimdWidth * 2); @@ -163,7 +216,6 @@ namespace Stockfish::Eval::NNUE { const int8x8_t Zero = {0}; #endif - const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; for (IndexType p = 0; p < 2; ++p) { const IndexType offset = HalfDimensions * p; @@ -240,6 +292,8 @@ namespace Stockfish::Eval::NNUE { #if defined(USE_MMX) _mm_empty(); #endif + + return { psqt, false }; } private: @@ -255,6 +309,7 @@ namespace Stockfish::Eval::NNUE { // Gcc-10.2 unnecessarily spills AVX2 registers if this array // is defined in the VECTOR code below, once in each branch vec_t acc[NumRegs]; + psqt_vec_t psqt[NumPsqtRegs]; #endif // Look for a usable accumulator of an earlier position. We keep track @@ -333,12 +388,52 @@ namespace Stockfish::Eval::NNUE { } } + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) + { + // Load accumulator + auto accTilePsqt = reinterpret_cast( + &st->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_load_psqt(&accTilePsqt[k]); + + for (IndexType i = 0; states_to_update[i]; ++i) + { + // Difference calculation for the deactivated features + for (const auto index : removed[i]) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); + } + + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + } + + // Store accumulator + accTilePsqt = reinterpret_cast( + &states_to_update[i]->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&accTilePsqt[k], psqt[k]); + } + } + #else for (IndexType i = 0; states_to_update[i]; ++i) { std::memcpy(states_to_update[i]->accumulator.accumulation[perspective], st->accumulator.accumulation[perspective], HalfDimensions * sizeof(BiasType)); + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + states_to_update[i]->accumulator.psqtAccumulation[perspective][k] = st->accumulator.psqtAccumulation[perspective][k]; + st = states_to_update[i]; // Difference calculation for the deactivated features @@ -348,6 +443,9 @@ namespace Stockfish::Eval::NNUE { for (IndexType j = 0; j < HalfDimensions; ++j) st->accumulator.accumulation[perspective][j] -= weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + st->accumulator.psqtAccumulation[perspective][k] -= psqtWeights[index * PSQTBuckets + k]; } // Difference calculation for the activated features @@ -357,6 +455,9 @@ namespace Stockfish::Eval::NNUE { for (IndexType j = 0; j < HalfDimensions; ++j) st->accumulator.accumulation[perspective][j] += weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + st->accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k]; } } #endif @@ -392,16 +493,42 @@ namespace Stockfish::Eval::NNUE { vec_store(&accTile[k], acc[k]); } + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) + { + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_zero_psqt(); + + for (const auto index : active) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + } + + auto accTilePsqt = reinterpret_cast( + &accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&accTilePsqt[k], psqt[k]); + } + #else std::memcpy(accumulator.accumulation[perspective], biases, HalfDimensions * sizeof(BiasType)); + for (std::size_t k = 0; k < PSQTBuckets; ++k) + accumulator.psqtAccumulation[perspective][k] = 0; + for (const auto index : active) { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) accumulator.accumulation[perspective][j] += weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k]; } #endif } @@ -413,9 +540,11 @@ namespace Stockfish::Eval::NNUE { using BiasType = std::int16_t; using WeightType = std::int16_t; + using PSQTWeightType = std::int32_t; alignas(CacheLineSize) BiasType biases[HalfDimensions]; alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions]; + alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets]; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/search.cpp b/src/search.cpp index 29b334ed..ac026a79 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -66,7 +66,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(234 * (d - improving)); + return Value(231 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -801,7 +801,7 @@ namespace { && (ss-1)->statScore < 24185 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 24 * depth - 34 * improving + 162 * ss->ttPv + 159 + && ss->staticEval >= beta - 22 * depth - 34 * improving + 162 * ss->ttPv + 159 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -1172,7 +1172,7 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4741; + - 4791; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) if (!ss->inCheck) From d37de3cb1de63da5b2f8e6978c45c5b36973063b Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 18 May 2021 10:02:20 +0300 Subject: [PATCH 049/282] Do more continuation history based pruning This patch increases lmrDepth threshold for continuation history based pruning in search. This part of code for a long time was known to be really TC sensitive - decreasing this threshold easily passed lower time controls but failed badly at LTC, on the other hand it increase was part of a tuning that resulted in being negative at STC but was +12 elo at 180+1.8. After recent simplification of special conditions that sometimes increase it from 4 to 5 it was logical to overall test at longer time controls if 5 is better than 4 with deeper searches. reduces strenght on STC https://tests.stockfishchess.org/tests/view/60a3a8bbce8ea25a3ef03c74 ELO: -2.57 +-2.0 (95%) LOS: 0.6% Total: 20000 W: 1820 L: 1968 D: 16212 Ptnml(0-2): 68, 1582, 6836, 1458, 56 Passed LTC with STC bounds https://tests.stockfishchess.org/tests/view/60a027395085663412d090ce LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 175256 W: 6774 L: 6548 D: 161934 Ptnml(0-2): 91, 5808, 75604, 6034, 91 Passed VLTC with LTC bounds https://tests.stockfishchess.org/tests/view/60a2bccce229097940a037a7 LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 65736 W: 1224 L: 1092 D: 63420 Ptnml(0-2): 5, 1012, 30706, 1136, 9 closes https://github.com/official-stockfish/Stockfish/pull/3473 bench 3689330 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ac026a79..8f8d42c0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1025,7 +1025,7 @@ moves_loop: // When in check, search starts from here else { // Continuation history based pruning (~20 Elo) - if ( lmrDepth < 4 + if ( lmrDepth < 5 && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) continue; From 0faf81d1f6b8ebe1a64482145a634d2e7d15db94 Mon Sep 17 00:00:00 2001 From: Yohaan Seth Nathan <73843275+TheYoBots@users.noreply.github.com> Date: Wed, 19 May 2021 00:22:59 +0530 Subject: [PATCH 050/282] Use Markdown syntax in the readme provide direct links to the mentioned files. closes https://github.com/official-stockfish/Stockfish/pull/3477 No Functional Change --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8d5ce8d0..0f6caec9 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,13 @@ intrinsics available on most CPUs (sse2, avx2, neon, or similar). This distribution of Stockfish consists of the following files: - * Readme.md, the file you are currently reading. + * [Readme.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md), the file you are currently reading. - * Copying.txt, a text file containing the GNU General Public License version 3. + * [Copying.txt](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt), a text file containing the GNU General Public License version 3. - * AUTHORS, a text file with the list of authors for the project + * [AUTHORS](https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS), a text file with the list of authors for the project - * src, a subdirectory containing the full source code, including a Makefile + * [src](https://github.com/official-stockfish/Stockfish/tree/master/src), a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. * a file with the .nnue extension, storing the neural network for the NNUE @@ -330,4 +330,4 @@ you are distributing. If you make any changes to the source code, these changes must also be made available under the GPL. For full details, read the copy of the GPL v3 found in the file named -*Copying.txt*. +[*Copying.txt*](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt). From 038487f95499665bf86ca5343d7a83f970d4b06e Mon Sep 17 00:00:00 2001 From: Fanael Linithien Date: Tue, 18 May 2021 19:17:59 +0200 Subject: [PATCH 051/282] Use packed 32-bit MMX operations for updating the PSQT accumulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the speed of NNUE by a bit on old hardware that code path is intended for, like a Pentium III 1.13 GHz: 10 repeats of "./stockfish bench 16 1 13 default depth NNUE": Before: 54 642 504 897 cycles (± 0.12%) 62 301 937 829 instructions (± 0.03%) After: 54 320 821 928 cycles (± 0.13%) 62 084 742 699 instructions (± 0.02%) Speed of go depth 20 from startpos: Before: 53103 nps After: 53856 nps closes https://github.com/official-stockfish/Stockfish/pull/3476 No functional change. --- src/nnue/nnue_feature_transformer.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2c0a0c6d..bfa2e25a 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -84,18 +84,18 @@ namespace Stockfish::Eval::NNUE { #elif USE_MMX typedef __m64 vec_t; - typedef std::int32_t psqt_vec_t; + typedef __m64 psqt_vec_t; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_pi16(a,b) #define vec_sub_16(a,b) _mm_sub_pi16(a,b) #define vec_load_psqt(a) (*(a)) #define vec_store_psqt(a,b) *(a)=(b) - #define vec_add_psqt_32(a,b) a+b - #define vec_sub_psqt_32(a,b) a-b - #define vec_zero_psqt() 0 + #define vec_add_psqt_32(a,b) _mm_add_pi32(a,b) + #define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b) + #define vec_zero_psqt() _mm_setzero_si64() static constexpr IndexType NumRegs = 8; - static constexpr IndexType NumPsqtRegs = 8; + static constexpr IndexType NumPsqtRegs = 4; #elif USE_NEON typedef int16x8_t vec_t; From 6b9a70ace8073f5ff4c50b4dd5ddc041cf9c819f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Prokop=20Rand=C3=A1=C4=8Dek?= Date: Wed, 12 May 2021 20:15:21 +0200 Subject: [PATCH 052/282] Use if instead of goto This PR inverts the if and removes goto in the generate_all function. closes https://github.com/official-stockfish/Stockfish/pull/3461 No functional change --- AUTHORS | 1 + src/movegen.cpp | 27 +++++++++++++-------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9042495f..7165363f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -175,6 +175,7 @@ Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) Steinar Gunderson (sesse) Stéphane Nicolet (snicolet) +Prokop Randáček (ProkopRandacek) Thanar2 thaspel theo77186 diff --git a/src/movegen.cpp b/src/movegen.cpp index be168450..bb81aeac 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -192,21 +192,20 @@ namespace { const Square ksq = pos.square(Us); Bitboard target; - if (Type == EVASIONS && more_than_one(pos.checkers())) - goto kingMoves; // Double check, only a king move can save the day + // Skip generating non-king moves when in double check + if (Type != EVASIONS || !more_than_one(pos.checkers())) + { + target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers())) + : Type == NON_EVASIONS ? ~pos.pieces( Us) + : Type == CAPTURES ? pos.pieces(~Us) + : ~pos.pieces( ); // QUIETS || QUIET_CHECKS - target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers())) - : Type == NON_EVASIONS ? ~pos.pieces( Us) - : Type == CAPTURES ? pos.pieces(~Us) - : ~pos.pieces( ); // QUIETS || QUIET_CHECKS - - moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - -kingMoves: + moveList = generate_pawn_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + } if (!Checks || pos.blockers_for_king(~Us) & ksq) { Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); From 2c3f7619f9ef267cbaec5216b71e0e435dc1393b Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 19 May 2021 20:57:04 +0300 Subject: [PATCH 053/282] Simplify usage of LMR for captures This patch simplifies a lot of "enablers" for LMR when move is a capture or promotion. After it we will have only 2 conditions - if node is a cutNode or if it's an allNode that was not in PV, so all captures or promotions wouldn't go thru LMR at any PVnodes. passed STC https://tests.stockfishchess.org/tests/view/60a40117ce8ea25a3ef03ca7 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 58976 W: 4875 L: 4807 D: 49294 Ptnml(0-2): 176, 3897, 21270, 3973, 172 passed LTC https://tests.stockfishchess.org/tests/view/60a43ff8ce8ea25a3ef03d18 LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 65272 W: 2203 L: 2165 D: 60904 Ptnml(0-2): 28, 1936, 28668, 1978, 26 closes https://github.com/official-stockfish/Stockfish/pull/3480 bench 4110764 --- src/search.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8f8d42c0..6e1d2b53 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1121,11 +1121,8 @@ moves_loop: // When in check, search starts from here if ( depth >= 3 && moveCount > 1 + 2 * rootNode && ( !captureOrPromotion - || moveCountPruning - || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || (!PvNode && !formerPv && captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 3678) - || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024) + || (!PvNode && !formerPv)) && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { Depth r = reduction(improving, depth, moveCount); From 754fc8a8b5ca7466926d54465eeb1df4d4a481ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 19 May 2021 01:24:51 +0200 Subject: [PATCH 054/282] Remove Tempo The Tempo variable was introduced 10 years ago in our search because the classical evaluation function was antisymmetrical in White and Black by design to gain speed: Eval(White to play) = -Eval(Black to play) Nowadays our neural networks know which side is to play in a position when they evaluate a position and are trained on real games, so the neural network encodes the advantage of moving as an output of search. This patch shows that the Tempo variable is not necessary anymore. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 33512 W: 2805 L: 2709 D: 27998 Ptnml(0-2): 80, 2209, 12095, 2279, 93 https://tests.stockfishchess.org/tests/view/60a44ceace8ea25a3ef03d30 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 53920 W: 1807 L: 1760 D: 50353 Ptnml(0-2): 16, 1617, 23650, 1658, 19 https://tests.stockfishchess.org/tests/view/60a477f0ce8ea25a3ef03d49 We also tried a match (20000 games) at STC using purely classical, result was neutral: https://tests.stockfishchess.org/tests/view/60a4eebcce8ea25a3ef03db5 Note: there are two locations left in search.cpp where we assume antisymmetry of evaluation (in relation with a speed optimization for null moves in lines 770 and 1439), but as the values are just used for heuristic pruning this approximation should not hurt too much because the order of magnitude is still true most of the time. closes https://github.com/official-stockfish/Stockfish/pull/3481 Bench: 4015864 --- src/evaluate.cpp | 4 ++-- src/search.cpp | 6 +++--- src/timeman.cpp | 8 -------- src/timeman.h | 1 - src/types.h | 1 - 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 256bd994..543644ee 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1058,7 +1058,7 @@ make_v: v = (v / 16) * 16; // Side to move point of view - v = (pos.side_to_move() == WHITE ? v : -v) + Tempo; + v = (pos.side_to_move() == WHITE ? v : -v); return v; } @@ -1119,7 +1119,7 @@ Value Eval::evaluate(const Position& pos) { int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE; + Value nnue = NNUE::evaluate(pos) * scale / 1024; if (pos.is_chess960()) nnue += fix_FRC(pos); diff --git a/src/search.cpp b/src/search.cpp index 6e1d2b53..359a774f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -767,7 +767,7 @@ namespace { if ((ss-1)->currentMove != MOVE_NULL) ss->staticEval = eval = evaluate(pos); else - ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo; + ss->staticEval = eval = -(ss-1)->staticEval; // Save static evaluation into transposition table tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); @@ -776,7 +776,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval - 2 * Tempo), -1000, 1000); + int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval), -1000, 1000); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } @@ -1436,7 +1436,7 @@ moves_loop: // When in check, search starts from here // and addition of two tempos ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) - : -(ss-1)->staticEval + 2 * Tempo; + : -(ss-1)->staticEval; // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) diff --git a/src/timeman.cpp b/src/timeman.cpp index 3236b6e9..f742d1e4 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -94,14 +94,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { optimumTime = TimePoint(optScale * timeLeft); maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); - if (Stockfish::Search::Limits.use_time_management()) - { - int strength = std::log( std::max(1, int(optimumTime * Threads.size() / 10))) * 60; - tempoNNUE = std::clamp( (strength + 264) / 24, 18, 30); - } - else - tempoNNUE = 28; // default for no time given - if (Options["Ponder"]) optimumTime += optimumTime / 4; } diff --git a/src/timeman.h b/src/timeman.h index 4ac0b4be..b1878d65 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -37,7 +37,6 @@ public: TimePoint(Threads.nodes_searched()) : now() - startTime; } int64_t availableNodes; // When in 'nodes as time' mode - int tempoNNUE; private: TimePoint startTime; diff --git a/src/types.h b/src/types.h index efebce1a..0bd4a1c4 100644 --- a/src/types.h +++ b/src/types.h @@ -191,7 +191,6 @@ enum Value : int { BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, QueenValueMg = 2538, QueenValueEg = 2682, - Tempo = 28, MidgameLimit = 15258, EndgameLimit = 3915 }; From f233ca1af4d36ded8ce924131f42bc4d0093ec6e Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Fri, 21 May 2021 20:22:29 +0200 Subject: [PATCH 055/282] Compact position structures Reorder the structures data members in position.h to reduce padding. Passed STC: https://tests.stockfishchess.org/tests/view/60a8011fce8ea25a3ef04069 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 14120 W: 1214 L: 1067 D: 11839 Ptnml(0-2): 26, 857, 5161, 976, 40 --- Also tested for speed locally by Joost: Result of 50 runs ================== base (./stockfish.master ) = 2254919 +/- 4439 test (./stockfish.patch ) = 2274003 +/- 5278 diff = +19084 +/- 6386 ================== speedup = +0.0085 P(speedup > 0) = 1.0000 --- closes https://github.com/official-stockfish/Stockfish/pull/3488 No functional change. --- src/position.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/position.h b/src/position.h index c226373b..e6b072bc 100644 --- a/src/position.h +++ b/src/position.h @@ -51,11 +51,11 @@ struct StateInfo { // Not copied when making a move (will be recomputed anyhow) Key key; Bitboard checkersBB; - Piece capturedPiece; StateInfo* previous; Bitboard blockersForKing[COLOR_NB]; Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; + Piece capturedPiece; int repetition; // Used by NNUE @@ -192,11 +192,11 @@ private: int castlingRightsMask[SQUARE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB]; + Thread* thisThread; + StateInfo* st; int gamePly; Color sideToMove; Score psq; - Thread* thisThread; - StateInfo* st; bool chess960; }; From fb2d175f97a12be6464fdcc20293682a192ca156 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 22 May 2021 07:00:53 +0200 Subject: [PATCH 056/282] Update default net to nn-7756374aaed3.nnue trained with pytorch using the master branch and recommended settings, same data set as previously used: python train.py ../../all_d9_fishd9_d8_d10_shuffle.binpack ../../all_d9_fishd9_d8_d10_shuffle.binpack \ --gpus 1 --threads 2 --num-workers 2 --batch-size 16384 --progress_bar_refresh_rate 300 \ --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 \ --max_epochs=400 --seed $RANDOM --default_root_dir exp/run_8 passed STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 21424 W: 2078 L: 1907 D: 17439 Ptnml(0-2): 80, 1512, 7385, 1627, 108 https://tests.stockfishchess.org/tests/view/60a6c749ce8ea25a3ef03f4d passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 67912 W: 2851 L: 2648 D: 62413 Ptnml(0-2): 40, 2348, 28984, 2537, 47 https://tests.stockfishchess.org/tests/view/60a722ecce8ea25a3ef03fb9 closes https://github.com/official-stockfish/Stockfish/pull/3489 Bench: 3779522 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index ee4c175b..40622e93 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-8a08400ed089.nnue" + #define EvalFileDefaultName "nn-7756374aaed3.nnue" namespace NNUE { From 49c79aa15ce5a0de54fe0f4cef1037751af3d7d1 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 22 May 2021 02:41:52 -0300 Subject: [PATCH 057/282] Simplify reduction for consecutive fails Revert the heuristic introduced in #3184, by which we reduced more the late sons of the root position after consecutive fail highs. --- Before new net architecture: STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 226336 W: 20373 L: 20500 D: 185463 Ptnml(0-2): 755, 16087, 79595, 15992, 739 https://tests.stockfishchess.org/tests/view/609dec205085663412d08e9d LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 67432 W: 2411 L: 2375 D: 62646 Ptnml(0-2): 33, 1944, 29714, 2004, 21 https://tests.stockfishchess.org/tests/view/609ee30f5085663412d08fc3 --- After new net architecture: STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 141752 W: 11591 L: 11617 D: 118544 Ptnml(0-2): 387, 9231, 51674, 9189, 395 https://tests.stockfishchess.org/tests/view/60a4320ace8ea25a3ef03cfd LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 294072 W: 9825 L: 9950 D: 274297 Ptnml(0-2): 121, 8610, 129681, 8521, 103 https://tests.stockfishchess.org/tests/view/60a51b5ece8ea25a3ef03dcd --- closes https://github.com/official-stockfish/Stockfish/pull/3490 Bench: 3752892 --- src/search.cpp | 6 +----- src/thread.h | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 359a774f..f8f956fa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -377,7 +377,7 @@ void Thread::search() { // Start with a small aspiration window and, in the case of a fail // high/low, re-search with a bigger window until we don't fail // high/low anymore. - failedHighCnt = 0; + int failedHighCnt = 0; while (true) { Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); @@ -1157,10 +1157,6 @@ moves_loop: // When in check, search starts from here if (ttCapture) r++; - // Increase reduction at root if failing high - if (rootNode) - r += thisThread->failedHighCnt * thisThread->failedHighCnt * moveCount / 512; - // Increase reduction for cut nodes (~3 Elo) if (cutNode) r += 2; diff --git a/src/thread.h b/src/thread.h index 5785fd25..ae662880 100644 --- a/src/thread.h +++ b/src/thread.h @@ -75,7 +75,6 @@ public: CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; Score contempt; - int failedHighCnt; }; From ff4c22238a199625cf7f02be1816b07fc49f5d45 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 22 May 2021 02:47:23 -0300 Subject: [PATCH 058/282] Tuning Search This patch tunes constant in search.cpp STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 30648 W: 2580 L: 2410 D: 25658 Ptnml(0-2): 80, 1969, 11093, 2065, 117 https://tests.stockfishchess.org/tests/view/60a71d3cce8ea25a3ef03fae LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 52896 W: 1776 L: 1617 D: 49503 Ptnml(0-2): 13, 1462, 23347, 1605, 21 https://tests.stockfishchess.org/tests/view/60a794ddce8ea25a3ef0400a closes https://github.com/official-stockfish/Stockfish/pull/3491 Bench: 4004731 --- src/search.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f8f956fa..143d0883 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -66,7 +66,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(231 * (d - improving)); + return Value(214 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -74,7 +74,7 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 503) / 1024 + (!i && r > 915); + return (r + 534) / 1024 + (!i && r > 904); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -83,7 +83,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 14 ? 66 : 6 * d * d + 231 * d - 206; + return d > 14 ? 73 : 6 * d * d + 229 * d - 215; } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -798,10 +798,10 @@ namespace { // Step 8. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 24185 + && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 22 * depth - 34 * improving + 162 * ss->ttPv + 159 + && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 159 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -809,7 +809,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (1062 + 68 * depth) / 256 + std::min(int(eval - beta) / 190, 3); + Depth R = (1090 + 81 * depth) / 256 + std::min(int(eval - beta) / 205, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -922,7 +922,7 @@ moves_loop: // When in check, search starts from here ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Step 11. A small Probcut idea, when we are in check - probCutBeta = beta + 400; + probCutBeta = beta + 409; if ( ss->inCheck && !PvNode && depth >= 4 @@ -1073,7 +1073,7 @@ moves_loop: // When in check, search starts from here { extension = 1; singularQuietLMR = !ttCapture; - if (!PvNode && value < singularBeta - 140) + if (!PvNode && value < singularBeta - 93) extension = 2; } @@ -1165,11 +1165,11 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4791; + - 4923; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) if (!ss->inCheck) - r -= ss->statScore / 14790; + r -= ss->statScore / 14721; } // In general we want to cap the LMR depth search at newDepth. But if From a2f01c07eb91524fc372bd82d6513ab058d3e043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 22 May 2021 19:44:15 +0200 Subject: [PATCH 059/282] Sometimes change the (materialist, positional) balance Our new nets output two values for the side to move in the last layer. We can interpret the first value as a material evaluation of the position, and the second one as the dynamic, positional value of the location of pieces. This patch changes the balance for the (materialist, positional) parts of the score from (128, 128) to (121, 135) when the piece material is equal between the two players, but keeps the standard (128, 128) balance when one player is at least an exchange up. Passed STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 15936 W: 1421 L: 1266 D: 13249 Ptnml(0-2): 37, 1037, 5694, 1134, 66 https://tests.stockfishchess.org/tests/view/60a82df9ce8ea25a3ef0408f Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 13904 W: 516 L: 410 D: 12978 Ptnml(0-2): 4, 374, 6088, 484, 2 https://tests.stockfishchess.org/tests/view/60a8bbf9ce8ea25a3ef04101 closes https://github.com/official-stockfish/Stockfish/pull/3492 Bench: 3856635 --- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 23 ++++++++++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 543644ee..c8094ca8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1119,7 +1119,7 @@ Value Eval::evaluate(const Position& pos) { int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos) * scale / 1024; + Value nnue = NNUE::evaluate(pos, true) * scale / 1024; if (pos.is_chess960()) nnue += fix_FRC(pos); diff --git a/src/evaluate.h b/src/evaluate.h index 40622e93..41aace67 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -43,7 +43,7 @@ namespace Eval { namespace NNUE { - Value evaluate(const Position& pos); + Value evaluate(const Position& pos, bool adjusted = false); bool load_eval(std::string name, std::istream& stream); bool save_eval(std::ostream& stream); void init(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 97cef814..cee77fe9 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -134,7 +134,7 @@ namespace Stockfish::Eval::NNUE { } // Evaluation function. Perform differential calculation. - Value evaluate(const Position& pos) { + Value evaluate(const Position& pos, bool adjusted) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -158,13 +158,26 @@ namespace Stockfish::Eval::NNUE { ASSERT_ALIGNED(buffer, alignment); const std::size_t bucket = (pos.count() - 1) / 4; - const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket); - if (lazy) { + + if (lazy) return static_cast(psqt / OutputScale); - } else { + else + { const auto output = network[bucket]->propagate(transformedFeatures, buffer); - return static_cast((output[0] + psqt) / OutputScale); + + int materialist = psqt; + int positional = output[0]; + + int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); + int entertainment = (adjusted && delta_npm <= BishopValueMg - KnightValueMg ? 7 : 0); + + int A = 128 - entertainment; + int B = 128 + entertainment; + + int sum = (A * materialist + B * positional) / 128; + + return static_cast( sum / OutputScale ); } } From e044068b435c0088171a2d02ad24a049354e6a5e Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 24 May 2021 08:15:04 -0300 Subject: [PATCH 060/282] Increased reduction for captures in LMR It now does, in LMR, an increased on reduction by 1 for captures in cut nodes. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 30656 W: 2565 L: 2397 D: 25694 Ptnml(0-2): 63, 2012, 11029, 2142, 82 https://tests.stockfishchess.org/tests/view/60a96733ce8ea25a3ef04178 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 124840 W: 4139 L: 3878 D: 116823 Ptnml(0-2): 48, 3480, 55100, 3747, 45 https://tests.stockfishchess.org/tests/view/60a995f5ce8ea25a3ef041b7 closes https://github.com/official-stockfish/Stockfish/pull/3494 bench: 3864295 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 143d0883..6cb42cc3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1151,16 +1151,16 @@ moves_loop: // When in check, search starts from here if (singularQuietLMR) r--; + // Increase reduction for cut nodes (~3 Elo) + if (cutNode) + r += 1 + !captureOrPromotion; + if (!captureOrPromotion) { // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) r++; - // Increase reduction for cut nodes (~3 Elo) - if (cutNode) - r += 2; - ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From 9d53129075177cb11b63b43236556051ba60f7dd Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 25 May 2021 13:09:40 +0200 Subject: [PATCH 061/282] Expose the lazy threshold for the feature transformer PSQT as a parameter. Definition of the lazy threshold moved to evaluate.cpp where all others are. Lazy threshold only used for real searches, not used for the "eval" call. This preserves the purity of NNUE evaluation, which is useful to verify consistency between the engine and the NNUE trainer. closes https://github.com/official-stockfish/Stockfish/pull/3499 No functional change --- src/evaluate.cpp | 13 +++++++------ src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 4 ++-- src/nnue/nnue_feature_transformer.h | 6 ++---- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c8094ca8..04d41d5f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -214,11 +214,12 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(1565); - constexpr Value LazyThreshold2 = Value(1102); - constexpr Value SpaceThreshold = Value(11551); - constexpr Value NNUEThreshold1 = Value(682); - constexpr Value NNUEThreshold2 = Value(176); + constexpr Value LazyThreshold1 = Value(1565); + constexpr Value LazyThreshold2 = Value(1102); + constexpr Value LazyThresholdNNUE = Value(1400); + constexpr Value SpaceThreshold = Value(11551); + constexpr Value NNUEThreshold1 = Value(682); + constexpr Value NNUEThreshold2 = Value(176); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -1119,7 +1120,7 @@ Value Eval::evaluate(const Position& pos) { int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos, true) * scale / 1024; + Value nnue = NNUE::evaluate(pos, true, LazyThresholdNNUE) * scale / 1024; if (pos.is_chess960()) nnue += fix_FRC(pos); diff --git a/src/evaluate.h b/src/evaluate.h index 41aace67..6bc1f0b3 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -43,7 +43,7 @@ namespace Eval { namespace NNUE { - Value evaluate(const Position& pos, bool adjusted = false); + Value evaluate(const Position& pos, bool adjusted = false, Value lazyThreshold = VALUE_INFINITE); bool load_eval(std::string name, std::istream& stream); bool save_eval(std::ostream& stream); void init(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index cee77fe9..99711cd5 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -134,7 +134,7 @@ namespace Stockfish::Eval::NNUE { } // Evaluation function. Perform differential calculation. - Value evaluate(const Position& pos, bool adjusted) { + Value evaluate(const Position& pos, bool adjusted, Value lazyThreshold) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -158,7 +158,7 @@ namespace Stockfish::Eval::NNUE { ASSERT_ALIGNED(buffer, alignment); const std::size_t bucket = (pos.count() - 1) / 4; - const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket); + const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket, lazyThreshold); if (lazy) return static_cast(psqt / OutputScale); diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index bfa2e25a..e81f54fa 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -124,8 +124,6 @@ namespace Stockfish::Eval::NNUE { // Number of output dimensions for one side static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; - static constexpr int LazyThreshold = 1400; - #ifdef VECTOR static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4; @@ -171,7 +169,7 @@ namespace Stockfish::Eval::NNUE { } // Convert input features - std::pair transform(const Position& pos, OutputType* output, int bucket) const { + std::pair transform(const Position& pos, OutputType* output, int bucket, Value lazyThreshold) const { update_accumulator(pos, WHITE); update_accumulator(pos, BLACK); @@ -184,7 +182,7 @@ namespace Stockfish::Eval::NNUE { - psqtAccumulation[static_cast(perspectives[1])][bucket] ) / 2; - if (abs(psqt) > LazyThreshold * OutputScale) + if (abs(psqt) > (int)lazyThreshold * OutputScale) return { psqt, true }; #if defined(USE_AVX512) From 83e0af288a8da69973721a8ae727d6c4a642e3f8 Mon Sep 17 00:00:00 2001 From: IIvec Date: Thu, 20 May 2021 20:02:32 +0200 Subject: [PATCH 062/282] Simplify the thread term for reduction formula Dependance on Threads.size() was removed Search::init() for the Reductions[] initialization. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 17376 W: 1024 L: 929 D: 15423 Ptnml(0-2): 24, 781, 6989, 864, 30 https://tests.stockfishchess.org/tests/view/60ac110812066fd2997957dc LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 145552 W: 3656 L: 3673 D: 138223 Ptnml(0-2): 37, 3351, 66014, 3340, 34 https://tests.stockfishchess.org/tests/view/60ac267412066fd299795825 closes https://github.com/official-stockfish/Stockfish/pull/3502 Bench 3864295 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6cb42cc3..c69b2408 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -152,7 +152,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((21.3 + 2 * std::log(Threads.size())) * std::log(i + 0.25 * std::log(i))); + Reductions[i] = int(21.3 * std::log(i + 0.25 * std::log(i))); } From 1b325bf86d02e02af8f693e7e1e70c8be5c7967b Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 26 May 2021 16:17:37 +0200 Subject: [PATCH 063/282] Less reduction for capture/promotions. Exclude captures/promotions at expected cut nodes (which also not a former PV node) from LMR if a response to the first previous opponent move. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 288656 W: 24886 L: 24413 D: 239357 Ptnml(0-2): 900, 19738, 102578, 20213, 899 https://tests.stockfishchess.org/tests/view/60ad505112066fd29979595b LTC: LLR: 2.97 (-2.94,2.94) <0.50,3.50> Total: 31344 W: 1107 L: 975 D: 29262 Ptnml(0-2): 12, 879, 13757, 1013, 11 https://tests.stockfishchess.org/tests/view/60adffce12066fd2997959d2 closes https://github.com/official-stockfish/Stockfish/pull/3500 Bench: 3827710 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c69b2408..9de8cc9c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1121,7 +1121,7 @@ moves_loop: // When in check, search starts from here if ( depth >= 3 && moveCount > 1 + 2 * rootNode && ( !captureOrPromotion - || cutNode + || (cutNode && (ss-1)->moveCount > 1) || (!PvNode && !formerPv)) && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { From f193778446acc6e60d7f0f99c6eb01489f89e962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 27 May 2021 01:10:00 +0200 Subject: [PATCH 064/282] Do not use lazy evaluation inside NNUE This simplification patch implements two changes: 1. it simplifies away the so-called "lazy" path in the NNUE evaluation internals, where we trusted the psqt head alone to avoid the costly "positional" head in some cases; 2. it raises a little bit the NNUEThreshold1 in evaluate.cpp (from 682 to 800), which increases the limit where we switched from NNUE eval to Classical eval. Both effects increase the number of positional evaluations done by our new net architecture, but the results of our tests below seem to indicate that the loss of speed will be compensated by the gain of eval quality. STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 26280 W: 2244 L: 2137 D: 21899 Ptnml(0-2): 72, 1755, 9405, 1810, 98 https://tests.stockfishchess.org/tests/view/60ae73f112066fd299795a51 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 20592 W: 750 L: 677 D: 19165 Ptnml(0-2): 9, 614, 8980, 681, 12 https://tests.stockfishchess.org/tests/view/60ae88e812066fd299795a82 closes https://github.com/official-stockfish/Stockfish/pull/3503 Bench: 3817907 --- src/evaluate.cpp | 10 ++++------ src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 28 +++++++++++----------------- src/nnue/nnue_feature_transformer.h | 7 ++----- 4 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 04d41d5f..40c41a86 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -216,9 +216,8 @@ namespace { // Threshold for lazy and space evaluation constexpr Value LazyThreshold1 = Value(1565); constexpr Value LazyThreshold2 = Value(1102); - constexpr Value LazyThresholdNNUE = Value(1400); constexpr Value SpaceThreshold = Value(11551); - constexpr Value NNUEThreshold1 = Value(682); + constexpr Value NNUEThreshold1 = Value(800); constexpr Value NNUEThreshold2 = Value(176); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -1120,7 +1119,7 @@ Value Eval::evaluate(const Position& pos) { int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos, true, LazyThresholdNNUE) * scale / 1024; + Value nnue = NNUE::evaluate(pos, true) * scale / 1024; if (pos.is_chess960()) nnue += fix_FRC(pos); @@ -1133,15 +1132,14 @@ Value Eval::evaluate(const Position& pos) { Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; - bool classical = largePsq; // Use classical evaluation for really low piece endgames. // One critical case is the draw for bishop + A/H file pawn vs naked king. bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg || (pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2); - v = classical || lowPieceEndgame ? Evaluation(pos).value() - : adjusted_NNUE(); + v = largePsq || lowPieceEndgame ? Evaluation(pos).value() // classical + : adjusted_NNUE(); // NNUE // If the classical eval is small and imbalance large, use NNUE nevertheless. // For the case of opposite colored bishops, switch to NNUE eval with small diff --git a/src/evaluate.h b/src/evaluate.h index 6bc1f0b3..41aace67 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -43,7 +43,7 @@ namespace Eval { namespace NNUE { - Value evaluate(const Position& pos, bool adjusted = false, Value lazyThreshold = VALUE_INFINITE); + Value evaluate(const Position& pos, bool adjusted = false); bool load_eval(std::string name, std::istream& stream); bool save_eval(std::ostream& stream); void init(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 99711cd5..4a3c206b 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -134,7 +134,7 @@ namespace Stockfish::Eval::NNUE { } // Evaluation function. Perform differential calculation. - Value evaluate(const Position& pos, bool adjusted, Value lazyThreshold) { + Value evaluate(const Position& pos, bool adjusted) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -158,27 +158,21 @@ namespace Stockfish::Eval::NNUE { ASSERT_ALIGNED(buffer, alignment); const std::size_t bucket = (pos.count() - 1) / 4; - const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket, lazyThreshold); + const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); + const auto output = network[bucket]->propagate(transformedFeatures, buffer); - if (lazy) - return static_cast(psqt / OutputScale); - else - { - const auto output = network[bucket]->propagate(transformedFeatures, buffer); + int materialist = psqt; + int positional = output[0]; - int materialist = psqt; - int positional = output[0]; + int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); + int entertainment = (adjusted && delta_npm <= BishopValueMg - KnightValueMg ? 7 : 0); - int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); - int entertainment = (adjusted && delta_npm <= BishopValueMg - KnightValueMg ? 7 : 0); + int A = 128 - entertainment; + int B = 128 + entertainment; - int A = 128 - entertainment; - int B = 128 + entertainment; + int sum = (A * materialist + B * positional) / 128; - int sum = (A * materialist + B * positional) / 128; - - return static_cast( sum / OutputScale ); - } + return static_cast( sum / OutputScale ); } // Load eval, from a file stream or a memory stream diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index e81f54fa..741d97cf 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -169,7 +169,7 @@ namespace Stockfish::Eval::NNUE { } // Convert input features - std::pair transform(const Position& pos, OutputType* output, int bucket, Value lazyThreshold) const { + std::int32_t transform(const Position& pos, OutputType* output, int bucket) const { update_accumulator(pos, WHITE); update_accumulator(pos, BLACK); @@ -182,9 +182,6 @@ namespace Stockfish::Eval::NNUE { - psqtAccumulation[static_cast(perspectives[1])][bucket] ) / 2; - if (abs(psqt) > (int)lazyThreshold * OutputScale) - return { psqt, true }; - #if defined(USE_AVX512) constexpr IndexType NumChunks = HalfDimensions / (SimdWidth * 2); static_assert(HalfDimensions % (SimdWidth * 2) == 0); @@ -291,7 +288,7 @@ namespace Stockfish::Eval::NNUE { _mm_empty(); #endif - return { psqt, false }; + return psqt; } private: From 6174a37a3742e318654cdbc5e8852fa37e797c92 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Thu, 27 May 2021 16:03:56 -0400 Subject: [PATCH 065/282] Remove Stat Reset at beta cutoff STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 63936 W: 5350 L: 5288 D: 53298 Ptnml(0-2): 184, 4295, 22954, 4345, 190 https://tests.stockfishchess.org/tests/view/60affb4c12066fd299795c64 LTC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 35856 W: 1201 L: 1142 D: 33513 Ptnml(0-2): 7, 1031, 15795, 1086, 9 https://tests.stockfishchess.org/tests/view/60b0537812066fd299795cc6 closes https://github.com/official-stockfish/Stockfish/pull/3505 bench: 3831936 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9de8cc9c..74a92bc6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1273,7 +1273,6 @@ moves_loop: // When in check, search starts from here else { assert(value >= beta); // Fail high - ss->statScore = 0; break; } } From 4c0299832555c3e35eb1f8779bedf7d1d2819cf1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 29 May 2021 15:44:57 +0200 Subject: [PATCH 066/282] Simplify NNUE / classical evaluation selection for the new network architecture these rules can be simplified, closer to the original PSQT difference based again. passed STC LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 22656 W: 1979 L: 1868 D: 18809 Ptnml(0-2): 70, 1496, 8087, 1603, 72 https://tests.stockfishchess.org/tests/view/60b24579db3c4776cb89d122 passed LTC LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 30224 W: 1015 L: 953 D: 28256 Ptnml(0-2): 4, 860, 13330, 906, 12 https://tests.stockfishchess.org/tests/view/60b27613db3c4776cb89d145 closes https://github.com/official-stockfish/Stockfish/pull/3511 Bench: 3937626 --- src/evaluate.cpp | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 40c41a86..0e66fea6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -218,7 +218,6 @@ namespace { constexpr Value LazyThreshold2 = Value(1102); constexpr Value SpaceThreshold = Value(11551); constexpr Value NNUEThreshold1 = Value(800); - constexpr Value NNUEThreshold2 = Value(176); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -1127,29 +1126,14 @@ Value Eval::evaluate(const Position& pos) { return nnue; }; - // If there is PSQ imbalance we use the classical eval. We also introduce - // a small probability of using the classical eval when PSQ imbalance is small. + // If there is PSQ imbalance we use the classical eval. Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; - // Use classical evaluation for really low piece endgames. - // One critical case is the draw for bishop + A/H file pawn vs naked king. - bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg - || (pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2); + v = largePsq ? Evaluation(pos).value() // classical + : adjusted_NNUE(); // NNUE - v = largePsq || lowPieceEndgame ? Evaluation(pos).value() // classical - : adjusted_NNUE(); // NNUE - - // If the classical eval is small and imbalance large, use NNUE nevertheless. - // For the case of opposite colored bishops, switch to NNUE eval with small - // probability if the classical eval is less than the threshold. - if ( largePsq - && !lowPieceEndgame - && ( abs(v) * 16 < NNUEThreshold2 * r50 - || ( pos.opposite_bishops() - && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50))) - v = adjusted_NNUE(); } // Damp down the evaluation linearly when shuffling From 5448cad29ee261706f6dcfd4a1fea779780eda33 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 29 May 2021 11:40:40 +0200 Subject: [PATCH 067/282] Fix export of the feature transformer. PSQT export was missing. fixes #3507 closes https://github.com/official-stockfish/Stockfish/pull/3508 No functional change --- src/nnue/nnue_feature_transformer.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 741d97cf..c249d3e7 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -165,6 +165,8 @@ namespace Stockfish::Eval::NNUE { write_little_endian(stream, biases[i]); for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) write_little_endian(stream, weights[i]); + for (std::size_t i = 0; i < PSQTBuckets * InputDimensions; ++i) + write_little_endian(stream, psqtWeights[i]); return !stream.fail(); } From e8418bb1b92800550264b9a4993f51208f8fe681 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Mon, 31 May 2021 06:47:35 +0200 Subject: [PATCH 068/282] Check Extension with Static Evaluation extension for checking moves, at higher depth and more decisive positions. stc: LLR: 2.97 (-2.94,2.94) <-0.50,2.50> Total: 87008 W: 7337 L: 7100 D: 72571 Ptnml(0-2): 264, 5737, 31270, 5964, 269 https://tests.stockfishchess.org/tests/view/60b1034787a1a67ae56c47b6 ltc: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 79320 W: 2629 L: 2432 D: 74259 Ptnml(0-2): 29, 2205, 35000, 2392, 34 https://tests.stockfishchess.org/tests/view/60b1ae0b87a1a67ae56c487c closes https://github.com/official-stockfish/Stockfish/pull/3514 Bench: 4447112 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 74a92bc6..2b4e4f1e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1097,6 +1097,10 @@ moves_loop: // When in check, search starts from here return beta; } } + else if ( givesCheck + && depth > 6 + && abs(ss->staticEval) > Value(100)) + extension = 1; // Add extension to new depth newDepth += extension; From 9fd5b44d60c53357bb76d4c51f20d3d5aa31ebe3 Mon Sep 17 00:00:00 2001 From: "J. Oster" Date: Mon, 31 May 2021 17:46:40 +0200 Subject: [PATCH 069/282] Pre-initialize ss->ply We pre-initialize ss->ply over the whole stack. There is no need to re-assign the same value(s) over and over again while searching. Probably a tiny speedup on longer searches. Tested for no regression: STC LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 25784 W: 2205 L: 2101 D: 21478 Ptnml(0-2): 62, 1660, 9368, 1716, 86 https://tests.stockfishchess.org/tests/view/60b516c6457376eb8bca9dfa LTC LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 26200 W: 944 L: 878 D: 24378 Ptnml(0-2): 12, 732, 11545, 800, 11 https://tests.stockfishchess.org/tests/view/60b53652457376eb8bca9e0e closes https://github.com/official-stockfish/Stockfish/pull/3516 No functional change. --- src/search.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2b4e4f1e..dac326e2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -253,7 +253,7 @@ void Thread::search() { // To allow access to (ss-7) up to (ss+2), the stack must be oversized. // The former is needed to allow update_continuation_histories(ss-1, ...), // which accesses its argument at ss-6, also near the root. - // The latter is needed for statScores and killer initialization. + // The latter is needed for statScore and killer initialization. Stack stack[MAX_PLY+10], *ss = stack+7; Move pv[MAX_PLY+1]; Value bestValue, alpha, beta, delta; @@ -268,6 +268,9 @@ void Thread::search() { for (int i = 7; i > 0; i--) (ss-i)->continuationHistory = &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel + for (int i = 0; i <= MAX_PLY + 2; ++i) + (ss+i)->ply = i; + ss->pv = pv; bestValue = delta = alpha = -VALUE_INFINITE; @@ -607,7 +610,6 @@ namespace { assert(0 <= ss->ply && ss->ply < MAX_PLY); - (ss+1)->ply = ss->ply + 1; (ss+1)->ttPv = false; (ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; @@ -1379,7 +1381,6 @@ moves_loop: // When in check, search starts from here } Thread* thisThread = pos.this_thread(); - (ss+1)->ply = ss->ply + 1; bestMove = MOVE_NONE; ss->inCheck = pos.checkers(); moveCount = 0; From 95f73ff393e5b87b2a3b4195d698b6620fd4a123 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 31 May 2021 20:26:05 +0200 Subject: [PATCH 070/282] Remove formerPV variable. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 75672 W: 6546 L: 6496 D: 62630 Ptnml(0-2): 238, 5274, 26761, 5326, 237 https://tests.stockfishchess.org/tests/view/60b349c0ec0c03148cbed055 LTC: LLR: 2.98 (-2.94,2.94) <-2.50,0.50> Total: 137816 W: 4676 L: 4689 D: 128451 Ptnml(0-2): 52, 4237, 60354, 4202, 63 https://tests.stockfishchess.org/tests/view/60b38970ec0c03148cbed075 closes https://github.com/official-stockfish/Stockfish/pull/3515 Bench: 4892288 --- src/search.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index dac326e2..2c7c6fd6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -564,7 +564,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool formerPv, givesCheck, improving, didLMR, priorCapture; + bool givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR; Piece movedPiece; @@ -634,7 +634,6 @@ namespace { : ss->ttHit ? tte->move() : MOVE_NONE; if (!excludedMove) ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); - formerPv = ss->ttPv && !PvNode; // Update low ply history for previous move if we are near root and position is or has been in PV if ( ss->ttPv @@ -1064,8 +1063,8 @@ moves_loop: // When in check, search starts from here && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2; - Depth singularDepth = (depth - 1 + 3 * formerPv) / 2; + Value singularBeta = ttValue - 2 * depth; + Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); @@ -1128,7 +1127,7 @@ moves_loop: // When in check, search starts from here && moveCount > 1 + 2 * rootNode && ( !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1) - || (!PvNode && !formerPv)) + || !ss->ttPv) && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { Depth r = reduction(improving, depth, moveCount); From 4ada291429df815558c99cac56fb47ff31a6c6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicolet?= Date: Wed, 2 Jun 2021 08:37:00 +0200 Subject: [PATCH 071/282] Typography change for bench --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0f6caec9..926d568d 100644 --- a/README.md +++ b/README.md @@ -143,9 +143,9 @@ For users, the following UCI options, which can typically be set via a GUI, are For developers the following non-standard commands might be of interest, mainly useful for debugging: - * #### bench ttSize threads limit fenFile limitType evalType - Performs a standard benchmark using various options. The signature or standard node - count is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. + * #### bench *ttSize threads limit fenFile limitType evalType* + Performs a standard benchmark using various options. The signature of a version (standard node + count) is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. * #### compiler Give information about the compiler and environment used for building a binary. From d53071eff4e75bc77dc86f65c52358d8014cb71c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 3 Jun 2021 13:53:28 +0200 Subject: [PATCH 072/282] Update default net to nn-7e66505906a6.nnue Trained with pytorch using the master branch and recommended settings, the data used is the previous 64B binpack enhanced with a 2B binpack generated using an opening book of positions for with the static eval is significantly different from d9 search. book : https://drive.google.com/file/d/1rHcKY5rv34kwku6g89OhnE8Bkfq3UWau/view?usp=sharing book generation: https://github.com/vondele/Stockfish/commit/3ce43ab0c4ce09c1fc5bca5ca27a248e67fddd24 binpack : https://drive.google.com/file/d/1rHcKY5rv34kwku6g89OhnE8Bkfq3UWau/view?usp=sharing ------- Data generation command: generate_training_data depth 9 count 31250000 random_multi_pv 2 random_multi_pv_diff 100 random_move_max_ply 8 random_move_count 3 set_recommended_uci_options eval_limit 32000 output_file_name output.binpack book wrongNNUE.epd seed ${RANDOM}${RANDOM} Training command: python train.py ../../all_d9_fishd9_d8_d10_wrong_shuffle.binpack ../../all_d9_fishd9_d8_d10_wrong_shuffle.binpack --gpus 1 --threads 2 --num-workers 2 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=400 --seed $RANDOM --default_root_dir exp/run_5 ------- passed STC: https://tests.stockfishchess.org/tests/view/60b7c79a457376eb8bcaa104 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 64592 W: 6254 L: 6028 D: 52310 Ptnml(0-2): 255, 4785, 22020, 4951, 285 passed LTC: https://tests.stockfishchess.org/tests/view/60b85307457376eb8bcaa182 LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 45560 W: 1998 L: 1826 D: 41736 Ptnml(0-2): 36, 1604, 19335, 1762, 43 closes https://github.com/official-stockfish/Stockfish/pull/3521 Bench: 4364128 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 41aace67..725d5a78 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-7756374aaed3.nnue" + #define EvalFileDefaultName "nn-7e66505906a6.nnue" namespace NNUE { From 9353e72103d4dc4880506c2f54b6426cbd182450 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 3 Jun 2021 19:46:55 +0100 Subject: [PATCH 073/282] Make extra time for bestMoveInstability dependent on rootdepth. This change allocates more base time to moves and makes the additional time added for best move instability dependent on rootdepth. STC 10+0.1 : LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 19432 W: 1711 L: 1553 D: 16168 Ptnml(0-2): 47, 1250, 6989, 1358, 72 https://tests.stockfishchess.org/tests/view/60b8cd41457376eb8bcaa1ad LTC 60+0.6 : LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 22480 W: 810 L: 693 D: 20977 Ptnml(0-2): 9, 603, 9902, 714, 12 https://tests.stockfishchess.org/tests/view/60b8e5bf457376eb8bcaa1e6 closes https://github.com/official-stockfish/Stockfish/pull/3526 Bench 4364128 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2c7c6fd6..100ddc71 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -480,8 +480,8 @@ void Thread::search() { totBestMoveChanges += th->bestMoveChanges; th->bestMoveChanges = 0; } - double bestMoveInstability = 1 + 2 * totBestMoveChanges / Threads.size(); - + double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) + * totBestMoveChanges / Threads.size(); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; // Cap used time in case of a single legal move for a better viewer experience in tournaments From 0b7cc8bd2f5a51de9f911fcdfb253b1c9f513897 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 3 Jun 2021 13:52:39 -0300 Subject: [PATCH 074/282] Introducing NodeType Root We transform rootNode into constexpr by adding a new NodeType `Root`, which causes a speed up. Local test: ``` Build Tester: 1.4.7.0 Windows 10 (Version 10.0, Build 0, 64-bit Edition) Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz SafeMode: No Running In VM: No HyperThreading Enabled: Yes CPU Warmup: Yes Command Line: bench Tests per Build: 25 ANOVA: n/a Engine# (NPS) Speedup Sp Conf. 95% S.S. patch (920.179,4) ---> master (906.329,2) ---> 1,528% 20.336,5 Yes No ``` --------- STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 98216 W: 8348 L: 8102 D: 81766 Ptnml(0-2): 295, 6357, 35549, 6621, 286 https://tests.stockfishchess.org/tests/view/60b797e2457376eb8bcaa0ab Yellow LTC: LLR: -2.95 (-2.94,2.94) <0.50,3.50> Total: 76936 W: 2651 L: 2626 D: 71659 Ptnml(0-2): 29, 2233, 33916, 2264, 26 https://tests.stockfishchess.org/tests/view/60b80d6d457376eb8bcaa145 closes https://github.com/official-stockfish/Stockfish/pull/3522 No functional change --- src/search.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 100ddc71..f634b2d2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -59,7 +59,7 @@ using namespace Search; namespace { // Different node types, used as a template parameter - enum NodeType { NonPV, PV }; + enum NodeType { NonPV, PV, Root }; constexpr uint64_t TtHitAverageWindow = 4096; constexpr uint64_t TtHitAverageResolution = 1024; @@ -102,10 +102,10 @@ namespace { Move best = MOVE_NONE; }; - template + template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); - template + template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0); Value value_to_tt(Value v, int ply); @@ -384,7 +384,7 @@ void Thread::search() { while (true) { Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); - bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); + bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the @@ -527,18 +527,18 @@ namespace { // search<>() is the main search function for both PV and non-PV nodes - template + template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { - constexpr bool PvNode = NT == PV; - const bool rootNode = PvNode && ss->ply == 0; + constexpr bool PvNode = nodeType != NonPV; + constexpr bool rootNode = nodeType == Root; const Depth maxNextDepth = rootNode ? depth : depth + 1; // Check if we have an upcoming move which draws by repetition, or // if the opponent had an alternative move earlier to this position. - if ( pos.rule50_count() >= 3 + if ( !rootNode + && pos.rule50_count() >= 3 && alpha < VALUE_DRAW - && !rootNode && pos.has_game_cycle(ss->ply)) { alpha = value_draw(pos.this_thread()); @@ -548,7 +548,7 @@ namespace { // Dive into quiescence search when the depth reaches zero if (depth <= 0) - return qsearch(pos, ss, alpha, beta); + return qsearch(pos, ss, alpha, beta); assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); @@ -1054,9 +1054,9 @@ moves_loop: // When in check, search starts from here // then that move is singular and should be extended. To verify this we do // a reduced search on all the other moves but the ttMove and if the // result is lower than ttValue minus a margin, then we will extend the ttMove. - if ( depth >= 7 + if ( !rootNode + && depth >= 7 && move == ttMove - && !rootNode && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ && abs(ttValue) < VALUE_KNOWN_WIN @@ -1351,10 +1351,11 @@ moves_loop: // When in check, search starts from here // qsearch() is the quiescence search function, which is called by the main search // function with zero depth, or recursively with further decreasing depth per call. - template + template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { - constexpr bool PvNode = NT == PV; + static_assert(nodeType != Root); + constexpr bool PvNode = nodeType == PV; assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); @@ -1532,7 +1533,7 @@ moves_loop: // When in check, search starts from here // Make and search the move pos.do_move(move, st, givesCheck); - value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); + value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); From 4445965f9714402050119f9e6a76c6a8fc4f8d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 2 Jun 2021 08:10:54 +0200 Subject: [PATCH 075/282] Makefile: better "make clean" for Windows Make clean should be really clean on Windows. Fixes issue https://github.com/official-stockfish/Stockfish/issues/3291 Closes https://github.com/official-stockfish/Stockfish/pull/3517 No functional change --- src/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile b/src/Makefile index 066e7697..bd75ec52 100644 --- a/src/Makefile +++ b/src/Makefile @@ -793,6 +793,9 @@ profileclean: @rm -rf profdir @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s @rm -f stockfish.profdata *.profraw + @rm -f stockfish.exe.lto_wrapper_args + @rm -f stockfish.exe.ltrans.out + @rm -f ./-lstdc++.res default: help From 8f081c86f7f8827ea35fc687e6f6591950cc8f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 4 Jun 2021 13:56:40 +0200 Subject: [PATCH 076/282] Clean SIMD code a bit Cleaner vector code structure in feature transformer. This patch just regroups the parts of the inner loop for each SIMD instruction set. Tested for non-regression: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 115760 W: 9835 L: 9831 D: 96094 Ptnml(0-2): 326, 7776, 41715, 7694, 369 https://tests.stockfishchess.org/tests/view/60b96b39457376eb8bcaa26e It would be nice if a future patch could use some of the macros at the top of the file to unify the code between the distincts SIMD instruction sets (of course, unifying the Relu will be the challenge). closes https://github.com/official-stockfish/Stockfish/pull/3506 No functional change --- src/nnue/nnue_feature_transformer.h | 194 ++++++++++++++++------------ 1 file changed, 110 insertions(+), 84 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index c249d3e7..10b226b3 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -180,118 +180,144 @@ namespace Stockfish::Eval::NNUE { const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation; const auto psqt = ( - psqtAccumulation[static_cast(perspectives[0])][bucket] - - psqtAccumulation[static_cast(perspectives[1])][bucket] + psqtAccumulation[perspectives[0]][bucket] + - psqtAccumulation[perspectives[1]][bucket] ) / 2; + #if defined(USE_AVX512) + constexpr IndexType NumChunks = HalfDimensions / (SimdWidth * 2); static_assert(HalfDimensions % (SimdWidth * 2) == 0); const __m512i Control = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); const __m512i Zero = _mm512_setzero_si512(); + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + auto out = reinterpret_cast<__m512i*>(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + __m512i sum0 = _mm512_load_si512(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 0]); + __m512i sum1 = _mm512_load_si512(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 1]); + + _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, + _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); + } + } + return psqt; + #elif defined(USE_AVX2) + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; constexpr int Control = 0b11011000; const __m256i Zero = _mm256_setzero_si256(); - #elif defined(USE_SSE2) - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + auto out = reinterpret_cast<__m256i*>(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + __m256i sum0 = _mm256_load_si256(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 0]); + __m256i sum1 = _mm256_load_si256(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 1]); - #ifdef USE_SSE41 + _mm256_store_si256(&out[j], _mm256_permute4x64_epi64( + _mm256_max_epi8(_mm256_packs_epi16(sum0, sum1), Zero), Control)); + } + } + return psqt; + + #elif defined(USE_SSE2) + + #ifdef USE_SSE41 + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; const __m128i Zero = _mm_setzero_si128(); - #else + #else + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; const __m128i k0x80s = _mm_set1_epi8(-128); - #endif + #endif + + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + auto out = reinterpret_cast<__m128i*>(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + __m128i sum0 = _mm_load_si128(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 0]); + __m128i sum1 = _mm_load_si128(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 1]); + const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); + + #ifdef USE_SSE41 + _mm_store_si128(&out[j], _mm_max_epi8(packedbytes, Zero)); + #else + _mm_store_si128(&out[j], _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)); + #endif + } + } + return psqt; #elif defined(USE_MMX) + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; const __m64 k0x80s = _mm_set1_pi8(-128); + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + auto out = reinterpret_cast<__m64*>(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + __m64 sum0 = *(&reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); + __m64 sum1 = *(&reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); + const __m64 packedbytes = _mm_packs_pi16(sum0, sum1); + out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); + } + } + _mm_empty(); + return psqt; + #elif defined(USE_NEON) + constexpr IndexType NumChunks = HalfDimensions / (SimdWidth / 2); const int8x8_t Zero = {0}; - #endif - - for (IndexType p = 0; p < 2; ++p) { - const IndexType offset = HalfDimensions * p; - - #if defined(USE_AVX512) - auto out = reinterpret_cast<__m512i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m512i sum0 = _mm512_load_si512( - &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); - __m512i sum1 = _mm512_load_si512( - &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); - _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, - _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); - } - - #elif defined(USE_AVX2) - auto out = reinterpret_cast<__m256i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m256i sum0 = _mm256_load_si256( - &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); - __m256i sum1 = _mm256_load_si256( - &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); - _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( - _mm256_packs_epi16(sum0, sum1), Zero), Control)); - } - - #elif defined(USE_SSE2) - auto out = reinterpret_cast<__m128i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m128i sum0 = _mm_load_si128(&reinterpret_cast( - accumulation[perspectives[p]])[j * 2 + 0]); - __m128i sum1 = _mm_load_si128(&reinterpret_cast( - accumulation[perspectives[p]])[j * 2 + 1]); - const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); - - _mm_store_si128(&out[j], - - #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, Zero) - #else - _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) - #endif - - ); - } - - #elif defined(USE_MMX) - auto out = reinterpret_cast<__m64*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m64 sum0 = *(&reinterpret_cast( - accumulation[perspectives[p]])[j * 2 + 0]); - __m64 sum1 = *(&reinterpret_cast( - accumulation[perspectives[p]])[j * 2 + 1]); - const __m64 packedbytes = _mm_packs_pi16(sum0, sum1); - out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); - } - - #elif defined(USE_NEON) - const auto out = reinterpret_cast(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - int16x8_t sum = reinterpret_cast( - accumulation[perspectives[p]])[j]; - out[j] = vmax_s8(vqmovn_s16(sum), Zero); - } - - #else - for (IndexType j = 0; j < HalfDimensions; ++j) { - BiasType sum = accumulation[static_cast(perspectives[p])][j]; - output[offset + j] = static_cast( - std::max(0, std::min(127, sum))); - } - #endif + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + const auto out = reinterpret_cast(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + int16x8_t sum = reinterpret_cast(accumulation[perspectives[p]])[j]; + out[j] = vmax_s8(vqmovn_s16(sum), Zero); + } } - #if defined(USE_MMX) - _mm_empty(); + return psqt; + + #else + + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + for (IndexType j = 0; j < HalfDimensions; ++j) + { + BiasType sum = accumulation[perspectives[p]][j]; + output[offset + j] = static_cast(std::max(0, std::min(127, sum))); + } + } + return psqt; + #endif - return psqt; - } + } // end of function transform() + + private: void update_accumulator(const Position& pos, const Color perspective) const { From 58307562b66597aeed9b5a7d76d2edeff53cf714 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Fri, 4 Jun 2021 12:20:27 +0200 Subject: [PATCH 077/282] Revert "Simplify En Passant" This reverts commit 9f8058bd26df1c3ca37b85f811026f1eb82e6524. Fixes the memory leak discussed in pull request #3523 https://github.com/official-stockfish/Stockfish/pull/3523 Passed non-regression STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 76184 W: 6330 L: 6282 D: 63572 Ptnml(0-2): 202, 5047, 27564, 5059, 220 https://tests.stockfishchess.org/tests/view/60ba146c457376eb8bcaa2e2 closes https://github.com/official-stockfish/Stockfish/pull/3527 Benched to verify there is no functional change. Bench: 4364128 --- src/position.cpp | 56 +++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index f1c36156..56cb34e8 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -251,8 +251,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th set_castling_right(c, rsq); } - set_state(st); - // 4. En passant square. // Ignore if square is invalid or not on side to move relative rank 6. bool enpassant = false; @@ -266,24 +264,12 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th // a) side to move have a pawn threatening epSquare // b) there is an enemy pawn in front of epSquare // c) there is no piece on epSquare or behind epSquare - // d) enemy pawn didn't block a check of its own color by moving forward enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN) && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))) - && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))) - && ( file_of(square(sideToMove)) == file_of(st->epSquare) - || !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove)))); + && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))); } - // It's necessary for st->previous to be intialized in this way because legality check relies on its existence - if (enpassant) { - st->previous = new StateInfo(); - remove_piece(st->epSquare - pawn_push(sideToMove)); - st->previous->checkersBB = attackers_to(square(~sideToMove)) & pieces(sideToMove); - st->previous->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), st->previous->pinners[BLACK]); - st->previous->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), st->previous->pinners[WHITE]); - put_piece(make_piece(~sideToMove, PAWN), st->epSquare - pawn_push(sideToMove)); - } - else + if (!enpassant) st->epSquare = SQ_NONE; // 5-6. Halfmove clock and fullmove number @@ -295,6 +281,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th chess960 = isChess960; thisThread = th; + set_state(st); st->accumulator.state[WHITE] = Eval::NNUE::INIT; st->accumulator.state[BLACK] = Eval::NNUE::INIT; @@ -517,11 +504,23 @@ bool Position::legal(Move m) const { assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); - // st->previous->blockersForKing consider capsq as empty. - // If pinned, it has to move along the king ray. + // En passant captures are a tricky special case. Because they are rather + // uncommon, we do it simply by testing whether the king is attacked after + // the move is made. if (type_of(m) == EN_PASSANT) - return !(st->previous->blockersForKing[sideToMove] & from) - || aligned(from, to, square(us)); + { + Square ksq = square(us); + Square capsq = to - pawn_push(us); + Bitboard occupied = (pieces() ^ from ^ capsq) | to; + + assert(to == ep_square()); + assert(moved_piece(m) == make_piece(us, PAWN)); + assert(piece_on(capsq) == make_piece(~us, PAWN)); + assert(piece_on(to) == NO_PIECE); + + return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK)) + && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); + } // Castling moves generation does not check if the castling path is clear of // enemy attacks, it is delayed at a later time: now! @@ -654,15 +653,18 @@ bool Position::gives_check(Move m) const { case PROMOTION: return attacks_bb(promotion_type(m), to, pieces() ^ from) & square(~sideToMove); - // The double-pushed pawn blocked a check? En Passant will remove the blocker. - // The only discovery check that wasn't handle is through capsq and fromsq - // So the King must be in the same rank as fromsq to consider this possibility. - // st->previous->blockersForKing consider capsq as empty. + // En passant capture with check? We have already handled the case + // of direct checks and ordinary discovered check, so the only case we + // need to handle is the unusual case of a discovered check through + // the captured pawn. case EN_PASSANT: - return st->previous->checkersBB - || ( rank_of(square(~sideToMove)) == rank_of(from) - && st->previous->blockersForKing[~sideToMove] & from); + { + Square capsq = make_square(file_of(to), rank_of(from)); + Bitboard b = (pieces() ^ from ^ capsq) | to; + return (attacks_bb< ROOK>(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) + | (attacks_bb(square(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP)); + } default: //CASTLING { // Castling is encoded as 'king captures the rook' From 98cbaa6c6b07ea594501ed7a53ea83feb7c8ea9c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 3 Jun 2021 19:18:24 +0200 Subject: [PATCH 078/282] Enhance CI to error on leaks Add flags to valgrind in our Continuous Integration scripts, to error on memory leaks. closes https://github.com/official-stockfish/Stockfish/pull/3525 No functional change. --- tests/instrumented.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/instrumented.sh b/tests/instrumented.sh index bfb50e94..d30c8e35 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -13,7 +13,7 @@ case $1 in --valgrind) echo "valgrind testing started" prefix='' - exeprefix='valgrind --error-exitcode=42' + exeprefix='valgrind --error-exitcode=42 --errors-for-leak-kinds=all --leak-check=full' postfix='1>/dev/null' threads="1" ;; From 3802cdf9b69dd0a8256dbed7c25141ac5757d151 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Thu, 3 Jun 2021 16:46:05 +0200 Subject: [PATCH 079/282] Makefile: Extend sanitize support Enable compiling with multiple sanitizers at once. Syntax: make build ARCH=x86-64-avx512 debug=on sanitize="address undefined" closes https://github.com/official-stockfish/Stockfish/pull/3524 No functional change. --- src/Makefile | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Makefile b/src/Makefile index bd75ec52..33a270fe 100644 --- a/src/Makefile +++ b/src/Makefile @@ -61,9 +61,11 @@ endif # ---------------------------------------------------------------------------- # # debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode -# sanitize = undefined/thread/no (-fsanitize ) +# sanitize = none/ ... (-fsanitize ) # --- ( undefined ) --- enable undefined behavior checks -# --- ( thread ) --- enable threading error checks +# --- ( thread ) --- enable threading error checks +# --- ( address ) --- enable memory access checks +# --- ...etc... --- see compiler documentation for supported sanitizers # optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations # arch = (name) --- (-arch) --- Target architecture # bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system @@ -84,6 +86,10 @@ endif # Note that Makefile is space sensitive, so when adding new architectures # or modifying existing flags, you have to make sure there are no extra spaces # at the end of the line for flag values. +# +# Example of use for these flags: +# make build ARCH=x86-64-avx512 debug=on sanitize="address undefined" + ### 2.1. General and architecture defaults @@ -105,7 +111,7 @@ endif optimize = yes debug = no -sanitize = no +sanitize = none bits = 64 prefetch = no popcnt = no @@ -473,9 +479,9 @@ else endif ### 3.2.2 Debugging with undefined behavior sanitizers -ifneq ($(sanitize),no) - CXXFLAGS += -g3 -fsanitize=$(sanitize) - LDFLAGS += -fsanitize=$(sanitize) +ifneq ($(sanitize),none) + CXXFLAGS += -g3 $(addprefix -fsanitize=,$(sanitize)) + LDFLAGS += $(addprefix -fsanitize=,$(sanitize)) endif ### 3.3 Optimization @@ -838,7 +844,6 @@ config-sanity: net @echo "Testing config sanity. If this fails, try 'make help' ..." @echo "" @test "$(debug)" = "yes" || test "$(debug)" = "no" - @test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no" @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(SUPPORTED_ARCH)" = "true" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ From 999e142c548f61852ace7b8f025d71bc8a805e3c Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sun, 6 Jun 2021 13:31:57 -0300 Subject: [PATCH 080/282] Reduce in LMR reduction on PvNode reduce reduction in LMR by 1 on PvNode. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 266080 W: 22438 L: 21996 D: 221646 Ptnml(0-2): 774, 17874, 95376, 18168, 848 https://tests.stockfishchess.org/tests/view/60bc0661457376eb8bcaa4bb LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 20144 W: 698 L: 587 D: 18859 Ptnml(0-2): 2, 529, 8906, 626, 9 https://tests.stockfishchess.org/tests/view/60bcc3f2457376eb8bcaa58d closes https://github.com/official-stockfish/Stockfish/pull/3534 bench: 5173012 --- src/search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index f634b2d2..67f259fc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1132,6 +1132,9 @@ moves_loop: // When in check, search starts from here { Depth r = reduction(improving, depth, moveCount); + if (PvNode) + r--; + // Decrease reduction if the ttHit running average is large (~0 Elo) if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; From 785b70809783430ff1e0bf856dac3b9bfa6fe826 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 7 Jun 2021 15:47:37 -0300 Subject: [PATCH 081/282] Simplify promotion move generator This patch removes Knight promotion checks from Captures. As a consequence, it also removes this underpromotion from qsearch. STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 37776 W: 3113 L: 3023 D: 31640 Ptnml(0-2): 103, 2419, 13755, 2507, 104 https://tests.stockfishchess.org/tests/view/60be6a06457376eb8bcaa775 LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 39760 W: 1257 L: 1203 D: 37300 Ptnml(0-2): 11, 1079, 17646, 1133, 11 https://tests.stockfishchess.org/tests/view/60beb972457376eb8bcaa7c5 closes https://github.com/official-stockfish/Stockfish/pull/3536 Bench: 5530620 --- src/movegen.cpp | 24 ++++++++++-------------- src/search.cpp | 2 +- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index bb81aeac..5f3ba90a 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -26,21 +26,16 @@ namespace Stockfish { namespace { template - ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { + ExtMove* make_promotions(ExtMove* moveList, Square to) { if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) - { *moveList++ = make(to - D, to, QUEEN); - if (attacks_bb(to) & ksq) - *moveList++ = make(to - D, to, KNIGHT); - } if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { *moveList++ = make(to - D, to, ROOK); *moveList++ = make(to - D, to, BISHOP); - if (!(attacks_bb(to) & ksq)) - *moveList++ = make(to - D, to, KNIGHT); + *moveList++ = make(to - D, to, KNIGHT); } return moveList; @@ -57,7 +52,6 @@ namespace { constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); - const Square ksq = pos.square(Them); const Bitboard emptySquares = Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces(); const Bitboard enemies = Type == EVASIONS ? pos.checkers() : Type == CAPTURES ? target : pos.pieces(Them); @@ -82,6 +76,7 @@ namespace { // To make a quiet check, you either make a direct check by pushing a pawn // or push a blocker pawn that is not on the same file as the enemy king. // Discovered check promotion has been already generated amongst the captures. + Square ksq = pos.square(Them); Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq); b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns); b2 &= pawn_attacks_bb(Them, ksq) | shift(dcCandidatePawns); @@ -111,13 +106,13 @@ namespace { b3 &= target; while (b1) - moveList = make_promotions(moveList, pop_lsb(b1), ksq); + moveList = make_promotions(moveList, pop_lsb(b1)); while (b2) - moveList = make_promotions(moveList, pop_lsb(b2), ksq); + moveList = make_promotions(moveList, pop_lsb(b2)); while (b3) - moveList = make_promotions(moveList, pop_lsb(b3), ksq); + moveList = make_promotions(moveList, pop_lsb(b3)); } // Standard and en passant captures @@ -206,6 +201,7 @@ namespace { moveList = generate_moves(pos, moveList, target); moveList = generate_moves(pos, moveList, target); } + if (!Checks || pos.blockers_for_king(~Us) & ksq) { Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); @@ -227,10 +223,10 @@ namespace { } // namespace -/// Generates all pseudo-legal captures plus queen and checking knight promotions -/// Generates all pseudo-legal non-captures and underpromotions (except checking knight) +/// Generates all pseudo-legal captures plus queen promotions +/// Generates all pseudo-legal non-captures and underpromotions /// Generates all pseudo-legal check evasions when the side to move is in check -/// Generates all pseudo-legal non-captures giving check, except castling +/// Generates all pseudo-legal non-captures giving check, except castling and promotions /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. diff --git a/src/search.cpp b/src/search.cpp index 67f259fc..5306bb9a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1464,7 +1464,7 @@ moves_loop: // When in check, search starts from here // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, - // queen and checking knight promotions, and other checks(only if depth >= DEPTH_QS_CHECKS) + // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS) // will be generated. MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, From 559942d64de9497c9d6d13b22307b4b9e1ce8ecf Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 9 Jun 2021 23:23:13 +0200 Subject: [PATCH 082/282] Limit double extensions Double extensions can lead to search explosions, for specific positions. Currently, however, these double extensions are worth about 10Elo and cannot be removed. This patch instead limits the number of double extensions given to a maximum of 3. This fixes https://github.com/official-stockfish/Stockfish/issues/3532 where the following testcase was shown to be problematic: ``` uci setoption name Hash value 4 setoption name Contempt value 0 ucinewgame position fen 8/Pk6/8/1p6/8/P1K5/8/6B1 w - - 37 130 go depth 20 ``` passed STC: https://tests.stockfishchess.org/tests/view/60c13161457376eb8bcaaa0f LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 73256 W: 6114 L: 6062 D: 61080 Ptnml(0-2): 222, 4912, 26306, 4968, 220 passed LTC: https://tests.stockfishchess.org/tests/view/60c196fb457376eb8bcaaa6b LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 166440 W: 5559 L: 5594 D: 155287 Ptnml(0-2): 106, 4921, 73197, 4894, 102 closes https://github.com/official-stockfish/Stockfish/pull/3544 Bench: 5067605 --- src/search.cpp | 14 ++++++++++---- src/search.h | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5306bb9a..d04898e8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -610,10 +610,11 @@ namespace { assert(0 <= ss->ply && ss->ply < MAX_PLY); - (ss+1)->ttPv = false; + (ss+1)->ttPv = false; (ss+1)->excludedMove = bestMove = MOVE_NONE; - (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; - Square prevSq = to_sq((ss-1)->currentMove); + (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + ss->doubleExtensions = (ss-1)->doubleExtensions; + Square prevSq = to_sq((ss-1)->currentMove); // Initialize statScore to zero for the grandchildren of the current position. // So statScore is shared between all grandchildren and only the first grandchild @@ -1074,7 +1075,11 @@ moves_loop: // When in check, search starts from here { extension = 1; singularQuietLMR = !ttCapture; - if (!PvNode && value < singularBeta - 93) + + // Avoid search explosion by limiting the number of double extensions to at most 3 + if ( !PvNode + && value < singularBeta - 93 + && ss->doubleExtensions < 3) extension = 2; } @@ -1105,6 +1110,7 @@ moves_loop: // When in check, search starts from here // Add extension to new depth newDepth += extension; + ss->doubleExtensions = (ss-1)->doubleExtensions + (extension == 2); // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); diff --git a/src/search.h b/src/search.h index 811b2e2a..801baacc 100644 --- a/src/search.h +++ b/src/search.h @@ -52,6 +52,7 @@ struct Stack { bool inCheck; bool ttPv; bool ttHit; + int doubleExtensions; }; From b84fa04db6ea5fc6d7d714539c11537bde64538b Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Wed, 9 Jun 2021 11:21:55 +0200 Subject: [PATCH 083/282] Read NNUE net faster Load feature transformer weights in bulk on little-endian machines. This is in particular useful to test new nets with c-chess-cli, see https://github.com/lucasart/c-chess-cli/issues/44 ``` $ time ./stockfish.exe uci Before : 0m0.914s After : 0m0.483s ``` No functional change --- src/misc.h | 13 +++-- src/nnue/nnue_common.h | 80 ++++++++++++++++++++++------- src/nnue/nnue_feature_transformer.h | 24 ++++----- src/syzygy/tbprobe.cpp | 3 -- 4 files changed, 81 insertions(+), 39 deletions(-) diff --git a/src/misc.h b/src/misc.h index 59ca6e37..dae37cd9 100644 --- a/src/misc.h +++ b/src/misc.h @@ -66,9 +66,10 @@ std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK -// `ptr` must point to an array of size at least -// `sizeof(T) * N + alignment` bytes, where `N` is the -// number of elements in the array. + +// align_ptr_up() : get the first aligned element of an array. +// ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, +// where N is the number of elements in the array. template T* align_ptr_up(T* ptr) { @@ -78,6 +79,12 @@ T* align_ptr_up(T* ptr) return reinterpret_cast(reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); } + +// IsLittleEndian : true if and only if the binary is compiled on a little endian machine +static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; +static inline const bool IsLittleEndian = (Le.c[0] == 4); + + template class ValueListInserter { public: diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index dc700061..390f61c3 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -24,6 +24,8 @@ #include #include +#include "../misc.h" // for IsLittleEndian + #if defined(USE_AVX2) #include @@ -86,37 +88,77 @@ namespace Stockfish::Eval::NNUE { // necessary to return a result with the byte ordering of the compiling machine. template inline IntType read_little_endian(std::istream& stream) { - IntType result; - std::uint8_t u[sizeof(IntType)]; - typename std::make_unsigned::type v = 0; - stream.read(reinterpret_cast(u), sizeof(IntType)); - for (std::size_t i = 0; i < sizeof(IntType); ++i) - v = (v << 8) | u[sizeof(IntType) - i - 1]; + if (IsLittleEndian) + stream.read(reinterpret_cast(&result), sizeof(IntType)); + else + { + std::uint8_t u[sizeof(IntType)]; + typename std::make_unsigned::type v = 0; + + stream.read(reinterpret_cast(u), sizeof(IntType)); + for (std::size_t i = 0; i < sizeof(IntType); ++i) + v = (v << 8) | u[sizeof(IntType) - i - 1]; + + std::memcpy(&result, &v, sizeof(IntType)); + } - std::memcpy(&result, &v, sizeof(IntType)); return result; } + // write_little_endian() is our utility to write an integer (signed or unsigned, any size) + // to a stream in little-endian order. We swap the byte order before the write if + // necessary to always write in little endian order, independantly of the byte + // ordering of the compiling machine. template inline void write_little_endian(std::ostream& stream, IntType value) { - std::uint8_t u[sizeof(IntType)]; - typename std::make_unsigned::type v = value; + if (IsLittleEndian) + stream.write(reinterpret_cast(&value), sizeof(IntType)); + else + { + std::uint8_t u[sizeof(IntType)]; + typename std::make_unsigned::type v = value; - std::size_t i = 0; - // if constexpr to silence the warning about shift by 8 - if constexpr (sizeof(IntType) > 1) { - for (; i + 1 < sizeof(IntType); ++i) { - u[i] = v; - v >>= 8; - } + std::size_t i = 0; + // if constexpr to silence the warning about shift by 8 + if constexpr (sizeof(IntType) > 1) + { + for (; i + 1 < sizeof(IntType); ++i) + { + u[i] = v; + v >>= 8; + } + } + u[i] = v; + + stream.write(reinterpret_cast(u), sizeof(IntType)); } - u[i] = v; - - stream.write(reinterpret_cast(u), sizeof(IntType)); } + + // read_little_endian(s, out, N) : read integers in bulk from a little indian stream. + // This reads N integers from stream s and put them in array out. + template + inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) { + if (IsLittleEndian) + stream.read(reinterpret_cast(out), sizeof(IntType) * count); + else + for (std::size_t i = 0; i < count; ++i) + out[i] = read_little_endian(stream); + } + + // write_little_endian(s, out, N) : write integers in bulk to a little indian stream. + // This takes N integers from array values and writes them on stream s. + template + inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { + if (IsLittleEndian) + stream.write(reinterpret_cast(values), sizeof(IntType) * count); + else + for (std::size_t i = 0; i < count; ++i) + write_little_endian(stream, values[i]); + } + } // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 10b226b3..300ce367 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -24,8 +24,6 @@ #include "nnue_common.h" #include "nnue_architecture.h" -#include "../misc.h" - #include // std::memset() namespace Stockfish::Eval::NNUE { @@ -150,23 +148,21 @@ namespace Stockfish::Eval::NNUE { // Read network parameters bool read_parameters(std::istream& stream) { - for (std::size_t i = 0; i < HalfDimensions; ++i) - biases[i] = read_little_endian(stream); - for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) - weights[i] = read_little_endian(stream); - for (std::size_t i = 0; i < PSQTBuckets * InputDimensions; ++i) - psqtWeights[i] = read_little_endian(stream); + + read_little_endian(stream, biases , HalfDimensions ); + read_little_endian(stream, weights , HalfDimensions * InputDimensions); + read_little_endian(stream, psqtWeights, PSQTBuckets * InputDimensions); + return !stream.fail(); } // Write network parameters bool write_parameters(std::ostream& stream) const { - for (std::size_t i = 0; i < HalfDimensions; ++i) - write_little_endian(stream, biases[i]); - for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) - write_little_endian(stream, weights[i]); - for (std::size_t i = 0; i < PSQTBuckets * InputDimensions; ++i) - write_little_endian(stream, psqtWeights[i]); + + write_little_endian(stream, biases , HalfDimensions ); + write_little_endian(stream, weights , HalfDimensions * InputDimensions); + write_little_endian(stream, psqtWeights, PSQTBuckets * InputDimensions); + return !stream.fail(); } diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 831c8259..a0ac727f 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -105,9 +105,6 @@ template<> inline void swap_endian(uint8_t&) {} template T number(void* addr) { - static const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; - static const bool IsLittleEndian = (Le.c[0] == 4); - T v; if ((uintptr_t)addr & (alignof(T) - 1)) // Unaligned pointer (very rare) From 7819412002374fac5ebf7bec1e49a25481f1c42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 13 Jun 2021 09:59:34 +0200 Subject: [PATCH 084/282] Clarify use of UCI options Update README.md to clarify use of UCI options closes https://github.com/official-stockfish/Stockfish/pull/3540 No functional change --- README.md | 12 +++++++----- src/nnue/nnue_common.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 926d568d..f4ee2e34 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,14 @@ This distribution of Stockfish consists of the following files: ## The UCI protocol and available options -The Universal Chess Interface (UCI) is a standard protocol used to communicate with a chess engine, -and is the recommended way to do so for typical graphical user interfaces (GUI) or chess tools. +The Universal Chess Interface (UCI) is a standard protocol used to communicate with +a chess engine, and is the recommended way to do so for typical graphical user interfaces +(GUI) or chess tools. Stockfish implements the majority of it options as described +in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip). -Stockfish implements most commands as described in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip) - -For users, the following UCI options, which can typically be set via a GUI, are available in Stockfish: +Developers can see the default values for UCI options available in Stockfish by typing +`./stockfish uci` in a terminal, but the majority of users will typically see them and +change them via a chess GUI. This is a list of available UCI options in Stockfish: * #### Threads The number of CPU threads used for searching a position. For best performance, set diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 390f61c3..75ac7862 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -148,7 +148,7 @@ namespace Stockfish::Eval::NNUE { out[i] = read_little_endian(stream); } - // write_little_endian(s, out, N) : write integers in bulk to a little indian stream. + // write_little_endian(s, values, N) : write integers in bulk to a little indian stream. // This takes N integers from array values and writes them on stream s. template inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { From e1f181ee643dcaa92c606b74b3abd23dede136cd Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 29 May 2021 06:39:14 +0300 Subject: [PATCH 085/282] Do less LMR extensions This patch restricts LMR extensions (of non-transposition table moves) from being used when the transposition table move was extended by two plies via singular extension. This may serve to limit search explosions in certain positions. This makes a lot of sense because the precondition for the tt-move to have been singular extended by two plies is that the result of the alternate search (with excluded the tt-move) has been a hard fail low: it is natural to later search less for non tt-moves in this situation. The current state of depth/extensions/reductions management is getting quite tricky in our search algo, see https://github.com/official-stockfish/Stockfish/pull/3546#issuecomment-860174549 for some discussion. Suggestions welcome! Passed STC https://tests.stockfishchess.org/tests/view/60c3f293457376eb8bcaac8d LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 117984 W: 9698 L: 9430 D: 98856 Ptnml(0-2): 315, 7708, 42703, 7926, 340 passed LTC https://tests.stockfishchess.org/tests/view/60c46ea5457376eb8bcaacc7 LLR: 2.97 (-2.94,2.94) <0.50,3.50> Total: 11280 W: 401 L: 302 D: 10577 Ptnml(0-2): 2, 271, 4998, 364, 5 closes https://github.com/official-stockfish/Stockfish/pull/3546 Bench: 4709974 --- src/search.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d04898e8..b8756d38 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -954,6 +954,7 @@ moves_loop: // When in check, search starts from here value = bestValue; singularQuietLMR = moveCountPruning = false; + bool doubleExtension = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal or greater than the current depth, and the result of this search was a fail low. @@ -1080,7 +1081,10 @@ moves_loop: // When in check, search starts from here if ( !PvNode && value < singularBeta - 93 && ss->doubleExtensions < 3) + { extension = 2; + doubleExtension = true; + } } // Multi-cut pruning @@ -1188,8 +1192,8 @@ moves_loop: // When in check, search starts from here // In general we want to cap the LMR depth search at newDepth. But if // reductions are really negative and movecount is low, we allow this move - // to be searched deeper than the first move. - Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && moveCount <= 5)); + // to be searched deeper than the first move, unless ttMove was extended by 2. + Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && moveCount <= 5 && !doubleExtension)); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); From ce4c523ad3defce2d19342d2dd1b43858ec33819 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 10 Jun 2021 17:43:42 +0200 Subject: [PATCH 086/282] Register count for feature transformer Compute optimal register count for feature transformer accumulation dynamically. This also introduces a change where AVX512 would only use 8 registers instead of 16 (now possible due to a 2x increase in feature transformer size). closes https://github.com/official-stockfish/Stockfish/pull/3543 No functional change --- src/nnue/nnue_feature_transformer.h | 74 +++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 300ce367..ab05f884 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -28,12 +28,17 @@ namespace Stockfish::Eval::NNUE { + using BiasType = std::int16_t; + using WeightType = std::int16_t; + using PSQTWeightType = std::int32_t; + // If vector instructions are enabled, we update and refresh the // accumulator tile by tile such that each tile fits in the CPU's // vector registers. #define VECTOR - static_assert(PSQTBuckets == 8, "Assumed by the current choice of constants."); + static_assert(PSQTBuckets % 8 == 0, + "Per feature PSQT values cannot be processed at granularity lower than 8 at a time."); #ifdef USE_AVX512 typedef __m512i vec_t; @@ -47,8 +52,7 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) #define vec_zero_psqt() _mm256_setzero_si256() - static constexpr IndexType NumRegs = 8; // only 8 are needed - static constexpr IndexType NumPsqtRegs = 1; + #define NumRegistersSIMD 32 #elif USE_AVX2 typedef __m256i vec_t; @@ -62,8 +66,7 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) #define vec_zero_psqt() _mm256_setzero_si256() - static constexpr IndexType NumRegs = 16; - static constexpr IndexType NumPsqtRegs = 1; + #define NumRegistersSIMD 16 #elif USE_SSE2 typedef __m128i vec_t; @@ -77,8 +80,7 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) _mm_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm_sub_epi32(a,b) #define vec_zero_psqt() _mm_setzero_si128() - static constexpr IndexType NumRegs = Is64Bit ? 16 : 8; - static constexpr IndexType NumPsqtRegs = 2; + #define NumRegistersSIMD (Is64Bit ? 16 : 8) #elif USE_MMX typedef __m64 vec_t; @@ -92,8 +94,7 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) _mm_add_pi32(a,b) #define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b) #define vec_zero_psqt() _mm_setzero_si64() - static constexpr IndexType NumRegs = 8; - static constexpr IndexType NumPsqtRegs = 4; + #define NumRegistersSIMD 8 #elif USE_NEON typedef int16x8_t vec_t; @@ -107,14 +108,61 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) vaddq_s32(a,b) #define vec_sub_psqt_32(a,b) vsubq_s32(a,b) #define vec_zero_psqt() psqt_vec_t{0} - static constexpr IndexType NumRegs = 16; - static constexpr IndexType NumPsqtRegs = 2; + #define NumRegistersSIMD 16 #else #undef VECTOR #endif + + #ifdef VECTOR + + // Compute optimal SIMD register count for feature transformer accumulation. + + // We use __m* types as template arguments, which causes GCC to emit warnings + // about losing some attribute information. This is irrelevant to us as we + // only take their size, so the following pragma are harmless. + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wignored-attributes" + + template + static constexpr int BestRegisterCount() + { + #define RegisterSize sizeof(SIMDRegisterType) + #define LaneSize sizeof(LaneType) + + static_assert(RegisterSize >= LaneSize); + static_assert(MaxRegisters <= NumRegistersSIMD); + static_assert(MaxRegisters > 0); + static_assert(NumRegistersSIMD > 0); + static_assert(RegisterSize % LaneSize == 0); + static_assert((NumLanes * LaneSize) % RegisterSize == 0); + + const int ideal = (NumLanes * LaneSize) / RegisterSize; + if (ideal <= MaxRegisters) + return ideal; + + // Look for the largest divisor of the ideal register count that is smaller than MaxRegisters + for (int divisor = MaxRegisters; divisor > 1; --divisor) + if (ideal % divisor == 0) + return divisor; + + return 1; + } + + static constexpr int NumRegs = BestRegisterCount(); + static constexpr int NumPsqtRegs = BestRegisterCount(); + + #pragma GCC diagnostic pop + + #endif + + + // Input feature converter class FeatureTransformer { @@ -557,10 +605,6 @@ namespace Stockfish::Eval::NNUE { #endif } - using BiasType = std::int16_t; - using WeightType = std::int16_t; - using PSQTWeightType = std::int32_t; - alignas(CacheLineSize) BiasType biases[HalfDimensions]; alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions]; alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets]; From f8c779dbe538315aa6f65556d0acf11640558504 Mon Sep 17 00:00:00 2001 From: JWmer Date: Sun, 13 Jun 2021 23:48:32 +0200 Subject: [PATCH 087/282] Update default net to nn-8e47cf062333.nnue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This net is the result of training on data used by the Leela project. More precisely, we shuffled T60 and T74 data kindly provided by borg (for different Tnn, the data is a result of Leela selfplay with differently sized Leela nets). The data is available at vondele's google drive: https://drive.google.com/drive/folders/1mftuzYdl9o6tBaceR3d_VBQIrgKJsFpl. The Leela data comes in small chunks of .binpack files. To shuffle them, we simply used a small python script to randomly rename the files, and then concatenated them using `cat`. As validation data we picked a file of T60 data. We will further investigate T74 data. The training for the NNUE architecture used 200 epochs with the Python trainer from the Stockfish project. Unlike the previous run we tried with this data, this run does not have adjusted scaling — not because we didn't want to, but because we forgot. However, this training randomly skips 40% more positions than previous run. The loss was very spiky and decreased slower than it does usually. Training loss: https://github.com/official-stockfish/images/blob/main/training-loss-8e47cf062333.png Validation loss: https://github.com/official-stockfish/images/blob/main/validation-loss-8e47cf062333.png This is the exact training command: python train.py --smart-fen-skipping --random-fen-skipping 14 --batch-size 16384 --threads 4 --num-workers 4 --gpus 1 trainingdata\training_data.binpack validationdata\val.binpack --- 10k STC result: ELO: 3.61 +-3.3 (95%) LOS: 98.4% Total: 10000 W: 1241 L: 1137 D: 7622 Ptnml(0-2): 68, 841, 3086, 929, 76 https://tests.stockfishchess.org/tests/view/60c67e50457376eb8bcaae70 10k LTC result: ELO: 2.71 +-2.4 (95%) LOS: 98.8% Total: 10000 W: 659 L: 581 D: 8760 Ptnml(0-2): 22, 485, 3900, 579, 14 https://tests.stockfishchess.org/tests/view/60c69deb457376eb8bcaae98 Passed LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 9648 W: 685 L: 545 D: 8418 Ptnml(0-2): 22, 448, 3740, 596, 18 https://tests.stockfishchess.org/tests/view/60c6d41c457376eb8bcaaecf --- closes https://github.com/official-stockfish/Stockfish/pull/3550 Bench: 4877339 --- AUTHORS | 3 ++- src/evaluate.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7165363f..7e63591a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of May 17, 2021 +# List of authors for Stockfish, as of June 14, 2021 # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) @@ -96,6 +96,7 @@ Joost VandeVondele (vondele) Jörg Oster (joergoster) Joseph Ellis (jhellis3) Joseph R. Prostko +Julian Willemer (NightlyKing) jundery Justin Blanchard (UncombedCoconut) Kelly Wilson diff --git a/src/evaluate.h b/src/evaluate.h index 725d5a78..c19b14ba 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-7e66505906a6.nnue" + #define EvalFileDefaultName "nn-8e47cf062333.nnue" namespace NNUE { From 900f249f596417b35129f1dc357cd5392018e575 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 12 Jun 2021 20:45:14 +0200 Subject: [PATCH 088/282] Reduce the number of accumulator states Reduce from 3 to 2. Make the intent of the states clearer. STC: https://tests.stockfishchess.org/tests/view/60c50111457376eb8bcaad03 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 61888 W: 5007 L: 4944 D: 51937 Ptnml(0-2): 164, 3947, 22649, 4030, 154 LTC: https://tests.stockfishchess.org/tests/view/60c52b1c457376eb8bcaad2c LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 20248 W: 688 L: 618 D: 18942 Ptnml(0-2): 7, 551, 8946, 605, 15 closes https://github.com/official-stockfish/Stockfish/pull/3548 No functional change. --- src/nnue/nnue_accumulator.h | 5 +---- src/nnue/nnue_feature_transformer.h | 10 +++++----- src/position.cpp | 10 ++++------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index e24902c4..d41ecf95 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -25,14 +25,11 @@ namespace Stockfish::Eval::NNUE { - // The accumulator of a StateInfo without parent is set to the INIT state - enum AccumulatorState { EMPTY, COMPUTED, INIT }; - // Class that holds the result of affine transformation of input features struct alignas(CacheLineSize) Accumulator { std::int16_t accumulation[2][TransformedFeatureDimensions]; std::int32_t psqtAccumulation[2][PSQTBuckets]; - AccumulatorState state[2]; + bool computed[2]; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index ab05f884..59a965ac 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -383,7 +383,7 @@ namespace Stockfish::Eval::NNUE { // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; int gain = FeatureSet::refresh_cost(pos); - while (st->accumulator.state[perspective] == EMPTY) + while (st->previous && !st->accumulator.computed[perspective]) { // This governs when a full feature refresh is needed and how many // updates are better than just one full refresh. @@ -394,7 +394,7 @@ namespace Stockfish::Eval::NNUE { st = st->previous; } - if (st->accumulator.state[perspective] == COMPUTED) + if (st->accumulator.computed[perspective]) { if (next == nullptr) return; @@ -412,8 +412,8 @@ namespace Stockfish::Eval::NNUE { ksq, st2, perspective, removed[1], added[1]); // Mark the accumulators as computed. - next->accumulator.state[perspective] = COMPUTED; - pos.state()->accumulator.state[perspective] = COMPUTED; + next->accumulator.computed[perspective] = true; + pos.state()->accumulator.computed[perspective] = true; // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. StateInfo *states_to_update[3] = @@ -533,7 +533,7 @@ namespace Stockfish::Eval::NNUE { { // Refresh the accumulator auto& accumulator = pos.state()->accumulator; - accumulator.state[perspective] = COMPUTED; + accumulator.computed[perspective] = true; IndexList active; FeatureSet::append_active_indices(pos, perspective, active); diff --git a/src/position.cpp b/src/position.cpp index 56cb34e8..ba015d3c 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -282,8 +282,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th chess960 = isChess960; thisThread = th; set_state(st); - st->accumulator.state[WHITE] = Eval::NNUE::INIT; - st->accumulator.state[BLACK] = Eval::NNUE::INIT; assert(pos_is_ok()); @@ -704,8 +702,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ++st->pliesFromNull; // Used by NNUE - st->accumulator.state[WHITE] = Eval::NNUE::EMPTY; - st->accumulator.state[BLACK] = Eval::NNUE::EMPTY; + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; auto& dp = st->dirtyPiece; dp.dirty_num = 1; @@ -1005,8 +1003,8 @@ void Position::do_null_move(StateInfo& newSt) { st->dirtyPiece.dirty_num = 0; st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() - st->accumulator.state[WHITE] = Eval::NNUE::EMPTY; - st->accumulator.state[BLACK] = Eval::NNUE::EMPTY; + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; if (st->epSquare != SQ_NONE) { From 4c4e104cadceae678668f1738b5ca6296b2b4ef8 Mon Sep 17 00:00:00 2001 From: "J. Oster" Date: Mon, 14 Jun 2021 17:28:30 +0200 Subject: [PATCH 089/282] Fix a rare case of wrong TB ranking of a root move leading to a 3-fold repetition. With this small fix a draw ranking and thus a draw score is being applied. This works for both, ranking by dtz or wdl tables. Fixes https://github.com/official-stockfish/Stockfish/issues/3542 (No functional change without TBs.) Bench: 4877339 --- src/syzygy/tbprobe.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index a0ac727f..96b2970f 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1536,6 +1536,14 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { WDLScore wdl = -probe_wdl(pos, &result); dtz = dtz_before_zeroing(wdl); } + else if (pos.is_draw(1)) + { + // In case a root move leads to a draw by repetition or + // 50-move rule, we set dtz to zero. Note: since we are + // only 1 ply from the root, this must be a true 3-fold + // repetition inside the game history. + dtz = 0; + } else { // Otherwise, take dtz for the new position and correct by 1 ply @@ -1586,6 +1594,7 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { ProbeState result; StateInfo st; + WDLScore wdl; bool rule50 = Options["Syzygy50MoveRule"]; @@ -1594,7 +1603,10 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { { pos.do_move(m.pv[0], st); - WDLScore wdl = -probe_wdl(pos, &result); + if (pos.is_draw(1)) + wdl = WDLDraw; + else + wdl = -probe_wdl(pos, &result); pos.undo_move(m.pv[0]); From 8ec9e108664ce38fa98ccfb69f048d7d804f99f9 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 15 Jun 2021 12:49:23 +0200 Subject: [PATCH 090/282] New default net nn-33c9d39e5eb6.nnue As the previous net, this net is trained on Leela games as provided by borg. See also https://lczero.org/blog/2021/06/the-importance-of-open-data/ The particular data set, which is a mix of T60 and T74 data, is now available as a single binpack: https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing The training command was: python train.py ../../training_data_pylon.binpack ../../training_data_pylon.binpack --gpus 1 --threads 2 --num-workers 2 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 10 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed $RANDOM --default_root_dir exp/run_2 passed STC: https://tests.stockfishchess.org/tests/view/60c887cb457376eb8bcab054 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 12792 W: 1483 L: 1311 D: 9998 Ptnml(0-2): 62, 989, 4131, 1143, 71 passed LTC: https://tests.stockfishchess.org/tests/view/60c8e5c4457376eb8bcab0f0 LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 11272 W: 601 L: 477 D: 10194 Ptnml(0-2): 9, 421, 4657, 535, 14 also had strong LTC performance against another strong net of the series: https://tests.stockfishchess.org/tests/view/60c8c40d457376eb8bcab0c6 closes https://github.com/official-stockfish/Stockfish/pull/3557 Bench: 5032320 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index c19b14ba..ef0b1040 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-8e47cf062333.nnue" + #define EvalFileDefaultName "nn-33c9d39e5eb6.nnue" namespace NNUE { From 68bf362ea2385a641be9f5ed9ce2acdf55a1ecf1 Mon Sep 17 00:00:00 2001 From: proukornew Date: Fri, 14 May 2021 00:49:28 +0300 Subject: [PATCH 091/282] Fix for Cygwin's environment build-profile The Cygwin environment has two g++ compilers, each with a different problem for compiling Stockfish at the moment: (a) g++.exe : full posix build compiler, linked to cygwin dll. => This one has a problem embedding the net. (b) x86_64-w64-mingw32-g++.exe : native Windows build compiler. => This one manages to embed the net, but has a problem related to libgcov when we use the profile-build target of Stockfish. This patch solves the problem for compiler (b), so that our recommended command line if you want to build an optimized version of Stockfish on Cygwin becomes something like the following (you can change the ARCH value to whatever you want, but note the COMP and CXX variables pointing at the right compiler): ``` make -j profile-build ARCH=x86-64-modern COMP=mingw CXX=x86_64-w64-mingw32-c++.exe ``` closes https://github.com/official-stockfish/Stockfish/pull/3463 No functional change --- AUTHORS | 1 + src/Makefile | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7e63591a..1334844f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,6 +23,7 @@ Alfredo Menezes (lonfom169) Ali AlZhrani (Cooffe) Andrew Grant (AndyGrant) Andrey Neporada (nepal) +Andreï Vetrov (proukornew) Andy Duplain Antoine Champion (antoinechampion) Aram Tumanian (atumanian) diff --git a/src/Makefile b/src/Makefile index 33a270fe..9508e18b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -884,13 +884,13 @@ clang-profile-use: gcc-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-generate' \ + EXTRACXXFLAGS='-fprofile-generate="./"' \ EXTRALDFLAGS='-lgcov' \ all gcc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-use -fno-peel-loops -fno-tracer' \ + EXTRACXXFLAGS='-fprofile-use="./" -fno-peel-loops -fno-tracer' \ EXTRALDFLAGS='-lgcov' \ all From 55e69dc88d84b627af938fe2a1ee69326db055d2 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 15 Jun 2021 20:56:09 -0300 Subject: [PATCH 092/282] Simplify reduction when best move doesn't change frequently. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 40400 W: 3468 L: 3377 D: 33555 Ptnml(0-2): 134, 2734, 14388, 2795, 149 https://tests.stockfishchess.org/tests/view/60c93e5a457376eb8bcab15f LTC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 34200 W: 1190 L: 1128 D: 31882 Ptnml(0-2): 22, 998, 15001, 1054, 25 https://tests.stockfishchess.org/tests/view/60c96a1a457376eb8bcab180 closes https://github.com/official-stockfish/Stockfish/pull/3559 bench: 5629669 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b8756d38..9bba563a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1157,7 +1157,6 @@ moves_loop: // When in check, search starts from here // Increase reduction at root and non-PV nodes when the best move does not change frequently if ( (rootNode || !PvNode) - && thisThread->rootDepth > 10 && thisThread->bestMoveChanges <= 2) r++; From 07c84480342ec156b1cbb651966521349ccfa6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 17 Jun 2021 18:09:42 +0200 Subject: [PATCH 093/282] Revert "Fix for Cygwin's environment build-profile" This reverts commit "Fix for Cygwin's environment build-profile", as it was giving errors for "make clean" on some Windows environments. See comments in https://github.com/official-stockfish/Stockfish/commit/68bf362ea2385a641be9f5ed9ce2acdf55a1ecf1 Possibly somebody can propose a solution that would fix Cygwin builds and not break on other system too, stay tuned! :-) No functional change --- AUTHORS | 1 - src/Makefile | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1334844f..7e63591a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,7 +23,6 @@ Alfredo Menezes (lonfom169) Ali AlZhrani (Cooffe) Andrew Grant (AndyGrant) Andrey Neporada (nepal) -Andreï Vetrov (proukornew) Andy Duplain Antoine Champion (antoinechampion) Aram Tumanian (atumanian) diff --git a/src/Makefile b/src/Makefile index 9508e18b..33a270fe 100644 --- a/src/Makefile +++ b/src/Makefile @@ -884,13 +884,13 @@ clang-profile-use: gcc-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-generate="./"' \ + EXTRACXXFLAGS='-fprofile-generate' \ EXTRALDFLAGS='-lgcov' \ all gcc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-use="./" -fno-peel-loops -fno-tracer' \ + EXTRACXXFLAGS='-fprofile-use -fno-peel-loops -fno-tracer' \ EXTRALDFLAGS='-lgcov' \ all From 14b673d90f7f486becda4622eb1e8f4b63bb0785 Mon Sep 17 00:00:00 2001 From: ap Date: Fri, 18 Jun 2021 01:43:58 +0200 Subject: [PATCH 094/282] New default net nn-3b20abec10c1.nnue This net was created by @pleomati, who manually edited with an hex editor 10 values randomly chosen in the LCSFNet10 net (nn-6ad41a9207d0.nnue) to create this one. The LCSFNet10 net was trained by Joost VandeVondele from a dataset combining Stockfish games and Leela games (16x10^9 positions from SF self-play at depth 9, and 6.3x10^9 positions from Leela games, so overall 72% of Stockfish positions and 28% of Leela positions). passed STC 10+0.1: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 50888 W: 5881 L: 5654 D: 39353 Ptnml(0-2): 281, 4290, 16085, 4497, 291 https://tests.stockfishchess.org/tests/view/60cbfa68457376eb8bcab49a passed LTC 60+0.6: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 25480 W: 1498 L: 1338 D: 22644 Ptnml(0-2): 36, 1155, 10193, 1325, 31 https://tests.stockfishchess.org/tests/view/60cc4af8457376eb8bcab4d4 closes https://github.com/official-stockfish/Stockfish/pull/3564 Bench: 4904930 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index ef0b1040..0e815d6b 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-33c9d39e5eb6.nnue" + #define EvalFileDefaultName "nn-3b20abec10c1.nnue" namespace NNUE { From 86afb6a7cf33640a4d7bb7dbb2a8203450e34938 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sat, 19 Jun 2021 03:09:20 +0800 Subject: [PATCH 095/282] Update default net to nn-aa9d7eeb397e.nnue Optimization of vondele's nn-33c9d39e5eb6.nnue using SPSA https://tests.stockfishchess.org/tests/view/60ca68be457376eb8bcab28b Setting: ck values are default based on how large the parameters are The new values for this net are the raw values at the end of the tuning (80k games) The significant changes are in buckets 1 and 2 (5-12 pieces) so the main difference is in playing endgames if we compare it to nn-33c9. There is also change in bucket 7 (29-32 pieces) but not as substantial as the changes in buckets 1 and 2. If we interpret the changes based on an experiment a few months ago, this new net plays more optimistically during endgames and less optimistically during openings. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 49504 W: 4246 L: 4053 D: 41205 Ptnml(0-2): 140, 3282, 17749, 3407, 174 https://tests.stockfishchess.org/tests/view/60cbd752457376eb8bcab478 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 88720 W: 4926 L: 4651 D: 79143 Ptnml(0-2): 105, 4048, 35793, 4295, 119 https://tests.stockfishchess.org/tests/view/60cc7828457376eb8bcab4fa closes https://github.com/official-stockfish/Stockfish/pull/3566 Bench: 4758885 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 0e815d6b..10298882 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-3b20abec10c1.nnue" + #define EvalFileDefaultName "nn-aa9d7eeb397e.nnue" namespace NNUE { From 07e6ceacd623402a8b83c834e95db77efaad3782 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Fri, 18 Jun 2021 12:03:03 +0200 Subject: [PATCH 096/282] Add basic github workflow move to github actions to replace travis CI. First version, testing on linux using gcc and clang. gcc build with sanitizers and valgrind. No functional change --- .github/workflows/stockfish.yml | 201 ++++++++++++++++++++++++++++++++ .travis.yml | 101 ---------------- README.md | 2 +- tests/instrumented.sh | 2 +- 4 files changed, 203 insertions(+), 103 deletions(-) create mode 100644 .github/workflows/stockfish.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml new file mode 100644 index 00000000..8970fcd1 --- /dev/null +++ b/.github/workflows/stockfish.yml @@ -0,0 +1,201 @@ +name: Stockfish +on: + push: + branches: + - master + - tools + - github_ci + pull_request: + branches: + - master + - tools +jobs: + Stockfish: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + CXXFLAGS: "-Werror" + strategy: + matrix: + config: + - { + name: "Ubuntu 20.04 GCC", + os: ubuntu-20.04, + compiler: g++, + comp: gcc, + run_expensive_tests: true + } + - { + name: "Ubuntu 20.04 Clang", + os: ubuntu-20.04, + compiler: clang++, + comp: clang, + run_expensive_tests: false + } + + defaults: + run: + working-directory: src + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Download required packages + run: | + sudo apt update + sudo apt install expect valgrind g++-multilib + + - name: Download the used network from the fishtest framework + run: | + make net + + - name: Extract the bench number from the commit history + run: | + git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig + [ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found" + + - name: Check compiler + run: | + $COMPILER -v + + - name: Test help target + run: | + make help + + # x86-32 tests + + - name: Test debug x86-32 build + run: | + export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" + make clean + make -j2 ARCH=x86-32 optimize=no debug=yes build + ../tests/signature.sh $benchref + + - name: Test x86-32 build + run: | + make clean + make -j2 ARCH=x86-32 build + ../tests/signature.sh $benchref + + - name: Test x86-32-sse41-popcnt build + run: | + make clean + make -j2 ARCH=x86-32-sse41-popcnt build + ../tests/signature.sh $benchref + + - name: Test x86-32-sse2 build + run: | + make clean + make -j2 ARCH=x86-32-sse2 build + ../tests/signature.sh $benchref + + - name: Test general-32 build + run: | + make clean + make -j2 ARCH=general-32 build + ../tests/signature.sh $benchref + + # x86-64 tests + + - name: Test debug x86-64-modern build + run: | + export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" + make clean + make -j2 ARCH=x86-64-modern optimize=no debug=yes build + ../tests/signature.sh $benchref + + - name: Test x86-64-modern build + run: | + make clean + make -j2 ARCH=x86-64-modern build + ../tests/signature.sh $benchref + + - name: Test x86-64-ssse3 build + run: | + make clean + make -j2 ARCH=x86-64-ssse3 build + ../tests/signature.sh $benchref + + - name: Test x86-64-sse3-popcnt build + run: | + make clean + make -j2 ARCH=x86-64-sse3-popcnt build + ../tests/signature.sh $benchref + + - name: Test x86-64 build + run: | + make clean + make -j2 ARCH=x86-64 build + ../tests/signature.sh $benchref + + - name: Test general-64 build + run: | + make clean + make -j2 ARCH=general-64 build + ../tests/signature.sh $benchref + + # x86-64 with newer extensions tests + + - name: Compile x86-64-avx2 build + run: | + make clean + make -j2 ARCH=x86-64-avx2 build + + - name: Compile x86-64-bmi2 build + run: | + make clean + make -j2 ARCH=x86-64-bmi2 build + + - name: Compile x86-64-avx512 build + run: | + make clean + make -j2 ARCH=x86-64-avx512 build + + - name: Compile x86-64-vnni512 build + run: | + make clean + make -j2 ARCH=x86-64-vnni512 build + + - name: Compile x86-64-vnni256 build + run: | + make clean + make -j2 ARCH=x86-64-vnni256 build + + # Other tests + + - name: Check perft and search reproducibility + run: | + make clean + make -j2 ARCH=x86-64-modern build + ../tests/perft.sh + ../tests/reprosearch.sh + + # Sanitizers + + - name: Run under valgrind + if: ${{ matrix.config.run_expensive_tests }} + run: | + export CXXFLAGS="-O1 -fno-inline" + make clean + make -j2 ARCH=x86-64-modern debug=yes optimize=no build > /dev/null + ../tests/instrumented.sh --valgrind + ../tests/instrumented.sh --valgrind-thread + + - name: Run with UB sanitizer + if: ${{ matrix.config.run_expensive_tests }} + run: | + export CXXFLAGS="-O1 -fno-inline" + make clean + make -j2 ARCH=x86-64-modern sanitize=undefined optimize=no debug=yes build > /dev/null + ../tests/instrumented.sh --sanitizer-undefined + + - name: Run with thread sanitizer + if: ${{ matrix.config.run_expensive_tests }} + run: | + export CXXFLAGS="-O1 -fno-inline" + make clean + make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build > /dev/null + ../tests/instrumented.sh --sanitizer-thread diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 092c7f53..00000000 --- a/.travis.yml +++ /dev/null @@ -1,101 +0,0 @@ -language: cpp -dist: bionic - -matrix: - include: - - os: linux - compiler: gcc - addons: - apt: - packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl'] - env: - - COMPILER=g++-8 - - COMP=gcc - - - os: linux - compiler: clang - addons: - apt: - packages: ['clang-10', 'llvm-10-dev', 'g++-multilib', 'valgrind', 'expect', 'curl'] - env: - - COMPILER=clang++-10 - - COMP=clang - - - os: osx - osx_image: xcode12 - compiler: gcc - env: - - COMPILER=g++ - - COMP=gcc - - - os: osx - osx_image: xcode12 - compiler: clang - env: - - COMPILER=clang++ - - COMP=clang - -branches: - only: - - master - -before_script: - - cd src - -script: - # Download net - - make net - - # Obtain bench reference from git log - - git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig - - export benchref=$(cat git_sig) - - echo "Reference bench:" $benchref - - # Compiler version string - - $COMPILER -v - - # test help target - - make help - - # Verify bench number against various builds - - export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" - - make clean && make -j2 ARCH=x86-64-modern optimize=no debug=yes build && ../tests/signature.sh $benchref - - export CXXFLAGS="-Werror" - - make clean && make -j2 ARCH=x86-64-modern build && ../tests/signature.sh $benchref - - make clean && make -j2 ARCH=x86-64-ssse3 build && ../tests/signature.sh $benchref - - make clean && make -j2 ARCH=x86-64-sse3-popcnt build && ../tests/signature.sh $benchref - - make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-64 build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-sse41-popcnt build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-sse2 build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-32 build && ../tests/signature.sh $benchref; fi - # workaround: exclude a custom version of llvm+clang, which doesn't find llvm-profdata on ubuntu - - if [[ "$TRAVIS_OS_NAME" != "linux" || "$COMP" == "gcc" ]]; then make clean && make -j2 ARCH=x86-64-modern profile-build && ../tests/signature.sh $benchref; fi - - # compile only for some more advanced architectures (might not run in travis) - - make clean && make -j2 ARCH=x86-64-avx2 build - - make clean && make -j2 ARCH=x86-64-bmi2 build - - make clean && make -j2 ARCH=x86-64-avx512 build - - make clean && make -j2 ARCH=x86-64-vnni512 build - - make clean && make -j2 ARCH=x86-64-vnni256 build - - # - # Check perft and reproducible search - - make clean && make -j2 ARCH=x86-64-modern build - - ../tests/perft.sh - - ../tests/reprosearch.sh - - # - # Valgrind - # - - export CXXFLAGS="-O1 -fno-inline" - - if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64-modern debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi - - if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi - - # - # Sanitizer - # - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64-modern sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi diff --git a/README.md b/README.md index f4ee2e34..0bbe4abb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Overview -[![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish) +[![Build Status](https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml/badge.svg)](https://github.com/official-stockfish/Stockfish/actions) [![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master) [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine diff --git a/tests/instrumented.sh b/tests/instrumented.sh index d30c8e35..e9455eab 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -98,7 +98,7 @@ cat << EOF > game.exp expect "bestmove" send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n" - send "go depth 20\n" + send "go depth 10\n" expect "bestmove" send "quit\n" From adfb23c029e54c7522aadca1adf3e0b15fdcebcd Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 18 Jun 2021 23:50:01 +0200 Subject: [PATCH 097/282] Make net nn-50144f835024.nnue the default trained with the Python command c:\nnue>python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed %random%%random% --default_root_dir exp/run_8 --resume-from-model ./pt/nn-6ad41a9207d0.pt ` all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - make one large Wrong_NNUE 2 binpack and one large Training_Data of approximate size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training _Data binpack . nn-6ad41a9207d0.pt was derived from a net vondele ran which passed STC quickly, but faltered in LTC. https://tests.stockfishchess.org/tests/view/60cba666457376eb8bcab443 STC: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 18792 W: 2068 L: 1889 D: 14835 Ptnml(0-2): 82, 1480, 6117, 1611, 106 https://tests.stockfishchess.org/tests/view/60ccda8b457376eb8bcab568 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 11376 W: 574 L: 454 D: 10348 Ptnml(0-2): 4, 412, 4747, 510, 15 https://tests.stockfishchess.org/tests/view/60ccf952457376eb8bcab58d closes https://github.com/official-stockfish/Stockfish/pull/3568 Bench: 4900906 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 10298882..4e6bb1a6 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-aa9d7eeb397e.nnue" + #define EvalFileDefaultName "nn-50144f835024.nnue" namespace NNUE { From 0171b506ec96e2b8c8fcc7758cd5624ad26c0552 Mon Sep 17 00:00:00 2001 From: proukornew Date: Sat, 19 Jun 2021 00:52:46 +0300 Subject: [PATCH 098/282] Fix for Cygwin's environment build-profile (fixed) The Cygwin environment has two g++ compilers, each with a different problem for compiling Stockfish at the moment: (a) g++.exe : full posix build compiler, linked to cygwin dll. => This one has a problem embedding the net. (b) x86_64-w64-mingw32-g++.exe : native Windows build compiler. => This one manages to embed the net, but has a problem related to libgcov when we use the profile-build target of Stockfish. This patch solves the problem for compiler (b), so that our recommended command line if you want to build an optimized version of Stockfish on Cygwin becomes something like the following (you can change the ARCH value to whatever you want, but note the COMP and CXX variables pointing at the right compiler): ``` make -j profile-build ARCH=x86-64-modern COMP=mingw CXX=x86_64-w64-mingw32-c++.exe ``` closes https://github.com/official-stockfish/Stockfish/pull/3569 No functional change --- src/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 33a270fe..bf3c3560 100644 --- a/src/Makefile +++ b/src/Makefile @@ -883,14 +883,15 @@ clang-profile-use: all gcc-profile-make: + @mkdir -p profdir $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-generate' \ + EXTRACXXFLAGS='-fprofile-generate=profdir' \ EXTRALDFLAGS='-lgcov' \ all gcc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-use -fno-peel-loops -fno-tracer' \ + EXTRACXXFLAGS='-fprofile-use=profdir -fno-peel-loops -fno-tracer' \ EXTRALDFLAGS='-lgcov' \ all From 2e745956c0478627c4822104fc48a079c2f15e77 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 17 Jun 2021 12:36:06 +0200 Subject: [PATCH 099/282] Change trace with NNUE eval support This patch adds some more output to the `eval` command. It adds a board display with estimated piece values (method is remove-piece, evaluate, put-piece), and splits the NNUE evaluation with (psqt,layers) for each bucket for the NNUE net. Example: ``` ./stockfish position fen 3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40 eval Contributing terms for the classical eval: +------------+-------------+-------------+-------------+ | Term | White | Black | Total | | | MG EG | MG EG | MG EG | +------------+-------------+-------------+-------------+ | Material | ---- ---- | ---- ---- | -0.73 -1.55 | | Imbalance | ---- ---- | ---- ---- | -0.21 -0.17 | | Pawns | 0.35 -0.00 | 0.19 -0.26 | 0.16 0.25 | | Knights | 0.04 -0.08 | 0.12 -0.01 | -0.08 -0.07 | | Bishops | -0.34 -0.87 | -0.17 -0.61 | -0.17 -0.26 | | Rooks | 0.12 0.00 | 0.08 0.00 | 0.04 0.00 | | Queens | 0.00 0.00 | -0.27 -0.07 | 0.27 0.07 | | Mobility | 0.84 1.76 | 0.01 0.66 | 0.83 1.10 | |King safety | -0.99 -0.17 | -0.72 -0.10 | -0.27 -0.07 | | Threats | 0.27 0.27 | 0.73 0.86 | -0.46 -0.59 | | Passed | 0.00 0.00 | 0.79 0.82 | -0.79 -0.82 | | Space | 0.61 0.00 | 0.24 0.00 | 0.37 0.00 | | Winnable | ---- ---- | ---- ---- | 0.00 -0.03 | +------------+-------------+-------------+-------------+ | Total | ---- ---- | ---- ---- | -1.03 -2.14 | +------------+-------------+-------------+-------------+ NNUE derived piece values: +-------+-------+-------+-------+-------+-------+-------+-------+ | | | | Q | b | | k | | | | | | +12.4 | -1.62 | | | | +-------+-------+-------+-------+-------+-------+-------+-------+ | | r | | | p | p | b | | | | -3.89 | | | -0.84 | -1.19 | -3.32 | | +-------+-------+-------+-------+-------+-------+-------+-------+ | p | N | | n | | | q | | | -1.81 | +3.71 | | -4.82 | | | -5.04 | | +-------+-------+-------+-------+-------+-------+-------+-------+ | P | p | | P | p | | P | r | | +1.16 | -0.91 | | +0.55 | +0.12 | | +0.50 | -4.02 | +-------+-------+-------+-------+-------+-------+-------+-------+ | | | | | P | | | p | | | | | | +2.33 | | | +1.17 | +-------+-------+-------+-------+-------+-------+-------+-------+ | | | | | B | P | | | | | | | | +4.79 | +1.54 | | | +-------+-------+-------+-------+-------+-------+-------+-------+ | | | | | B | | R | | | | | | | +4.54 | | +6.03 | | +-------+-------+-------+-------+-------+-------+-------+-------+ | | R | | | | | | K | | | +4.81 | | | | | | | +-------+-------+-------+-------+-------+-------+-------+-------+ NNUE network contributions (Black to move) +------------+------------+------------+------------+ | Bucket | Material | Positional | Total | | | (PSQT) | (Layers) | | +------------+------------+------------+------------+ | 0 | + 0.32 | - 1.46 | - 1.13 | | 1 | + 0.25 | - 0.68 | - 0.43 | | 2 | + 0.46 | - 1.72 | - 1.25 | | 3 | + 0.55 | - 1.80 | - 1.25 | | 4 | + 0.48 | - 1.77 | - 1.29 | | 5 | + 0.40 | - 2.00 | - 1.60 | | 6 | + 0.57 | - 2.12 | - 1.54 | <-- this bucket is used | 7 | + 3.38 | - 2.00 | + 1.37 | +------------+------------+------------+------------+ Classical evaluation -1.00 (white side) NNUE evaluation +1.54 (white side) Final evaluation +2.38 (white side) [with scaled NNUE, hybrid, ...] ``` Also renames the export_net() function to save_eval() while there. closes https://github.com/official-stockfish/Stockfish/pull/3562 No functional change --- src/evaluate.cpp | 87 ++++++------- src/evaluate.h | 11 +- src/nnue/evaluate_nnue.cpp | 248 +++++++++++++++++++++++++++++++++++++ src/position.h | 7 +- src/uci.cpp | 10 +- 5 files changed, 300 insertions(+), 63 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0e66fea6..fe136614 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,30 +114,6 @@ namespace Eval { } } - /// NNUE::export_net() exports the currently loaded network to a file - void NNUE::export_net(const std::optional& filename) { - std::string actualFilename; - - if (filename.has_value()) - actualFilename = filename.value(); - else - { - if (eval_file_loaded != EvalFileDefaultName) - { - sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl; - return; - } - actualFilename = EvalFileDefaultName; - } - - ofstream stream(actualFilename, std::ios_base::binary); - - if (save_eval(stream)) - sync_cout << "Network saved successfully to " << actualFilename << "." << sync_endl; - else - sync_cout << "Failed to export a net." << sync_endl; - } - /// NNUE::verify() verifies that the last net used was loaded successfully void NNUE::verify() { @@ -204,7 +180,7 @@ namespace Trace { else os << scores[t][WHITE] << " | " << scores[t][BLACK]; - os << " | " << scores[t][WHITE] - scores[t][BLACK] << "\n"; + os << " | " << scores[t][WHITE] - scores[t][BLACK] << " |\n"; return os; } } @@ -1150,7 +1126,7 @@ Value Eval::evaluate(const Position& pos) { /// descriptions and values of each evaluation term. Useful for debugging. /// Trace scores are from white's point of view -std::string Eval::trace(const Position& pos) { +std::string Eval::trace(Position& pos) { if (pos.checkers()) return "Final evaluation: none (in check)"; @@ -1167,39 +1143,48 @@ std::string Eval::trace(const Position& pos) { v = Evaluation(pos).value(); ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) - << " Term | White | Black | Total \n" - << " | MG EG | MG EG | MG EG \n" - << " ------------+-------------+-------------+------------\n" - << " Material | " << Term(MATERIAL) - << " Imbalance | " << Term(IMBALANCE) - << " Pawns | " << Term(PAWN) - << " Knights | " << Term(KNIGHT) - << " Bishops | " << Term(BISHOP) - << " Rooks | " << Term(ROOK) - << " Queens | " << Term(QUEEN) - << " Mobility | " << Term(MOBILITY) - << " King safety | " << Term(KING) - << " Threats | " << Term(THREAT) - << " Passed | " << Term(PASSED) - << " Space | " << Term(SPACE) - << " Winnable | " << Term(WINNABLE) - << " ------------+-------------+-------------+------------\n" - << " Total | " << Term(TOTAL); - - v = pos.side_to_move() == WHITE ? v : -v; - - ss << "\nClassical evaluation: " << to_cp(v) << " (white side)\n"; + << " Contributing terms for the classical eval:\n" + << "+------------+-------------+-------------+-------------+\n" + << "| Term | White | Black | Total |\n" + << "| | MG EG | MG EG | MG EG |\n" + << "+------------+-------------+-------------+-------------+\n" + << "| Material | " << Term(MATERIAL) + << "| Imbalance | " << Term(IMBALANCE) + << "| Pawns | " << Term(PAWN) + << "| Knights | " << Term(KNIGHT) + << "| Bishops | " << Term(BISHOP) + << "| Rooks | " << Term(ROOK) + << "| Queens | " << Term(QUEEN) + << "| Mobility | " << Term(MOBILITY) + << "|King safety | " << Term(KING) + << "| Threats | " << Term(THREAT) + << "| Passed | " << Term(PASSED) + << "| Space | " << Term(SPACE) + << "| Winnable | " << Term(WINNABLE) + << "+------------+-------------+-------------+-------------+\n" + << "| Total | " << Term(TOTAL) + << "+------------+-------------+-------------+-------------+\n"; + if (Eval::useNNUE) + ss << '\n' << NNUE::trace(pos) << '\n'; + + ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); + + v = pos.side_to_move() == WHITE ? v : -v; + ss << "\nClassical evaluation " << to_cp(v) << " (white side)\n"; if (Eval::useNNUE) { - v = NNUE::evaluate(pos); + v = NNUE::evaluate(pos, false); v = pos.side_to_move() == WHITE ? v : -v; - ss << "\nNNUE evaluation: " << to_cp(v) << " (white side)\n"; + ss << "NNUE evaluation " << to_cp(v) << " (white side)\n"; } v = evaluate(pos); v = pos.side_to_move() == WHITE ? v : -v; - ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; + ss << "Final evaluation " << to_cp(v) << " (white side)"; + if (Eval::useNNUE) + ss << " [with scaled NNUE, hybrid, ...]"; + ss << "\n"; return ss.str(); } diff --git a/src/evaluate.h b/src/evaluate.h index 4e6bb1a6..f888dd91 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -30,7 +30,7 @@ class Position; namespace Eval { - std::string trace(const Position& pos); + std::string trace(Position& pos); Value evaluate(const Position& pos); extern bool useNNUE; @@ -43,12 +43,15 @@ namespace Eval { namespace NNUE { + std::string trace(Position& pos); Value evaluate(const Position& pos, bool adjusted = false); + + void init(); + void verify(); + bool load_eval(std::string name, std::istream& stream); bool save_eval(std::ostream& stream); - void init(); - void export_net(const std::optional& filename); - void verify(); + bool save_eval(const std::optional& filename); } // namespace NNUE diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 4a3c206b..a918b925 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -20,6 +20,9 @@ #include #include +#include +#include +#include #include "../evaluate.h" #include "../position.h" @@ -175,6 +178,220 @@ namespace Stockfish::Eval::NNUE { return static_cast( sum / OutputScale ); } + struct NnueEvalTrace { + static_assert(LayerStacks == PSQTBuckets); + + Value psqt[LayerStacks]; + Value positional[LayerStacks]; + std::size_t correctBucket; + }; + + static NnueEvalTrace trace_evaluate(const Position& pos) { + + // We manually align the arrays on the stack because with gcc < 9.3 + // overaligning stack variables with alignas() doesn't work correctly. + + constexpr uint64_t alignment = CacheLineSize; + +#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) + TransformedFeatureType transformedFeaturesUnaligned[ + FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; + char bufferUnaligned[Network::BufferSize + alignment]; + + auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); + auto* buffer = align_ptr_up(&bufferUnaligned[0]); +#else + alignas(alignment) + TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + alignas(alignment) char buffer[Network::BufferSize]; +#endif + + ASSERT_ALIGNED(transformedFeatures, alignment); + ASSERT_ALIGNED(buffer, alignment); + + NnueEvalTrace t{}; + t.correctBucket = (pos.count() - 1) / 4; + for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) { + const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); + const auto output = network[bucket]->propagate(transformedFeatures, buffer); + + int materialist = psqt; + int positional = output[0]; + + t.psqt[bucket] = static_cast( materialist / OutputScale ); + t.positional[bucket] = static_cast( positional / OutputScale ); + } + + return t; + } + + static const std::string PieceToChar(" PNBRQK pnbrqk"); + + // Requires the buffer to have capacity for at least 5 values + static void format_cp_compact(Value v, char* buffer) { + + buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); + + int cp = (int)(std::abs(100.0 * double(v) / PawnValueEg)); + + if (cp >= 10000) + { + buffer[1] = '0' + cp / 10000; cp %= 10000; + buffer[2] = '0' + cp / 1000; cp %= 1000; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = ' '; + } + else if (cp >= 1000) + { + buffer[1] = '0' + cp / 1000; cp %= 1000; + buffer[2] = '0' + cp / 100; cp %= 100; + buffer[3] = '.'; + buffer[4] = '0' + cp / 10; + } + else + { + buffer[1] = '0' + cp / 100; cp %= 100; + buffer[2] = '.'; + buffer[3] = '0' + cp / 10; cp %= 10; + buffer[4] = '0' + cp / 1; + } + } + + // Requires the buffer to have capacity for at least 7 values + static void format_cp_aligned_dot(Value v, char* buffer) { + buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); + + int cp = (int)(std::abs(100.0 * double(v) / PawnValueEg)); + + if (cp >= 10000) + { + buffer[1] = '0' + cp / 10000; cp %= 10000; + buffer[2] = '0' + cp / 1000; cp %= 1000; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = '.'; + buffer[5] = '0' + cp / 10; cp %= 10; + buffer[6] = '0' + cp; + } + else if (cp >= 1000) + { + buffer[1] = ' '; + buffer[2] = '0' + cp / 1000; cp %= 1000; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = '.'; + buffer[5] = '0' + cp / 10; cp %= 10; + buffer[6] = '0' + cp; + } + else + { + buffer[1] = ' '; + buffer[2] = ' '; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = '.'; + buffer[5] = '0' + cp / 10; cp %= 10; + buffer[6] = '0' + cp / 1; + } + } + + + // trace() returns a string with the value of each piece on a board, + // and a table for (PSQT, Layers) values bucket by bucket. + + std::string trace(Position& pos) { + + std::stringstream ss; + + char board[3*8+1][8*8+2]; + std::memset(board, ' ', sizeof(board)); + for (int row = 0; row < 3*8+1; ++row) + board[row][8*8+1] = '\0'; + + // A lambda to output one box of the board + auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) { + + const int x = ((int)file) * 8; + const int y = (7 - (int)rank) * 3; + for (int i = 1; i < 8; ++i) + board[y][x+i] = board[y+3][x+i] = '-'; + for (int i = 1; i < 3; ++i) + board[y+i][x] = board[y+i][x+8] = '|'; + board[y][x] = board[y][x+8] = board[y+3][x+8] = board[y+3][x] = '+'; + if (pc != NO_PIECE) + board[y+1][x+4] = PieceToChar[pc]; + if (value != VALUE_NONE) + format_cp_compact(value, &board[y+2][x+2]); + }; + + // We estimate the value of each piece by doing a differential evaluation from + // the current base eval, simulating the removal of the piece from its square. + Value base = evaluate(pos); + base = pos.side_to_move() == WHITE ? base : -base; + + for (File f = FILE_A; f <= FILE_H; ++f) + for (Rank r = RANK_1; r <= RANK_8; ++r) + { + Square sq = make_square(f, r); + Piece pc = pos.piece_on(sq); + Value v = VALUE_NONE; + + if (pc != NO_PIECE && type_of(pc) != KING) + { + auto st = pos.state(); + + pos.remove_piece(sq); + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; + + Value eval = evaluate(pos); + eval = pos.side_to_move() == WHITE ? eval : -eval; + v = base - eval; + + pos.put_piece(pc, sq); + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; + } + + writeSquare(f, r, pc, v); + } + + ss << " NNUE derived piece values:\n"; + for (int row = 0; row < 3*8+1; ++row) + ss << board[row] << '\n'; + ss << '\n'; + + auto t = trace_evaluate(pos); + + ss << " NNUE network contributions " + << (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl + << "+------------+------------+------------+------------+\n" + << "| Bucket | Material | Positional | Total |\n" + << "| | (PSQT) | (Layers) | |\n" + << "+------------+------------+------------+------------+\n"; + + for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) + { + char buffer[3][8]; + std::memset(buffer, '\0', sizeof(buffer)); + + format_cp_aligned_dot(t.psqt[bucket], buffer[0]); + format_cp_aligned_dot(t.positional[bucket], buffer[1]); + format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], buffer[2]); + + ss << "| " << bucket << " " + << " | " << buffer[0] << " " + << " | " << buffer[1] << " " + << " | " << buffer[2] << " " + << " |"; + if (bucket == t.correctBucket) + ss << " <-- this bucket is used"; + ss << '\n'; + } + + ss << "+------------+------------+------------+------------+\n"; + + return ss.str(); + } + + // Load eval, from a file stream or a memory stream bool load_eval(std::string name, std::istream& stream) { @@ -192,4 +409,35 @@ namespace Stockfish::Eval::NNUE { return write_parameters(stream); } + /// Save eval, to a file given by its name + bool save_eval(const std::optional& filename) { + + std::string actualFilename; + std::string msg; + + if (filename.has_value()) + actualFilename = filename.value(); + else + { + if (eval_file_loaded != EvalFileDefaultName) + { + msg = "Failed to export a net. A non-embedded net can only be saved if the filename is specified"; + + sync_cout << msg << sync_endl; + return false; + } + actualFilename = EvalFileDefaultName; + } + + std::ofstream stream(actualFilename, std::ios_base::binary); + bool saved = save_eval(stream); + + msg = saved ? "Network saved successfully to " + actualFilename + : "Failed to export a net"; + + sync_cout << msg << sync_endl; + return saved; + } + + } // namespace Stockfish::Eval::NNUE diff --git a/src/position.h b/src/position.h index e6b072bc..9f694a79 100644 --- a/src/position.h +++ b/src/position.h @@ -171,6 +171,9 @@ public: // Used by NNUE StateInfo* state() const; + void put_piece(Piece pc, Square s); + void remove_piece(Square s); + private: // Initialization helpers (used while setting up a position) void set_castling_right(Color c, Square rfrom); @@ -178,8 +181,6 @@ private: void set_check_info(StateInfo* si) const; // Other helpers - void put_piece(Piece pc, Square s); - void remove_piece(Square s); void move_piece(Square from, Square to); template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); @@ -386,7 +387,7 @@ inline void Position::remove_piece(Square s) { byTypeBB[ALL_PIECES] ^= s; byTypeBB[type_of(pc)] ^= s; byColorBB[color_of(pc)] ^= s; - /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ + board[s] = NO_PIECE; pieceCount[pc]--; pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; psq -= PSQT::psq[pc][s]; diff --git a/src/uci.cpp b/src/uci.cpp index bb17b8d7..d5ffa228 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -277,13 +277,13 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; - else if (token == "export_net") { + else if (token == "export_net") + { std::optional filename; std::string f; - if (is >> skipws >> f) { - filename = f; - } - Eval::NNUE::export_net(filename); + if (is >> skipws >> f) + filename = f; + Eval::NNUE::save_eval(filename); } else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; From ba01f4b95448bcb324755f4dd2a632a57c6e67bc Mon Sep 17 00:00:00 2001 From: MichaelB7 Date: Sat, 19 Jun 2021 09:57:09 -0400 Subject: [PATCH 100/282] Make net nn-75980ca503c6.nnue the default. trained with the Python command c:\nnue>python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed %random%%random% --default_root_dir exp/run_10 --resume-from-model ./pt/nn-3b20abec10c1.pt ` all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - making one large Wrong_NNUE 2 binpack and one large Training so the were approximately equal in size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training_Data binpack . Net nn-3b20abec10c1.nnue was chosen as the --resume-from-model with the idea that through learning, the manually hex edited values will be learned and will not need to be manually adjusted going forward. They would also be fine tuned by the learning process. passed STC: https://tests.stockfishchess.org/tests/view/60cdf91e457376eb8bcab66f LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 18256 W: 1639 L: 1479 D: 15138 Ptnml(0-2): 59, 1179, 6505, 1313, 72 passed LTC: https://tests.stockfishchess.org/tests/view/60ce2166457376eb8bcab6e1 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 18792 W: 654 L: 542 D: 17596 Ptnml(0-2): 9, 490, 8291, 592, 14 closes https://github.com/official-stockfish/Stockfish/pull/3570 Bench: 5020972 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index f888dd91..0bcfd37f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-50144f835024.nnue" + #define EvalFileDefaultName "nn-75980ca503c6.nnue" namespace NNUE { From 70ac5ecbb62b68382073b2367644945c1327ddfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 20 Jun 2021 10:29:20 +0200 Subject: [PATCH 101/282] Keep more pawns and pieces when attacking This patch increase the weight of pawns and pieces from 28 to 32 in the scaling formula we apply to the output of the NNUE pure eval. Increasing this gradient for pawns and pieces means that Stockfish will try a little harder to keep material when she has the advantage, and try a little bit harder to escape into an endgame when she is under pressure. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 53168 W: 4371 L: 4177 D: 44620 Ptnml(0-2): 160, 3389, 19283, 3601, 151 https://tests.stockfishchess.org/tests/view/60cefd1d457376eb8bcab7ab LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 10888 W: 386 L: 288 D: 10214 Ptnml(0-2): 3, 260, 4821, 356, 4 https://tests.stockfishchess.org/tests/view/60cf709d2114332881e7352b closes https://github.com/official-stockfish/Stockfish/pull/3571 Bench: 4965430 --- src/evaluate.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index fe136614..1366d0fb 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,8 +1091,9 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&]() { - - int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; + int scale = 903 + + 32 * pos.count() + + 32 * pos.non_pawn_material() / 1024; Value nnue = NNUE::evaluate(pos, true) * scale / 1024; From ed436a36bade82422753f8be9c16d790232e9c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 16 Jun 2021 07:23:26 +0200 Subject: [PATCH 102/282] Remove the Contempt UCI option This patch removes the UCI option for setting Contempt in classical evaluation. It is exactly equivalent to using Contempt=0 for the UCI contempt value and keeping the dynamic part in the algo (renaming this dynamic part `trend` to better describe what it does). We have tried quite hard to implement a working Contempt feature for NNUE but nothing really worked, so it is probably time to give up. Interested chess fans wishing to keep playing with the UCI option for Contempt and use it with the classical eval are urged to download the version tagged "SF_Classical" of Stockfish (dated 31 July 2020), as it was the last version where our search algorithm was tuned for the classical eval and is probably our strongest classical player ever: https://github.com/official-stockfish/Stockfish/tags Passed STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 72904 W: 6228 L: 6175 D: 60501 Ptnml(0-2): 221, 5006, 25971, 5007, 247 https://tests.stockfishchess.org/tests/view/60c98bf9457376eb8bcab18d Passed LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 45168 W: 1601 L: 1547 D: 42020 Ptnml(0-2): 38, 1331, 19786, 1397, 32 https://tests.stockfishchess.org/tests/view/60c9c7fa457376eb8bcab1bb closes https://github.com/official-stockfish/Stockfish/pull/3575 Bench: 4947716 --- README.md | 8 -------- src/evaluate.cpp | 4 ++-- src/search.cpp | 22 +++++----------------- src/thread.h | 2 +- src/ucioption.cpp | 2 -- 5 files changed, 8 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 0bbe4abb..79db8170 100644 --- a/README.md +++ b/README.md @@ -120,14 +120,6 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis Limit Syzygy tablebase probing to positions with at most this many pieces left (including kings and pawns). - * #### Contempt - A positive value for contempt favors middle game positions and avoids draws, - effective for the classical evaluation only. - - * #### Analysis Contempt - By default, contempt is set to prefer the side to move. Set this option to "White" - or "Black" to analyse with contempt for that side, or "Off" to disable contempt. - * #### Move Overhead Assume a time delay of x ms due to network and GUI overheads. This is useful to avoid losses on time in those cases. diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1366d0fb..6a032fc0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -979,7 +979,7 @@ namespace { // Initialize score by reading the incrementally updated scores included in // the position object (material + piece square tables) and the material // imbalance. Score is computed internally from the white point of view. - Score score = pos.psq_score() + me->imbalance() + pos.this_thread()->contempt; + Score score = pos.psq_score() + me->imbalance() + pos.this_thread()->trend; // Probe the pawn hash table pe = Pawns::probe(pos); @@ -1139,7 +1139,7 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); - pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt + pos.this_thread()->trend = SCORE_ZERO; // Reset any dynamic contempt v = Evaluation(pos).value(); diff --git a/src/search.cpp b/src/search.cpp index 9bba563a..62f02d83 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -312,19 +312,7 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); ttHitAverage = TtHitAverageWindow * TtHitAverageResolution / 2; - int ct = int(Options["Contempt"]) * PawnValueEg / 100; // From centipawns - - // In analysis mode, adjust contempt in accordance with user preference - if (Limits.infinite || Options["UCI_AnalyseMode"]) - ct = Options["Analysis Contempt"] == "Off" ? 0 - : Options["Analysis Contempt"] == "Both" ? ct - : Options["Analysis Contempt"] == "White" && us == BLACK ? -ct - : Options["Analysis Contempt"] == "Black" && us == WHITE ? -ct - : ct; - - // Evaluation score is from the white point of view - contempt = (us == WHITE ? make_score(ct, ct / 2) - : -make_score(ct, ct / 2)); + trend = SCORE_ZERO; int searchAgainCounter = 0; @@ -370,11 +358,11 @@ void Thread::search() { alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); - // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + (113 - ct / 2) * prev / (abs(prev) + 147); + // Adjust trend based on root move's previousScore (dynamic contempt) + int tr = 113 * prev / (abs(prev) + 147); - contempt = (us == WHITE ? make_score(dct, dct / 2) - : -make_score(dct, dct / 2)); + trend = (us == WHITE ? make_score(tr, tr / 2) + : -make_score(tr, tr / 2)); } // Start with a small aspiration window and, in the case of a fail diff --git a/src/thread.h b/src/thread.h index ae662880..5bfa2359 100644 --- a/src/thread.h +++ b/src/thread.h @@ -74,7 +74,7 @@ public: LowPlyHistory lowPlyHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; - Score contempt; + Score trend; }; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index d59c0100..07b3027d 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -61,8 +61,6 @@ void init(OptionsMap& o) { constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; o["Debug Log File"] << Option("", on_logger); - o["Contempt"] << Option(24, -100, 100); - o["Analysis Contempt"] << Option("Both var Off var White var Black var Both", "Both"); o["Threads"] << Option(1, 1, 512, on_threads); o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); o["Clear Hash"] << Option(on_clear_hash); From 2e2865d34b6767e60ea55277bc6b1d642795ecc3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 21 Jun 2021 08:23:50 +0200 Subject: [PATCH 103/282] Fix build error on OSX directly use integer version for cp calculation. fixes https://github.com/official-stockfish/Stockfish/issues/3573 closes https://github.com/official-stockfish/Stockfish/pull/3574 No functional change --- src/nnue/evaluate_nnue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index a918b925..8828ae51 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -232,7 +232,7 @@ namespace Stockfish::Eval::NNUE { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = (int)(std::abs(100.0 * double(v) / PawnValueEg)); + int cp = std::abs(100 * v / PawnValueEg); if (cp >= 10000) { @@ -261,7 +261,7 @@ namespace Stockfish::Eval::NNUE { static void format_cp_aligned_dot(Value v, char* buffer) { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = (int)(std::abs(100.0 * double(v) / PawnValueEg)); + int cp = std::abs(100 * v / PawnValueEg); if (cp >= 10000) { From 9b82414b67aa7d1279e1cc99a6970ab766025bfa Mon Sep 17 00:00:00 2001 From: MichaelB7 Date: Mon, 21 Jun 2021 08:10:35 -0400 Subject: [PATCH 104/282] Make net nn-190f102a22c3.nnue the default net. Trained with the pytorch trainer: https://github.com/glinscott/nnue-pytorch python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed %random%%random% --default_root_dir exp/run_17 --resume-from-model ./pt/nn-75980ca503c6.pt This run is thus started from the previous master net. all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - making one large Wrong_NNUE 2 binpack and one large Training so the were approximately equal in size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training_Data binpack passed LTC https://tests.stockfishchess.org/tests/view/60d09f52b4c17000d679517f LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 32184 W: 1100 L: 970 D: 30114 Ptnml(0-2): 10, 878, 14193, 994, 17 passed STC https://tests.stockfishchess.org/tests/view/60d086c02114332881e7368e LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 11360 W: 1056 L: 906 D: 9398 Ptnml(0-2): 25, 735, 4026, 853, 41 closes https://github.com/official-stockfish/Stockfish/pull/3576 Bench: 4631244 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 0bcfd37f..d909c7ce 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-75980ca503c6.nnue" + #define EvalFileDefaultName "nn-190f102a22c3.nnue" namespace NNUE { From 0470bcef0e1962b4f8da15108170b991d3f90d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 22 Jun 2021 09:08:37 +0200 Subject: [PATCH 105/282] Detect fortresses a little bit quicker In the so-called "hybrid" method of evaluation of current master, we use the classical eval (because of its speed) instead of the NNUE eval when the classical material balance approximation hints that the position is "winning enough" to rely on the classical eval. This trade-off idea between speed and accuracy works well in general, but in some fortress positions the classical eval is just bad. So in shuffling branches of the search tree, we (slowly) increase the thresehold so that eventually we don't trust classical anymore and switch to NNUE evaluation. This patch increases that threshold faster, so that we switch to NNUE quicker in shuffling branches. Idea is to incite Stockfish to spend less time in fortresses lines in the search tree, and spend more time searching the critical lines. passed STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 47872 W: 3908 L: 3720 D: 40244 Ptnml(0-2): 122, 3053, 17419, 3199, 143 https://tests.stockfishchess.org/tests/view/60cef34b457376eb8bcab79d passed LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 73616 W: 2326 L: 2143 D: 69147 Ptnml(0-2): 21, 1940, 32705, 2119, 23 https://tests.stockfishchess.org/tests/view/60cf6d842114332881e73528 Retested at LTC against lastest master: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 18264 W: 642 L: 532 D: 17090 Ptnml(0-2): 6, 479, 8055, 583, 9 https://tests.stockfishchess.org/tests/view/60d18cd540925195e7a6c351 closes https://github.com/official-stockfish/Stockfish/pull/3578 Bench: 5139233 --- src/evaluate.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6a032fc0..f9754795 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -192,8 +192,7 @@ namespace { // Threshold for lazy and space evaluation constexpr Value LazyThreshold1 = Value(1565); constexpr Value LazyThreshold2 = Value(1102); - constexpr Value SpaceThreshold = Value(11551); - constexpr Value NNUEThreshold1 = Value(800); + constexpr Value SpaceThreshold = Value(11551); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -1103,14 +1102,14 @@ Value Eval::evaluate(const Position& pos) { return nnue; }; - // If there is PSQ imbalance we use the classical eval. + // If there is PSQ imbalance we use the classical eval, but we switch to + // NNUE eval faster when shuffling or if the material on the board is high. + int r50 = pos.rule50_count(); Value psq = Value(abs(eg_value(pos.psq_score()))); - int r50 = 16 + pos.rule50_count(); - bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; - - v = largePsq ? Evaluation(pos).value() // classical - : adjusted_NNUE(); // NNUE + bool classical = psq * 5 > (750 + pos.non_pawn_material() / 64) * (5 + r50); + v = classical ? Evaluation(pos).value() // classical + : adjusted_NNUE(); // NNUE } // Damp down the evaluation linearly when shuffling From e47b74457ef12ff28ced317fef942cfad18b18a0 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 22 Jun 2021 19:33:14 -0300 Subject: [PATCH 106/282] Simplify Reductions Initialization passed STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 45032 W: 3600 L: 3518 D: 37914 Ptnml(0-2): 111, 2893, 16435, 2957, 120 https://tests.stockfishchess.org/tests/view/60d2655d40925195e7a6c527 LTC: LLR: 3.00 (-2.94,2.94) <-2.50,0.50> Total: 25728 W: 786 L: 722 D: 24220 Ptnml(0-2): 5, 650, 11494, 706, 9 https://tests.stockfishchess.org/tests/view/60d2b14240925195e7a6c577 closes https://github.com/official-stockfish/Stockfish/pull/3584 bench: 4602977 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 62f02d83..a413bd38 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -152,7 +152,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int(21.3 * std::log(i + 0.25 * std::log(i))); + Reductions[i] = int(21.9 * std::log(i)); } From dc4983327df7bf30eafd049aa777d0636ee4c0ed Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 23 Jun 2021 07:23:21 +0200 Subject: [PATCH 107/282] Update WDL model for NNUE This updates the WDL model based on the LTC statistics in June this year (10M games), so from pre-NNUE to NNUE based results. (for old results see, https://github.com/official-stockfish/Stockfish/pull/2778) As before the fit by the model to the data is quite good. closes https://github.com/official-stockfish/Stockfish/pull/3582 No functional change --- src/uci.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index d5ffa228..b3738a4a 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -207,13 +207,13 @@ namespace { // Coefficients of a 3rd order polynomial fit based on fishtest data // for two parameters needed to transform eval to the argument of a // logistic function. - double as[] = {-8.24404295, 64.23892342, -95.73056462, 153.86478679}; - double bs[] = {-3.37154371, 28.44489198, -56.67657741, 72.05858751}; + double as[] = {-3.68389304, 30.07065921, -60.52878723, 149.53378557}; + double bs[] = {-2.0181857, 15.85685038, -29.83452023, 47.59078827}; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; // Transform eval to centipawns with limited range - double x = std::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0); + double x = std::clamp(double(100 * v) / PawnValueEg, -2000.0, 2000.0); // Return win rate in per mille (rounded to nearest) return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); From b94a65187857721319ced93f5215ac72bbdfe472 Mon Sep 17 00:00:00 2001 From: MichaelB7 Date: Sun, 27 Jun 2021 11:26:09 -0400 Subject: [PATCH 108/282] Make net nn-956480d8378f.nnue the default Trained with the pytorch trainer: https://github.com/glinscott/nnue-pytorch python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed %random%%random% --default_root_dir exp/run_18 --resume-from-model ./pt/nn-75980ca503c6.pt This run is thus started from a previous master net. all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - making one large Wrong_NNUE 2 binpack and one large Training so the were approximately equal in size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training_Data binpack passed STC: https://tests.stockfishchess.org/tests/view/60d0c0a7a8ec07dc34c072b2 LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 18440 W: 1693 L: 1531 D: 15216 Ptnml(0-2): 67, 1225, 6464, 1407, 57 passed LTC: https://tests.stockfishchess.org/tests/view/60d762793beab81350ac9d72 LLR: 2.98 (-2.94,2.94) <0.50,3.50> Total: 93120 W: 3152 L: 2933 D: 87035 Ptnml(0-2): 48, 2581, 41076, 2814, 41 passed LTC (rebased branch to current master): https://tests.stockfishchess.org/tests/view/60d85eeb3beab81350ac9e2b LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 42688 W: 1347 L: 1206 D: 40135 Ptnml(0-2): 14, 1097, 18981, 1238, 14. closes https://github.com/official-stockfish/Stockfish/pull/3592 Bench: 4906727 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index d909c7ce..5e12db45 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-190f102a22c3.nnue" + #define EvalFileDefaultName "nn-956480d8378f.nnue" namespace NNUE { From 49283d3a6676e114b531d6a8b9e5f69000655912 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Mon, 28 Jun 2021 14:58:51 +0800 Subject: [PATCH 109/282] Update default net to nn-3475407dc199.nnue Optimization of eight subnetwork output layers of Michael's nn-190f102a22c3.nnue using SPSA https://tests.stockfishchess.org/tests/view/60d5510642a522cc50282ef3 Parameters: A total of 256 net weights and 8 net biases were tuned New best values: The raw values at the end of the tuning run were used (800k games, 5 seconds TC) Settings: default ck value and SPSA A is 30,000 (3.75% of the total number of games) STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 29064 W: 2435 L: 2269 D: 24360 Ptnml(0-2): 72, 1857, 10505, 2029, 69 https://tests.stockfishchess.org/tests/view/60d8ea123beab81350ac9eb6 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 61848 W: 2055 L: 1884 D: 57909 Ptnml(0-2): 18, 1708, 27310, 1861, 27 https://tests.stockfishchess.org/tests/view/60d8f0393beab81350ac9ec6 closes https://github.com/official-stockfish/Stockfish/pull/3593 Bench: 4770936 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 5e12db45..91da01da 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-956480d8378f.nnue" + #define EvalFileDefaultName "nn-3475407dc199.nnue" namespace NNUE { From 2275923d3cbca85b433a4d16d40fae5c8de6784a Mon Sep 17 00:00:00 2001 From: Brad Knox <64992190+bknox83@users.noreply.github.com> Date: Tue, 29 Jun 2021 01:40:16 -0500 Subject: [PATCH 110/282] Update Top CPU Contributors closes https://github.com/official-stockfish/Stockfish/pull/3595 No functional change --- Top CPU Contributors.txt | 390 ++++++++++++++++++++------------------- 1 file changed, 203 insertions(+), 187 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index f5347ea1..dacc5781 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,189 +1,205 @@ -Contributors to Fishtest with >10,000 CPU hours, as of Feb 15, 2021. +Contributors to Fishtest with >10,000 CPU hours, as of Jun 29, 2021. Thank you! -Username CPU Hours Games played ----------------------------------------------------- -noobpwnftw 23930906 1560559941 -dew 1169948 70333008 -mlang 957168 61657446 -mibere 703840 46867607 -tvijlbrief 517888 33379462 -JojoM 515404 30334272 -cw 443276 29385549 -crunchy 427035 27344275 -grandphish2 425794 26347253 -fastgm 414133 24519696 -gvreuls 377843 24708884 -CSU_Dynasty 338718 23030006 -Fisherman 326795 21820747 -TueRens 313730 19490246 -ctoks 298442 20052551 -velislav 270519 17355456 -bcross 241064 17196165 -glinscott 217799 13780820 -nordlandia 211692 13484886 -bking_US 198894 11876016 -drabel 191096 13129722 -leszek 189170 11446821 -mgrabiak 187153 12013300 -robal 181389 11539242 -Thanar 179852 12365359 -vdv 175274 9889046 -spams 157128 10319326 -marrco 150292 9401741 -sqrt2 147963 9724586 -CoffeeOne 137086 5022516 -vdbergh 137041 8926915 -malala 136182 8002293 -mhoram 132780 8398229 -xoto 124729 8652088 -davar 122092 7960001 -dsmith 122059 7570238 -Data 113305 8220352 -BrunoBanani 112960 7436849 -pemo 109598 5036441 -Dantist 106768 6431396 -MaZePallas 102741 6630419 -ElbertoOne 99028 7023771 -brabos 92118 6186135 -linrock 90903 6708639 -psk 89957 5984901 -sunu 88614 6020673 -sterni1971 86948 5613788 -Vizvezdenec 83761 5344740 -BRAVONE 81239 5054681 -nssy 76497 5259388 -cuistot 76366 4370584 -racerschmacer 75753 5442626 -teddybaer 75125 5407666 -Pking_cda 73776 5293873 -0x3C33 73133 4670293 -jromang 72117 5054915 -solarlight 70517 5028306 -dv8silencer 70287 3883992 -Bobo1239 68515 4652287 -manap 66273 4121774 -tinker 64321 4268390 -robnjr 57262 4053117 -Freja 56938 3733019 -ttruscott 56010 3680085 -rkl 54986 4150767 -renouve 53811 3501516 -finfish 51360 3370515 -eva42 51272 3599691 -rap 49985 3219146 -pb00067 49727 3298270 -amicic 49691 3042481 -ronaldjerum 47654 3240695 -bigpen0r 47278 3291647 -biffhero 46564 3111352 -VoyagerOne 45476 3452465 -eastorwest 45033 3071805 -speedycpu 43842 3003273 -jbwiebe 43305 2805433 -Antihistamine 41788 2761312 -mhunt 41735 2691355 -homyur 39893 2850481 -gri 39871 2515779 -oryx 38282 2944400 -Spprtr 38157 2470529 -SC 37290 2731014 -csnodgrass 36207 2688994 -jmdana 36157 2210661 -strelock 34716 2074055 -Garf 33800 2747562 -skiminki 33515 2055584 -EthanOConnor 33370 2090311 -slakovv 32915 2021889 -yurikvelo 32600 2255966 -Prcuvu 30377 2170122 -manapbk 30326 1770143 -anst 30301 2190091 -jkiiski 30136 1904470 -hyperbolic.tom 29840 2017394 -Pyafue 29650 1902349 -qurashee 27758 1509620 -OuaisBla 27636 1578800 -chriswk 26902 1868317 -achambord 26582 1767323 -Fifis 26376 1776853 -Patrick_G 26276 1801617 -yorkman 26193 1992080 -SFTUser 25182 1675689 -nabildanial 24942 1519409 -Sharaf_DG 24765 1786697 -ncfish1 24411 1520927 -agg177 23890 1395014 -JanErik 23408 1703875 -Isidor 23388 1680691 -Norabor 23164 1591830 -cisco2015 22895 1762069 -Zirie 22542 1472937 -team-oh 22272 1636708 -MazeOfGalious 21978 1629593 -sg4032 21945 1643065 -ianh2105 21725 1632562 -xor12 21628 1680365 -dex 21612 1467203 -nesoneg 21494 1463031 -jjoshua2 20997 1422689 -horst.prack 20878 1465656 -0xB00B1ES 20590 1208666 -sphinx 20515 1352368 -j3corre 20405 941444 -Adrian.Schmidt123 20316 1281436 -Ente 20017 1432602 -wei 19973 1745989 -rstoesser 19569 1293588 -eudhan 19274 1283717 -jundery 18445 1115855 -iisiraider 18247 1101015 -ville 17883 1384026 -chris 17698 1487385 -purplefishies 17595 1092533 -DMBK 17357 1279152 -DragonLord 17014 1162790 -dju 16515 929427 -IgorLeMasson 16064 1147232 -ako027ako 15671 1173203 -Nikolay.IT 15154 1068349 -Andrew Grant 15114 895539 -OssumOpossum 14857 1007129 -enedene 14476 905279 -bpfliegel 14298 884523 -jpulman 13982 870599 -joster 13794 950160 -Nesa92 13786 1114691 -crocogoat 13753 1114622 -Hjax 13535 915487 -Dark_wizzie 13422 1007152 -mpx86 12941 693640 -mabichito 12903 749391 -thijsk 12886 722107 -AdrianSA 12860 804972 -Flopzee 12698 894821 -fatmurphy 12547 853210 -scuzzi 12511 845761 -Karby 12429 735880 -SapphireBrand 12416 969604 -modolief 12386 896470 -pgontarz 12151 848794 -stocky 11954 699440 -mschmidt 11941 803401 -infinity 11470 727027 -torbjo 11395 729145 -Thomas A. Anderson 11372 732094 -d64 11263 789184 -Maxim 11129 804704 -snicolet 11106 869170 -MooTheCow 11008 694942 -savage84 10965 641068 -Rudolphous 10915 741268 -Wolfgang 10809 580032 -rpngn 10712 688203 -basepi 10637 744851 -michaelrpg 10409 735127 -dzjp 10343 732529 -ali-al-zhrani 10324 726502 -ols 10259 570669 -lbraesch 10252 647825 +Username CPU Hours Games played +----------------------------------------------------- +noobpwnftw 27649494 1834734733 +mlang 1426107 89454622 +dew 1380910 82831648 +mibere 703840 46867607 +grandphish2 692707 41737913 +tvijlbrief 669642 42371594 +JojoM 597778 35297180 +TueRens 519226 31823562 +cw 458421 30307421 +fastgm 439667 25950040 +gvreuls 436599 28177460 +crunchy 427035 27344275 +CSU_Dynasty 374765 25106278 +Fisherman 326901 21822979 +ctoks 325477 21767943 +velislav 295343 18844324 +linrock 292789 10624427 +bcross 278584 19488961 +okrout 262818 13803272 +pemo 245982 11376085 +glinscott 217799 13780820 +leszek 212346 12959025 +nordlandia 211692 13484886 +bking_US 198894 11876016 +drabel 196463 13450602 +robal 195473 12375650 +mgrabiak 187226 12016564 +Dantist 183202 10990484 +Thanar 179852 12365359 +vdv 175274 9889046 +spams 157128 10319326 +marrco 150295 9402141 +sqrt2 147963 9724586 +mhoram 141278 8901241 +CoffeeOne 137100 5024116 +vdbergh 137041 8926915 +malala 136182 8002293 +xoto 133702 9156676 +davar 122092 7960001 +dsmith 122059 7570238 +Data 113305 8220352 +BrunoBanani 112960 7436849 +MaZePallas 102823 6633619 +sterni1971 100532 5880772 +ElbertoOne 99028 7023771 +brabos 92118 6186135 +oz 92100 6486640 +psk 89957 5984901 +amicic 89156 5392305 +sunu 88851 6028873 +Vizvezdenec 83761 5344740 +0x3C33 82614 5271253 +BRAVONE 81239 5054681 +racerschmacer 80899 5759262 +cuistot 80300 4606144 +nssy 76497 5259388 +teddybaer 75125 5407666 +Pking_cda 73776 5293873 +jromang 72192 5057715 +solarlight 70517 5028306 +dv8silencer 70287 3883992 +Bobo1239 68515 4652287 +manap 66273 4121774 +skiminki 65088 4023328 +tinker 64333 4268790 +sschnee 60767 3500800 +qurashee 57344 3168264 +robnjr 57262 4053117 +Freja 56938 3733019 +ttruscott 56010 3680085 +rkl 55132 4164467 +renouve 53811 3501516 +finfish 51360 3370515 +eva42 51272 3599691 +rap 49985 3219146 +pb00067 49727 3298270 +ronaldjerum 47654 3240695 +bigpen0r 47653 3335327 +eastorwest 47585 3221629 +biffhero 46564 3111352 +VoyagerOne 45476 3452465 +yurikvelo 44834 3034550 +speedycpu 43842 3003273 +jbwiebe 43305 2805433 +Spprtr 42279 2680153 +DesolatedDodo 42007 2447516 +Antihistamine 41788 2761312 +mhunt 41735 2691355 +homyur 39893 2850481 +gri 39871 2515779 +Fifis 38776 2529121 +oryx 38724 2966648 +SC 37290 2731014 +csnodgrass 36207 2688994 +jmdana 36157 2210661 +strelock 34716 2074055 +rpngn 33951 2057395 +Garf 33922 2751802 +EthanOConnor 33370 2090311 +slakovv 32915 2021889 +manapbk 30987 1810399 +Prcuvu 30377 2170122 +anst 30301 2190091 +jkiiski 30136 1904470 +hyperbolic.tom 29840 2017394 +Pyafue 29650 1902349 +Wolfgang 29260 1658936 +zeryl 28156 1579911 +OuaisBla 27636 1578800 +DMBK 27051 1999456 +chriswk 26902 1868317 +achambord 26582 1767323 +Patrick_G 26276 1801617 +yorkman 26193 1992080 +SFTUser 25182 1675689 +nabildanial 24942 1519409 +Sharaf_DG 24765 1786697 +ncfish1 24411 1520927 +rodneyc 24227 1409514 +agg177 23890 1395014 +JanErik 23408 1703875 +Isidor 23388 1680691 +Norabor 23164 1591830 +cisco2015 22897 1762669 +Zirie 22542 1472937 +team-oh 22272 1636708 +MazeOfGalious 21978 1629593 +sg4032 21947 1643265 +ianh2105 21725 1632562 +xor12 21628 1680365 +dex 21612 1467203 +nesoneg 21494 1463031 +sphinx 21211 1384728 +jjoshua2 21001 1423089 +horst.prack 20878 1465656 +Ente 20865 1477066 +0xB00B1ES 20590 1208666 +j3corre 20405 941444 +Adrian.Schmidt123 20316 1281436 +wei 19973 1745989 +MaxKlaxxMiner 19850 1009176 +rstoesser 19569 1293588 +gopeto 19491 1174952 +eudhan 19274 1283717 +jundery 18445 1115855 +megaman7de 18377 1067540 +iisiraider 18247 1101015 +ville 17883 1384026 +chris 17698 1487385 +purplefishies 17595 1092533 +dju 17353 978595 +DragonLord 17014 1162790 +IgorLeMasson 16064 1147232 +ako027ako 15671 1173203 +chuckstablers 15289 891576 +Nikolay.IT 15154 1068349 +Andrew Grant 15114 895539 +OssumOpossum 14857 1007129 +Karby 14808 867120 +enedene 14476 905279 +bpfliegel 14298 884523 +mpx86 14019 759568 +jpulman 13982 870599 +crocogoat 13803 1117422 +joster 13794 950160 +Nesa92 13786 1114691 +Hjax 13535 915487 +jsys14 13459 785000 +Dark_wizzie 13422 1007152 +mabichito 12903 749391 +thijsk 12886 722107 +AdrianSA 12860 804972 +Flopzee 12698 894821 +fatmurphy 12547 853210 +Rudolphous 12520 832340 +scuzzi 12511 845761 +SapphireBrand 12416 969604 +modolief 12386 896470 +Machariel 12335 810784 +pgontarz 12151 848794 +stocky 11954 699440 +mschmidt 11941 803401 +Maxim 11543 836024 +infinity 11470 727027 +torbjo 11395 729145 +Thomas A. Anderson 11372 732094 +savage84 11358 670860 +d64 11263 789184 +MooTheCow 11237 720174 +snicolet 11106 869170 +ali-al-zhrani 11086 767926 +AndreasKrug 10875 887457 +pirt 10806 836519 +basepi 10637 744851 +michaelrpg 10508 739039 +dzjp 10343 732529 +aga 10302 622975 +ols 10259 570669 +lbraesch 10252 647825 +FormazChar 10059 757283 From 773dff020968f7a6f590cfd53e8fd89f12e15e36 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 28 Jun 2021 21:46:04 +0200 Subject: [PATCH 111/282] Stockfish 14 Official release version of Stockfish 14 Bench: 4770936 --- Today, we have the pleasure to announce Stockfish 14. As usual, downloads will be freely available at https://stockfishchess.org The engine is now significantly stronger than just a few months ago, and wins four times more game pairs than it loses against the previous release version [0]. Stockfish 14 is now at least 400 Elo ahead of Stockfish 7, a top engine in 2016 [1]. During the last five years, Stockfish has thus gained about 80 Elo per year. Stockfish 14 evaluates positions more accurately than Stockfish 13 as a result of two major steps forward in defining and training the efficiently updatable neural network (NNUE) that provides the evaluation for positions. First, the collaboration with the Leela Chess Zero team - announced previously [2] - has come to fruition. The LCZero team has provided a collection of billions of positions evaluated by Leela that we have combined with billions of positions evaluated by Stockfish to train the NNUE net that powers Stockfish 14. The fact that we could use and combine these datasets freely was essential for the progress made and demonstrates the power of open source and open data [3]. Second, the architecture of the NNUE network was significantly updated: the new network is not only larger, but more importantly, it deals better with large material imbalances and can specialize for multiple phases of the game [4]. A new project, kick-started by Gary Linscott and Tomasz Sobczyk, led to a GPU accelerated net trainer written in pytorch.[5] This tool allows for training high-quality nets in a couple of hours. Finally, this release features some search refinements, minor bug fixes and additional improvements. For example, Stockfish is now about 90 Elo stronger for chess960 (Fischer random chess) at short time control. The Stockfish project builds on a thriving community of enthusiasts (thanks everybody!) that contribute their expertise, time, and resources to build a free and open-source chess engine that is robust, widely available, and very strong. We invite our chess fans to join the fishtest testing framework and programmers to contribute to the project on github [6]. Stay safe and enjoy chess! The Stockfish team [0] https://tests.stockfishchess.org/tests/view/60dae5363beab81350aca077 [1] https://nextchessmove.com/dev-builds [2] https://stockfishchess.org/blog/2021/stockfish-13/ [3] https://lczero.org/blog/2021/06/the-importance-of-open-data/ [4] https://github.com/official-stockfish/Stockfish/commit/e8d64af1 [5] https://github.com/glinscott/nnue-pytorch/ [6] https://stockfishchess.org/get-involved/ --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 9e7b7e37..78227ee2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,7 +67,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = ""; +const string Version = "14"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 7cfc1f9b150d387788a9b02360e49ba2a56505f7 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 3 Jul 2021 09:20:06 +0200 Subject: [PATCH 112/282] Restore development version No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 78227ee2..9e7b7e37 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,7 +67,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = "14"; +const string Version = ""; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From b51b094419e301b16ce8f639952993dd9abfcc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 23 Jun 2021 09:55:42 +0200 Subject: [PATCH 113/282] Simplify format_cp_aligned_dot() closes https://github.com/official-stockfish/Stockfish/pull/3583 No functional change --- src/nnue/evaluate_nnue.cpp | 65 ++++++++++++-------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 8828ae51..891f8faa 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -227,69 +227,46 @@ namespace Stockfish::Eval::NNUE { static const std::string PieceToChar(" PNBRQK pnbrqk"); - // Requires the buffer to have capacity for at least 5 values + + // format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer. + // The buffer must have capacity for at least 5 chars. static void format_cp_compact(Value v, char* buffer) { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); int cp = std::abs(100 * v / PawnValueEg); - if (cp >= 10000) { - buffer[1] = '0' + cp / 10000; cp %= 10000; - buffer[2] = '0' + cp / 1000; cp %= 1000; - buffer[3] = '0' + cp / 100; cp %= 100; - buffer[4] = ' '; + buffer[1] = '0' + cp / 10000; cp %= 10000; + buffer[2] = '0' + cp / 1000; cp %= 1000; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = ' '; } else if (cp >= 1000) { - buffer[1] = '0' + cp / 1000; cp %= 1000; - buffer[2] = '0' + cp / 100; cp %= 100; - buffer[3] = '.'; - buffer[4] = '0' + cp / 10; + buffer[1] = '0' + cp / 1000; cp %= 1000; + buffer[2] = '0' + cp / 100; cp %= 100; + buffer[3] = '.'; + buffer[4] = '0' + cp / 10; } else { - buffer[1] = '0' + cp / 100; cp %= 100; - buffer[2] = '.'; - buffer[3] = '0' + cp / 10; cp %= 10; - buffer[4] = '0' + cp / 1; + buffer[1] = '0' + cp / 100; cp %= 100; + buffer[2] = '.'; + buffer[3] = '0' + cp / 10; cp %= 10; + buffer[4] = '0' + cp / 1; } } - // Requires the buffer to have capacity for at least 7 values + + // format_cp_aligned_dot() converts a Value into (centi)pawns and writes it in a buffer, + // always keeping two decimals. The buffer must have capacity for at least 7 chars. static void format_cp_aligned_dot(Value v, char* buffer) { + buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = std::abs(100 * v / PawnValueEg); - - if (cp >= 10000) - { - buffer[1] = '0' + cp / 10000; cp %= 10000; - buffer[2] = '0' + cp / 1000; cp %= 1000; - buffer[3] = '0' + cp / 100; cp %= 100; - buffer[4] = '.'; - buffer[5] = '0' + cp / 10; cp %= 10; - buffer[6] = '0' + cp; - } - else if (cp >= 1000) - { - buffer[1] = ' '; - buffer[2] = '0' + cp / 1000; cp %= 1000; - buffer[3] = '0' + cp / 100; cp %= 100; - buffer[4] = '.'; - buffer[5] = '0' + cp / 10; cp %= 10; - buffer[6] = '0' + cp; - } - else - { - buffer[1] = ' '; - buffer[2] = ' '; - buffer[3] = '0' + cp / 100; cp %= 100; - buffer[4] = '.'; - buffer[5] = '0' + cp / 10; cp %= 10; - buffer[6] = '0' + cp / 1; - } + double cp = 1.0 * std::abs(int(v)) / PawnValueEg; + sprintf(&buffer[1], "%6.2f", cp); } From d297d1d8a78166b609af112b6208ace7c645b2f3 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Wed, 30 Jun 2021 09:22:59 +0100 Subject: [PATCH 114/282] Simplify lazy_skip. Small speedup by removing operations in lazy_skip. STC 10+0.1 : LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 55088 W: 4553 L: 4482 D: 46053 Ptnml(0-2): 163, 3546, 20045, 3637, 153 https://tests.stockfishchess.org/tests/view/60daa2cb3beab81350aca04d LTC 60+0.6 : LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 46136 W: 1457 L: 1407 D: 43272 Ptnml(0-2): 10, 1282, 20442, 1316, 18 https://tests.stockfishchess.org/tests/view/60db0e753beab81350aca08e closes https://github.com/official-stockfish/Stockfish/pull/3599 Bench 5122403 --- src/evaluate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f9754795..538214d3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -190,8 +190,8 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(1565); - constexpr Value LazyThreshold2 = Value(1102); + constexpr Value LazyThreshold1 = Value(3130); + constexpr Value LazyThreshold2 = Value(2204); constexpr Value SpaceThreshold = Value(11551); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -986,7 +986,7 @@ namespace { // Early exit if score is high auto lazy_skip = [&](Value lazyThreshold) { - return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64; + return abs(mg_value(score) + eg_value(score)) > lazyThreshold + pos.non_pawn_material() / 32; }; if (lazy_skip(LazyThreshold1)) From ec8dfe7315c865053afb198af2f1231a551c2c26 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Thu, 1 Jul 2021 19:51:41 +0200 Subject: [PATCH 115/282] no cut node reduction for killer moves. stc: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 44344 W: 3474 L: 3294 D: 37576 Ptnml(0-2): 117, 2710, 16338, 2890, 117 https://tests.stockfishchess.org/tests/view/60d8ea673beab81350ac9eb8 ltc: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 82600 W: 2638 L: 2441 D: 77521 Ptnml(0-2): 38, 2147, 36749, 2312, 54 https://tests.stockfishchess.org/tests/view/60d9048f3beab81350ac9eed closes https://github.com/official-stockfish/Stockfish/pull/3600 Bench: 5160239 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a413bd38..5974cba7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1157,8 +1157,8 @@ moves_loop: // When in check, search starts from here r--; // Increase reduction for cut nodes (~3 Elo) - if (cutNode) - r += 1 + !captureOrPromotion; + if (cutNode && move != ss->killers[0]) + r += 2; if (!captureOrPromotion) { From 516ad1c9bf7dceeeb055e250d8cd83598c01a531 Mon Sep 17 00:00:00 2001 From: Paul Mulders Date: Tue, 29 Jun 2021 11:13:54 +0200 Subject: [PATCH 116/282] Allow passing RTLIB=compiler-rt to make Not all linux users will have libatomic installed. When using clang as the system compiler with compiler-rt as the default runtime library instead of libgcc, atomic builtins may be provided by compiler-rt. This change allows such users to pass RTLIB=compiler-rt to make sure the build doesn't error out on the missing (unnecessary) libatomic. closes https://github.com/official-stockfish/Stockfish/pull/3597 No functional change --- src/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Makefile b/src/Makefile index bf3c3560..1d972cff 100644 --- a/src/Makefile +++ b/src/Makefile @@ -386,10 +386,12 @@ ifeq ($(COMP),clang) ifneq ($(KERNEL),Darwin) ifneq ($(KERNEL),OpenBSD) ifneq ($(KERNEL),FreeBSD) + ifneq ($(RTLIB),compiler-rt) LDFLAGS += -latomic endif endif endif + endif ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(OS),Android) From 8fc297c50647317185d4c41b3443a0e686412681 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sat, 3 Jul 2021 06:13:13 +0800 Subject: [PATCH 117/282] Update default net to nn-9e3c6298299a.nnue Optimization of nn-956480d8378f.nnue using SPSA https://tests.stockfishchess.org/tests/view/60da2bf63beab81350ac9fe7 Same method as described in PR #3593 STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 17792 W: 1525 L: 1372 D: 14895 Ptnml(0-2): 28, 1156, 6401, 1257, 54 https://tests.stockfishchess.org/tests/view/60deffc59ea99d7c2d693c19 LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 36544 W: 1245 L: 1109 D: 34190 Ptnml(0-2): 12, 988, 16139, 1118, 15 https://tests.stockfishchess.org/tests/view/60df11339ea99d7c2d693c22 closes https://github.com/official-stockfish/Stockfish/pull/3601 Bench: 4687476 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 91da01da..54f20baf 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-3475407dc199.nnue" + #define EvalFileDefaultName "nn-9e3c6298299a.nnue" namespace NNUE { From 09b6d28391cf582d99897360b225bcbbe38dd1c6 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 6 Jul 2021 20:44:50 +0300 Subject: [PATCH 118/282] Remove futility pruning depth limit This patch removes futility pruning depth limit for child node futility pruning. In current master it was double capped by depth and by futility margin, which is also a function of depth, which didn't make much sense. passed STC https://tests.stockfishchess.org/tests/view/60e2418f9ea99d7c2d693e64 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 116168 W: 9100 L: 9097 D: 97971 Ptnml(0-2): 319, 7496, 42476, 7449, 344 passed LTC https://tests.stockfishchess.org/tests/view/60e3374f9ea99d7c2d693f20 LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 43304 W: 1282 L: 1231 D: 40791 Ptnml(0-2): 8, 1126, 19335, 1173, 10 closes https://github.com/official-stockfish/Stockfish/pull/3606 bench 4965493 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 5974cba7..008a60ed 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -780,7 +780,6 @@ namespace { // Step 7. Futility pruning: child node (~50 Elo) if ( !PvNode - && depth < 9 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; From f4986f45967c613d92657f46b5a50f12dae15398 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Wed, 7 Jul 2021 14:32:54 +0200 Subject: [PATCH 119/282] SEE: simplify stm variable initialization Pull #3458 removed the only usage of pos.see_ge() moving pieces that don't belong to the side to move, so we can simplify this, adding an assert. closes https://github.com/official-stockfish/Stockfish/pull/3607 No functional change --- src/position.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/position.cpp b/src/position.cpp index ba015d3c..0686d245 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1080,8 +1080,9 @@ bool Position::see_ge(Move m, Value threshold) const { if (swap <= 0) return true; + assert(color_of(piece_on(from)) == sideToMove); Bitboard occupied = pieces() ^ from ^ to; - Color stm = color_of(piece_on(from)); + Color stm = sideToMove; Bitboard attackers = attackers_to(to, occupied); Bitboard stmAttackers, bb; int res = 1; From dbd7f602d3c7622df294f87d7239b5aaf31f695f Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 11 Jul 2021 00:09:15 +0300 Subject: [PATCH 120/282] Remove second futility pruning depth limit This patch removes futility pruning lmrDepth limit for futility pruning at parent nodes. Since it's already capped by margin that is a function of lmrDepth there is no need to extra cap it with lmrDepth. passed STC https://tests.stockfishchess.org/tests/view/60e9b5dfd1189bed71812777 LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 14872 W: 1264 L: 1145 D: 12463 Ptnml(0-2): 37, 942, 5369, 1041, 47 passed LTC https://tests.stockfishchess.org/tests/view/60e9c635d1189bed71812790 LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 40336 W: 1280 L: 1225 D: 37831 Ptnml(0-2): 24, 1057, 17960, 1094, 33 closes https://github.com/official-stockfish/Stockfish/pull/3612 bench: 5064969 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 008a60ed..3cf50eb5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1021,8 +1021,7 @@ moves_loop: // When in check, search starts from here continue; // Futility pruning: parent node (~5 Elo) - if ( lmrDepth < 7 - && !ss->inCheck + if ( !ss->inCheck && ss->staticEval + 174 + 157 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From 36f8d3806bb1c6e498ac8fd1a746c1714d1485a3 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Mon, 12 Jul 2021 14:44:29 -0400 Subject: [PATCH 121/282] Don't save excluded move eval in TT STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 17544 W: 1384 L: 1236 D: 14924 Ptnml(0-2): 37, 1031, 6499, 1157, 48 https://tests.stockfishchess.org/tests/view/60ec8d9bd1189bed71812999 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 26136 W: 823 L: 707 D: 24606 Ptnml(0-2): 6, 643, 11656, 755, 8 https://tests.stockfishchess.org/tests/view/60ecb11ed1189bed718129ba closes https://github.com/official-stockfish/Stockfish/pull/3614 Bench: 5505251 --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index 3cf50eb5..fef1b518 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -760,6 +760,7 @@ namespace { ss->staticEval = eval = -(ss-1)->staticEval; // Save static evaluation into transposition table + if(!excludedMove) tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } From bc654257e74af3ffe837c0fff6e663a073ab8bbf Mon Sep 17 00:00:00 2001 From: Liam Keegan Date: Wed, 21 Jul 2021 09:33:13 +0200 Subject: [PATCH 122/282] Add macOS and windows to CI - macOS - system clang - gcc - windows / msys2 - mingw 64-bit gcc - mingw 32-bit gcc - minor code fixes to get new CI jobs to pass - code: suppress unused-parameter warning on 32-bit windows - Makefile: if arch=any on macos, don't specify arch at all fixes https://github.com/official-stockfish/Stockfish/issues/2958 closes https://github.com/official-stockfish/Stockfish/pull/3623 No functional change --- .github/workflows/stockfish.yml | 82 +++++++++++++++++++++++++++++++-- AUTHORS | 1 + src/Makefile | 8 +++- src/misc.cpp | 1 + 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 8970fcd1..54b0cb12 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -25,29 +25,88 @@ jobs: os: ubuntu-20.04, compiler: g++, comp: gcc, - run_expensive_tests: true + run_expensive_tests: true, + run_32bit_tests: true, + run_64bit_tests: true, + shell: 'bash {0}' } - { name: "Ubuntu 20.04 Clang", os: ubuntu-20.04, compiler: clang++, comp: clang, - run_expensive_tests: false + run_expensive_tests: false, + run_32bit_tests: true, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "MacOS 10.15 Apple Clang", + os: macos-10.15, + compiler: clang++, + comp: clang, + run_expensive_tests: false, + run_32bit_tests: false, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "MacOS 10.15 GCC 10", + os: macos-10.15, + compiler: g++-10, + comp: gcc, + run_expensive_tests: false, + run_32bit_tests: false, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "Windows 2019 Mingw-w64 GCC x86_64", + os: windows-2019, + compiler: g++, + comp: gcc, + run_expensive_tests: false, + run_32bit_tests: false, + run_64bit_tests: true, + msys_sys: 'mingw64', + msys_env: 'x86_64', + shell: 'msys2 {0}' + } + - { + name: "Windows 2019 Mingw-w64 GCC i686", + os: windows-2019, + compiler: g++, + comp: gcc, + run_expensive_tests: false, + run_32bit_tests: true, + run_64bit_tests: false, + msys_sys: 'mingw32', + msys_env: 'i686', + shell: 'msys2 {0}' } defaults: run: working-directory: src + shell: ${{ matrix.config.shell }} steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Download required packages + - name: Download required linux packages + if: runner.os == 'Linux' run: | sudo apt update sudo apt install expect valgrind g++-multilib + - name: Setup msys and install required packages + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.config.msys_sys}} + install: mingw-w64-${{matrix.config.msys_env}}-gcc make git expect + - name: Download the used network from the fishtest framework run: | make net @@ -68,6 +127,7 @@ jobs: # x86-32 tests - name: Test debug x86-32 build + if: ${{ matrix.config.run_32bit_tests }} run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean @@ -75,24 +135,28 @@ jobs: ../tests/signature.sh $benchref - name: Test x86-32 build + if: ${{ matrix.config.run_32bit_tests }} run: | make clean make -j2 ARCH=x86-32 build ../tests/signature.sh $benchref - name: Test x86-32-sse41-popcnt build + if: ${{ matrix.config.run_32bit_tests }} run: | make clean make -j2 ARCH=x86-32-sse41-popcnt build ../tests/signature.sh $benchref - name: Test x86-32-sse2 build + if: ${{ matrix.config.run_32bit_tests }} run: | make clean make -j2 ARCH=x86-32-sse2 build ../tests/signature.sh $benchref - name: Test general-32 build + if: ${{ matrix.config.run_32bit_tests }} run: | make clean make -j2 ARCH=general-32 build @@ -101,6 +165,7 @@ jobs: # x86-64 tests - name: Test debug x86-64-modern build + if: ${{ matrix.config.run_64bit_tests }} run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean @@ -108,30 +173,35 @@ jobs: ../tests/signature.sh $benchref - name: Test x86-64-modern build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-modern build ../tests/signature.sh $benchref - name: Test x86-64-ssse3 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-ssse3 build ../tests/signature.sh $benchref - name: Test x86-64-sse3-popcnt build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-sse3-popcnt build ../tests/signature.sh $benchref - name: Test x86-64 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64 build ../tests/signature.sh $benchref - name: Test general-64 build + if: matrix.config.run_64bit_tests run: | make clean make -j2 ARCH=general-64 build @@ -140,26 +210,31 @@ jobs: # x86-64 with newer extensions tests - name: Compile x86-64-avx2 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-avx2 build - name: Compile x86-64-bmi2 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-bmi2 build - name: Compile x86-64-avx512 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-avx512 build - name: Compile x86-64-vnni512 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-vnni512 build - name: Compile x86-64-vnni256 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-vnni256 build @@ -167,6 +242,7 @@ jobs: # Other tests - name: Check perft and search reproducibility + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-modern build diff --git a/AUTHORS b/AUTHORS index 7e63591a..18471d4d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -107,6 +107,7 @@ Kojirion Krystian Kuzniarek (kuzkry) Leonardo Ljubičić (ICCF World Champion) Leonid Pechenik (lp--) +Liam Keegan (lkeegan) Linus Arver (listx) loco-loco Lub van den Berg (ElbertoOne) diff --git a/src/Makefile b/src/Makefile index 1d972cff..b021ba12 100644 --- a/src/Makefile +++ b/src/Makefile @@ -405,8 +405,12 @@ ifeq ($(COMP),clang) endif ifeq ($(KERNEL),Darwin) - CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14 - LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14 + CXXFLAGS += -mmacosx-version-min=10.14 + LDFLAGS += -mmacosx-version-min=10.14 + ifneq ($(arch),any) + CXXFLAGS += -arch $(arch) + LDFLAGS += -arch $(arch) + endif XCRUN = xcrun endif diff --git a/src/misc.cpp b/src/misc.cpp index 9e7b7e37..3b071ccb 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -378,6 +378,7 @@ void std_aligned_free(void* ptr) { static void* aligned_large_pages_alloc_windows(size_t allocSize) { #if !defined(_WIN64) + (void)allocSize; // suppress unused-parameter compiler warning return nullptr; #else From d957179df7285f8d032803661b378b7f1a80382e Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 18 Jul 2021 13:51:14 +0300 Subject: [PATCH 123/282] Prune illegal moves in qsearch earlier The main idea is that illegal moves influencing search or qsearch obviously can't be any sort of good. The only reason why initially legality checks for search and qsearch were done after they actually can influence some heuristics is because legality check is expensive computationally. Eventually in search it was moved to the place where it makes sure that illegal moves can't influence search. This patch shows that the same can be done for qsearch + it passed STC with elo-gaining bounds + it removes 3 lines of code because one no longer needs to increment/decrement movecount on illegal moves. passed STC with elo-gaining bounds https://tests.stockfishchess.org/tests/view/60f20aefd1189bed71812da0 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 61512 W: 4688 L: 4492 D: 52332 Ptnml(0-2): 139, 3730, 22848, 3874, 165 The same version functionally but with moving condition ever earlier passed LTC with simplification bounds. https://tests.stockfishchess.org/tests/view/60f292cad1189bed71812de9 LLR: 2.98 (-2.94,2.94) <-2.50,0.50> Total: 60944 W: 1724 L: 1685 D: 57535 Ptnml(0-2): 11, 1556, 27298, 1597, 10 closes https://github.com/official-stockfish/Stockfish/pull/3618 bench 4709569 --- src/search.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fef1b518..66402f04 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1472,6 +1472,10 @@ moves_loop: // When in check, search starts from here { assert(is_ok(move)); + // Check for legality + if (!pos.legal(move)) + continue; + givesCheck = pos.gives_check(move); captureOrPromotion = pos.capture_or_promotion(move); @@ -1510,13 +1514,6 @@ moves_loop: // When in check, search starts from here // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); - // Check for legality just before making the move - if (!pos.legal(move)) - { - moveCount--; - continue; - } - ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [captureOrPromotion] From 760b7462bc89e4202ef6b43dc052b4364167ed4d Mon Sep 17 00:00:00 2001 From: pb00067 Date: Thu, 15 Jul 2021 20:56:21 +0200 Subject: [PATCH 124/282] Simplify lowply-history scoring logic STC: https://tests.stockfishchess.org/tests/view/60eee559d1189bed71812b16 LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 33976 W: 2523 L: 2431 D: 29022 Ptnml(0-2): 66, 2030, 12730, 2070, 92 LTC: https://tests.stockfishchess.org/tests/view/60eefa12d1189bed71812b24 LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 107240 W: 3053 L: 3046 D: 101141 Ptnml(0-2): 56, 2668, 48154, 2697, 45 closes https://github.com/official-stockfish/Stockfish/pull/3616 bench: 5199177 --- src/movepick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 4ff4cff4..20640fe2 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -111,7 +111,7 @@ void MovePicker::score() { + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] - + (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0); + + (ply < MAX_LPH ? 6 * (*lowPlyHistory)[ply][from_to(m)] : 0); else // Type == EVASIONS { From a85928e7ecf36431aee5bf48d46bfea4e6085cb7 Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Sun, 18 Jul 2021 20:14:11 +0200 Subject: [PATCH 125/282] Apply good/bad history reduction also when inCheck Main idea is that, in some cases, 'in check' situations are not so different from 'not in check' ones. Trying to use piece count in order to select only a few 'in check' situations have failed LTC testing. It could be interesting to apply one of those ideas in other parts of the search function. passed STC: https://tests.stockfishchess.org/tests/view/60f1b68dd1189bed71812d40 LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 53472 W: 4078 L: 4008 D: 45386 Ptnml(0-2): 127, 3297, 19795, 3413, 104 passed LTC: https://tests.stockfishchess.org/tests/view/60f291e6d1189bed71812de3 LLR: 2.92 (-2.94,2.94) <-2.50,0.50> Total: 89712 W: 2651 L: 2632 D: 84429 Ptnml(0-2): 60, 2261, 40188, 2294, 53 closes https://github.com/official-stockfish/Stockfish/pull/3619 Bench: 5185789 --- AUTHORS | 1 + src/search.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 18471d4d..4d72314f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -69,6 +69,7 @@ gamander Gary Heckman (gheckman) George Sobala (gsobala) gguliash +Giacomo Lorenzetti (G-Lorenz) Gian-Carlo Pascutto (gcp) Gontran Lemaire (gonlem) Goodkov Vasiliy Aleksandrovich (goodkov) diff --git a/src/search.cpp b/src/search.cpp index 66402f04..c41df1f0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1172,8 +1172,7 @@ moves_loop: // When in check, search starts from here - 4923; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - if (!ss->inCheck) - r -= ss->statScore / 14721; + r -= ss->statScore / 14721; } // In general we want to cap the LMR depth search at newDepth. But if From b939c805139e4b37f04fbf177f580c35ebe9f130 Mon Sep 17 00:00:00 2001 From: MichaelB7 Date: Sat, 24 Jul 2021 08:42:00 -0400 Subject: [PATCH 126/282] Update the default net to nn-76a8a7ffb820.nnue. combined work by Serio Vieri, Michael Byrne, and Jonathan D (aka SFisGod) based on top of previous developments, by restarts from good nets. Sergio generated the net https://tests.stockfishchess.org/api/nn/nn-d8609abe8caf.nnue: The initial net nn-d8609abe8caf.nnue is trained by generating around 16B of training data from the last master net nn-9e3c6298299a.nnue, then trained, continuing from the master net, with lambda=0.2 and sampling ratio of 1. Starting with LR=2e-3, dropping LR with a factor of 0.5 until it reaches LR=5e-4. in_scaling is set to 361. No other significant changes made to the pytorch trainer. Training data gen command (generates in chunks of 200k positions): generate_training_data min_depth 9 max_depth 11 count 200000 random_move_count 10 random_move_max_ply 80 random_multi_pv 12 random_multi_pv_diff 100 random_multi_pv_depth 8 write_min_ply 10 eval_limit 1500 book noob_3moves.epd output_file_name gendata/$(date +"%Y%m%d-%H%M")_${HOSTNAME}.binpack PyTorch trainer command (Note that this only trains for 20 epochs, repeatedly train until convergence): python train.py --features "HalfKAv2^" --max_epochs 20 --smart-fen-skipping --random-fen-skipping 500 --batch-size 8192 --default_root_dir $dir --seed $RANDOM --threads 4 --num-workers 32 --gpus $gpuids --track_grad_norm 2 --gradient_clip_val 0.05 --lambda 0.2 --log_every_n_steps 50 $resumeopt $data $val See https://github.com/sergiovieri/Stockfish/tree/tools_mod/rl for the scripts used to generate data. Based on that Michael generated nn-76a8a7ffb820.nnue in the following way: The net being submitted was trained with the pytorch trainer: https://github.com/glinscott/nnue-pytorch python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 30 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --auto_lr_find True --lambda=1.0 --max_epochs=240 --seed %random%%random% --default_root_dir exp/run_109 --resume-from-model ./pt/nn-d8609abe8caf.pt This run is thus started from Segio Vieri's net nn-d8609abe8caf.nnue all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - making one large Wrong_NNUE 2 binpack and one large Training so the were approximately equal in size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training_Data binpack model.py modifications: loss = torch.pow(torch.abs(p - q), 2.6).mean() LR = 8.0e-5 calculated as follows: 1.5e-3*(.992^360) - the idea here was to take a highly trained net and just use all.binpack as a finishing micro refinement touch for the last 2 Elo or so. This net was discovered on the 59th epoch. optimizer = ranger.Ranger(train_params, betas=(.90, 0.999), eps=1.0e-7, gc_loc=False, use_gc=False) scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.992) For this micro optimization, I had set the period to "5" in train.py. This changes the checkpoint output so that every 5th checkpoint file is created The final touches were to adjust the NNUE scale, as was done by Jonathan in tests running at the same time. passed LTC https://tests.stockfishchess.org/tests/view/60fa45aed8a6b65b2f3a77a4 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 53040 W: 1732 L: 1575 D: 49733 Ptnml(0-2): 14, 1432, 23474, 1583, 17 passed STC https://tests.stockfishchess.org/tests/view/60f9fee2d8a6b65b2f3a7775 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 37928 W: 3178 L: 3001 D: 31749 Ptnml(0-2): 100, 2446, 13695, 2623, 100. closes https://github.com/official-stockfish/Stockfish/pull/3626 Bench: 5169957 --- src/evaluate.cpp | 4 ++-- src/evaluate.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 538214d3..64f91725 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1090,7 +1090,7 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&]() { - int scale = 903 + int scale = 883 + 32 * pos.count() + 32 * pos.non_pawn_material() / 1024; @@ -1106,7 +1106,7 @@ Value Eval::evaluate(const Position& pos) { // NNUE eval faster when shuffling or if the material on the board is high. int r50 = pos.rule50_count(); Value psq = Value(abs(eg_value(pos.psq_score()))); - bool classical = psq * 5 > (750 + pos.non_pawn_material() / 64) * (5 + r50); + bool classical = psq * 5 > (850 + pos.non_pawn_material() / 64) * (5 + r50); v = classical ? Evaluation(pos).value() // classical : adjusted_NNUE(); // NNUE diff --git a/src/evaluate.h b/src/evaluate.h index 54f20baf..0a580c61 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-9e3c6298299a.nnue" + #define EvalFileDefaultName "nn-76a8a7ffb820.nnue" namespace NNUE { From 910d26b5c30bb68845e032a8f2aac82f19c96262 Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Sat, 24 Jul 2021 22:03:29 +0200 Subject: [PATCH 127/282] Simplification in LMR This commit removes the `!captureOrPromotion` condition from ttCapture reduction and from good/bad history reduction (similar to #3619). passed STC: https://tests.stockfishchess.org/tests/view/60fc734ad8a6b65b2f3a7922 LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 48680 W: 3855 L: 3776 D: 41049 Ptnml(0-2): 118, 3145, 17744, 3206, 127 passed LTC: https://tests.stockfishchess.org/tests/view/60fce7d5d8a6b65b2f3a794c LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 86528 W: 2471 L: 2450 D: 81607 Ptnml(0-2): 28, 2203, 38777, 2232, 24 closes https://github.com/official-stockfish/Stockfish/pull/3629 Bench: 4951406 --- src/search.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c41df1f0..f412d9d6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,21 +1159,18 @@ moves_loop: // When in check, search starts from here if (cutNode && move != ss->killers[0]) r += 2; - if (!captureOrPromotion) - { - // Increase reduction if ttMove is a capture (~3 Elo) - if (ttCapture) - r++; + // Increase reduction if ttMove is a capture (~3 Elo) + if (ttCapture) + r++; - ss->statScore = thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - - 4923; + ss->statScore = thisThread->mainHistory[us][from_to(move)] + + (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)] + - 4923; - // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 14721; - } + // Decrease/increase reduction for moves with a good/bad history (~30 Elo) + r -= ss->statScore / 14721; // In general we want to cap the LMR depth search at newDepth. But if // reductions are really negative and movecount is low, we allow this move From 237ed1ef8fddea77779e7fdcde8e4195d92f123b Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sun, 25 Jul 2021 19:43:25 +0800 Subject: [PATCH 128/282] Update default net to nn-26abeed38351.nnue SPSA: https://tests.stockfishchess.org/tests/view/60fba335d8a6b65b2f3a7891 New best values: Half of the changes from the tuning run. Setting: nodestime=300 with 10+0.1 (approximate real TC is 2.5 seconds) The rest is the same as described in #3593 The change from nodestime=600 to 300 was suggested by gekkehenker to prevent time losses for some slow workers SFisGOD@94cd757#commitcomment-53324840 STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 67448 W: 5241 L: 5036 D: 57171 Ptnml(0-2): 151, 4198, 24827, 4391, 157 https://tests.stockfishchess.org/tests/view/60fd50f2d8a6b65b2f3a798e LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 48752 W: 1504 L: 1358 D: 45890 Ptnml(0-2): 13, 1226, 21754, 1368, 15 https://tests.stockfishchess.org/tests/view/60fd7bb2d8a6b65b2f3a79a9 Closes https://github.com/official-stockfish/Stockfish/pull/3630 Bench: 5124774 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 0a580c61..aa5c6260 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-76a8a7ffb820.nnue" + #define EvalFileDefaultName "nn-26abeed38351.nnue" namespace NNUE { From e973eee919a8d450f095d102a0d52c196a8e7793 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Wed, 28 Jul 2021 00:43:58 +0800 Subject: [PATCH 129/282] Update default net to nn-56a5f1c4173a.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/60fd24efd8a6b65b2f3a796e Parameters: A total of 256 net biases were tuned (hidden layer 2) New best values: Half of the changes from the tuning run New net: nn-5992d3ba79f3.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/60fec7d6d8a6b65b2f3a7aa2 Parameters: A total of 128 net biases were tuned (hidden layer 1) New best values: Half of the changes from the tuning run New net: nn-56a5f1c4173a.nnue STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 140392 W: 10863 L: 10578 D: 118951 Ptnml(0-2): 347, 8754, 51718, 9021, 356 https://tests.stockfishchess.org/tests/view/610037e396b86d98abf6a79e LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 14216 W: 454 L: 355 D: 13407 Ptnml(0-2): 4, 323, 6356, 420, 5 https://tests.stockfishchess.org/tests/view/61019995afad2da4f4ae3a3c Closes #3633 Bench: 4801359 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index aa5c6260..e3730c8a 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-26abeed38351.nnue" + #define EvalFileDefaultName "nn-56a5f1c4173a.nnue" namespace NNUE { From 26edf9534ad571a6d26bf9db47d21776cbf45d54 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 27 Jul 2021 22:12:14 +0200 Subject: [PATCH 130/282] Avoid unnecessary stores in the affine transform This patch improves the codegen in the AffineTransform::forward function for architectures >=SSSE3. Current code works directly on memory and the compiler cannot see that the stores through outptr do not alias the loads through weights and input32. The solution implemented is to perform the affine transform with local variables as accumulators and only store the result to memory at the end. The number of accumulators required is OutputDimensions / OutputSimdWidth, which means that for the 1024->16 affine transform it requires 4 registers with SSSE3, 2 with AVX2, 1 with AVX512. It also cuts the number of stores required by NumRegs * 256 for each node evaluated. The local accumulators are expected to be assigned to registers, but even if this cannot be done in some case due to register pressure it will help the compiler to see that there is no aliasing between the loads and stores and may still result in better codegen. See https://godbolt.org/z/59aTKbbYc for codegen comparison. passed STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 140328 W: 10635 L: 10358 D: 119335 Ptnml(0-2): 302, 8339, 52636, 8554, 333 closes https://github.com/official-stockfish/Stockfish/pull/3634 No functional change --- src/nnue/layers/affine_transform.h | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 9a3b778e..9a5f62c0 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -251,9 +251,6 @@ namespace Stockfish::Eval::NNUE::Layers { #endif #if defined (USE_SSSE3) - // Different layout, we process 4 inputs at a time, always. - static_assert(InputDimensions % 4 == 0); - const auto output = reinterpret_cast(buffer); const auto inputVector = reinterpret_cast(input); @@ -263,13 +260,18 @@ namespace Stockfish::Eval::NNUE::Layers { // because then it is also an input dimension. if constexpr (OutputDimensions % OutputSimdWidth == 0) { + static_assert(InputDimensions % 16 == 0); + constexpr IndexType NumChunks = InputDimensions / 4; + constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; const auto input32 = reinterpret_cast(input); - vec_t* outptr = reinterpret_cast(output); - std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); + const vec_t* biasvec = reinterpret_cast(biases); + vec_t outs[NumRegs]; + for (IndexType k = 0; k < NumRegs; ++k) + outs[k] = biasvec[k]; - for (int i = 0; i < (int)NumChunks - 3; i += 4) + for (IndexType i = 0; i < NumChunks; i += 4) { const vec_t in0 = vec_set_32(input32[i + 0]); const vec_t in1 = vec_set_32(input32[i + 1]); @@ -279,12 +281,18 @@ namespace Stockfish::Eval::NNUE::Layers { const auto col1 = reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); const auto col2 = reinterpret_cast(&weights[(i + 2) * OutputDimensions * 4]); const auto col3 = reinterpret_cast(&weights[(i + 3) * OutputDimensions * 4]); - for (int j = 0; j * OutputSimdWidth < OutputDimensions; ++j) - vec_add_dpbusd_32x4(outptr[j], in0, col0[j], in1, col1[j], in2, col2[j], in3, col3[j]); + for (IndexType k = 0; k < NumRegs; ++k) + vec_add_dpbusd_32x4(outs[k], in0, col0[k], in1, col1[k], in2, col2[k], in3, col3[k]); } + + vec_t* outptr = reinterpret_cast(output); + for (IndexType k = 0; k < NumRegs; ++k) + outptr[k] = outs[k]; } else if constexpr (OutputDimensions == 1) { + static_assert(InputDimensions % 4 == 0); + #if defined (USE_AVX512) if constexpr (PaddedInputDimensions % (SimdWidth * 2) != 0) { From a0fca67da4c5ae21bcc783acc03f3da2a6fbcd80 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sat, 31 Jul 2021 08:18:49 -0400 Subject: [PATCH 131/282] CMH Pruning Tweak replace CounterMovePruneThreshold by a depth dependent threshold STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 35512 W: 2718 L: 2552 D: 30242 Ptnml(0-2): 66, 2138, 13194, 2280, 78 https://tests.stockfishchess.org/tests/view/6104442fafad2da4f4ae3b94 LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 36536 W: 1150 L: 1019 D: 34367 Ptnml(0-2): 10, 920, 16278, 1049, 11 https://tests.stockfishchess.org/tests/view/6104b033afad2da4f4ae3bbc closes https://github.com/official-stockfish/Stockfish/pull/3636 Bench: 5848718 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f412d9d6..6275cdf2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -907,7 +907,7 @@ namespace { && !ttMove) depth -= 2; -moves_loop: // When in check, search starts from here +moves_loop: // When in check, search starts here ttCapture = ttMove && pos.capture_or_promotion(ttMove); @@ -1017,8 +1017,8 @@ moves_loop: // When in check, search starts from here { // Continuation history based pruning (~20 Elo) if ( lmrDepth < 5 - && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold - && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) + && (*contHist[0])[movedPiece][to_sq(move)] < (depth == 1 ? 0 : -stat_bonus(depth-1)) + && (*contHist[1])[movedPiece][to_sq(move)] < (depth == 1 ? 0 : -stat_bonus(depth-1))) continue; // Futility pruning: parent node (~5 Elo) From 31ebd918ea54b8c9fccbc1e1fa269772ed0949df Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Mon, 2 Aug 2021 13:52:48 -0400 Subject: [PATCH 132/282] Futile pruning simplification Remove CMH conditions in futile pruning. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 93520 W: 7165 L: 7138 D: 79217 Ptnml(0-2): 222, 5923, 34427, 5982, 206 https://tests.stockfishchess.org/tests/view/61083104e50a153c346ef8df LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 59072 W: 1746 L: 1706 D: 55620 Ptnml(0-2): 13, 1562, 26353, 1588, 20 https://tests.stockfishchess.org/tests/view/610894f2e50a153c346ef913 closes https://github.com/official-stockfish/Stockfish/pull/3638 Bench: 5229673 --- src/search.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6275cdf2..187f2169 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1023,11 +1023,7 @@ moves_loop: // When in check, search starts here // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck - && ss->staticEval + 174 + 157 * lmrDepth <= alpha - && (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 3 < 28255) + && ss->staticEval + 174 + 157 * lmrDepth <= alpha) continue; // Prune moves with negative SEE (~20 Elo) From 5cd42f6b0b30b7b3332367230aeef2efc93e5aad Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 3 Aug 2021 16:32:48 +0200 Subject: [PATCH 133/282] Simplify new cmh pruning thresholds by using directly a quadratic formula. This decouples also the stat bonus updates from the threshold which creates less dependencies for tuning of stat bonus parameters. Perhaps a further fine tuning of the now separated coefficients for constHist[0] and constHist[1] could give further gains. STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 78384 W: 6134 L: 6090 D: 66160 Ptnml(0-2): 207, 5013, 28705, 5063, 204 https://tests.stockfishchess.org/tests/view/6106d235afad2da4f4ae3d4b LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 38176 W: 1149 L: 1095 D: 35932 Ptnml(0-2): 6, 1000, 17030, 1038, 14 https://tests.stockfishchess.org/tests/view/6107a080afad2da4f4ae3def closes https://github.com/official-stockfish/Stockfish/pull/3639 Bench: 5098146 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 187f2169..af99c902 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1017,8 +1017,8 @@ moves_loop: // When in check, search starts here { // Continuation history based pruning (~20 Elo) if ( lmrDepth < 5 - && (*contHist[0])[movedPiece][to_sq(move)] < (depth == 1 ? 0 : -stat_bonus(depth-1)) - && (*contHist[1])[movedPiece][to_sq(move)] < (depth == 1 ? 0 : -stat_bonus(depth-1))) + && (*contHist[0])[movedPiece][to_sq(move)] < 23 - 23 * depth * depth + && (*contHist[1])[movedPiece][to_sq(move)] < 23 - 23 * depth * depth) continue; // Futility pruning: parent node (~5 Elo) From 73ef5b8c4a3ef5e37cc62344d4393b1ee366b7b4 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Wed, 4 Aug 2021 19:26:06 +0800 Subject: [PATCH 134/282] Update default net to nn-46832cfbead3.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/6100e7f096b86d98abf6a832 Parameters: A total of 256 net weights and 8 net biases were tuned (output layer) Base net: nn-56a5f1c4173a.nnue New net: nn-ec3c8e029926.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/610733caafad2da4f4ae3da7 Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-ec3c8e029926.nnue New net: nn-46832cfbead3.nnue STC: LLR: 2.98 (-2.94,2.94) <-0.50,2.50> Total: 50520 W: 3953 L: 3765 D: 42802 Ptnml(0-2): 138, 3063, 18678, 3235, 146 https://tests.stockfishchess.org/tests/view/610a79692a8a49ac5be793f4 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 57256 W: 1723 L: 1566 D: 53967 Ptnml(0-2): 12, 1442, 25568, 1589, 17 https://tests.stockfishchess.org/tests/view/610ac5bb2a8a49ac5be79434 Closes https://github.com/official-stockfish/Stockfish/pull/3642 Bench: 5359314 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index e3730c8a..1ac224b6 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-56a5f1c4173a.nnue" + #define EvalFileDefaultName "nn-46832cfbead3.nnue" namespace NNUE { From a1a83f38691ae3f39c827a46e59ae9875605aa3b Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Thu, 5 Aug 2021 08:50:24 -0400 Subject: [PATCH 135/282] SEE simplification Simplified SEE formula by removing std::min. Should also be easier to tune. STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 22656 W: 1836 L: 1729 D: 19091 Ptnml(0-2): 54, 1426, 8267, 1521, 60 https://tests.stockfishchess.org/tests/view/610ae62f2a8a49ac5be79449 LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 26248 W: 806 L: 744 D: 24698 Ptnml(0-2): 6, 668, 11715, 728, 7 https://tests.stockfishchess.org/tests/view/610b17ad2a8a49ac5be79466 closes https://github.com/official-stockfish/Stockfish/pull/3643 bench: 4915145 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index af99c902..af8ddaba 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1027,7 +1027,7 @@ moves_loop: // When in check, search starts here continue; // Prune moves with negative SEE (~20 Elo) - if (!pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-21 * lmrDepth * lmrDepth - 21 * lmrDepth))) continue; } } From dabaf2220fe0c77400a5f71a91952f510e6a126b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 5 Aug 2021 16:34:37 +0200 Subject: [PATCH 136/282] Revert futility pruning patches reverts 09b6d28391cf582d99897360b225bcbbe38dd1c6 and dbd7f602d3c7622df294f87d7239b5aaf31f695f that significantly impact mate finding capabilities. For example on ChestUCI_23102018.epd, at 1M nodes, the number of mates found is nearly reduced 2x without these depth conditions: sf6 2091 sf7 2093 sf8 2107 sf9 2062 sf10 2208 sf11 2552 sf12 2563 sf13 2509 sf14 2427 master 1246 patched 2467 (script for testing at https://github.com/official-stockfish/Stockfish/files/6936412/matecheck.zip) closes https://github.com/official-stockfish/Stockfish/pull/3641 fixes https://github.com/official-stockfish/Stockfish/issues/3627 Bench: 5467570 --- src/search.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index af8ddaba..c48b74bc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -779,8 +779,10 @@ namespace { ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE : ss->staticEval > (ss-2)->staticEval; - // Step 7. Futility pruning: child node (~50 Elo) + // Step 7. Futility pruning: child node (~50 Elo). + // The depth condition is important for mate finding. if ( !PvNode + && depth < 9 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; @@ -989,7 +991,7 @@ moves_loop: // When in check, search starts here // Calculate new depth for this move newDepth = depth - 1; - // Step 13. Pruning at shallow depth (~200 Elo) + // Step 13. Pruning at shallow depth (~200 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) @@ -1023,6 +1025,7 @@ moves_loop: // When in check, search starts here // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck + && lmrDepth < 7 && ss->staticEval + 174 + 157 * lmrDepth <= alpha) continue; From d61d38586ee35fd4d93445eb547e4af27cc86e6b Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 27 Jul 2021 15:00:22 +0200 Subject: [PATCH 137/282] New NNUE architecture and net Introduces a new NNUE network architecture and associated network parameters The summary of the changes: * Position for each perspective mirrored such that the king is on e..h files. Cuts the feature transformer size in half, while preserving enough knowledge to be good. See https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY/edit#heading=h.b40q4rb1w7on. * The number of neurons after the feature transformer increased two-fold, to 1024x2. This is possibly mostly due to the now very optimized feature transformer update code. * The number of neurons after the second layer is reduced from 16 to 8, to reduce the speed impact. This, perhaps surprisingly, doesn't harm the strength much. See https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY/edit#heading=h.6qkocr97fezq The AffineTransform code did not work out-of-the box with the smaller number of neurons after the second layer, so some temporary changes have been made to add a special case for InputDimensions == 8. Also additional 0 padding is added to the output for some archs that cannot process inputs by <=8 (SSE2, NEON). VNNI uses an implementation that can keep all outputs in the registers while reducing the number of loads by 3 for each 16 inputs, thanks to the reduced number of output neurons. However GCC is particularily bad at optimization here (and perhaps why the current way the affine transform is done even passed sprt) (see https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY/edit# for details) and more work will be done on this in the following days. I expect the current VNNI implementation to be improved and extended to other architectures. The network was trained with a slightly modified version of the pytorch trainer (https://github.com/glinscott/nnue-pytorch); the changes are in https://github.com/glinscott/nnue-pytorch/pull/143 The training utilized 2 datasets. dataset A - https://drive.google.com/file/d/1VlhnHL8f-20AXhGkILujnNXHwy9T-MQw/view?usp=sharing dataset B - as described in https://github.com/official-stockfish/Stockfish/commit/ba01f4b95448bcb324755f4dd2a632a57c6e67bc The training process was as following: train on dataset A for 350 epochs, take the best net in terms of elo at 20k nodes per move (it's fine to take anything from later stages of training). convert the .ckpt to .pt --resume-from-model from the .pt file, train on dataset B for <600 epochs, take the best net. Lambda=0.8, applied before the loss function. The first training command: python3 train.py \ ../nnue-pytorch-training/data/large_gensfen_multipvdiff_100_d9.binpack \ ../nnue-pytorch-training/data/large_gensfen_multipvdiff_100_d9.binpack \ --gpus "$3," \ --threads 1 \ --num-workers 1 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --smart-fen-skipping \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=1.0 \ --max_epochs=600 \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$2 The second training command: python3 serialize.py \ --features=HalfKAv2_hm^ \ ../nnue-pytorch-training/experiment_131/run_6/default/version_0/checkpoints/epoch-499.ckpt \ ../nnue-pytorch-training/experiment_$1/base/base.pt python3 train.py \ ../nnue-pytorch-training/data/michael_commit_b94a65.binpack \ ../nnue-pytorch-training/data/michael_commit_b94a65.binpack \ --gpus "$3," \ --threads 1 \ --num-workers 1 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --smart-fen-skipping \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=0.8 \ --max_epochs=600 \ --resume-from-model ../nnue-pytorch-training/experiment_$1/base/base.pt \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$2 STC: https://tests.stockfishchess.org/tests/view/611120b32a8a49ac5be798c4 LLR: 2.97 (-2.94,2.94) <-0.50,2.50> Total: 22480 W: 2434 L: 2251 D: 17795 Ptnml(0-2): 101, 1736, 7410, 1865, 128 LTC: https://tests.stockfishchess.org/tests/view/611152b32a8a49ac5be798ea LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 9776 W: 442 L: 333 D: 9001 Ptnml(0-2): 5, 295, 4180, 402, 6 closes https://github.com/official-stockfish/Stockfish/pull/3646 bench: 5189338 --- src/Makefile | 2 +- src/evaluate.h | 2 +- .../{half_ka_v2.cpp => half_ka_v2_hm.cpp} | 30 +-- .../{half_ka_v2.h => half_ka_v2_hm.h} | 31 ++- src/nnue/layers/affine_transform.h | 220 ++++++++++++++++-- src/nnue/nnue_architecture.h | 8 +- 6 files changed, 247 insertions(+), 46 deletions(-) rename src/nnue/features/{half_ka_v2.cpp => half_ka_v2_hm.cpp} (68%) rename src/nnue/features/{half_ka_v2.h => half_ka_v2_hm.h} (80%) diff --git a/src/Makefile b/src/Makefile index b021ba12..c1dab86b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,7 +41,7 @@ endif SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ - nnue/evaluate_nnue.cpp nnue/features/half_ka_v2.cpp + nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp OBJS = $(notdir $(SRCS:.cpp=.o)) diff --git a/src/evaluate.h b/src/evaluate.h index 1ac224b6..4430a28c 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-46832cfbead3.nnue" + #define EvalFileDefaultName "nn-e8321e467bf6.nnue" namespace NNUE { diff --git a/src/nnue/features/half_ka_v2.cpp b/src/nnue/features/half_ka_v2_hm.cpp similarity index 68% rename from src/nnue/features/half_ka_v2.cpp rename to src/nnue/features/half_ka_v2_hm.cpp index 57f43e50..098a6d60 100644 --- a/src/nnue/features/half_ka_v2.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -16,31 +16,32 @@ along with this program. If not, see . */ -//Definition of input features HalfKAv2 of NNUE evaluation function +//Definition of input features HalfKAv2_hm of NNUE evaluation function -#include "half_ka_v2.h" +#include "half_ka_v2_hm.h" #include "../../position.h" namespace Stockfish::Eval::NNUE::Features { // Orient a square according to perspective (rotates by 180 for black) - inline Square HalfKAv2::orient(Color perspective, Square s) { - return Square(int(s) ^ (bool(perspective) * 56)); + inline Square HalfKAv2_hm::orient(Color perspective, Square s, Square ksq) { + return Square(int(s) ^ (bool(perspective) * SQ_A8) ^ ((file_of(ksq) < FILE_E) * SQ_H1)); } // Index of a feature for a given king position and another piece on some square - inline IndexType HalfKAv2::make_index(Color perspective, Square s, Piece pc, Square ksq) { - return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); + inline IndexType HalfKAv2_hm::make_index(Color perspective, Square s, Piece pc, Square ksq) { + Square o_ksq = orient(perspective, ksq, ksq); + return IndexType(orient(perspective, s, ksq) + PieceSquareIndex[perspective][pc] + PS_NB * KingBuckets[o_ksq]); } // Get a list of indices for active features - void HalfKAv2::append_active_indices( + void HalfKAv2_hm::append_active_indices( const Position& pos, Color perspective, ValueListInserter active ) { - Square ksq = orient(perspective, pos.square(perspective)); + Square ksq = pos.square(perspective); Bitboard bb = pos.pieces(); while (bb) { @@ -52,7 +53,7 @@ namespace Stockfish::Eval::NNUE::Features { // append_changed_indices() : get a list of indices for recently changed features - void HalfKAv2::append_changed_indices( + void HalfKAv2_hm::append_changed_indices( Square ksq, StateInfo* st, Color perspective, @@ -60,25 +61,24 @@ namespace Stockfish::Eval::NNUE::Features { ValueListInserter added ) { const auto& dp = st->dirtyPiece; - Square oriented_ksq = orient(perspective, ksq); for (int i = 0; i < dp.dirty_num; ++i) { Piece pc = dp.piece[i]; if (dp.from[i] != SQ_NONE) - removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq)); + removed.push_back(make_index(perspective, dp.from[i], pc, ksq)); if (dp.to[i] != SQ_NONE) - added.push_back(make_index(perspective, dp.to[i], pc, oriented_ksq)); + added.push_back(make_index(perspective, dp.to[i], pc, ksq)); } } - int HalfKAv2::update_cost(StateInfo* st) { + int HalfKAv2_hm::update_cost(StateInfo* st) { return st->dirtyPiece.dirty_num; } - int HalfKAv2::refresh_cost(const Position& pos) { + int HalfKAv2_hm::refresh_cost(const Position& pos) { return pos.count(); } - bool HalfKAv2::requires_refresh(StateInfo* st, Color perspective) { + bool HalfKAv2_hm::requires_refresh(StateInfo* st, Color perspective) { return st->dirtyPiece.piece[0] == make_piece(perspective, KING); } diff --git a/src/nnue/features/half_ka_v2.h b/src/nnue/features/half_ka_v2_hm.h similarity index 80% rename from src/nnue/features/half_ka_v2.h rename to src/nnue/features/half_ka_v2_hm.h index e4b2edd9..2c1144f6 100644 --- a/src/nnue/features/half_ka_v2.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -18,8 +18,8 @@ //Definition of input features HalfKP of NNUE evaluation function -#ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED -#define NNUE_FEATURES_HALF_KA_V2_H_INCLUDED +#ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED +#define NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED #include "../nnue_common.h" @@ -32,9 +32,9 @@ namespace Stockfish { namespace Stockfish::Eval::NNUE::Features { - // Feature HalfKAv2: Combination of the position of own king - // and the position of pieces - class HalfKAv2 { + // Feature HalfKAv2_hm: Combination of the position of own king + // and the position of pieces. Position mirrored such that king always on e..h files. + class HalfKAv2_hm { // unique number for each piece type on each square enum { @@ -63,21 +63,32 @@ namespace Stockfish::Eval::NNUE::Features { }; // Orient a square according to perspective (rotates by 180 for black) - static Square orient(Color perspective, Square s); + static Square orient(Color perspective, Square s, Square ksq); // Index of a feature for a given king position and another piece on some square static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq); public: // Feature name - static constexpr const char* Name = "HalfKAv2(Friend)"; + static constexpr const char* Name = "HalfKAv2_hm(Friend)"; // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = 0x5f234cb8u; + static constexpr std::uint32_t HashValue = 0x7f234cb8u; // Number of feature dimensions static constexpr IndexType Dimensions = - static_cast(SQUARE_NB) * static_cast(PS_NB); + static_cast(SQUARE_NB) * static_cast(PS_NB) / 2; + + static constexpr int KingBuckets[64] = { + -1, -1, -1, -1, 31, 30, 29, 28, + -1, -1, -1, -1, 27, 26, 25, 24, + -1, -1, -1, -1, 23, 22, 21, 20, + -1, -1, -1, -1, 19, 18, 17, 16, + -1, -1, -1, -1, 15, 14, 13, 12, + -1, -1, -1, -1, 11, 10, 9, 8, + -1, -1, -1, -1, 7, 6, 5, 4, + -1, -1, -1, -1, 3, 2, 1, 0 + }; // Maximum number of simultaneously active features. static constexpr IndexType MaxActiveDimensions = 32; @@ -108,4 +119,4 @@ namespace Stockfish::Eval::NNUE::Features { } // namespace Stockfish::Eval::NNUE::Features -#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED +#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 9a5f62c0..d1318368 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -46,6 +46,11 @@ namespace Stockfish::Eval::NNUE::Layers { #elif defined (USE_SSSE3) static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; #endif +#if defined (USE_AVX512) + static constexpr const IndexType InputSimdWidth = SimdWidth * 2; +#elif defined (USE_SSSE3) + static constexpr const IndexType InputSimdWidth = SimdWidth; +#endif // Size of forward propagation buffer used in this layer static constexpr std::size_t SelfBufferSize = @@ -72,6 +77,15 @@ namespace Stockfish::Eval::NNUE::Layers { for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) #if !defined (USE_SSSE3) weights[i] = read_little_endian(stream); +#elif defined (USE_VNNI) || defined (USE_AVX512) + if constexpr (OutputDimensions <= 8 && OutputDimensions != 1) + weights[i] = read_little_endian(stream); + else + weights[ + (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + + i / PaddedInputDimensions * 4 + + i % 4 + ] = read_little_endian(stream); #else weights[ (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + @@ -108,7 +122,6 @@ namespace Stockfish::Eval::NNUE::Layers { return !stream.fail(); } - // Forward propagation const OutputType* propagate( const TransformedFeatureType* transformedFeatures, char* buffer) const { @@ -123,6 +136,40 @@ namespace Stockfish::Eval::NNUE::Layers { return _mm512_reduce_add_epi32(sum) + bias; }; + [[maybe_unused]] auto m512_hadd128x16_interleave = []( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) -> __m512i { + + __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); + __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); + + __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); + __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); + + __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); + __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); + + __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); + __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); + + return _mm512_add_epi32(sum0123a, sum0123b); + }; + + [[maybe_unused]] auto m512_haddx4 = [m512_hadd128x16_interleave]( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m128i bias) -> __m128i { + + __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); + + __m256i sum256lo = _mm512_castsi512_si256(sum); + __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); + + sum256lo = _mm256_add_epi32(sum256lo, sum256hi); + + __m128i sum128lo = _mm256_castsi256_si128(sum256lo); + __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + }; + [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { #if defined (USE_VNNI) acc = _mm512_dpbusd_epi32(acc, a, b); @@ -133,6 +180,19 @@ namespace Stockfish::Eval::NNUE::Layers { #endif }; + [[maybe_unused]] auto m512_add_dpbusd_epi32x2 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1) { +#if defined (USE_VNNI) + acc = _mm512_dpbusd_epi32(acc, a0, b0); + acc = _mm512_dpbusd_epi32(acc, a1, b1); +#else + __m512i product0 = _mm512_maddubs_epi16(a0, b0); + __m512i product1 = _mm512_maddubs_epi16(a1, b1); + product0 = _mm512_adds_epi16(product0, product1); + product0 = _mm512_madd_epi16(product0, Ones512); + acc = _mm512_add_epi32(acc, product0); +#endif + }; + [[maybe_unused]] auto m512_add_dpbusd_epi32x4 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1, __m512i a2, __m512i b2, __m512i a3, __m512i b3) { #if defined (USE_VNNI) @@ -165,6 +225,18 @@ namespace Stockfish::Eval::NNUE::Layers { return _mm_cvtsi128_si32(sum128) + bias; }; + [[maybe_unused]] auto m256_haddx4 = [](__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, __m128i bias) -> __m128i { + sum0 = _mm256_hadd_epi32(sum0, sum1); + sum2 = _mm256_hadd_epi32(sum2, sum3); + + sum0 = _mm256_hadd_epi32(sum0, sum2); + + __m128i sum128lo = _mm256_castsi256_si128(sum0); + __m128i sum128hi = _mm256_extracti128_si256(sum0, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + }; + [[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) { #if defined (USE_VNNI) acc = _mm256_dpbusd_epi32(acc, a, b); @@ -175,6 +247,19 @@ namespace Stockfish::Eval::NNUE::Layers { #endif }; + [[maybe_unused]] auto m256_add_dpbusd_epi32x2 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1) { +#if defined (USE_VNNI) + acc = _mm256_dpbusd_epi32(acc, a0, b0); + acc = _mm256_dpbusd_epi32(acc, a1, b1); +#else + __m256i product0 = _mm256_maddubs_epi16(a0, b0); + __m256i product1 = _mm256_maddubs_epi16(a1, b1); + product0 = _mm256_adds_epi16(product0, product1); + product0 = _mm256_madd_epi16(product0, Ones256); + acc = _mm256_add_epi32(acc, product0); +#endif + }; + [[maybe_unused]] auto m256_add_dpbusd_epi32x4 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1, __m256i a2, __m256i b2, __m256i a3, __m256i b3) { #if defined (USE_VNNI) @@ -206,12 +291,27 @@ namespace Stockfish::Eval::NNUE::Layers { return _mm_cvtsi128_si32(sum) + bias; }; + [[maybe_unused]] auto m128_haddx4 = [](__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, __m128i bias) -> __m128i { + sum0 = _mm_hadd_epi32(sum0, sum1); + sum2 = _mm_hadd_epi32(sum2, sum3); + sum0 = _mm_hadd_epi32(sum0, sum2); + return _mm_add_epi32(sum0, bias); + }; + [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { __m128i product0 = _mm_maddubs_epi16(a, b); product0 = _mm_madd_epi16(product0, Ones128); acc = _mm_add_epi32(acc, product0); }; + [[maybe_unused]] auto m128_add_dpbusd_epi32x2 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1) { + __m128i product0 = _mm_maddubs_epi16(a0, b0); + __m128i product1 = _mm_maddubs_epi16(a1, b1); + product0 = _mm_adds_epi16(product0, product1); + product0 = _mm_madd_epi16(product0, Ones128); + acc = _mm_add_epi32(acc, product0); + }; + [[maybe_unused]] auto m128_add_dpbusd_epi32x4 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1, __m128i a2, __m128i b2, __m128i a3, __m128i b3) { __m128i product0 = _mm_maddubs_epi16(a0, b0); @@ -231,33 +331,116 @@ namespace Stockfish::Eval::NNUE::Layers { using vec_t = __m512i; #define vec_setzero _mm512_setzero_si512 #define vec_set_32 _mm512_set1_epi32 - auto& vec_add_dpbusd_32 = m512_add_dpbusd_epi32; - auto& vec_add_dpbusd_32x4 = m512_add_dpbusd_epi32x4; - auto& vec_hadd = m512_hadd; + [[maybe_unused]] auto& vec_add_dpbusd_32 = m512_add_dpbusd_epi32; + [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m512_add_dpbusd_epi32x2; + [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m512_add_dpbusd_epi32x4; + [[maybe_unused]] auto& vec_hadd = m512_hadd; + [[maybe_unused]] auto& vec_haddx4 = m512_haddx4; #elif defined (USE_AVX2) using vec_t = __m256i; #define vec_setzero _mm256_setzero_si256 #define vec_set_32 _mm256_set1_epi32 - auto& vec_add_dpbusd_32 = m256_add_dpbusd_epi32; - auto& vec_add_dpbusd_32x4 = m256_add_dpbusd_epi32x4; - auto& vec_hadd = m256_hadd; + [[maybe_unused]] auto& vec_add_dpbusd_32 = m256_add_dpbusd_epi32; + [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m256_add_dpbusd_epi32x2; + [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m256_add_dpbusd_epi32x4; + [[maybe_unused]] auto& vec_hadd = m256_hadd; + [[maybe_unused]] auto& vec_haddx4 = m256_haddx4; #elif defined (USE_SSSE3) using vec_t = __m128i; #define vec_setzero _mm_setzero_si128 #define vec_set_32 _mm_set1_epi32 - auto& vec_add_dpbusd_32 = m128_add_dpbusd_epi32; - auto& vec_add_dpbusd_32x4 = m128_add_dpbusd_epi32x4; - auto& vec_hadd = m128_hadd; + [[maybe_unused]] auto& vec_add_dpbusd_32 = m128_add_dpbusd_epi32; + [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m128_add_dpbusd_epi32x2; + [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m128_add_dpbusd_epi32x4; + [[maybe_unused]] auto& vec_hadd = m128_hadd; + [[maybe_unused]] auto& vec_haddx4 = m128_haddx4; #endif #if defined (USE_SSSE3) const auto output = reinterpret_cast(buffer); const auto inputVector = reinterpret_cast(input); +#endif - static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); +#if defined (USE_VNNI) || defined (USE_AVX512) + + static_assert(OutputDimensions == 1 || OutputDimensions % 4 == 0); // OutputDimensions is either 1 or a multiple of SimdWidth // because then it is also an input dimension. + if constexpr (OutputDimensions <= 8 && OutputDimensions != 1) + { + constexpr IndexType NumChunks = PaddedInputDimensions / InputSimdWidth; + + static_assert(NumChunks % 2 == 0); + + const auto input_vec = reinterpret_cast(input); + const auto bias_vec = reinterpret_cast(biases); + auto out_vec = reinterpret_cast<__m128i*>(output); + + vec_t regs[OutputDimensions]; + for (IndexType k = 0; k < OutputDimensions; ++k) + regs[k] = vec_setzero(); + + for (IndexType i = 0; i < NumChunks / 2; ++i) + { + const vec_t in0 = input_vec[i * 2 + 0]; + const vec_t in1 = input_vec[i * 2 + 1]; + for (IndexType k = 0; k < OutputDimensions; ++k) + { + const vec_t w0 = reinterpret_cast(&weights[k * PaddedInputDimensions])[i * 2 + 0]; + const vec_t w1 = reinterpret_cast(&weights[k * PaddedInputDimensions])[i * 2 + 1]; + vec_add_dpbusd_32(regs[k], in0, w0); + vec_add_dpbusd_32(regs[k], in1, w1); + } + } + + for (IndexType k = 0; k < OutputDimensions / 4; ++k) + { + out_vec[k] = vec_haddx4( + regs[k * 4 + 0], + regs[k * 4 + 1], + regs[k * 4 + 2], + regs[k * 4 + 3], + bias_vec[k] + ); + } + } + else if constexpr (InputDimensions == 8) + { + const auto input32 = reinterpret_cast(input); + __m256i* outptr = reinterpret_cast<__m256i*>(output); + std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); + + const __m256i in0 = _mm256_set1_epi32(input32[0]); + const __m256i in1 = _mm256_set1_epi32(input32[1]); + const auto col0 = reinterpret_cast(&weights[0]); + const auto col1 = reinterpret_cast(&weights[OutputDimensions * 4]); + for (IndexType j = 0; j * 8 < OutputDimensions; ++j) + m256_add_dpbusd_epi32x2(outptr[j], in0, col0[j], in1, col1[j]); + } + else + +#elif defined (USE_SSSE3) + + if constexpr (OutputDimensions % OutputSimdWidth == 0 && InputDimensions == 8) + { + const auto input32 = reinterpret_cast(input); + vec_t* outptr = reinterpret_cast(output); + std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); + + const vec_t in0 = vec_set_32(input32[0]); + const vec_t in1 = vec_set_32(input32[1]); + const auto col0 = reinterpret_cast(&weights[0]); + const auto col1 = reinterpret_cast(&weights[OutputDimensions * 4]); + for (IndexType j = 0; j * OutputSimdWidth < OutputDimensions; ++j) + vec_add_dpbusd_32x2(outptr[j], in0, col0[j], in1, col1[j]); + } + else + +#endif + +#if defined (USE_SSSE3) + if constexpr (OutputDimensions % OutputSimdWidth == 0) { static_assert(InputDimensions % 16 == 0); @@ -337,8 +520,8 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined(USE_SSE2) // At least a multiple of 16, with SSE2. - static_assert(InputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = InputDimensions / SimdWidth; + static_assert(PaddedInputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; const __m128i Zeros = _mm_setzero_si128(); const auto inputVector = reinterpret_cast(input); @@ -349,8 +532,8 @@ namespace Stockfish::Eval::NNUE::Layers { const auto inputVector = reinterpret_cast(input); #elif defined(USE_NEON) - static_assert(InputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = InputDimensions / SimdWidth; + static_assert(PaddedInputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; const auto inputVector = reinterpret_cast(input); #endif @@ -423,6 +606,13 @@ namespace Stockfish::Eval::NNUE::Layers { _mm_empty(); #endif +#endif + +#if (!defined (USE_SSSE3) && defined (USE_SSE2)) || defined (USE_NEON) + static_assert(SimdWidth <= 16, "Otherwise we run outside of the padding for the output."); + if constexpr (SimdWidth > OutputDimensions && OutputDimensions != 1) + for (IndexType i = OutputDimensions; i < SimdWidth; ++i) + output[i] = 0; #endif return output; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 879a39cd..193a197d 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -23,7 +23,7 @@ #include "nnue_common.h" -#include "features/half_ka_v2.h" +#include "features/half_ka_v2_hm.h" #include "layers/input_slice.h" #include "layers/affine_transform.h" @@ -32,10 +32,10 @@ namespace Stockfish::Eval::NNUE { // Input features used in evaluation function - using FeatureSet = Features::HalfKAv2; + using FeatureSet = Features::HalfKAv2_hm; // Number of input feature dimensions after conversion - constexpr IndexType TransformedFeatureDimensions = 512; + constexpr IndexType TransformedFeatureDimensions = 1024; constexpr IndexType PSQTBuckets = 8; constexpr IndexType LayerStacks = 8; @@ -43,7 +43,7 @@ namespace Stockfish::Eval::NNUE { // Define network structure using InputLayer = InputSlice; - using HiddenLayer1 = ClippedReLU>; + using HiddenLayer1 = ClippedReLU>; using HiddenLayer2 = ClippedReLU>; using OutputLayer = AffineTransform; From f10ebc2bdf52dbc413e74ed1e41c29bd9b818408 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 15 Aug 2021 15:11:04 +0200 Subject: [PATCH 138/282] Regenerate dependencies on code change fixes https://github.com/official-stockfish/Stockfish/issues/3658 dependencies are now regenerated for each code change, this adds some 1s overhead in compile time, but avoids potential miscompilations or build problems. closes https://github.com/official-stockfish/Stockfish/pull/3659 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index c1dab86b..901ddd92 100644 --- a/src/Makefile +++ b/src/Makefile @@ -912,7 +912,7 @@ icc-profile-use: EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \ all -.depend: +.depend: $(SRCS) -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null -include .depend From 1946a675677aee8e485b75525bd06184badc79f2 Mon Sep 17 00:00:00 2001 From: Torsten Hellwig Date: Wed, 18 Aug 2021 09:12:14 +0200 Subject: [PATCH 139/282] Update default net to nn-ac5605a608d6.nnue This net was created with the nnue-pytorch trainer, it used the previous master net as a starting point. The training data includes all T60 data (https://drive.google.com/drive/folders/1rzZkgIgw7G5vQMLr2hZNiUXOp7z80613), all T74 data (https://drive.google.com/drive/folders/1aFUv3Ih3-A8Vxw9064Kw_FU4sNhMHZU-) and the wrongNNUE_02_d9.binpack (https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq). The Leela data were randomly named and then concatenated. All data was merged into one binpack using interleave_binpacks.py. python3 train.py \ ../data/t60_t74_wrong.binpack \ ../data/t60_t74_wrong.binpack \ --resume-from-model ../data/nn-e8321e467bf6.pt \ --gpus 1 \ --threads 4 \ --num-workers 1 \ --batch-size 16384 \ --progress_bar_refresh_rate 300 \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=1.0 \ --max_epochs=600 \ --seed $RANDOM \ --default_root_dir ../output/exp_24 STC: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 15320 W: 1415 L: 1257 D: 12648 Ptnml(0-2): 50, 1002, 5402, 1152, 54 https://tests.stockfishchess.org/tests/view/611c404a4977aa1525c9c97f LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 9440 W: 345 L: 248 D: 8847 Ptnml(0-2): 3, 222, 4175, 315, 5 https://tests.stockfishchess.org/tests/view/611c6c7d4977aa1525c9c996 LTC with UHO_XXL_+0.90_+1.19.epd: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 6232 W: 1638 L: 1459 D: 3135 Ptnml(0-2): 5, 592, 1744, 769, 6 https://tests.stockfishchess.org/tests/view/611c9b214977aa1525c9c9cb closes https://github.com/official-stockfish/Stockfish/pull/3664 Bench: 5375286 --- AUTHORS | 1 + src/evaluate.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 4d72314f..d7f90de0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -186,6 +186,7 @@ Tom Truscott Tom Vijlbrief (tomtor) Tomasz Sobczyk (Sopel97) Torsten Franz (torfranz, tfranzer) +Torsten Hellwig (Torom) Tracey Emery (basepr1me) tttak Unai Corzo (unaiic) diff --git a/src/evaluate.h b/src/evaluate.h index 4430a28c..322db398 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-e8321e467bf6.nnue" + #define EvalFileDefaultName "nn-ac5605a608d6.nnue" namespace NNUE { From ccf0239bc468c1fb599785e0dea178e84279674d Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Fri, 13 Aug 2021 22:20:11 +0200 Subject: [PATCH 140/282] Improve handling of the debug log file. Fix handling of empty strings in uci options and reassigning of the log file Fixes https://github.com/official-stockfish/Stockfish/issues/3650 Closes https://github.com/official-stockfish/Stockfish/pull/3655 No functional change --- AUTHORS | 2 +- src/misc.cpp | 15 ++++++++------- src/ucioption.cpp | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/AUTHORS b/AUTHORS index d7f90de0..5b5bbf22 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of June 14, 2021 +# List of authors for Stockfish # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) diff --git a/src/misc.cpp b/src/misc.cpp index 3b071ccb..f9c12337 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -110,7 +110,14 @@ public: static Logger l; - if (!fname.empty() && !l.file.is_open()) + if (l.file.is_open()) + { + cout.rdbuf(l.out.buf); + cin.rdbuf(l.in.buf); + l.file.close(); + } + + if (!fname.empty()) { l.file.open(fname, ifstream::out); @@ -123,12 +130,6 @@ public: cin.rdbuf(&l.in); cout.rdbuf(&l.out); } - else if (fname.empty() && l.file.is_open()) - { - cout.rdbuf(l.out.buf); - cin.rdbuf(l.in.buf); - l.file.close(); - } } }; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 07b3027d..0cafd3e9 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -164,7 +164,7 @@ Option& Option::operator=(const string& v) { assert(!type.empty()); - if ( (type != "button" && v.empty()) + if ( (type != "button" && type != "string" && v.empty()) || (type == "check" && v != "true" && v != "false") || (type == "spin" && (stof(v) < min || stof(v) > max))) return *this; From 18dcf1f09754284325157f2d270df10a09297958 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Mon, 16 Aug 2021 12:19:26 +0200 Subject: [PATCH 141/282] Optimize and tidy up affine transform code. The new network caused some issues initially due to the very narrow neuron set between the first two FC layers. Necessary changes were hacked together to make it work. This patch is a mature approach to make the affine transform code faster, more readable, and easier to maintain should the layer sizes change again. The following changes were made: * ClippedReLU always produces a multiple of 32 outputs. This is about as good of a solution for AffineTransform's SIMD requirements as it can get without a bigger rewrite. * All self-contained simd helpers are moved to a separate file (simd.h). Inline asm is utilized to work around GCC's issues with code generation and register assignment. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101693, https://godbolt.org/z/da76fY1n7 * AffineTransform has 2 specializations. While it's more lines of code due to the boilerplate, the logic in both is significantly reduced, as these two are impossible to nicely combine into one. 1) The first specialization is for cases when there's >=128 inputs. It uses a different approach to perform the affine transform and can make full use of AVX512 without any edge cases. Furthermore, it has higher theoretical throughput because less loads are needed in the hot path, requiring only a fixed amount of instructions for horizontal additions at the end, which are amortized by the large number of inputs. 2) The second specialization is made to handle smaller layers where performance is still necessary but edge cases need to be handled. AVX512 implementation for this was ommited by mistake, a remnant from the temporary implementation for the new... This could be easily reintroduced if needed. A slightly more detailed description of both implementations is in the code. Overall it should be a minor speedup, as shown on fishtest: passed STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 51520 W: 4074 L: 3888 D: 43558 Ptnml(0-2): 111, 3136, 19097, 3288, 128 and various tests shown in the pull request closes https://github.com/official-stockfish/Stockfish/pull/3663 No functional change --- src/nnue/layers/affine_transform.h | 929 +++++++++++++---------------- src/nnue/layers/clipped_relu.h | 14 +- src/simd.h | 341 +++++++++++ 3 files changed, 778 insertions(+), 506 deletions(-) create mode 100644 src/simd.h diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index d1318368..b2871278 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -22,13 +22,357 @@ #define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED #include +#include +#include #include "../nnue_common.h" +#include "../../simd.h" + +/* + This file contains the definition for a fully connected layer (aka affine transform). + Two approaches are employed, depending on the sizes of the transform. + + Approach 1: + - used when the PaddedInputDimensions >= 128 + - uses AVX512 if possible + - processes inputs in batches of 2*InputSimdWidth + - so in batches of 128 for AVX512 + - the weight blocks of size InputSimdWidth are transposed such that + access is sequential + - N columns of the weight matrix are processed a time, where N + depends on the architecture (the amount of registers) + - accumulate + hadd is used + + Approach 2: + - used when the PaddedInputDimensions < 128 + - does not use AVX512 + - expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32. + - that's why AVX512 is hard to implement + - expected use-case is small layers + - not optimized as well as the approach 1 + - inputs are processed in chunks of 4, weights are respectively transposed + - accumulation happens directly to int32s +*/ namespace Stockfish::Eval::NNUE::Layers { - // Affine transformation layer +// Fallback implementation for older/other architectures. +// Identical for both approaches. Requires the input to be padded to at least 16 values. +#if !defined(USE_SSSE3) + template + static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input) + { +# if defined(USE_SSE2) + // At least a multiple of 16, with SSE2. + static_assert(PaddedInputDimensions % 16 == 0); + constexpr IndexType NumChunks = PaddedInputDimensions / 16; + const __m128i Zeros = _mm_setzero_si128(); + const auto inputVector = reinterpret_cast(input); + +# elif defined(USE_MMX) + static_assert(InputDimensions % 8 == 0); + constexpr IndexType NumChunks = InputDimensions / 8; + const __m64 Zeros = _mm_setzero_si64(); + const auto inputVector = reinterpret_cast(input); + +# elif defined(USE_NEON) + static_assert(PaddedInputDimensions % 16 == 0); + constexpr IndexType NumChunks = PaddedInputDimensions / 16; + const auto inputVector = reinterpret_cast(input); +# endif + + for (IndexType i = 0; i < OutputDimensions; ++i) { + const IndexType offset = i * PaddedInputDimensions; + +# if defined(USE_SSE2) + __m128i sumLo = _mm_cvtsi32_si128(biases[i]); + __m128i sumHi = Zeros; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { + __m128i row_j = _mm_load_si128(&row[j]); + __m128i input_j = _mm_load_si128(&inputVector[j]); + __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); + __m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); + __m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros); + __m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros); + __m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo); + __m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi); + sumLo = _mm_add_epi32(sumLo, productLo); + sumHi = _mm_add_epi32(sumHi, productHi); + } + __m128i sum = _mm_add_epi32(sumLo, sumHi); + __m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sumHigh_64); + __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sum_second_32); + output[i] = _mm_cvtsi128_si32(sum); + +# elif defined(USE_MMX) + __m64 sumLo = _mm_cvtsi32_si64(biases[i]); + __m64 sumHi = Zeros; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { + __m64 row_j = row[j]; + __m64 input_j = inputVector[j]; + __m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); + __m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); + __m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros); + __m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros); + __m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo); + __m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi); + sumLo = _mm_add_pi32(sumLo, productLo); + sumHi = _mm_add_pi32(sumHi, productHi); + } + __m64 sum = _mm_add_pi32(sumLo, sumHi); + sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); + output[i] = _mm_cvtsi64_si32(sum); + +# elif defined(USE_NEON) + int32x4_t sum = {biases[i]}; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { + int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]); + product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); + sum = vpadalq_s16(sum, product); + } + output[i] = sum[0] + sum[1] + sum[2] + sum[3]; + +# else + std::int32_t sum = biases[i]; + for (IndexType j = 0; j < InputDimensions; ++j) { + sum += weights[offset + j] * input[j]; + } + output[i] = sum; +# endif + } + +# if defined(USE_MMX) + _mm_empty(); +# endif + } +#endif + + template + class AffineTransform; + + // A specialization for large inputs. template - class AffineTransform { + class AffineTransform= 2*64-1)>> { + public: + // Input/output type + using InputType = typename PreviousLayer::OutputType; + using OutputType = std::int32_t; + static_assert(std::is_same::value, ""); + + // Number of input/output dimensions + static constexpr IndexType InputDimensions = PreviousLayer::OutputDimensions; + static constexpr IndexType OutputDimensions = OutDims; + + static constexpr IndexType PaddedInputDimensions = + ceil_to_multiple(InputDimensions, MaxSimdWidth); + + static_assert(PaddedInputDimensions >= 128, "Something went wrong. This specialization should not have been chosen."); + +#if defined (USE_AVX512) + static constexpr const IndexType InputSimdWidth = 64; + static constexpr const IndexType MaxNumOutputRegs = 16; +#elif defined (USE_AVX2) + static constexpr const IndexType InputSimdWidth = 32; + static constexpr const IndexType MaxNumOutputRegs = 8; +#elif defined (USE_SSSE3) + static constexpr const IndexType InputSimdWidth = 16; + static constexpr const IndexType MaxNumOutputRegs = 8; +#else + // The fallback implementation will not have permuted weights. + // We define these to avoid a lot of ifdefs later. + static constexpr const IndexType InputSimdWidth = 1; + static constexpr const IndexType MaxNumOutputRegs = 1; +#endif + + // A big block is a region in the weight matrix of the size [PaddedInputDimensions, NumOutputRegs]. + // A small block is a region of size [InputSimdWidth, 1] + + static constexpr const IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions); + static constexpr const IndexType SmallBlockSize = InputSimdWidth; + static constexpr const IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions; + static constexpr const IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize; + static constexpr const IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize; + static constexpr const IndexType NumBigBlocks = OutputDimensions / NumOutputRegs; + + static_assert(OutputDimensions % NumOutputRegs == 0); + + // Size of forward propagation buffer used in this layer + static constexpr std::size_t SelfBufferSize = + ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); + + // Size of the forward propagation buffer used from the input layer to this layer + static constexpr std::size_t BufferSize = + PreviousLayer::BufferSize + SelfBufferSize; + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t get_hash_value() { + std::uint32_t hashValue = 0xCC03DAE4u; + hashValue += OutputDimensions; + hashValue ^= PreviousLayer::get_hash_value() >> 1; + hashValue ^= PreviousLayer::get_hash_value() << 31; + return hashValue; + } + + /* + Transposes the small blocks within a block. + Effectively means that weights can be traversed sequentially during inference. + */ + static IndexType get_weight_index(IndexType i) + { + const IndexType smallBlock = (i / SmallBlockSize) % NumSmallBlocksInBigBlock; + const IndexType smallBlockCol = smallBlock / NumSmallBlocksPerOutput; + const IndexType smallBlockRow = smallBlock % NumSmallBlocksPerOutput; + const IndexType bigBlock = i / BigBlockSize; + const IndexType rest = i % SmallBlockSize; + + const IndexType idx = + bigBlock * BigBlockSize + + smallBlockRow * SmallBlockSize * NumOutputRegs + + smallBlockCol * SmallBlockSize + + rest; + + return idx; + } + + // Read network parameters + bool read_parameters(std::istream& stream) { + if (!previousLayer.read_parameters(stream)) return false; + for (std::size_t i = 0; i < OutputDimensions; ++i) + biases[i] = read_little_endian(stream); + + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + weights[get_weight_index(i)] = read_little_endian(stream); + + return !stream.fail(); + } + + // Write network parameters + bool write_parameters(std::ostream& stream) const { + if (!previousLayer.write_parameters(stream)) return false; + for (std::size_t i = 0; i < OutputDimensions; ++i) + write_little_endian(stream, biases[i]); + + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + write_little_endian(stream, weights[get_weight_index(i)]); + + return !stream.fail(); + } + + // Forward propagation + const OutputType* propagate( + const TransformedFeatureType* transformedFeatures, char* buffer) const { + const auto input = previousLayer.propagate( + transformedFeatures, buffer + SelfBufferSize); + OutputType* output = reinterpret_cast(buffer); + +#if defined (USE_AVX512) + using vec_t = __m512i; + #define vec_setzero _mm512_setzero_si512 + #define vec_set_32 _mm512_set1_epi32 + #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2 + #define vec_hadd Simd::m512_hadd + #define vec_haddx4 Simd::m512_haddx4 +#elif defined (USE_AVX2) + using vec_t = __m256i; + #define vec_setzero _mm256_setzero_si256 + #define vec_set_32 _mm256_set1_epi32 + #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 + #define vec_hadd Simd::m256_hadd + #define vec_haddx4 Simd::m256_haddx4 +#elif defined (USE_SSSE3) + using vec_t = __m128i; + #define vec_setzero _mm_setzero_si128 + #define vec_set_32 _mm_set1_epi32 + #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 + #define vec_hadd Simd::m128_hadd + #define vec_haddx4 Simd::m128_haddx4 +#endif + +#if defined (USE_SSSE3) + const vec_t* invec = reinterpret_cast(input); + + + // Perform accumulation to registers for each big block + for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock) + { + vec_t acc[NumOutputRegs] = { vec_setzero() }; + + // Each big block has NumOutputRegs small blocks in each "row", one per register. + // We process two small blocks at a time to save on one addition without VNNI. + for (IndexType smallBlock = 0; smallBlock < NumSmallBlocksPerOutput; smallBlock += 2) + { + const vec_t* weightvec = + reinterpret_cast( + weights + + bigBlock * BigBlockSize + + smallBlock * SmallBlockSize * NumOutputRegs); + + const vec_t in0 = invec[smallBlock + 0]; + const vec_t in1 = invec[smallBlock + 1]; + + for (IndexType k = 0; k < NumOutputRegs; ++k) + vec_add_dpbusd_32x2(acc[k], in0, weightvec[k], in1, weightvec[k + NumOutputRegs]); + } + + // Horizontally add all accumulators. + if constexpr (NumOutputRegs % 4 == 0) + { + __m128i* outputvec = reinterpret_cast<__m128i*>(output); + const __m128i* biasvec = reinterpret_cast(biases); + + for (IndexType k = 0; k < NumOutputRegs; k += 4) + { + const IndexType idx = (bigBlock * NumOutputRegs + k) / 4; + outputvec[idx] = vec_haddx4(acc[k+0], acc[k+1], acc[k+2], acc[k+3], biasvec[idx]); + } + } + else + { + for (IndexType k = 0; k < NumOutputRegs; ++k) + { + const IndexType idx = (bigBlock * NumOutputRegs + k); + output[idx] = vec_hadd(acc[k], biases[idx]); + } + } + } + +# undef vec_setzero +# undef vec_set_32 +# undef vec_add_dpbusd_32 +# undef vec_add_dpbusd_32x2 +# undef vec_hadd +# undef vec_haddx4 +#else + // Use old implementation for the other architectures. + affine_transform_non_ssse3< + InputDimensions, + PaddedInputDimensions, + OutputDimensions>(output, weights, biases, input); + +#endif + + return output; + } + + private: + using BiasType = OutputType; + using WeightType = std::int8_t; + + PreviousLayer previousLayer; + + alignas(CacheLineSize) BiasType biases[OutputDimensions]; + alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; + }; + + template + class AffineTransform> { public: // Input/output type using InputType = typename PreviousLayer::OutputType; @@ -41,24 +385,21 @@ namespace Stockfish::Eval::NNUE::Layers { static constexpr IndexType OutputDimensions = OutDims; static constexpr IndexType PaddedInputDimensions = ceil_to_multiple(InputDimensions, MaxSimdWidth); -#if defined (USE_AVX512) - static constexpr const IndexType OutputSimdWidth = SimdWidth / 2; -#elif defined (USE_SSSE3) + + static_assert(PaddedInputDimensions < 128, "Something went wrong. This specialization should not have been chosen."); + +#if defined (USE_SSSE3) static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; -#endif -#if defined (USE_AVX512) - static constexpr const IndexType InputSimdWidth = SimdWidth * 2; -#elif defined (USE_SSSE3) static constexpr const IndexType InputSimdWidth = SimdWidth; #endif // Size of forward propagation buffer used in this layer static constexpr std::size_t SelfBufferSize = - ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); + ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); // Size of the forward propagation buffer used from the input layer to this layer static constexpr std::size_t BufferSize = - PreviousLayer::BufferSize + SelfBufferSize; + PreviousLayer::BufferSize + SelfBufferSize; // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value() { @@ -69,30 +410,30 @@ namespace Stockfish::Eval::NNUE::Layers { return hashValue; } + static IndexType get_weight_index_scrambled(IndexType i) + { + return + (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + + i / PaddedInputDimensions * 4 + + i % 4; + } + + static IndexType get_weight_index(IndexType i) + { +#if defined (USE_SSSE3) + return get_weight_index_scrambled(i); +#else + return i; +#endif + } + // Read network parameters bool read_parameters(std::istream& stream) { if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) -#if !defined (USE_SSSE3) - weights[i] = read_little_endian(stream); -#elif defined (USE_VNNI) || defined (USE_AVX512) - if constexpr (OutputDimensions <= 8 && OutputDimensions != 1) - weights[i] = read_little_endian(stream); - else - weights[ - (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + - i / PaddedInputDimensions * 4 + - i % 4 - ] = read_little_endian(stream); -#else - weights[ - (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + - i / PaddedInputDimensions * 4 + - i % 4 - ] = read_little_endian(stream); -#endif + weights[get_weight_index(i)] = read_little_endian(stream); return !stream.fail(); } @@ -101,24 +442,10 @@ namespace Stockfish::Eval::NNUE::Layers { bool write_parameters(std::ostream& stream) const { if (!previousLayer.write_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) - write_little_endian(stream, biases[i]); -#if !defined (USE_SSSE3) - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - write_little_endian(stream, weights[i]); -#else - std::unique_ptr unscrambledWeights = std::make_unique(OutputDimensions * PaddedInputDimensions); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) { - unscrambledWeights[i] = - weights[ - (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + - i / PaddedInputDimensions * 4 + - i % 4 - ]; - } + write_little_endian(stream, biases[i]); for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - write_little_endian(stream, unscrambledWeights[i]); -#endif + write_little_endian(stream, weights[get_weight_index(i)]); return !stream.fail(); } @@ -126,493 +453,87 @@ namespace Stockfish::Eval::NNUE::Layers { const OutputType* propagate( const TransformedFeatureType* transformedFeatures, char* buffer) const { const auto input = previousLayer.propagate( - transformedFeatures, buffer + SelfBufferSize); + transformedFeatures, buffer + SelfBufferSize); + const auto output = reinterpret_cast(buffer); -#if defined (USE_AVX512) - - [[maybe_unused]] const __m512i Ones512 = _mm512_set1_epi16(1); - - [[maybe_unused]] auto m512_hadd = [](__m512i sum, int bias) -> int { - return _mm512_reduce_add_epi32(sum) + bias; - }; - - [[maybe_unused]] auto m512_hadd128x16_interleave = []( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) -> __m512i { - - __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); - __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); - - __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); - __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); - - __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); - __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); - - __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); - __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); - - return _mm512_add_epi32(sum0123a, sum0123b); - }; - - [[maybe_unused]] auto m512_haddx4 = [m512_hadd128x16_interleave]( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m128i bias) -> __m128i { - - __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); - - __m256i sum256lo = _mm512_castsi512_si256(sum); - __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); - - sum256lo = _mm256_add_epi32(sum256lo, sum256hi); - - __m128i sum128lo = _mm256_castsi256_si128(sum256lo); - __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1); - - return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); - }; - - [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { -#if defined (USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a, b); -#else - __m512i product0 = _mm512_maddubs_epi16(a, b); - product0 = _mm512_madd_epi16(product0, Ones512); - acc = _mm512_add_epi32(acc, product0); -#endif - }; - - [[maybe_unused]] auto m512_add_dpbusd_epi32x2 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1) { -#if defined (USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a0, b0); - acc = _mm512_dpbusd_epi32(acc, a1, b1); -#else - __m512i product0 = _mm512_maddubs_epi16(a0, b0); - __m512i product1 = _mm512_maddubs_epi16(a1, b1); - product0 = _mm512_adds_epi16(product0, product1); - product0 = _mm512_madd_epi16(product0, Ones512); - acc = _mm512_add_epi32(acc, product0); -#endif - }; - - [[maybe_unused]] auto m512_add_dpbusd_epi32x4 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1, - __m512i a2, __m512i b2, __m512i a3, __m512i b3) { -#if defined (USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a0, b0); - acc = _mm512_dpbusd_epi32(acc, a1, b1); - acc = _mm512_dpbusd_epi32(acc, a2, b2); - acc = _mm512_dpbusd_epi32(acc, a3, b3); -#else - __m512i product0 = _mm512_maddubs_epi16(a0, b0); - __m512i product1 = _mm512_maddubs_epi16(a1, b1); - __m512i product2 = _mm512_maddubs_epi16(a2, b2); - __m512i product3 = _mm512_maddubs_epi16(a3, b3); - product0 = _mm512_adds_epi16(product0, product1); - product0 = _mm512_madd_epi16(product0, Ones512); - product2 = _mm512_adds_epi16(product2, product3); - product2 = _mm512_madd_epi16(product2, Ones512); - acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product2)); -#endif - }; - -#endif #if defined (USE_AVX2) - - [[maybe_unused]] const __m256i Ones256 = _mm256_set1_epi16(1); - - [[maybe_unused]] auto m256_hadd = [](__m256i sum, int bias) -> int { - __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); - return _mm_cvtsi128_si32(sum128) + bias; - }; - - [[maybe_unused]] auto m256_haddx4 = [](__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, __m128i bias) -> __m128i { - sum0 = _mm256_hadd_epi32(sum0, sum1); - sum2 = _mm256_hadd_epi32(sum2, sum3); - - sum0 = _mm256_hadd_epi32(sum0, sum2); - - __m128i sum128lo = _mm256_castsi256_si128(sum0); - __m128i sum128hi = _mm256_extracti128_si256(sum0, 1); - - return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); - }; - - [[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) { -#if defined (USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a, b); -#else - __m256i product0 = _mm256_maddubs_epi16(a, b); - product0 = _mm256_madd_epi16(product0, Ones256); - acc = _mm256_add_epi32(acc, product0); -#endif - }; - - [[maybe_unused]] auto m256_add_dpbusd_epi32x2 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1) { -#if defined (USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a0, b0); - acc = _mm256_dpbusd_epi32(acc, a1, b1); -#else - __m256i product0 = _mm256_maddubs_epi16(a0, b0); - __m256i product1 = _mm256_maddubs_epi16(a1, b1); - product0 = _mm256_adds_epi16(product0, product1); - product0 = _mm256_madd_epi16(product0, Ones256); - acc = _mm256_add_epi32(acc, product0); -#endif - }; - - [[maybe_unused]] auto m256_add_dpbusd_epi32x4 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1, - __m256i a2, __m256i b2, __m256i a3, __m256i b3) { -#if defined (USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a0, b0); - acc = _mm256_dpbusd_epi32(acc, a1, b1); - acc = _mm256_dpbusd_epi32(acc, a2, b2); - acc = _mm256_dpbusd_epi32(acc, a3, b3); -#else - __m256i product0 = _mm256_maddubs_epi16(a0, b0); - __m256i product1 = _mm256_maddubs_epi16(a1, b1); - __m256i product2 = _mm256_maddubs_epi16(a2, b2); - __m256i product3 = _mm256_maddubs_epi16(a3, b3); - product0 = _mm256_adds_epi16(product0, product1); - product0 = _mm256_madd_epi16(product0, Ones256); - product2 = _mm256_adds_epi16(product2, product3); - product2 = _mm256_madd_epi16(product2, Ones256); - acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product2)); -#endif - }; - -#endif -#if defined (USE_SSSE3) - - [[maybe_unused]] const __m128i Ones128 = _mm_set1_epi16(1); - - [[maybe_unused]] auto m128_hadd = [](__m128i sum, int bias) -> int { - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB - return _mm_cvtsi128_si32(sum) + bias; - }; - - [[maybe_unused]] auto m128_haddx4 = [](__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, __m128i bias) -> __m128i { - sum0 = _mm_hadd_epi32(sum0, sum1); - sum2 = _mm_hadd_epi32(sum2, sum3); - sum0 = _mm_hadd_epi32(sum0, sum2); - return _mm_add_epi32(sum0, bias); - }; - - [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { - __m128i product0 = _mm_maddubs_epi16(a, b); - product0 = _mm_madd_epi16(product0, Ones128); - acc = _mm_add_epi32(acc, product0); - }; - - [[maybe_unused]] auto m128_add_dpbusd_epi32x2 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1) { - __m128i product0 = _mm_maddubs_epi16(a0, b0); - __m128i product1 = _mm_maddubs_epi16(a1, b1); - product0 = _mm_adds_epi16(product0, product1); - product0 = _mm_madd_epi16(product0, Ones128); - acc = _mm_add_epi32(acc, product0); - }; - - [[maybe_unused]] auto m128_add_dpbusd_epi32x4 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1, - __m128i a2, __m128i b2, __m128i a3, __m128i b3) { - __m128i product0 = _mm_maddubs_epi16(a0, b0); - __m128i product1 = _mm_maddubs_epi16(a1, b1); - __m128i product2 = _mm_maddubs_epi16(a2, b2); - __m128i product3 = _mm_maddubs_epi16(a3, b3); - product0 = _mm_adds_epi16(product0, product1); - product0 = _mm_madd_epi16(product0, Ones128); - product2 = _mm_adds_epi16(product2, product3); - product2 = _mm_madd_epi16(product2, Ones128); - acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product2)); - }; - -#endif - -#if defined (USE_AVX512) - using vec_t = __m512i; - #define vec_setzero _mm512_setzero_si512 - #define vec_set_32 _mm512_set1_epi32 - [[maybe_unused]] auto& vec_add_dpbusd_32 = m512_add_dpbusd_epi32; - [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m512_add_dpbusd_epi32x2; - [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m512_add_dpbusd_epi32x4; - [[maybe_unused]] auto& vec_hadd = m512_hadd; - [[maybe_unused]] auto& vec_haddx4 = m512_haddx4; -#elif defined (USE_AVX2) using vec_t = __m256i; #define vec_setzero _mm256_setzero_si256 #define vec_set_32 _mm256_set1_epi32 - [[maybe_unused]] auto& vec_add_dpbusd_32 = m256_add_dpbusd_epi32; - [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m256_add_dpbusd_epi32x2; - [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m256_add_dpbusd_epi32x4; - [[maybe_unused]] auto& vec_hadd = m256_hadd; - [[maybe_unused]] auto& vec_haddx4 = m256_haddx4; + #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 + #define vec_add_dpbusd_32x4 Simd::m256_add_dpbusd_epi32x4 + #define vec_hadd Simd::m256_hadd + #define vec_haddx4 Simd::m256_haddx4 #elif defined (USE_SSSE3) using vec_t = __m128i; #define vec_setzero _mm_setzero_si128 #define vec_set_32 _mm_set1_epi32 - [[maybe_unused]] auto& vec_add_dpbusd_32 = m128_add_dpbusd_epi32; - [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m128_add_dpbusd_epi32x2; - [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m128_add_dpbusd_epi32x4; - [[maybe_unused]] auto& vec_hadd = m128_hadd; - [[maybe_unused]] auto& vec_haddx4 = m128_haddx4; + #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 + #define vec_add_dpbusd_32x4 Simd::m128_add_dpbusd_epi32x4 + #define vec_hadd Simd::m128_hadd + #define vec_haddx4 Simd::m128_haddx4 #endif #if defined (USE_SSSE3) - const auto output = reinterpret_cast(buffer); const auto inputVector = reinterpret_cast(input); -#endif -#if defined (USE_VNNI) || defined (USE_AVX512) - - static_assert(OutputDimensions == 1 || OutputDimensions % 4 == 0); - - // OutputDimensions is either 1 or a multiple of SimdWidth - // because then it is also an input dimension. - if constexpr (OutputDimensions <= 8 && OutputDimensions != 1) - { - constexpr IndexType NumChunks = PaddedInputDimensions / InputSimdWidth; - - static_assert(NumChunks % 2 == 0); - - const auto input_vec = reinterpret_cast(input); - const auto bias_vec = reinterpret_cast(biases); - auto out_vec = reinterpret_cast<__m128i*>(output); - - vec_t regs[OutputDimensions]; - for (IndexType k = 0; k < OutputDimensions; ++k) - regs[k] = vec_setzero(); - - for (IndexType i = 0; i < NumChunks / 2; ++i) - { - const vec_t in0 = input_vec[i * 2 + 0]; - const vec_t in1 = input_vec[i * 2 + 1]; - for (IndexType k = 0; k < OutputDimensions; ++k) - { - const vec_t w0 = reinterpret_cast(&weights[k * PaddedInputDimensions])[i * 2 + 0]; - const vec_t w1 = reinterpret_cast(&weights[k * PaddedInputDimensions])[i * 2 + 1]; - vec_add_dpbusd_32(regs[k], in0, w0); - vec_add_dpbusd_32(regs[k], in1, w1); - } - } - - for (IndexType k = 0; k < OutputDimensions / 4; ++k) - { - out_vec[k] = vec_haddx4( - regs[k * 4 + 0], - regs[k * 4 + 1], - regs[k * 4 + 2], - regs[k * 4 + 3], - bias_vec[k] - ); - } - } - else if constexpr (InputDimensions == 8) - { - const auto input32 = reinterpret_cast(input); - __m256i* outptr = reinterpret_cast<__m256i*>(output); - std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); - - const __m256i in0 = _mm256_set1_epi32(input32[0]); - const __m256i in1 = _mm256_set1_epi32(input32[1]); - const auto col0 = reinterpret_cast(&weights[0]); - const auto col1 = reinterpret_cast(&weights[OutputDimensions * 4]); - for (IndexType j = 0; j * 8 < OutputDimensions; ++j) - m256_add_dpbusd_epi32x2(outptr[j], in0, col0[j], in1, col1[j]); - } - else - -#elif defined (USE_SSSE3) - - if constexpr (OutputDimensions % OutputSimdWidth == 0 && InputDimensions == 8) - { - const auto input32 = reinterpret_cast(input); - vec_t* outptr = reinterpret_cast(output); - std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); - - const vec_t in0 = vec_set_32(input32[0]); - const vec_t in1 = vec_set_32(input32[1]); - const auto col0 = reinterpret_cast(&weights[0]); - const auto col1 = reinterpret_cast(&weights[OutputDimensions * 4]); - for (IndexType j = 0; j * OutputSimdWidth < OutputDimensions; ++j) - vec_add_dpbusd_32x2(outptr[j], in0, col0[j], in1, col1[j]); - } - else - -#endif - -#if defined (USE_SSSE3) + static_assert(InputDimensions % 8 == 0); + static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); if constexpr (OutputDimensions % OutputSimdWidth == 0) { - static_assert(InputDimensions % 16 == 0); + constexpr IndexType NumChunks = InputDimensions / 4; + constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; - constexpr IndexType NumChunks = InputDimensions / 4; - constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; + const auto input32 = reinterpret_cast(input); + const vec_t* biasvec = reinterpret_cast(biases); + vec_t acc[NumRegs]; + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = biasvec[k]; - const auto input32 = reinterpret_cast(input); - const vec_t* biasvec = reinterpret_cast(biases); - vec_t outs[NumRegs]; + for (IndexType i = 0; i < NumChunks; i += 2) + { + const vec_t in0 = vec_set_32(input32[i + 0]); + const vec_t in1 = vec_set_32(input32[i + 1]); + const auto col0 = reinterpret_cast(&weights[(i + 0) * OutputDimensions * 4]); + const auto col1 = reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); for (IndexType k = 0; k < NumRegs; ++k) - outs[k] = biasvec[k]; + vec_add_dpbusd_32x2(acc[k], in0, col0[k], in1, col1[k]); + } - for (IndexType i = 0; i < NumChunks; i += 4) - { - const vec_t in0 = vec_set_32(input32[i + 0]); - const vec_t in1 = vec_set_32(input32[i + 1]); - const vec_t in2 = vec_set_32(input32[i + 2]); - const vec_t in3 = vec_set_32(input32[i + 3]); - const auto col0 = reinterpret_cast(&weights[(i + 0) * OutputDimensions * 4]); - const auto col1 = reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); - const auto col2 = reinterpret_cast(&weights[(i + 2) * OutputDimensions * 4]); - const auto col3 = reinterpret_cast(&weights[(i + 3) * OutputDimensions * 4]); - for (IndexType k = 0; k < NumRegs; ++k) - vec_add_dpbusd_32x4(outs[k], in0, col0[k], in1, col1[k], in2, col2[k], in3, col3[k]); - } - - vec_t* outptr = reinterpret_cast(output); - for (IndexType k = 0; k < NumRegs; ++k) - outptr[k] = outs[k]; + vec_t* outptr = reinterpret_cast(output); + for (IndexType k = 0; k < NumRegs; ++k) + outptr[k] = acc[k]; } else if constexpr (OutputDimensions == 1) { - static_assert(InputDimensions % 4 == 0); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + vec_t sum0 = vec_setzero(); + const auto row0 = reinterpret_cast(&weights[0]); -#if defined (USE_AVX512) - if constexpr (PaddedInputDimensions % (SimdWidth * 2) != 0) - { - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; - const auto inputVector256 = reinterpret_cast(input); - - __m256i sum0 = _mm256_setzero_si256(); - const auto row0 = reinterpret_cast(&weights[0]); - - for (int j = 0; j < (int)NumChunks; ++j) - { - const __m256i in = inputVector256[j]; - m256_add_dpbusd_epi32(sum0, in, row0[j]); - } - output[0] = m256_hadd(sum0, biases[0]); - } - else -#endif - { -#if defined (USE_AVX512) - constexpr IndexType NumChunks = PaddedInputDimensions / (SimdWidth * 2); -#else - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; -#endif - vec_t sum0 = vec_setzero(); - const auto row0 = reinterpret_cast(&weights[0]); - - for (int j = 0; j < (int)NumChunks; ++j) - { - const vec_t in = inputVector[j]; - vec_add_dpbusd_32(sum0, in, row0[j]); - } - output[0] = vec_hadd(sum0, biases[0]); - } + for (int j = 0; j < (int)NumChunks; ++j) + { + const vec_t in = inputVector[j]; + vec_add_dpbusd_32(sum0, in, row0[j]); + } + output[0] = vec_hadd(sum0, biases[0]); } +# undef vec_setzero +# undef vec_set_32 +# undef vec_add_dpbusd_32 +# undef vec_add_dpbusd_32x2 +# undef vec_add_dpbusd_32x4 +# undef vec_hadd +# undef vec_haddx4 #else - -// Use old implementation for the other architectures. - - auto output = reinterpret_cast(buffer); - -#if defined(USE_SSE2) - // At least a multiple of 16, with SSE2. - static_assert(PaddedInputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; - const __m128i Zeros = _mm_setzero_si128(); - const auto inputVector = reinterpret_cast(input); - -#elif defined(USE_MMX) - static_assert(InputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = InputDimensions / SimdWidth; - const __m64 Zeros = _mm_setzero_si64(); - const auto inputVector = reinterpret_cast(input); - -#elif defined(USE_NEON) - static_assert(PaddedInputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; - const auto inputVector = reinterpret_cast(input); -#endif - - for (IndexType i = 0; i < OutputDimensions; ++i) { - const IndexType offset = i * PaddedInputDimensions; - -#if defined(USE_SSE2) - __m128i sumLo = _mm_cvtsi32_si128(biases[i]); - __m128i sumHi = Zeros; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m128i row_j = _mm_load_si128(&row[j]); - __m128i input_j = _mm_load_si128(&inputVector[j]); - __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); - __m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); - __m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros); - __m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros); - __m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo); - __m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi); - sumLo = _mm_add_epi32(sumLo, productLo); - sumHi = _mm_add_epi32(sumHi, productHi); - } - __m128i sum = _mm_add_epi32(sumLo, sumHi); - __m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); - sum = _mm_add_epi32(sum, sumHigh_64); - __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2)); - sum = _mm_add_epi32(sum, sum_second_32); - output[i] = _mm_cvtsi128_si32(sum); - -#elif defined(USE_MMX) - __m64 sumLo = _mm_cvtsi32_si64(biases[i]); - __m64 sumHi = Zeros; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m64 row_j = row[j]; - __m64 input_j = inputVector[j]; - __m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); - __m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); - __m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros); - __m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros); - __m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo); - __m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi); - sumLo = _mm_add_pi32(sumLo, productLo); - sumHi = _mm_add_pi32(sumHi, productHi); - } - __m64 sum = _mm_add_pi32(sumLo, sumHi); - sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); - output[i] = _mm_cvtsi64_si32(sum); - -#elif defined(USE_NEON) - int32x4_t sum = {biases[i]}; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]); - product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); - sum = vpadalq_s16(sum, product); - } - output[i] = sum[0] + sum[1] + sum[2] + sum[3]; - -#else - OutputType sum = biases[i]; - for (IndexType j = 0; j < InputDimensions; ++j) { - sum += weights[offset + j] * input[j]; - } - output[i] = sum; -#endif - - } -#if defined(USE_MMX) - _mm_empty(); -#endif - -#endif - -#if (!defined (USE_SSSE3) && defined (USE_SSE2)) || defined (USE_NEON) - static_assert(SimdWidth <= 16, "Otherwise we run outside of the padding for the output."); - if constexpr (SimdWidth > OutputDimensions && OutputDimensions != 1) - for (IndexType i = OutputDimensions; i < SimdWidth; ++i) - output[i] = 0; + // Use old implementation for the other architectures. + affine_transform_non_ssse3< + InputDimensions, + PaddedInputDimensions, + OutputDimensions>(output, weights, biases, input); #endif return output; diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 65455df4..c6f3ccad 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -35,9 +35,10 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType InputDimensions = - PreviousLayer::OutputDimensions; + static constexpr IndexType InputDimensions = PreviousLayer::OutputDimensions; static constexpr IndexType OutputDimensions = InputDimensions; + static constexpr IndexType PaddedOutputDimensions = + ceil_to_multiple(OutputDimensions, 32); // Size of forward propagation buffer used in this layer static constexpr std::size_t SelfBufferSize = @@ -179,6 +180,15 @@ namespace Stockfish::Eval::NNUE::Layers { output[i] = static_cast( std::max(0, std::min(127, input[i] >> WeightScaleBits))); } + + // Affine transform layers expect that there is at least + // ceil_to_multiple(OutputDimensions, 32) initialized values. + // We cannot do this in the affine transform because it requires + // preallocating space here. + for (IndexType i = OutputDimensions; i < PaddedOutputDimensions; ++i) { + output[i] = 0; + } + return output; } diff --git a/src/simd.h b/src/simd.h new file mode 100644 index 00000000..584148f1 --- /dev/null +++ b/src/simd.h @@ -0,0 +1,341 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 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 . +*/ + +#ifndef STOCKFISH_SIMD_H_INCLUDED +#define STOCKFISH_SIMD_H_INCLUDED + +#if defined(USE_AVX2) +# include + +#elif defined(USE_SSE41) +# include + +#elif defined(USE_SSSE3) +# include + +#elif defined(USE_SSE2) +# include + +#elif defined(USE_MMX) +# include + +#elif defined(USE_NEON) +# include +#endif + +// The inline asm is only safe for GCC, where it is necessary to get good codegen. +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101693 +// Clang does fine without it. +// Play around here: https://godbolt.org/z/7EWqrYq51 +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#define USE_INLINE_ASM +#endif + +namespace Stockfish::Simd { + +#if defined (USE_AVX512) + + [[maybe_unused]] static int m512_hadd(__m512i sum, int bias) { + return _mm512_reduce_add_epi32(sum) + bias; + } + + /* + Parameters: + sum0 = [zmm0.i128[0], zmm0.i128[1], zmm0.i128[2], zmm0.i128[3]] + sum1 = [zmm1.i128[0], zmm1.i128[1], zmm1.i128[2], zmm1.i128[3]] + sum2 = [zmm2.i128[0], zmm2.i128[1], zmm2.i128[2], zmm2.i128[3]] + sum3 = [zmm3.i128[0], zmm3.i128[1], zmm3.i128[2], zmm3.i128[3]] + + Returns: + ret = [ + reduce_add_epi32(zmm0.i128[0]), reduce_add_epi32(zmm1.i128[0]), reduce_add_epi32(zmm2.i128[0]), reduce_add_epi32(zmm3.i128[0]), + reduce_add_epi32(zmm0.i128[1]), reduce_add_epi32(zmm1.i128[1]), reduce_add_epi32(zmm2.i128[1]), reduce_add_epi32(zmm3.i128[1]), + reduce_add_epi32(zmm0.i128[2]), reduce_add_epi32(zmm1.i128[2]), reduce_add_epi32(zmm2.i128[2]), reduce_add_epi32(zmm3.i128[2]), + reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3]) + ] + */ + [[maybe_unused]] static __m512i m512_hadd128x16_interleave( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) { + + __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); + __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); + + __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); + __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); + + __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); + __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); + + __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); + __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); + + return _mm512_add_epi32(sum0123a, sum0123b); + } + + [[maybe_unused]] static __m128i m512_haddx4( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, + __m128i bias) { + + __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); + + __m256i sum256lo = _mm512_castsi512_si256(sum); + __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); + + sum256lo = _mm256_add_epi32(sum256lo, sum256hi); + + __m128i sum128lo = _mm256_castsi256_si128(sum256lo); + __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + } + + [[maybe_unused]] static void m512_add_dpbusd_epi32( + __m512i& acc, + __m512i a, + __m512i b) { + +# if defined (USE_VNNI) +# if defined (USE_INLINE_ASM) + asm( + "vpdpbusd %[b], %[a], %[acc]\n\t" + : [acc]"+v"(acc) + : [a]"v"(a), [b]"vm"(b) + ); +# else + acc = _mm512_dpbusd_epi32(acc, a, b); +# endif +# else +# if defined (USE_INLINE_ASM) + __m512i tmp = _mm512_maddubs_epi16(a, b); + asm( + "vpmaddwd %[tmp], %[ones], %[tmp]\n\t" + "vpaddd %[acc], %[tmp], %[acc]\n\t" + : [acc]"+v"(acc), [tmp]"+&v"(tmp) + : [ones]"v"(_mm512_set1_epi16(1)) + ); +# else + __m512i product0 = _mm512_maddubs_epi16(a, b); + product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); + acc = _mm512_add_epi32(acc, product0); +# endif +# endif + } + + [[maybe_unused]] static void m512_add_dpbusd_epi32x2( + __m512i& acc, + __m512i a0, __m512i b0, + __m512i a1, __m512i b1) { + +# if defined (USE_VNNI) +# if defined (USE_INLINE_ASM) + asm( + "vpdpbusd %[b0], %[a0], %[acc]\n\t" + "vpdpbusd %[b1], %[a1], %[acc]\n\t" + : [acc]"+v"(acc) + : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) + ); +# else + acc = _mm512_dpbusd_epi32(acc, a0, b0); + acc = _mm512_dpbusd_epi32(acc, a1, b1); +# endif +# else +# if defined (USE_INLINE_ASM) + __m512i tmp0 = _mm512_maddubs_epi16(a0, b0); + __m512i tmp1 = _mm512_maddubs_epi16(a1, b1); + asm( + "vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t" + "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t" + "vpaddd %[acc], %[tmp0], %[acc]\n\t" + : [acc]"+v"(acc), [tmp0]"+&v"(tmp0) + : [tmp1]"v"(tmp1), [ones]"v"(_mm512_set1_epi16(1)) + ); +# else + __m512i product0 = _mm512_maddubs_epi16(a0, b0); + __m512i product1 = _mm512_maddubs_epi16(a1, b1); + product0 = _mm512_adds_epi16(product0, product1); + product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); + acc = _mm512_add_epi32(acc, product0); +# endif +# endif + } + +#endif + +#if defined (USE_AVX2) + + [[maybe_unused]] static int m256_hadd(__m256i sum, int bias) { + __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); + return _mm_cvtsi128_si32(sum128) + bias; + } + + [[maybe_unused]] static __m128i m256_haddx4( + __m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, + __m128i bias) { + + sum0 = _mm256_hadd_epi32(sum0, sum1); + sum2 = _mm256_hadd_epi32(sum2, sum3); + + sum0 = _mm256_hadd_epi32(sum0, sum2); + + __m128i sum128lo = _mm256_castsi256_si128(sum0); + __m128i sum128hi = _mm256_extracti128_si256(sum0, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + } + + [[maybe_unused]] static void m256_add_dpbusd_epi32( + __m256i& acc, + __m256i a, + __m256i b) { + +# if defined (USE_VNNI) +# if defined (USE_INLINE_ASM) + asm( + "vpdpbusd %[b], %[a], %[acc]\n\t" + : [acc]"+v"(acc) + : [a]"v"(a), [b]"vm"(b) + ); +# else + acc = _mm256_dpbusd_epi32(acc, a, b); +# endif +# else +# if defined (USE_INLINE_ASM) + __m256i tmp = _mm256_maddubs_epi16(a, b); + asm( + "vpmaddwd %[tmp], %[ones], %[tmp]\n\t" + "vpaddd %[acc], %[tmp], %[acc]\n\t" + : [acc]"+v"(acc), [tmp]"+&v"(tmp) + : [ones]"v"(_mm256_set1_epi16(1)) + ); +# else + __m256i product0 = _mm256_maddubs_epi16(a, b); + product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); + acc = _mm256_add_epi32(acc, product0); +# endif +# endif + } + + [[maybe_unused]] static void m256_add_dpbusd_epi32x2( + __m256i& acc, + __m256i a0, __m256i b0, + __m256i a1, __m256i b1) { + +# if defined (USE_VNNI) +# if defined (USE_INLINE_ASM) + asm( + "vpdpbusd %[b0], %[a0], %[acc]\n\t" + "vpdpbusd %[b1], %[a1], %[acc]\n\t" + : [acc]"+v"(acc) + : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) + ); +# else + acc = _mm256_dpbusd_epi32(acc, a0, b0); + acc = _mm256_dpbusd_epi32(acc, a1, b1); +# endif +# else +# if defined (USE_INLINE_ASM) + __m256i tmp0 = _mm256_maddubs_epi16(a0, b0); + __m256i tmp1 = _mm256_maddubs_epi16(a1, b1); + asm( + "vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t" + "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t" + "vpaddd %[acc], %[tmp0], %[acc]\n\t" + : [acc]"+v"(acc), [tmp0]"+&v"(tmp0) + : [tmp1]"v"(tmp1), [ones]"v"(_mm256_set1_epi16(1)) + ); +# else + __m256i product0 = _mm256_maddubs_epi16(a0, b0); + __m256i product1 = _mm256_maddubs_epi16(a1, b1); + product0 = _mm256_adds_epi16(product0, product1); + product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); + acc = _mm256_add_epi32(acc, product0); +# endif +# endif + } + +#endif + +#if defined (USE_SSSE3) + + [[maybe_unused]] static int m128_hadd(__m128i sum, int bias) { + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB + return _mm_cvtsi128_si32(sum) + bias; + } + + [[maybe_unused]] static __m128i m128_haddx4( + __m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, + __m128i bias) { + + sum0 = _mm_hadd_epi32(sum0, sum1); + sum2 = _mm_hadd_epi32(sum2, sum3); + sum0 = _mm_hadd_epi32(sum0, sum2); + return _mm_add_epi32(sum0, bias); + } + + [[maybe_unused]] static void m128_add_dpbusd_epi32( + __m128i& acc, + __m128i a, + __m128i b) { + +# if defined (USE_INLINE_ASM) + __m128i tmp = _mm_maddubs_epi16(a, b); + asm( + "pmaddwd %[ones], %[tmp]\n\t" + "paddd %[tmp], %[acc]\n\t" + : [acc]"+v"(acc), [tmp]"+&v"(tmp) + : [ones]"v"(_mm_set1_epi16(1)) + ); +# else + __m128i product0 = _mm_maddubs_epi16(a, b); + product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); + acc = _mm_add_epi32(acc, product0); +# endif + } + + [[maybe_unused]] static void m128_add_dpbusd_epi32x2( + __m128i& acc, + __m128i a0, __m128i b0, + __m128i a1, __m128i b1) { + +# if defined (USE_INLINE_ASM) + __m128i tmp0 = _mm_maddubs_epi16(a0, b0); + __m128i tmp1 = _mm_maddubs_epi16(a1, b1); + asm( + "paddsw %[tmp1], %[tmp0]\n\t" + "pmaddwd %[ones], %[tmp0]\n\t" + "paddd %[tmp0], %[acc]\n\t" + : [acc]"+v"(acc), [tmp0]"+&v"(tmp0) + : [tmp1]"v"(tmp1), [ones]"v"(_mm_set1_epi16(1)) + ); +# else + __m128i product0 = _mm_maddubs_epi16(a0, b0); + __m128i product1 = _mm_maddubs_epi16(a1, b1); + product0 = _mm_adds_epi16(product0, product1); + product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); + acc = _mm_add_epi32(acc, product0); +# endif + } + +#endif + +} + +#endif // STOCKFISH_SIMD_H_INCLUDED From e57d2d9d4714fd085042f88a037be93772956105 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 7 Jun 2021 01:20:39 -0300 Subject: [PATCH 142/282] Simplify Null Move Search Reduction slightly simpler formula for reduction computation. first round of tests: STC: LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 15632 W: 1319 L: 1204 D: 13109 Ptnml(0-2): 33, 956, 5733, 1051, 43 https://tests.stockfishchess.org/tests/view/60bd03c7457376eb8bcaa600 LTC: LLR: 3.37 (-2.94,2.94) <-2.50,0.50> Total: 86296 W: 2814 L: 2779 D: 80703 Ptnml(0-2): 33, 2500, 38039, 2551, 25 https://tests.stockfishchess.org/tests/view/60bd1ff0457376eb8bcaa653 recent tests: STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 23936 W: 1895 L: 1793 D: 20248 Ptnml(0-2): 40, 1470, 8869, 1526, 63 https://tests.stockfishchess.org/tests/view/611f9b7d4977aa1525c9cb6b LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 62568 W: 1750 L: 1713 D: 59105 Ptnml(0-2): 19, 1560, 28085, 1605, 15 https://tests.stockfishchess.org/tests/view/611fa4814977aa1525c9cb71 functional on high depth closes https://github.com/official-stockfish/Stockfish/pull/3535 Bench: 5375286 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c48b74bc..83ed2aeb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -801,7 +801,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (1090 + 81 * depth) / 256 + std::min(int(eval - beta) / 205, 3); + Depth R = std::min(int(eval - beta) / 205, 3) + depth / 3 + 4; ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; From 939ffe454d28556ee7653af4d3322b8c8ba470be Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Fri, 20 Aug 2021 10:37:22 +0200 Subject: [PATCH 143/282] do more LMR extensions for PV nodes LMR Pv and depth 6 Extension tweak: LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 52488 W: 1542 L: 1394 D: 49552 Ptnml(0-2): 18, 1253, 23552, 1405, 16 https://tests.stockfishchess.org/tests/view/611e49c34977aa1525c9caa7 STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 76216 W: 6000 L: 5784 D: 64432 Ptnml(0-2): 204, 4745, 28006, 4937, 216 https://tests.stockfishchess.org/tests/view/611e0e254977aa1525c9ca89 closes https://github.com/official-stockfish/Stockfish/pull/3666 Bench: 5046381 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 83ed2aeb..a487ed13 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1173,8 +1173,8 @@ moves_loop: // When in check, search starts here // In general we want to cap the LMR depth search at newDepth. But if // reductions are really negative and movecount is low, we allow this move - // to be searched deeper than the first move, unless ttMove was extended by 2. - Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && moveCount <= 5 && !doubleExtension)); + // to be searched deeper than the first move in specific cases. + Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && (moveCount <= 5 || (depth > 6 && PvNode)) && !doubleExtension)); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); From 590447d7a162058ed1f68270c84aac4e2f256bb1 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Fri, 20 Aug 2021 18:30:27 +0800 Subject: [PATCH 144/282] Update default net to nn-517c4f68b5df.nnue SPSA: https://tests.stockfishchess.org/tests/view/611cf0da4977aa1525c9ca03 Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-ac5605a608d6.nnue New net: nn-517c4f68b5df.nnue STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 11600 W: 998 L: 851 D: 9751 Ptnml(0-2): 30, 705, 4186, 846, 33 https://tests.stockfishchess.org/tests/view/611f84524977aa1525c9cb5b LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 9360 W: 338 L: 243 D: 8779 Ptnml(0-2): 0, 220, 4151, 303, 6 https://tests.stockfishchess.org/tests/view/611f8c5b4977aa1525c9cb64 closes https://github.com/official-stockfish/Stockfish/pull/3667 Bench: 4844618 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 322db398..b8503877 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-ac5605a608d6.nnue" + #define EvalFileDefaultName "nn-517c4f68b5df.nnue" namespace NNUE { From d754ea50a8be3091f02ab279fb9069f453cc1bcc Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 21 Aug 2021 01:53:03 -0300 Subject: [PATCH 145/282] Simplify Declaration on Pawn Move Generation Removes possible micro-optimization in favor of readability. STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 75432 W: 5824 L: 5777 D: 63831 Ptnml(0-2): 178, 4648, 28036, 4657, 197 https://tests.stockfishchess.org/tests/view/611fa7f84977aa1525c9cb75 LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 41200 W: 1156 L: 1106 D: 38938 Ptnml(0-2): 13, 981, 18562, 1031, 13 https://tests.stockfishchess.org/tests/view/611fcc694977aa1525c9cb9b Closes https://github.com/official-stockfish/Stockfish/pull/3669 No functional change --- src/movegen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 5f3ba90a..5095bb74 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -52,9 +52,9 @@ namespace { constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); - const Bitboard emptySquares = Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces(); - const Bitboard enemies = Type == EVASIONS ? pos.checkers() - : Type == CAPTURES ? target : pos.pieces(Them); + const Bitboard emptySquares = ~pos.pieces(); + const Bitboard enemies = Type == EVASIONS ? pos.checkers() + : pos.pieces(Them); Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; From af0d82792e21f0362b781fc671ae8b7a0642e61a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 26 Aug 2021 22:44:49 +0200 Subject: [PATCH 146/282] Fix empty EvalFile option some GUIs send an empty string for EvalFile, in that case explicitly try the default name fixes https://github.com/official-stockfish/Stockfish/issues/3675 closes https://github.com/official-stockfish/Stockfish/pull/3678 No functional change. --- src/evaluate.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 64f91725..62d4be84 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -78,6 +78,8 @@ namespace Eval { return; string eval_file = string(Options["EvalFile"]); + if (eval_file.empty()) + eval_file = EvalFileDefaultName; #if defined(DEFAULT_NNUE_DIRECTORY) #define stringify2(x) #x @@ -118,16 +120,16 @@ namespace Eval { void NNUE::verify() { string eval_file = string(Options["EvalFile"]); + if (eval_file.empty()) + eval_file = EvalFileDefaultName; if (useNNUE && eval_file_loaded != eval_file) { - UCI::OptionsMap defaults; - UCI::init(defaults); string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available."; string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully."; string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; - string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + string(defaults["EvalFile"]); + string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName); string msg5 = "The engine will be terminated now."; sync_cout << "info string ERROR: " << msg1 << sync_endl; From f30f231cbf74ddefd7881a653d0941a119c9b1bb Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Sun, 22 Aug 2021 15:44:30 +0200 Subject: [PATCH 147/282] Use "pedantic" flag also for mingw This will avoid to run in fishtest a test where the linux machines exit from the building process and only the windows machines run the test. See: https://tests.stockfishchess.org/tests/view/61122d732a8a49ac5be79996 https://github.com/SFisGOD/Stockfish/commit/4e422577d6ebd1f6ecf606189190b8f6fb03f6c9#comments closes https://github.com/official-stockfish/Stockfish/pull/3671 No functional change. --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 901ddd92..d92854bc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -368,7 +368,7 @@ ifeq ($(COMP),mingw) CXX=g++ endif - CXXFLAGS += -Wextra -Wshadow + CXXFLAGS += -pedantic -Wextra -Wshadow LDFLAGS += -static endif From 69eede7d0818be7bf7ae893dcd4408e9d621b2d2 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Thu, 26 Aug 2021 18:07:41 +0800 Subject: [PATCH 148/282] Update default net to nn-33495fe25081.nnue STC: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 37368 W: 9621 L: 9391 D: 18356 Ptnml(0-2): 117, 4287, 9664, 4481, 135 https://tests.stockfishchess.org/tests/view/612768165318138ee1204977 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 13328 W: 3446 L: 3246 D: 6636 Ptnml(0-2): 11, 1383, 3682, 1571, 17 https://tests.stockfishchess.org/tests/view/6127dc8d62d20cf82b5ad196 Closes https://github.com/official-stockfish/Stockfish/pull/3679 Bench: 5179347 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index b8503877..f73fc37f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-517c4f68b5df.nnue" + #define EvalFileDefaultName "nn-33495fe25081.nnue" namespace NNUE { From ad357e147a1b8481a04761d726ce1db14115a68f Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Fri, 27 Aug 2021 14:25:09 -0400 Subject: [PATCH 149/282] CMH Pruning Tweak Tweak pruning formula by adding up CMH values. STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 14608 W: 3837 L: 3641 D: 7130 Ptnml(0-2): 27, 1681, 3723, 1815, 58 https://tests.stockfishchess.org/tests/view/612792f362d20cf82b5ad156 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 53520 W: 13580 L: 13276 D: 26664 Ptnml(0-2): 28, 5610, 15183, 5908, 31 https://tests.stockfishchess.org/tests/view/6127d27062d20cf82b5ad191 closes https://github.com/official-stockfish/Stockfish/pull/3682 Bench: 5186641 --- src/search.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a487ed13..5aa50644 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1018,9 +1018,10 @@ moves_loop: // When in check, search starts here else { // Continuation history based pruning (~20 Elo) - if ( lmrDepth < 5 - && (*contHist[0])[movedPiece][to_sq(move)] < 23 - 23 * depth * depth - && (*contHist[1])[movedPiece][to_sq(move)] < 23 - 23 * depth * depth) + if (lmrDepth < 5 + && (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)] < -3000 * depth + 3000) continue; // Futility pruning: parent node (~5 Elo) From 2807dcfab671bfc7a1bea79f5639dbbd505703ad Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Mon, 30 Aug 2021 00:19:46 +0800 Subject: [PATCH 150/282] Update default net to nn-735bba95dec0.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/61286d8b62d20cf82b5ad1bd Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-33495fe25081.nnue New net: nn-83e3cf2af92b.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/6129cf2162d20cf82b5ad25f Parameters: A total of 64 net biases were tuned (hidden layer 1) Base net: nn-83e3cf2af92b.nnue New net: nn-69a528eaef35.nnue SPSA 3: https://tests.stockfishchess.org/tests/view/612a0dcb62d20cf82b5ad2a0 Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-69a528eaef35.nnue New net: nn-735bba95dec0.nnue STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 95144 W: 24310 L: 23999 D: 46835 Ptnml(0-2): 232, 11059, 24748, 11232, 301 https://tests.stockfishchess.org/tests/view/612bb3be0fdf40644b4b9996 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 33632 W: 8522 L: 8271 D: 16839 Ptnml(0-2): 18, 3511, 9516, 3744, 27 https://tests.stockfishchess.org/tests/view/612ce5b9bb4956d8b78eb5b3 Closes https://github.com/official-stockfish/Stockfish/pull/3685 Bench: 5600615 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index f73fc37f..f26b95f4 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-33495fe25081.nnue" + #define EvalFileDefaultName "nn-735bba95dec0.nnue" namespace NNUE { From e404a7d97c94890dda18ca3a16cfd15b5a7ef235 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 6 Sep 2021 00:17:46 +0300 Subject: [PATCH 151/282] Extend captures and promotions This patch introduces extension for captures and promotions. Every capture or promotion that is not the first move in the list gets extended at PvNodes and cutNodes. Special thanks to @locutus2 - all my previous attepmts that failed on this idea were done only for PvNodes - idea to include also cutNodes was based on his latest passed patch. STC https://tests.stockfishchess.org/tests/view/6134abf325b9b35584838574 LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 188920 W: 47754 L: 47304 D: 93862 Ptnml(0-2): 595, 21754, 49344, 22140, 627 LTC https://tests.stockfishchess.org/tests/view/613521de25b9b355848385d7 LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 8768 W: 2283 L: 2098 D: 4387 Ptnml(0-2): 7, 866, 2452, 1053, 6 closes https://github.com/official-stockfish/Stockfish/pull/3692 bench: 5564555 --- src/search.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 5aa50644..0d7575aa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1094,6 +1094,14 @@ moves_loop: // When in check, search starts here return beta; } } + + // Capture extensions for PvNodes and cutNodes + else if ( (PvNode || cutNode) + && captureOrPromotion + && moveCount != 1) + extension = 1; + + // Check extensions else if ( givesCheck && depth > 6 && abs(ss->staticEval) > Value(100)) From be63ce1bb59d24cce3346be0e41923204ffcca13 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sun, 5 Sep 2021 08:33:01 +0800 Subject: [PATCH 152/282] Update default net to nn-6762d36ad265.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/612cdb1fbb4956d8b78eb5ab Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-fe433fd8c7f6.nnue New net: nn-5f134823db04.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/612fcde645091e810014af19 Parameters: A total of 64 net biases were tuned (hidden layer 1) Base net: nn-5f134823db04.nnue New net: nn-8eca5dd4e3f7.nnue SPSA 3: https://tests.stockfishchess.org/tests/view/6130822345091e810014af61 Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-8eca5dd4e3f7.nnue New net: nn-4556108e4f00.nnue SPSA 4: https://tests.stockfishchess.org/tests/view/613287652ffb3c36aceb923c Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-4556108e4f00.nnue New net: nn-6762d36ad265.nnue STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 162776 W: 41220 L: 40807 D: 80749 Ptnml(0-2): 517, 18800, 42359, 19177, 535 https://tests.stockfishchess.org/tests/view/6134107125b9b35584838559 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 41056 W: 10428 L: 10156 D: 20472 Ptnml(0-2): 30, 4288, 11618, 4564, 28 https://tests.stockfishchess.org/tests/view/6134ad6525b9b3558483857a closes https://github.com/official-stockfish/Stockfish/pull/3691 Bench: 5812158 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index f26b95f4..d7cc6e29 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-735bba95dec0.nnue" + #define EvalFileDefaultName "nn-6762d36ad265.nnue" namespace NNUE { From c31fc8d163ed2c5ff5697206f8a5eef65efa0044 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 6 Sep 2021 11:22:58 +0200 Subject: [PATCH 153/282] Improve history updates If a search failed low at an expected PV or CUT node do greater history updates. STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 95112 W: 24293 L: 23982 D: 46837 Ptnml(0-2): 285, 10893, 24906, 11170, 302 https://tests.stockfishchess.org/tests/view/6132aa1a2ffb3c36aceb926f LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 116352 W: 29450 L: 28975 D: 57927 Ptnml(0-2): 93, 12263, 32984, 12748, 88 https://tests.stockfishchess.org/tests/view/613394d12ffb3c36aceb92f4 closes https://github.com/official-stockfish/Stockfish/pull/3693 Bench: 6130736 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0d7575aa..6affa510 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1325,7 +1325,7 @@ moves_loop: // When in check, search starts here // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth)); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth + (PvNode || cutNode))); if (PvNode) bestValue = std::min(bestValue, maxValue); From b7b6b4ba18bc9d291c2c2fd300c22a3e008dcbb2 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 7 Sep 2021 13:22:20 +0200 Subject: [PATCH 154/282] Further improve history updates Now even double history updates if a search failed low at an expected PV or CUT node. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 30736 W: 7891 L: 7674 D: 15171 Ptnml(0-2): 90, 3477, 8017, 3694, 90 https://tests.stockfishchess.org/tests/view/61364ae30cd98ab40c0c9da5 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 73600 W: 18684 L: 18326 D: 36590 Ptnml(0-2): 41, 7734, 20899, 8078, 48 https://tests.stockfishchess.org/tests/view/6136940f0cd98ab40c0c9df3 closes https://github.com/official-stockfish/Stockfish/pull/3694 Bench: 6030657 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6affa510..ed5dd6c2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1325,7 +1325,7 @@ moves_loop: // When in check, search starts here // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth + (PvNode || cutNode))); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + (PvNode || cutNode))); if (PvNode) bestValue = std::min(bestValue, maxValue); From 30fdbf43283eb0d2f8e5ca325877d3e514758804 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 10 Sep 2021 11:38:50 +0300 Subject: [PATCH 155/282] Decrease depth for cutnodes with no tt move By analogy to existing logic of decreasing depth for PvNodes w/o tt move do the same for cutNodes. Passed STC https://tests.stockfishchess.org/tests/view/613abf5a689039fce12e1155 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 90336 W: 23108 L: 22804 D: 44424 Ptnml(0-2): 286, 10316, 23642, 10656, 268 Passed LTC https://tests.stockfishchess.org/tests/view/613ae330689039fce12e1172 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 37736 W: 9607 L: 9346 D: 18783 Ptnml(0-2): 21, 3917, 10730, 4180, 20 closes https://github.com/official-stockfish/Stockfish/pull/3697 bench 5891181 --- src/search.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ed5dd6c2..a4b17d9d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -903,12 +903,17 @@ namespace { ss->ttPv = ttPv; } - // Step 10. If the position is not in TT, decrease depth by 2 + // Step 10. If the position is not in TT, decrease depth by 2 or 1 depending on node type if ( PvNode && depth >= 6 && !ttMove) depth -= 2; + if ( cutNode + && depth >= 9 + && !ttMove) + depth--; + moves_loop: // When in check, search starts here ttCapture = ttMove && pos.capture_or_promotion(ttMove); From fd5e77950ea0742fb815b1b92f7ed9ac625d66cf Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 12 Sep 2021 09:19:38 +0100 Subject: [PATCH 156/282] Update 2 search parameters after tune. A tuning run on 3 search parameters was done with 200k games, narrow ranges (50-150%) and a small value for A (3% of total games) : https://tests.stockfishchess.org/tests/view/613b5f4b689039fce12e1220 STC 10+0.1 : LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 73112 W: 18800 L: 18520 D: 35792 Ptnml(0-2): 205, 8395, 19115, 8597, 244 https://tests.stockfishchess.org/tests/view/613cb8d2689039fce12e1308 LTC 60+0.6 : LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 45616 W: 11604 L: 11321 D: 22691 Ptnml(0-2): 24, 4769, 12946, 5038, 31 https://tests.stockfishchess.org/tests/view/613d07048253e53e97b55b32 closes https://github.com/official-stockfish/Stockfish/pull/3698 Bench 6504816 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a4b17d9d..0c05713c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -793,7 +793,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 159 + && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 177 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -1032,7 +1032,7 @@ moves_loop: // When in check, search starts here // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 7 - && ss->staticEval + 174 + 157 * lmrDepth <= alpha) + && ss->staticEval + 172 + 157 * lmrDepth <= alpha) continue; // Prune moves with negative SEE (~20 Elo) @@ -1101,8 +1101,8 @@ moves_loop: // When in check, search starts here } // Capture extensions for PvNodes and cutNodes - else if ( (PvNode || cutNode) - && captureOrPromotion + else if ( (PvNode || cutNode) + && captureOrPromotion && moveCount != 1) extension = 1; From 723f48dec0eb05204b51cace54b033a5a85a66b9 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Tue, 14 Sep 2021 00:28:33 +0800 Subject: [PATCH 157/282] Update default net to nn-13406b1dcbe0.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/6134abc425b9b35584838572 Parameters: A total of 64 net biases were tuned (hidden layer 1) Base net: nn-6762d36ad265.nnue New net: nn-c9fdeea14cb2.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/61355b7e25b9b3558483860e Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-c9fdeea14cb2.nnue New net: nn-0ddc28184f4c.nnue SPSA 3: https://tests.stockfishchess.org/tests/view/613737be0cd98ab40c0c9e4e Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-0ddc28184f4c.nnue New net: nn-2419828bb394.nnue SPSA 4: https://tests.stockfishchess.org/tests/view/613966ff689039fce12e0fe7 Parameters: A total of 64 net biases were tuned (hidden layer 1) Base net: nn-2419828bb394.nnue New net: nn-05d9b1ee3037.nnue SPSA 5: https://tests.stockfishchess.org/tests/view/613b4a38689039fce12e1209 Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-05d9b1ee3037.nnue New net: nn-98c6ce0fc15f.nnue SPSA 6: https://tests.stockfishchess.org/tests/view/613e331515591e7c9ebc3fe9 Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-98c6ce0fc15f.nnue New net: nn-13406b1dcbe0.nnue STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 82008 W: 21044 L: 20752 D: 40212 Ptnml(0-2): 264, 9341, 21525, 9587, 287 https://tests.stockfishchess.org/tests/view/613f7c6cf29dda16fcca870c LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 182928 W: 46258 L: 45602 D: 91068 Ptnml(0-2): 107, 19448, 51712, 20076, 121 https://tests.stockfishchess.org/tests/view/613fccb97315e7c73204a48c Closes #3703 Bench: 6658747 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index d7cc6e29..d7b3e55f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-6762d36ad265.nnue" + #define EvalFileDefaultName "nn-13406b1dcbe0.nnue" namespace NNUE { From 5b47b4e6c04719e66559efe91e505569e0f7aafa Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 16 Sep 2021 08:43:53 +0100 Subject: [PATCH 158/282] Increase optimumTime by 10% STC 10+0.1 : LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 47032 W: 12078 L: 11841 D: 23113 Ptnml(0-2): 159, 5098, 12746, 5373, 140 https://tests.stockfishchess.org/tests/view/613f9df1f29dda16fcca8731 LTC 60+0.6 : LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 66248 W: 16631 L: 16301 D: 33316 Ptnml(0-2): 44, 6560, 19578, 6906, 36 https://tests.stockfishchess.org/tests/view/6140603d7315e7c73204a4c1 Non-regression tests with other time control styles: Moves/Time 40/10+0 : LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 51640 W: 13350 L: 13254 D: 25036 Ptnml(0-2): 183, 5770, 13797, 5908, 162 https://tests.stockfishchess.org/tests/view/6141592b7315e7c73204a599 TCEC Style 10+0.01 : LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 20592 W: 5300 L: 5157 D: 10135 Ptnml(0-2): 81, 2240, 5544, 2317, 114 https://tests.stockfishchess.org/tests/view/61425bb27315e7c73204a6a2 Sudden death 15+0 : LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 127104 W: 32728 L: 32741 D: 61635 Ptnml(0-2): 735, 13973, 34149, 13960, 735 https://tests.stockfishchess.org/tests/view/614256a77315e7c73204a699 The first 3 tests were run with an initial version of the code, which was then modified to make the amount of extra time dependent on the size of increment. No increment gives no extra time, and the extra time given increases until an increment of 1% or more of remaining time gives 10% extra thinking time. closes https://github.com/official-stockfish/Stockfish/pull/3702 Bench 6658747 --- src/timeman.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index f742d1e4..d3713ee4 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -68,6 +68,9 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); + // Use extra time with larger increments + double optExtra = std::clamp(1.0 + 10.0 * limits.inc[us] / limits.time[us], 1.0, 1.1); + // A user may scale time usage by setting UCI option "Slow Mover" // Default is 100 and changing this value will probably lose elo. timeLeft = slowMover * timeLeft / 100; @@ -78,15 +81,16 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { if (limits.movestogo == 0) { optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042, - 0.2 * limits.time[us] / double(timeLeft)); + 0.2 * limits.time[us] / double(timeLeft)) + * optExtra; maxScale = std::min(7.0, 4.0 + ply / 12.0); } // x moves in y seconds (+ z increment) else { - optScale = std::min((0.8 + ply / 128.0) / mtg, - 0.8 * limits.time[us] / double(timeLeft)); + optScale = std::min((0.88 + ply / 116.4) / mtg, + 0.88 * limits.time[us] / double(timeLeft)); maxScale = std::min(6.3, 1.5 + 0.11 * mtg); } From e8788d1b32c9356fa0a127952d48c3748d8ec826 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 20 Sep 2021 15:04:13 +0300 Subject: [PATCH 159/282] Combo of various parameter tweaks Combination of parameter tweaks in search, evaluation and time management. Original patches by snicolet xoto10 lonfom169 and Vizvezdenec. Includes: * Use bigger grain of positional evaluation more frequently (up to 1 exchange difference in non-pawn-material); * More extra time according to increment; * Increase margin for singular extensions; * Do more aggresive parent node futility pruning. Passed STC https://tests.stockfishchess.org/tests/view/6147deab3733d0e0dd9f313d LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 45488 W: 11691 L: 11450 D: 22347 Ptnml(0-2): 145, 5208, 11824, 5395, 172 Passed LTC https://tests.stockfishchess.org/tests/view/6147f1d53733d0e0dd9f3141 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 62520 W: 15808 L: 15482 D: 31230 Ptnml(0-2): 43, 6439, 17960, 6785, 33 closes https://github.com/official-stockfish/Stockfish/pull/3710 bench 5575265 --- src/nnue/evaluate_nnue.cpp | 2 +- src/search.cpp | 6 +++--- src/timeman.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 891f8faa..bfd69987 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -168,7 +168,7 @@ namespace Stockfish::Eval::NNUE { int positional = output[0]; int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); - int entertainment = (adjusted && delta_npm <= BishopValueMg - KnightValueMg ? 7 : 0); + int entertainment = (adjusted && delta_npm <= RookValueMg - BishopValueMg ? 7 : 0); int A = 128 - entertainment; int B = 128 + entertainment; diff --git a/src/search.cpp b/src/search.cpp index 0c05713c..b400b8f8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1031,8 +1031,8 @@ moves_loop: // When in check, search starts here // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck - && lmrDepth < 7 - && ss->staticEval + 172 + 157 * lmrDepth <= alpha) + && lmrDepth < 8 + && ss->staticEval + 172 + 145 * lmrDepth <= alpha) continue; // Prune moves with negative SEE (~20 Elo) @@ -1057,7 +1057,7 @@ moves_loop: // When in check, search starts here && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - 2 * depth; + Value singularBeta = ttValue - 3 * depth; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; diff --git a/src/timeman.cpp b/src/timeman.cpp index d3713ee4..69d1c96f 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -69,7 +69,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); // Use extra time with larger increments - double optExtra = std::clamp(1.0 + 10.0 * limits.inc[us] / limits.time[us], 1.0, 1.1); + double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12); // A user may scale time usage by setting UCI option "Slow Mover" // Default is 100 and changing this value will probably lose elo. From 73018a03375b4b72ee482eb5a4a2152d7e4f0aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 23 Sep 2021 23:19:06 +0200 Subject: [PATCH 160/282] Detect search explosions This patch detects some search explosions (due to double extensions in search.cpp) which can happen in some pathological positions, and takes measures to ensure progress in search even for these pathological situations. While a small number of double extensions can be useful during search (for example to resolve a tactical sequence), a sustained regime of double extensions leads to search explosion and a non-finishing search. See the discussion in https://github.com/official-stockfish/Stockfish/pull/3544 and the issue https://github.com/official-stockfish/Stockfish/issues/3532 . The implemented algorithm is the following: a) at each node during search, store the current depth in the stack. Double extensions are by definition levels of the stack where the depth at ply N is strictly higher than depth at ply N-1. b) during search, calculate for each thread a running average of the number of double extensions in the last 4096 visited nodes. c) if one thread has more than 2% of double extensions for a sustained period of time (6 millions consecutive nodes, or about 4 seconds on my iMac), we decide that this thread is in an explosion state and we calm down this thread by preventing it to do any double extension for the next 6 millions nodes. To calculate the running averages, we also introduced a auxiliary class generalizing the computations of ttHitAverage variable we already had in code. The implementation uses an exponential moving average of period 4096 and resolution 1/1024, and all computations are done with integers for efficiency. ----------- Example where the patch solves a search explosion: ``` ./stockfish ucinewgame position fen 8/Pk6/8/1p6/8/P1K5/8/6B1 w - - 37 130 go infinite ``` This algorithm does not affect search in normal, non-pathological positions. We verified, for instance, that the usual bench is unchanged up to depth 20 at least, and that the node numbers are unchanged for a search of the starting position at depth 32. ------------- See https://github.com/official-stockfish/Stockfish/pull/3714 Bench: 5575265 --- src/misc.h | 27 ++++++++++++++++++ src/search.cpp | 76 +++++++++++++++++++++++++++++++++++++++----------- src/search.h | 1 + src/thread.h | 8 ++++-- src/types.h | 5 ++++ 5 files changed, 99 insertions(+), 18 deletions(-) diff --git a/src/misc.h b/src/misc.h index dae37cd9..7a3369e8 100644 --- a/src/misc.h +++ b/src/misc.h @@ -85,6 +85,33 @@ static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; static inline const bool IsLittleEndian = (Le.c[0] == 4); +// RunningAverage : a class to calculate a running average of a series of values. +// For efficiency, all computations are done with integers. +class RunningAverage { + public: + + // Constructor + RunningAverage() {} + + // Reset the running average to rational value p / q + void set(int64_t p, int64_t q) + { average = p * PERIOD * RESOLUTION / q; } + + // Update average with value v + void update(int64_t v) + { average = RESOLUTION * v + (PERIOD - 1) * average / PERIOD; } + + // Test if average is strictly greater than rational a / b + bool is_greater(int64_t a, int64_t b) + { return b * average > a * PERIOD * RESOLUTION ; } + + private : + static constexpr int64_t PERIOD = 4096; + static constexpr int64_t RESOLUTION = 1024; + int64_t average; +}; + + template class ValueListInserter { public: diff --git a/src/search.cpp b/src/search.cpp index b400b8f8..10a9027b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -61,9 +61,6 @@ namespace { // Different node types, used as a template parameter enum NodeType { NonPV, PV, Root }; - constexpr uint64_t TtHitAverageWindow = 4096; - constexpr uint64_t TtHitAverageResolution = 1024; - // Futility margin Value futility_margin(Depth d, bool improving) { return Value(214 * (d - improving)); @@ -91,6 +88,30 @@ namespace { return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); } + // Check if the current thread is in a search explosion + ExplosionState search_explosion(Thread* thisThread) { + + uint64_t nodesNow = thisThread->nodes; + bool explosive = thisThread->doubleExtensionAverage[WHITE].is_greater(2, 100) + || thisThread->doubleExtensionAverage[BLACK].is_greater(2, 100); + + if (explosive) + thisThread->nodesLastExplosive = nodesNow; + else + thisThread->nodesLastNormal = nodesNow; + + if ( explosive + && thisThread->state == EXPLOSION_NONE + && nodesNow - thisThread->nodesLastNormal > 6000000) + thisThread->state = MUST_CALM_DOWN; + + if ( thisThread->state == MUST_CALM_DOWN + && nodesNow - thisThread->nodesLastExplosive > 6000000) + thisThread->state = EXPLOSION_NONE; + + return thisThread->state; + } + // Skill structure is used to implement strength limit struct Skill { explicit Skill(int l) : level(l) {} @@ -310,8 +331,14 @@ void Thread::search() { multiPV = std::max(multiPV, (size_t)4); multiPV = std::min(multiPV, rootMoves.size()); - ttHitAverage = TtHitAverageWindow * TtHitAverageResolution / 2; + ttHitAverage.set(50, 100); // initialize the running average at 50% + doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0% + doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0% + + nodesLastExplosive = nodes; + nodesLastNormal = nodes; + state = EXPLOSION_NONE; trend = SCORE_ZERO; int searchAgainCounter = 0; @@ -518,6 +545,14 @@ namespace { template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { + Thread* thisThread = pos.this_thread(); + + // Step 0. Limit search explosion + if ( ss->ply > 10 + && search_explosion(thisThread) == MUST_CALM_DOWN + && depth > (ss-1)->depth) + depth = (ss-1)->depth; + constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; const Depth maxNextDepth = rootNode ? depth : depth + 1; @@ -554,12 +589,11 @@ namespace { Value bestValue, value, ttValue, eval, maxValue, probCutBeta; bool givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, - ttCapture, singularQuietLMR; + ttCapture, singularQuietLMR, noLMRExtension; Piece movedPiece; int moveCount, captureCount, quietCount; // Step 1. Initialize node - Thread* thisThread = pos.this_thread(); ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); @@ -602,8 +636,12 @@ namespace { (ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; ss->doubleExtensions = (ss-1)->doubleExtensions; + ss->depth = depth; Square prevSq = to_sq((ss-1)->currentMove); + // Update the running average statistics for double extensions + thisThread->doubleExtensionAverage[us].update(ss->depth > (ss-1)->depth); + // Initialize statScore to zero for the grandchildren of the current position. // So statScore is shared between all grandchildren and only the first grandchild // starts with statScore = 0. Later grandchildren start with the last calculated @@ -632,9 +670,8 @@ namespace { && is_ok((ss-1)->currentMove)) thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); - // thisThread->ttHitAverage can be used to approximate the running average of ttHit - thisThread->ttHitAverage = (TtHitAverageWindow - 1) * thisThread->ttHitAverage / TtHitAverageWindow - + TtHitAverageResolution * ss->ttHit; + // running average of ttHit + thisThread->ttHitAverage.update(ss->ttHit); // At non-PV nodes we check for an early TT cutoff if ( !PvNode @@ -948,8 +985,7 @@ moves_loop: // When in check, search starts here ss->ply); value = bestValue; - singularQuietLMR = moveCountPruning = false; - bool doubleExtension = false; + singularQuietLMR = moveCountPruning = noLMRExtension = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal or greater than the current depth, and the result of this search was a fail low. @@ -1069,13 +1105,13 @@ moves_loop: // When in check, search starts here extension = 1; singularQuietLMR = !ttCapture; - // Avoid search explosion by limiting the number of double extensions to at most 3 + // Avoid search explosion by limiting the number of double extensions if ( !PvNode && value < singularBeta - 93 && ss->doubleExtensions < 3) { extension = 2; - doubleExtension = true; + noLMRExtension = true; } } @@ -1146,7 +1182,7 @@ moves_loop: // When in check, search starts here r--; // Decrease reduction if the ttHit running average is large (~0 Elo) - if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024) + if (thisThread->ttHitAverage.is_greater(537, 1024)) r--; // Decrease reduction if position is or has been on the PV @@ -1187,8 +1223,16 @@ moves_loop: // When in check, search starts here // In general we want to cap the LMR depth search at newDepth. But if // reductions are really negative and movecount is low, we allow this move - // to be searched deeper than the first move in specific cases. - Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && (moveCount <= 5 || (depth > 6 && PvNode)) && !doubleExtension)); + // to be searched deeper than the first move in specific cases (note that + // this may lead to hidden double extensions if newDepth got it own extension + // before). + int deeper = r >= -1 ? 0 + : noLMRExtension ? 0 + : moveCount <= 5 ? 1 + : (depth > 6 && PvNode) ? 1 + : 0; + + Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); diff --git a/src/search.h b/src/search.h index 801baacc..ba9d0677 100644 --- a/src/search.h +++ b/src/search.h @@ -47,6 +47,7 @@ struct Stack { Move excludedMove; Move killers[2]; Value staticEval; + Depth depth; int statScore; int moveCount; bool inCheck; diff --git a/src/thread.h b/src/thread.h index 5bfa2359..2475d2ec 100644 --- a/src/thread.h +++ b/src/thread.h @@ -60,10 +60,14 @@ public: Pawns::Table pawnsTable; Material::Table materialTable; size_t pvIdx, pvLast; - uint64_t ttHitAverage; + RunningAverage ttHitAverage; + RunningAverage doubleExtensionAverage[COLOR_NB]; + uint64_t nodesLastExplosive; + uint64_t nodesLastNormal; + std::atomic nodes, tbHits, bestMoveChanges; int selDepth, nmpMinPly; Color nmpColor; - std::atomic nodes, tbHits, bestMoveChanges; + ExplosionState state; Position rootPos; StateInfo rootState; diff --git a/src/types.h b/src/types.h index 0bd4a1c4..fd643117 100644 --- a/src/types.h +++ b/src/types.h @@ -173,6 +173,11 @@ enum Bound { BOUND_EXACT = BOUND_UPPER | BOUND_LOWER }; +enum ExplosionState { + EXPLOSION_NONE, + MUST_CALM_DOWN +}; + enum Value : int { VALUE_ZERO = 0, VALUE_DRAW = 0, From ff3fa0c664a5799e5531b0908018c59633d761bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 23 Sep 2021 11:20:03 +0200 Subject: [PATCH 161/282] Tweak doubly singular condition (Topo's patch) This patch relax a little bit the condition for doubly singular moves (ie moves that are so forced that we think that they deserve a local double extension of the search). We lower the margin and allow up to six such double extensions in the path between the root and the critical node. Original idea by Siad Daboul (@TopoIogist) in PR #3709 Tested with the previous commit: passed STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 33048 W: 8458 L: 8236 D: 16354 Ptnml(0-2): 120, 3701, 8660, 3923, 120 https://tests.stockfishchess.org/tests/view/614b24347bdc23e77ceb88fe passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 54176 W: 13712 L: 13406 D: 27058 Ptnml(0-2): 36, 5653, 15399, 5969, 31 https://tests.stockfishchess.org/tests/view/614b3b727bdc23e77ceb8911 closes https://github.com/official-stockfish/Stockfish/pull/3714 Bench: 5792377 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 10a9027b..9b56bd2b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1107,8 +1107,8 @@ moves_loop: // When in check, search starts here // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 93 - && ss->doubleExtensions < 3) + && value < singularBeta - 75 + && ss->doubleExtensions <= 6) { extension = 2; noLMRExtension = true; From 00e34a758f2ca170986550a1f8f25dfe691ca511 Mon Sep 17 00:00:00 2001 From: OfekShochat Date: Thu, 23 Sep 2021 23:16:17 +0300 Subject: [PATCH 162/282] Range reductions adding reductions for when the delta between the static eval and the child's eval is consistently low. passed STC https://tests.stockfishchess.org/html/live_elo.html?614d7b3c7bdc23e77ceb8a5d LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 88872 W: 22672 L: 22366 D: 43834 Ptnml(0-2): 343, 10150, 23117, 10510, 316 passed LTC https://tests.stockfishchess.org/html/live_elo.html?614daf3e7bdc23e77ceb8a82 LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 24368 W: 6153 L: 5928 D: 12287 Ptnml(0-2): 13, 2503, 6937, 2708, 23 closes https://github.com/official-stockfish/Stockfish/pull/3717 Bench: 5443950 --- AUTHORS | 1 + src/search.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 5b5bbf22..be35127a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -143,6 +143,7 @@ Nikolay Kostov (NikolayIT) Nguyen Pham (nguyenpham) Norman Schmidt (FireFather) notruck +Ofek Shochat (OfekShochat, ghostway) Ondrej Mosnáček (WOnder93) Oskar Werkelin Ahlin Pablo Vazquez diff --git a/src/search.cpp b/src/search.cpp index 9b56bd2b..ffeb5110 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -69,9 +69,9 @@ namespace { // Reductions lookup table, initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] - Depth reduction(bool i, Depth d, int mn) { + Depth reduction(bool i, Depth d, int mn, bool rangeReduction) { int r = Reductions[d] * Reductions[mn]; - return (r + 534) / 1024 + (!i && r > 904); + return (r + 534) / 1024 + (!i && r > 904) + rangeReduction; } constexpr int futility_move_count(bool improving, Depth depth) { @@ -954,6 +954,7 @@ namespace { moves_loop: // When in check, search starts here ttCapture = ttMove && pos.capture_or_promotion(ttMove); + int rangeReduction = 0; // Step 11. A small Probcut idea, when we are in check probCutBeta = beta + 409; @@ -1041,7 +1042,7 @@ moves_loop: // When in check, search starts here moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, rangeReduction > 2), 0); if ( captureOrPromotion || givesCheck) @@ -1176,7 +1177,7 @@ moves_loop: // When in check, search starts here || !ss->ttPv) && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { - Depth r = reduction(improving, depth, moveCount); + Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); if (PvNode) r--; @@ -1236,6 +1237,10 @@ moves_loop: // When in check, search starts here value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); + // Range reductions (~3 Elo) + if (ss->staticEval - value < 30 && depth > 7) + rangeReduction++; + // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; didLMR = true; From 919da65d70f0041abbb0102133ed0abbf25b1af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 25 Sep 2021 19:37:47 +0200 Subject: [PATCH 163/282] Reduction instead of cutoff In master, during singular move analysis, when both the transposition value and a reduced search for the other moves seem to indicate a fail high, we heuristically prune the whole subtree and return an fail high score. This patch is a little bit more cautious in this case, and instead of the risky cutoff, we now search the ttMove with a reduced depth (by two plies). STC: https://tests.stockfishchess.org/tests/view/614dafe07bdc23e77ceb8a89 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 46728 W: 11909 L: 11666 D: 23153 Ptnml(0-2): 181, 5288, 12168, 5561, 166 LTC: https://tests.stockfishchess.org/tests/view/614dc84abe4c07e0ecac3c95 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 74520 W: 18809 L: 18450 D: 37261 Ptnml(0-2): 45, 7735, 21346, 8084, 50 closes https://github.com/official-stockfish/Stockfish/pull/3718 Bench: 5499262 --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ffeb5110..8db295f1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1125,7 +1125,8 @@ moves_loop: // When in check, search starts here return singularBeta; // If the eval of ttMove is greater than beta we try also if there is another - // move that pushes it over beta, if so also produce a cutoff. + // move that pushes it over beta, if so the position also has probably multiple + // moves giving fail highs. We will then reduce the ttMove (negative extension). else if (ttValue >= beta) { ss->excludedMove = move; @@ -1133,7 +1134,7 @@ moves_loop: // When in check, search starts here ss->excludedMove = MOVE_NONE; if (value >= beta) - return beta; + extension = -2; } } From 21ad356c0900c9eba9b7b1f7453f934eab80f303 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 26 Sep 2021 06:39:27 +0200 Subject: [PATCH 164/282] Extend quiet tt moves at PvNodes Idea is to extend some quiet ttMoves if a lot of things indicate that the transposition table move is going to be a good move: 1) move being a killer - so being the best move in nearby node; 2) reply continuation history is really good. This is basically saying that move is good "in general" in this position, that it is a good reply to the opponent move and that it was the best in this position somewhere in search - so extending it makes a lot of sense. In general in past year we had a lot of extensions of different types, maybe there is something more in it :) passed STC LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 42944 W: 10932 L: 10695 D: 21317 Ptnml(0-2): 141, 4869, 11210, 5116, 136 https://tests.stockfishchess.org/tests/view/614cca8e7bdc23e77ceb89f0 passed LTC LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 156848 W: 39473 L: 38893 D: 78482 Ptnml(0-2): 125, 16327, 44913, 16961, 98 https://tests.stockfishchess.org/tests/view/614cf93d7bdc23e77ceb8a13 closes https://github.com/official-stockfish/Stockfish/pull/3719 Bench: 5714575 --- src/search.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8db295f1..48694cb2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1147,7 +1147,14 @@ moves_loop: // When in check, search starts here // Check extensions else if ( givesCheck && depth > 6 - && abs(ss->staticEval) > Value(100)) + && abs(ss->staticEval) > 100) + extension = 1; + + // Quiet ttMove extensions + else if ( PvNode + && move == ttMove + && move == ss->killers[0] + && (*contHist[0])[movedPiece][to_sq(move)] >= 10000) extension = 1; // Add extension to new depth @@ -1223,11 +1230,10 @@ moves_loop: // When in check, search starts here // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r -= ss->statScore / 14721; - // In general we want to cap the LMR depth search at newDepth. But if - // reductions are really negative and movecount is low, we allow this move - // to be searched deeper than the first move in specific cases (note that - // this may lead to hidden double extensions if newDepth got it own extension - // before). + // In general we want to cap the LMR depth search at newDepth. But if reductions + // are really negative and movecount is low, we allow this move to be searched + // deeper than the first move (this may lead to hidden double extensions if + // newDepth got its own extension before). int deeper = r >= -1 ? 0 : noLMRExtension ? 0 : moveCount <= 5 ? 1 From 135caee606c86ade9e9c199ef469661c374eb9ba Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 3 Oct 2021 11:27:40 +0300 Subject: [PATCH 165/282] Increase reductions with thread count Respin of multi-thread idea that was simplified away recently: basically doing more reductions with thread count since Lazy SMP naturally widens search. With drawish book this idea got simplified away but with less drawish book it again gains elo, maybe trying to reinstall other ideas that were simplified away previously can be beneficial. passed STC LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 39736 W: 10205 L: 9986 D: 19545 Ptnml(0-2): 45, 4254, 11064, 4447, 58 https://tests.stockfishchess.org/tests/view/615750702d02f48db3961b00 passed LTC LLR: 2.97 (-2.94,2.94) <0.50,3.50> Total: 60352 W: 15530 L: 15218 D: 29604 Ptnml(0-2): 24, 5900, 18016, 6212, 24 https://tests.stockfishchess.org/tests/view/6157d8935488e26ea5eace7f closes https://github.com/official-stockfish/Stockfish/pull/3724 Bench 5714575 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 48694cb2..3eb8e9e1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -173,7 +173,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int(21.9 * std::log(i)); + Reductions[i] = int((21.9 + std::log(Threads.size()) / 2) * std::log(i)); } From 371b522e9ed9cab91274ff111c0bf4b0f6ec3340 Mon Sep 17 00:00:00 2001 From: "J. Oster" Date: Tue, 5 Oct 2021 12:02:25 +0200 Subject: [PATCH 166/282] Time-management fix in MultiPV mode. When playing games in MultiPV mode we must take care to only track the best move changing for the first PV line. Otherwise, SF will spend most of its time for the initial moves after the book exit. This has been observed and reported on Discord, but can also be seen in games played in Stefan Pohl's MultiPV experiment. Tested with MultiPV=4. STC: https://tests.stockfishchess.org/tests/view/615c24b59d256038a969b990 LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 1744 W: 694 L: 447 D: 603 Ptnml(0-2): 32, 125, 358, 278, 79 LTC: https://tests.stockfishchess.org/tests/view/615c31769d256038a969b993 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 2048 W: 723 L: 525 D: 800 Ptnml(0-2): 10, 158, 511, 314, 31 closes https://github.com/official-stockfish/Stockfish/pull/3729 Bench: 5714575 --- src/search.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3eb8e9e1..df4af54a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1314,9 +1314,11 @@ moves_loop: // When in check, search starts here for (Move* m = (ss+1)->pv; *m != MOVE_NONE; ++m) rm.pv.push_back(*m); - // We record how often the best move has been changed in each - // iteration. This information is used for time management and LMR - if (moveCount > 1) + // We record how often the best move has been changed in each iteration. + // This information is used for time management and LMR. In MultiPV mode, + // we must take care to only do this for the first PV line. + if ( moveCount > 1 + && !thisThread->pvIdx) ++thisThread->bestMoveChanges; } else From 329bdbd9cfa270dd7141e5184180fbde1b5898b4 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 5 Oct 2021 22:14:13 +0200 Subject: [PATCH 167/282] Improve the Chess960 correction for cornered bishops As Chess960 patches can not be tested on fishtest, this was locally tuned and tested: Elo: 2.36 +- 1.07 LOS: 0.999992 closes https://github.com/official-stockfish/Stockfish/pull/3730 Bench: 5714575 --- src/evaluate.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 62d4be84..58dd0026 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1053,26 +1053,22 @@ make_v: if ( pos.piece_on(SQ_A1) == W_BISHOP && pos.piece_on(SQ_B2) == W_PAWN) - correction += !pos.empty(SQ_B3) ? -CorneredBishop * 4 - : -CorneredBishop * 3; + correction -= CorneredBishop; if ( pos.piece_on(SQ_H1) == W_BISHOP && pos.piece_on(SQ_G2) == W_PAWN) - correction += !pos.empty(SQ_G3) ? -CorneredBishop * 4 - : -CorneredBishop * 3; + correction -= CorneredBishop; if ( pos.piece_on(SQ_A8) == B_BISHOP && pos.piece_on(SQ_B7) == B_PAWN) - correction += !pos.empty(SQ_B6) ? CorneredBishop * 4 - : CorneredBishop * 3; + correction += CorneredBishop; if ( pos.piece_on(SQ_H8) == B_BISHOP && pos.piece_on(SQ_G7) == B_PAWN) - correction += !pos.empty(SQ_G6) ? CorneredBishop * 4 - : CorneredBishop * 3; + correction += CorneredBishop; - return pos.side_to_move() == WHITE ? Value(correction) - : -Value(correction); + return pos.side_to_move() == WHITE ? Value(5 * correction) + : -Value(5 * correction); } } // namespace Eval From 54a989930ebed200c3278c725151e26a2c0da37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 4 Oct 2021 20:37:26 +0200 Subject: [PATCH 168/282] Capping stat bonus at 2000 This patch updates the stat_bonus() function (used in the history tables to help move ordering), keeping the same quadratic for small depths but changing the values for depth >= 9: The old bonus formula was increasing from zero at depth 1 to 4100 at depth 14, then used the strange, small value of 73 for all depths >= 15. The new bonus formula increases from 0 at depth 1 to 2000 at depth 8, then keeps 2000 for all depths >= 8. passed STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 169624 W: 42875 L: 42454 D: 84295 Ptnml(0-2): 585, 19340, 44557, 19729, 601 https://tests.stockfishchess.org/tests/view/615bd69e9d256038a969b97c passed LTC: LLR: 3.07 (-2.94,2.94) <0.50,3.50> Total: 37336 W: 9456 L: 9191 D: 18689 Ptnml(0-2): 20, 3810, 10747, 4067, 24 https://tests.stockfishchess.org/tests/view/615c75d99d256038a969b9b2 closes https://github.com/official-stockfish/Stockfish/pull/3731 Bench: 6261865 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index df4af54a..ef691500 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -80,7 +80,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 14 ? 73 : 6 * d * d + 229 * d - 215; + return std::min((6 * d + 229) * d - 215 , 2000); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -1709,8 +1709,8 @@ moves_loop: // When in check, search starts here PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); bonus1 = stat_bonus(depth + 1); - bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus - : std::min(bonus1, stat_bonus(depth)); // smaller bonus + bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus + : stat_bonus(depth); // smaller bonus if (!pos.capture_or_promotion(bestMove)) { From f21a66f70dce4e9d72031a13d25ac530bbafc830 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 27 May 2021 16:04:47 +0100 Subject: [PATCH 169/282] Small clean-up, Sept 2021 Closes https://github.com/official-stockfish/Stockfish/pull/3485 No functional change --- AUTHORS | 1 + src/Makefile | 6 +++--- src/evaluate.cpp | 12 ++++++------ src/evaluate.h | 2 +- src/movepick.h | 4 ++-- src/nnue/evaluate_nnue.cpp | 2 +- src/search.cpp | 3 +-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/AUTHORS b/AUTHORS index be35127a..56725e98 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,6 +21,7 @@ Alexander Kure Alexander Pagel (Lolligerhans) Alfredo Menezes (lonfom169) Ali AlZhrani (Cooffe) +Andrei Vetrov (proukornew) Andrew Grant (AndyGrant) Andrey Neporada (nepal) Andy Duplain diff --git a/src/Makefile b/src/Makefile index d92854bc..fea597e7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -88,7 +88,7 @@ endif # at the end of the line for flag values. # # Example of use for these flags: -# make build ARCH=x86-64-avx512 debug=on sanitize="address undefined" +# make build ARCH=x86-64-avx512 debug=yes sanitize="address undefined" ### 2.1. General and architecture defaults @@ -517,7 +517,7 @@ ifeq ($(bits),64) CXXFLAGS += -DIS_64BIT endif -### 3.5 prefetch +### 3.5 prefetch and popcount ifeq ($(prefetch),yes) ifeq ($(sse),yes) CXXFLAGS += -msse @@ -526,7 +526,6 @@ else CXXFLAGS += -DNO_PREFETCH endif -### 3.6 popcnt ifeq ($(popcnt),yes) ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8 arm64)) CXXFLAGS += -DUSE_POPCNT @@ -537,6 +536,7 @@ ifeq ($(popcnt),yes) endif endif +### 3.6 SIMD architectures ifeq ($(avx2),yes) CXXFLAGS += -DUSE_AVX2 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 58dd0026..2195f913 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -61,7 +61,7 @@ namespace Stockfish { namespace Eval { bool useNNUE; - string eval_file_loaded = "None"; + string currentEvalFileName = "None"; /// NNUE::init() tries to load a NNUE network at startup time, or when the engine /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" @@ -90,13 +90,13 @@ namespace Eval { #endif for (string directory : dirs) - if (eval_file_loaded != eval_file) + if (currentEvalFileName != eval_file) { if (directory != "") { ifstream stream(directory + eval_file, ios::binary); if (load_eval(eval_file, stream)) - eval_file_loaded = eval_file; + currentEvalFileName = eval_file; } if (directory == "" && eval_file == EvalFileDefaultName) @@ -111,7 +111,7 @@ namespace Eval { istream stream(&buffer); if (load_eval(eval_file, stream)) - eval_file_loaded = eval_file; + currentEvalFileName = eval_file; } } } @@ -123,7 +123,7 @@ namespace Eval { if (eval_file.empty()) eval_file = EvalFileDefaultName; - if (useNNUE && eval_file_loaded != eval_file) + if (useNNUE && currentEvalFileName != eval_file) { string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available."; @@ -1081,7 +1081,7 @@ Value Eval::evaluate(const Position& pos) { Value v; - if (!Eval::useNNUE) + if (!useNNUE) v = Evaluation(pos).value(); else { diff --git a/src/evaluate.h b/src/evaluate.h index d7b3e55f..e2cdb210 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -34,7 +34,7 @@ namespace Eval { Value evaluate(const Position& pos); extern bool useNNUE; - extern std::string eval_file_loaded; + extern std::string currentEvalFileName; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the diff --git a/src/movepick.h b/src/movepick.h index c76d4957..2932d9a8 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -89,8 +89,8 @@ enum StatsType { NoCaptures, Captures }; typedef Stats ButterflyHistory; /// At higher depths LowPlyHistory records successful quiet moves near the root -/// and quiet moves which are/were in the PV (ttPv). It is cleared with each new -/// search and filled during iterative deepening. +/// and quiet moves which are/were in the PV (ttPv). LowPlyHistory is populated during +/// iterative deepening and at each new search the data is shifted down by 2 plies constexpr int MAX_LPH = 4; typedef Stats LowPlyHistory; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index bfd69987..7f5925a7 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -396,7 +396,7 @@ namespace Stockfish::Eval::NNUE { actualFilename = filename.value(); else { - if (eval_file_loaded != EvalFileDefaultName) + if (currentEvalFileName != EvalFileDefaultName) { msg = "Failed to export a net. A non-embedded net can only be saved if the filename is specified"; diff --git a/src/search.cpp b/src/search.cpp index ef691500..ac6b8608 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -790,7 +790,6 @@ namespace { else { // In case of null move search use previous static eval with a different sign - // and addition of two tempos if ((ss-1)->currentMove != MOVE_NULL) ss->staticEval = eval = evaluate(pos); else @@ -1187,6 +1186,7 @@ moves_loop: // When in check, search starts here { Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); + // Decrease reduction if on the PV (~1 Elo) if (PvNode) r--; @@ -1499,7 +1499,6 @@ moves_loop: // When in check, search starts here } else // In case of null move search use previous static eval with a different sign - // and addition of two tempos ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval; From c8459b18ba2d6ddc76d6db90d6eab346ed682e69 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 9 Oct 2021 02:15:43 +0300 Subject: [PATCH 170/282] Reduce more if multiple moves exceed alpha Idea of this patch is the following: in case we already have four moves that exceeded alpha in the current node, the probability of finding fifth should be reasonably low. Note that four is completely arbitrary - there could and probably should be some tweaks, both in tweaking best move count threshold for more reductions and tweaking how they work - for example making more reductions with best move count linearly. passed STC: https://tests.stockfishchess.org/tests/view/615f614783dd501a05b0aee2 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 141816 W: 36056 L: 35686 D: 70074 Ptnml(0-2): 499, 15131, 39273, 15511, 494 passed LTC: https://tests.stockfishchess.org/tests/view/615fdff683dd501a05b0af35 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 68536 W: 17221 L: 16891 D: 34424 Ptnml(0-2): 38, 6573, 20725, 6885, 47 closes https://github.com/official-stockfish/Stockfish/pull/3736 Bench: 6131513 --- src/search.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ac6b8608..73fc8a55 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -591,13 +591,13 @@ namespace { bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR, noLMRExtension; Piece movedPiece; - int moveCount, captureCount, quietCount; + int moveCount, captureCount, quietCount, bestMoveCount; // Step 1. Initialize node ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); - moveCount = captureCount = quietCount = ss->moveCount = 0; + moveCount = bestMoveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; @@ -1186,8 +1186,9 @@ moves_loop: // When in check, search starts here { Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); - // Decrease reduction if on the PV (~1 Elo) - if (PvNode) + // Decrease reduction if on the PV (~2 Elo) + if ( PvNode + && bestMoveCount <= 3) r--; // Decrease reduction if the ttHit running average is large (~0 Elo) @@ -1340,7 +1341,10 @@ moves_loop: // When in check, search starts here update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha! Always alpha < beta + { alpha = value; + bestMoveCount++; + } else { assert(value >= beta); // Fail high From 673841301b0cc6ed78c4db3e6ec2a0b9a010c8cb Mon Sep 17 00:00:00 2001 From: Joseph Ellis Date: Wed, 13 Oct 2021 11:10:50 -0500 Subject: [PATCH 171/282] Simplify multi-cut condition Now that the multi-cut condition is safer, we can avoid the cost of the sub-search. STC: https://tests.stockfishchess.org/tests/view/6165fd9283dd501a05b0b2fe LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 18648 W: 4745 L: 4600 D: 9303 Ptnml(0-2): 47, 2111, 4887, 2208, 71 LTC: https://tests.stockfishchess.org/tests/view/616629ea83dd501a05b0b320 LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 41704 W: 10407 L: 10302 D: 20995 Ptnml(0-2): 35, 4425, 11823, 4538, 31 closes https://github.com/official-stockfish/Stockfish/pull/3738 Bench: 5905086 --- src/search.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 73fc8a55..28049e87 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1123,18 +1123,9 @@ moves_loop: // When in check, search starts here else if (singularBeta >= beta) return singularBeta; - // If the eval of ttMove is greater than beta we try also if there is another - // move that pushes it over beta, if so the position also has probably multiple - // moves giving fail highs. We will then reduce the ttMove (negative extension). + // If the eval of ttMove is greater than beta, we reduce it (negative extension) else if (ttValue >= beta) - { - ss->excludedMove = move; - value = search(pos, ss, beta - 1, beta, (depth + 3) / 2, cutNode); - ss->excludedMove = MOVE_NONE; - - if (value >= beta) - extension = -2; - } + extension = -2; } // Capture extensions for PvNodes and cutNodes From 0bddd942b4d096ff31132a4c3e7aef016d0f2d41 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 14 Oct 2021 00:44:46 -0300 Subject: [PATCH 172/282] Simplify ttHitAverage away Simplify ttHitAverage away, which was introduced in the following commit: [here](https://github.com/BM123499/Stockfish/commit/fe124896b241b4791454fd151da10101ad48f6d7) A few tweaks with Elo gaining bounds have been tried to keep the code, but they all failed: https://tests.stockfishchess.org/tests/view/61656f7683dd501a05b0b292 https://tests.stockfishchess.org/tests/view/6165c0ca83dd501a05b0b2ca https://tests.stockfishchess.org/tests/view/6165bf9683dd501a05b0b2c8 https://tests.stockfishchess.org/tests/view/6165719483dd501a05b0b29b https://tests.stockfishchess.org/tests/view/6166c7fd83dd501a05b0b353 https://tests.stockfishchess.org/tests/view/6166c63b83dd501a05b0b350 STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 58504 W: 14781 L: 14694 D: 29029 Ptnml(0-2): 175, 6718, 15426, 6711, 222 https://tests.stockfishchess.org/tests/view/6165112c83dd501a05b0b257 LTC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 33480 W: 8448 L: 8332 D: 16700 Ptnml(0-2): 21, 3569, 9447, 3679, 24 https://tests.stockfishchess.org/tests/view/61656fcf83dd501a05b0b294 change https://github.com/official-stockfish/Stockfish/pull/3739 bench: 4540339 --- src/search.cpp | 8 -------- src/thread.h | 1 - 2 files changed, 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 28049e87..bdd139ce 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -332,7 +332,6 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - ttHitAverage.set(50, 100); // initialize the running average at 50% doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0% doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0% @@ -670,9 +669,6 @@ namespace { && is_ok((ss-1)->currentMove)) thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); - // running average of ttHit - thisThread->ttHitAverage.update(ss->ttHit); - // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit @@ -1182,10 +1178,6 @@ moves_loop: // When in check, search starts here && bestMoveCount <= 3) r--; - // Decrease reduction if the ttHit running average is large (~0 Elo) - if (thisThread->ttHitAverage.is_greater(537, 1024)) - r--; - // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv diff --git a/src/thread.h b/src/thread.h index 2475d2ec..e04d303a 100644 --- a/src/thread.h +++ b/src/thread.h @@ -60,7 +60,6 @@ public: Pawns::Table pawnsTable; Material::Table materialTable; size_t pvIdx, pvLast; - RunningAverage ttHitAverage; RunningAverage doubleExtensionAverage[COLOR_NB]; uint64_t nodesLastExplosive; uint64_t nodesLastNormal; From 580698e5e57f40dcba52b92a7f0c7a0e9ab09437 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 6 Oct 2021 19:16:02 +0200 Subject: [PATCH 173/282] Compute ttCapture earlier Compute ttCapture earlier, and reuse. passed STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 74128 W: 18640 L: 18578 D: 36910 Ptnml(0-2): 224, 7970, 20649, 7962, 259 https://tests.stockfishchess.org/tests/view/615dd9fa1a32f4036ac7fc4d closes https://github.com/official-stockfish/Stockfish/pull/3734 No functional change --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index bdd139ce..1aaf53d8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -658,6 +658,7 @@ namespace { ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ss->ttHit ? tte->move() : MOVE_NONE; + ttCapture = ttMove && pos.capture_or_promotion(ttMove); if (!excludedMove) ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); @@ -683,7 +684,7 @@ namespace { if (ttValue >= beta) { // Bonus for a quiet ttMove that fails high - if (!pos.capture_or_promotion(ttMove)) + if (!ttCapture) update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth); // Extra penalty for early quiet moves of the previous ply @@ -691,7 +692,7 @@ namespace { update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); } // Penalty for a quiet ttMove that fails low - else if (!pos.capture_or_promotion(ttMove)) + else if (!ttCapture) { int penalty = -stat_bonus(depth); thisThread->mainHistory[us][from_to(ttMove)] << penalty; @@ -948,7 +949,6 @@ namespace { moves_loop: // When in check, search starts here - ttCapture = ttMove && pos.capture_or_promotion(ttMove); int rangeReduction = 0; // Step 11. A small Probcut idea, when we are in check From 4231d99ab408674115623f42f7ff89f3f189ca23 Mon Sep 17 00:00:00 2001 From: Stefano Cardanobile Date: Thu, 14 Oct 2021 22:26:42 +0200 Subject: [PATCH 174/282] Smooth improving Smooth dependency on improvement margin in null move search. STC LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 17384 W: 4468 L: 4272 D: 8644 Ptnml(0-2): 42, 1919, 4592, 2079, 60 https://tests.stockfishchess.org/tests/view/61689b8a1e5f6627cc1c0fdc LTC LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 45648 W: 11525 L: 11243 D: 22880 Ptnml(0-2): 26, 4731, 13036, 4997, 34 https://tests.stockfishchess.org/tests/view/6168a12c1e5f6627cc1c0fe3 It would be interesting to test if the other pruning/reduction heuristics in master which are using the improving variable (ie the sign of improvement) could benefit from a smooth function of the improvement value (or maybe a Relu of the improvement value). closes https://github.com/official-stockfish/Stockfish/pull/3740 Bench: 4916775 --- src/search.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1aaf53d8..9f005587 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -590,7 +590,7 @@ namespace { bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR, noLMRExtension; Piece movedPiece; - int moveCount, captureCount, quietCount, bestMoveCount; + int moveCount, captureCount, quietCount, bestMoveCount, improvement; // Step 1. Initialize node ss->inCheck = pos.checkers(); @@ -766,6 +766,7 @@ namespace { // Skip early pruning when in check ss->staticEval = eval = VALUE_NONE; improving = false; + improvement = 0; goto moves_loop; } else if (ss->ttHit) @@ -804,13 +805,15 @@ namespace { thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } - // Set up improving flag that is used in various pruning heuristics - // We define position as improving if static evaluation of position is better - // Than the previous static evaluation at our turn - // In case of us being in check at our previous move we look at move prior to it - improving = (ss-2)->staticEval == VALUE_NONE - ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE - : ss->staticEval > (ss-2)->staticEval; + // Set up the improvement variable, which is the difference between the current + // static evaluation and the previous static evaluation at our turn (if we were + // in check at our previous move we look at the move prior to it). The improvement + // margin and the improving flag are used in various pruning heuristics. + improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval + : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval + : 200; + + improving = improvement > 0; // Step 7. Futility pruning: child node (~50 Elo). // The depth condition is important for mate finding. @@ -826,7 +829,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 177 + && ss->staticEval >= beta - 20 * depth - improvement / 15 + 168 * ss->ttPv + 177 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From 6847be2c752b3f41906be87af7f3cde4e31be5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 17 Oct 2021 11:56:35 +0200 Subject: [PATCH 175/282] Allow some LMR double extensions Allow some LMR double extensions for the second and third sons of each node. STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 170320 W: 42608 L: 42187 D: 85525 Ptnml(0-2): 516, 19635, 44422, 20086, 501 https://tests.stockfishchess.org/tests/view/616a9e3899b580bf37797cf4 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 74400 W: 18783 L: 18423 D: 37194 Ptnml(0-2): 46, 7812, 21129, 8162, 51 https://tests.stockfishchess.org/tests/view/616b378499b580bf37797d61 closes https://github.com/official-stockfish/Stockfish/pull/3742 Bench: 4877152 --- src/search.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9f005587..39bb1fdb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1219,13 +1219,13 @@ moves_loop: // When in check, search starts here // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched - // deeper than the first move (this may lead to hidden double extensions if - // newDepth got its own extension before). - int deeper = r >= -1 ? 0 - : noLMRExtension ? 0 - : moveCount <= 5 ? 1 - : (depth > 6 && PvNode) ? 1 - : 0; + // deeper than the first move (this may lead to hidden double extensions). + int deeper = r >= -1 ? 0 + : noLMRExtension ? 0 + : moveCount <= 3 && r <= -3 ? 2 + : moveCount <= 5 ? 1 + : PvNode && depth > 6 ? 1 + : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); From 8a74c089286913f24a641aa37532006088d0f438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 17 Oct 2021 13:06:33 +0200 Subject: [PATCH 176/282] Remove noLMRExtension flag This simplification patch removes the noLMRExtension flag. It was introduced in June (see following link for that commit), but does not seem to be necessary anymore. Link: https://github.com/official-stockfish/Stockfish/commit/e1f181ee643dcaa92c606b74b3abd23dede136cd STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 21200 W: 5369 L: 5228 D: 10603 Ptnml(0-2): 67, 2355, 5616, 2494, 68 https://tests.stockfishchess.org/tests/view/616c03d299b580bf37797dcb LTC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 37536 W: 9387 L: 9278 D: 18871 Ptnml(0-2): 23, 3988, 10643, 4085, 29 https://tests.stockfishchess.org/tests/view/616c10f499b580bf37797ddd closes https://github.com/official-stockfish/Stockfish/pull/3743 Bench: 4792969 --- src/search.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 39bb1fdb..9a2bd415 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -588,7 +588,7 @@ namespace { Value bestValue, value, ttValue, eval, maxValue, probCutBeta; bool givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, - ttCapture, singularQuietLMR, noLMRExtension; + ttCapture, singularQuietLMR; Piece movedPiece; int moveCount, captureCount, quietCount, bestMoveCount, improvement; @@ -984,7 +984,7 @@ moves_loop: // When in check, search starts here ss->ply); value = bestValue; - singularQuietLMR = moveCountPruning = noLMRExtension = false; + singularQuietLMR = moveCountPruning = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal or greater than the current depth, and the result of this search was a fail low. @@ -1108,10 +1108,7 @@ moves_loop: // When in check, search starts here if ( !PvNode && value < singularBeta - 75 && ss->doubleExtensions <= 6) - { extension = 2; - noLMRExtension = true; - } } // Multi-cut pruning @@ -1221,7 +1218,6 @@ moves_loop: // When in check, search starts here // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 - : noLMRExtension ? 0 : moveCount <= 3 && r <= -3 ? 2 : moveCount <= 5 ? 1 : PvNode && depth > 6 ? 1 From f7494961de13c3341a1ca2d05ea5f3d28fa35d31 Mon Sep 17 00:00:00 2001 From: Stefano Cardanobile Date: Sun, 17 Oct 2021 19:01:45 +0200 Subject: [PATCH 177/282] Reformat Eval::evaluate() Non functional simplification: the goal of this patch is to make the style in the evaluate() function similar to the rest of the code. passed STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 95608 W: 24058 L: 24026 D: 47524 Ptnml(0-2): 292, 10379, 26396, 10479, 258 https://tests.stockfishchess.org/tests/view/616c64fd99b580bf37797e4f closes https://github.com/official-stockfish/Stockfish/pull/3744 Non-functional change --- src/evaluate.cpp | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2195f913..2f1d5067 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1081,33 +1081,22 @@ Value Eval::evaluate(const Position& pos) { Value v; - if (!useNNUE) - v = Evaluation(pos).value(); + // Deciding between classical and NNUE eval: for high PSQ imbalance we use classical, + // but we switch to NNUE during long shuffling or with high material on the board. + + if ( !useNNUE + || abs(eg_value(pos.psq_score())) * 5 > (850 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) + v = Evaluation(pos).value(); // classical else { - // Scale and shift NNUE for compatibility with search and classical evaluation - auto adjusted_NNUE = [&]() - { - int scale = 883 - + 32 * pos.count() - + 32 * pos.non_pawn_material() / 1024; + int scale = 883 + + 32 * pos.count() + + 32 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos, true) * scale / 1024; + v = NNUE::evaluate(pos, true) * scale / 1024; // NNUE - if (pos.is_chess960()) - nnue += fix_FRC(pos); - - return nnue; - }; - - // If there is PSQ imbalance we use the classical eval, but we switch to - // NNUE eval faster when shuffling or if the material on the board is high. - int r50 = pos.rule50_count(); - Value psq = Value(abs(eg_value(pos.psq_score()))); - bool classical = psq * 5 > (850 + pos.non_pawn_material() / 64) * (5 + r50); - - v = classical ? Evaluation(pos).value() // classical - : adjusted_NNUE(); // NNUE + if (pos.is_chess960()) + v += fix_FRC(pos); } // Damp down the evaluation linearly when shuffling From 67d06164833857d3497010e952fd0d2c5f00c095 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 18 Oct 2021 09:03:12 -0300 Subject: [PATCH 178/282] Simplify probCutCount away Simplify away the limitation in number of moves in probCut. STC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 286768 W: 71888 L: 72133 D: 142747 Ptnml(0-2): 983, 33084, 75471, 32887, 959 https://tests.stockfishchess.org/tests/view/616c9b9b90e1312a3cd0ef0a LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 69312 W: 17243 L: 17176 D: 34893 Ptnml(0-2): 42, 7452, 19614, 7493, 55 https://tests.stockfishchess.org/tests/view/616cebbf4f95b438f7a85f93 closes https://github.com/official-stockfish/Stockfish/pull/3745 bench: 5005810 --- src/search.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9a2bd415..55a68956 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -893,19 +893,16 @@ namespace { assert(probCutBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); - int probCutCount = 0; bool ttPv = ss->ttPv; ss->ttPv = false; - while ( (move = mp.next_move()) != MOVE_NONE - && probCutCount < 2 + 2 * cutNode) + while ((move = mp.next_move()) != MOVE_NONE) if (move != excludedMove && pos.legal(move)) { assert(pos.capture_or_promotion(move)); assert(depth >= 5); captureOrPromotion = true; - probCutCount++; ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] From b37054c310876850f6ff65b19f6cdb5f941c57dc Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 19 Oct 2021 09:23:30 -0300 Subject: [PATCH 179/282] Simplify evaluate condition on search Remove condition for MOVE_NULL on search. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 47544 W: 11968 L: 11864 D: 23712 Ptnml(0-2): 150, 5535, 12318, 5599, 170 https://tests.stockfishchess.org/tests/view/616e37143799eb91f1f071ee LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 67472 W: 16938 L: 16870 D: 33664 Ptnml(0-2): 49, 7119, 19331, 7189, 48 https://tests.stockfishchess.org/tests/view/616e3fab3799eb91f1f071f1 closes https://github.com/official-stockfish/Stockfish/pull/3746 bench: 5255771 --- src/search.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 55a68956..e7e33ede 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -787,11 +787,7 @@ namespace { } else { - // In case of null move search use previous static eval with a different sign - if ((ss-1)->currentMove != MOVE_NULL) - ss->staticEval = eval = evaluate(pos); - else - ss->staticEval = eval = -(ss-1)->staticEval; + ss->staticEval = eval = evaluate(pos); // Save static evaluation into transposition table if(!excludedMove) From 4af1ae82c6b017a3d0cac04ad1ad916a7e26a529 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 19 Oct 2021 15:44:08 -0300 Subject: [PATCH 180/282] Adjust TTdepth acceptance on early cutoff STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 63784 W: 16185 L: 15917 D: 31682 Ptnml(0-2): 231, 7309, 16531, 7603, 218 https://tests.stockfishchess.org/tests/view/616ed03a942d40685e3237c0 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 12728 W: 3268 L: 3072 D: 6388 Ptnml(0-2): 8, 1298, 3563, 1480, 15 https://tests.stockfishchess.org/tests/view/616ef156942d40685e32380a closes https://github.com/official-stockfish/Stockfish/pull/3748 bench: 7050445 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e7e33ede..b30af89f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -673,7 +673,7 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit - && tte->depth() >= depth + && tte->depth() > depth && ttValue != VALUE_NONE // Possible in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) From 42a895d9c988e6d2db2cbae43a4992c853efee6e Mon Sep 17 00:00:00 2001 From: bmc4 Date: Wed, 20 Oct 2021 10:37:20 -0300 Subject: [PATCH 181/282] Simplify null move search condition Remove `ss->ttPv` condition on null move search condition STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 80832 W: 20276 L: 20221 D: 40335 Ptnml(0-2): 267, 9335, 21168, 9368, 278 https://tests.stockfishchess.org/tests/view/616ed4a0942d40685e3237c6 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 54184 W: 13464 L: 13377 D: 27343 Ptnml(0-2): 37, 5758, 15435, 5805, 57 https://tests.stockfishchess.org/tests/view/616ef71f40f619782fd4f72d closes https://github.com/official-stockfish/Stockfish/pull/3750 bench: 6201607 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b30af89f..65e496b8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -825,7 +825,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 15 + 168 * ss->ttPv + 177 + && ss->staticEval >= beta - 20 * depth - improvement / 15 + 177 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From 8a8640a761f266979c7130a49ebbc9ed4c680102 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 21 Oct 2021 09:39:28 +0200 Subject: [PATCH 182/282] Double extend more often via LMR Allow for first three moves always a two plies deeper LMR search. STC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 206096 W: 51966 L: 52093 D: 102037 Ptnml(0-2): 664, 23817, 54293, 23530, 744 https://tests.stockfishchess.org/tests/view/616f197d40f619782fd4f75a LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 62384 W: 15567 L: 15492 D: 31325 Ptnml(0-2): 40, 6633, 17777, 6696, 46 https://tests.stockfishchess.org/tests/view/616ffa1b4f0b65a0e231e682 closes https://github.com/official-stockfish/Stockfish/pull/3752 Bench: 6154836 --- src/search.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 65e496b8..f511cce6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -790,8 +790,8 @@ namespace { ss->staticEval = eval = evaluate(pos); // Save static evaluation into transposition table - if(!excludedMove) - tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); + if (!excludedMove) + tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } // Use static evaluation difference to improve quiet move ordering @@ -1210,11 +1210,11 @@ moves_loop: // When in check, search starts here // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). - int deeper = r >= -1 ? 0 - : moveCount <= 3 && r <= -3 ? 2 - : moveCount <= 5 ? 1 - : PvNode && depth > 6 ? 1 - : 0; + int deeper = r >= -1 ? 0 + : moveCount <= 3 ? 2 + : moveCount <= 5 ? 1 + : PvNode && depth > 6 ? 1 + : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); From 644f6d47909f3cf2bd3e70485db792789ccad89e Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 19 Oct 2021 10:49:02 -0700 Subject: [PATCH 183/282] Simplify away ValueListInserter plus minor cleanups STC: https://tests.stockfishchess.org/tests/view/616f059b40f619782fd4f73f LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 84992 W: 21244 L: 21197 D: 42551 Ptnml(0-2): 279, 9005, 23868, 9078, 266 closes https://github.com/official-stockfish/Stockfish/pull/3749 No functional change --- src/misc.h | 17 ----------------- src/nnue/features/half_ka_v2_hm.cpp | 18 ++++++++---------- src/nnue/features/half_ka_v2_hm.h | 22 ++++++++++++---------- src/nnue/nnue_feature_transformer.h | 9 ++++----- 4 files changed, 24 insertions(+), 42 deletions(-) diff --git a/src/misc.h b/src/misc.h index 7a3369e8..718e5558 100644 --- a/src/misc.h +++ b/src/misc.h @@ -111,22 +111,6 @@ class RunningAverage { int64_t average; }; - -template -class ValueListInserter { -public: - ValueListInserter(T* v, std::size_t& s) : - values(v), - size(&s) - { - } - - void push_back(const T& value) { values[(*size)++] = value; } -private: - T* values; - std::size_t* size; -}; - template class ValueList { @@ -140,7 +124,6 @@ public: const T& operator[](std::size_t index) const { return values_[index]; } const T* begin() const { return values_; } const T* end() const { return values_ + size_; } - operator ValueListInserter() { return ValueListInserter(values_, size_); } void swap(ValueList& other) { const std::size_t maxSize = std::max(size_, other.size_); diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 098a6d60..6face217 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -39,7 +39,7 @@ namespace Stockfish::Eval::NNUE::Features { void HalfKAv2_hm::append_active_indices( const Position& pos, Color perspective, - ValueListInserter active + IndexList& active ) { Square ksq = pos.square(perspective); Bitboard bb = pos.pieces(); @@ -55,22 +55,20 @@ namespace Stockfish::Eval::NNUE::Features { void HalfKAv2_hm::append_changed_indices( Square ksq, - StateInfo* st, + const DirtyPiece& dp, Color perspective, - ValueListInserter removed, - ValueListInserter added + IndexList& removed, + IndexList& added ) { - const auto& dp = st->dirtyPiece; for (int i = 0; i < dp.dirty_num; ++i) { - Piece pc = dp.piece[i]; if (dp.from[i] != SQ_NONE) - removed.push_back(make_index(perspective, dp.from[i], pc, ksq)); + removed.push_back(make_index(perspective, dp.from[i], dp.piece[i], ksq)); if (dp.to[i] != SQ_NONE) - added.push_back(make_index(perspective, dp.to[i], pc, ksq)); + added.push_back(make_index(perspective, dp.to[i], dp.piece[i], ksq)); } } - int HalfKAv2_hm::update_cost(StateInfo* st) { + int HalfKAv2_hm::update_cost(const StateInfo* st) { return st->dirtyPiece.dirty_num; } @@ -78,7 +76,7 @@ namespace Stockfish::Eval::NNUE::Features { return pos.count(); } - bool HalfKAv2_hm::requires_refresh(StateInfo* st, Color perspective) { + bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) { return st->dirtyPiece.piece[0] == make_piece(perspective, KING); } diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index 2c1144f6..c7b1a68d 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -50,7 +50,7 @@ namespace Stockfish::Eval::NNUE::Features { PS_W_QUEEN = 8 * SQUARE_NB, PS_B_QUEEN = 9 * SQUARE_NB, PS_KING = 10 * SQUARE_NB, - PS_NB = 11 * SQUARE_NB + PS_NB = 11 * SQUARE_NB }; static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { @@ -85,36 +85,38 @@ namespace Stockfish::Eval::NNUE::Features { -1, -1, -1, -1, 23, 22, 21, 20, -1, -1, -1, -1, 19, 18, 17, 16, -1, -1, -1, -1, 15, 14, 13, 12, - -1, -1, -1, -1, 11, 10, 9, 8, - -1, -1, -1, -1, 7, 6, 5, 4, - -1, -1, -1, -1, 3, 2, 1, 0 + -1, -1, -1, -1, 11, 10, 9, 8, + -1, -1, -1, -1, 7, 6, 5, 4, + -1, -1, -1, -1, 3, 2, 1, 0 }; // Maximum number of simultaneously active features. static constexpr IndexType MaxActiveDimensions = 32; + using IndexList = ValueList; // Get a list of indices for active features static void append_active_indices( const Position& pos, Color perspective, - ValueListInserter active); + IndexList& active); // Get a list of indices for recently changed features static void append_changed_indices( Square ksq, - StateInfo* st, + const DirtyPiece& dp, Color perspective, - ValueListInserter removed, - ValueListInserter added); + IndexList& removed, + IndexList& added + ); // Returns the cost of updating one perspective, the most costly one. // Assumes no refresh needed. - static int update_cost(StateInfo* st); + static int update_cost(const StateInfo* st); static int refresh_cost(const Position& pos); // Returns whether the change stored in this StateInfo means that // a full accumulator refresh is required. - static bool requires_refresh(StateInfo* st, Color perspective); + static bool requires_refresh(const StateInfo* st, Color perspective); }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 59a965ac..0297b323 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -370,7 +370,6 @@ namespace Stockfish::Eval::NNUE { // That might depend on the feature set and generally relies on the // feature set's update cost calculation to be correct and never // allow updates with more added/removed features than MaxActiveDimensions. - using IndexList = ValueList; #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array @@ -404,12 +403,12 @@ namespace Stockfish::Eval::NNUE { // Gather all features to be updated. const Square ksq = pos.square(perspective); - IndexList removed[2], added[2]; + FeatureSet::IndexList removed[2], added[2]; FeatureSet::append_changed_indices( - ksq, next, perspective, removed[0], added[0]); + ksq, next->dirtyPiece, perspective, removed[0], added[0]); for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) FeatureSet::append_changed_indices( - ksq, st2, perspective, removed[1], added[1]); + ksq, st2->dirtyPiece, perspective, removed[1], added[1]); // Mark the accumulators as computed. next->accumulator.computed[perspective] = true; @@ -534,7 +533,7 @@ namespace Stockfish::Eval::NNUE { // Refresh the accumulator auto& accumulator = pos.state()->accumulator; accumulator.computed[perspective] = true; - IndexList active; + FeatureSet::IndexList active; FeatureSet::append_active_indices(pos, perspective, active); #ifdef VECTOR From 2214fcecf7ae5d1d4165596bcd238b6e6bc909c1 Mon Sep 17 00:00:00 2001 From: Stefano Cardanobile Date: Tue, 19 Oct 2021 22:03:26 +0200 Subject: [PATCH 184/282] Rewrite NNUE evaluation adjustments Make the eval code in the evaluate_nnue.cpp more similar to the rest of the codebase: * remove multiple variable assignment * make if conditions explicit and indent on multiple lines passed STC LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 59032 W: 14834 L: 14751 D: 29447 Ptnml(0-2): 176, 6310, 16459, 6397, 174 https://tests.stockfishchess.org/tests/view/616f250540f619782fd4f76d closes https://github.com/official-stockfish/Stockfish/pull/3753 No functional change --- src/nnue/evaluate_nnue.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 7f5925a7..6e40deab 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -143,6 +143,7 @@ namespace Stockfish::Eval::NNUE { // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; + int delta = 7; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformedFeaturesUnaligned[ @@ -162,20 +163,14 @@ namespace Stockfish::Eval::NNUE { const std::size_t bucket = (pos.count() - 1) / 4; const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); - const auto output = network[bucket]->propagate(transformedFeatures, buffer); + const auto positional = network[bucket]->propagate(transformedFeatures, buffer)[0]; - int materialist = psqt; - int positional = output[0]; - - int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); - int entertainment = (adjusted && delta_npm <= RookValueMg - BishopValueMg ? 7 : 0); - - int A = 128 - entertainment; - int B = 128 + entertainment; - - int sum = (A * materialist + B * positional) / 128; - - return static_cast( sum / OutputScale ); + // Give more value to positional evaluation when material is balanced + if ( adjusted + && abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)) <= RookValueMg - BishopValueMg) + return static_cast(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale); + else + return static_cast((psqt + positional) / OutputScale); } struct NnueEvalTrace { From 927a84d310fb41222518ea80d398933a0ba3e5b7 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Fri, 22 Oct 2021 10:35:42 -0300 Subject: [PATCH 185/282] Increase TTdepth acceptance some Threads Increase TTdepth acceptance only on half of the Threads STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 19272 W: 4956 L: 4766 D: 9550 Ptnml(0-2): 25, 1989, 5423, 2169, 30 https://tests.stockfishchess.org/tests/view/6172be6238cb9784038af9a7 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 23688 W: 6111 L: 5897 D: 11680 Ptnml(0-2): 2, 2275, 7081, 2479, 7 https://tests.stockfishchess.org/tests/view/6172e32938cb9784038af9c7 closes https://github.com/official-stockfish/Stockfish/pull/3754 No functional change in the single-threaded case --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f511cce6..71634c56 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -673,7 +673,7 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit - && tte->depth() > depth + && tte->depth() > depth - (thisThread->id() % 2 == 1) && ttValue != VALUE_NONE // Possible in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) From fc8213c7df7422c0c321db5fb066cbd08d3bf3f8 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 22 Oct 2021 03:04:28 +0300 Subject: [PATCH 186/282] Tuning of a Null Move Parameter STC: LLR: 2.99 (-2.94,2.94) <-0.50,2.50> Total: 78744 W: 19956 L: 19664 D: 39124 Ptnml(0-2): 259, 9005, 20573, 9255, 280 https://tests.stockfishchess.org/tests/view/6172017a38cb9784038af947 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 68528 W: 17309 L: 16964 D: 34255 Ptnml(0-2): 41, 7194, 19455, 7527, 47 https://tests.stockfishchess.org/tests/view/6172994d38cb9784038af983 closes https://github.com/official-stockfish/Stockfish/pull/3756 bench: 6689428 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 71634c56..0aa590f4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -825,7 +825,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 15 + 177 + && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From 1163d972a9a1e480d9130c5fabbf869cdb7f7ecb Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 23 Oct 2021 09:22:41 -0300 Subject: [PATCH 187/282] Simplify LMR multiThread condition STC (8 threads): LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 110584 W: 27818 L: 27807 D: 54959 Ptnml(0-2): 156, 12089, 30791, 12100, 156 https://tests.stockfishchess.org/tests/view/6172ef436ce927be325583a9 LTC (8 threads): LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 23632 W: 6025 L: 5903 D: 11704 Ptnml(0-2): 5, 2292, 7100, 2414, 5 https://tests.stockfishchess.org/tests/view/6173cf096ce927be32558412 closes https://github.com/official-stockfish/Stockfish/pull/3757 No functional change (in the single-threaded case) Bench: 6689428 --- src/search.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0aa590f4..8becdd3f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,10 +1159,9 @@ moves_loop: // When in check, search starts here // cases where we extend a son if it has good chances to be "interesting". if ( depth >= 3 && moveCount > 1 + 2 * rootNode - && ( !captureOrPromotion - || (cutNode && (ss-1)->moveCount > 1) - || !ss->ttPv) - && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) + && ( !ss->ttPv + || !captureOrPromotion + || (cutNode && (ss-1)->moveCount > 1))) { Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); From 8557f35aa5c0f1ea45b2ccba5ebfd0165dbd33da Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 24 Oct 2021 16:07:00 +0200 Subject: [PATCH 188/282] Double extend search even more via LMR Allow now for the first five moves a two plies deeper LMR search. STC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 99608 W: 25143 L: 25115 D: 49350 Ptnml(0-2): 291, 11444, 26328, 11428, 313 https://tests.stockfishchess.org/tests/view/61718c9438cb9784038af8d7 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 52064 W: 13234 L: 13145 D: 25685 Ptnml(0-2): 35, 5431, 15014, 5514, 38 https://tests.stockfishchess.org/tests/view/6171e13e38cb9784038af928 closes https://github.com/official-stockfish/Stockfish/pull/3760 Bench: 7222293 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8becdd3f..36329f8f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1210,8 +1210,7 @@ moves_loop: // When in check, search starts here // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 - : moveCount <= 3 ? 2 - : moveCount <= 5 ? 1 + : moveCount <= 5 ? 2 : PvNode && depth > 6 ? 1 : 0; From 2c86ae196df8b2a1197e0de1853a6458e404a976 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 23 Oct 2021 15:02:59 +0200 Subject: [PATCH 189/282] Adjust ButterflyHistory decay parameter passed STC: LLR: 2.98 (-2.94,2.94) <-0.50,2.50> Total: 26680 W: 6807 L: 6593 D: 13280 Ptnml(0-2): 73, 3007, 6989, 3175, 96 https://tests.stockfishchess.org/tests/view/6174094e6ce927be32558441 passed LTC: LLR: 2.98 (-2.94,2.94) <0.50,3.50> Total: 21104 W: 5403 L: 5185 D: 10516 Ptnml(0-2): 8, 2160, 6001, 2372, 11 https://tests.stockfishchess.org/tests/view/61744927351812fe5f969864 closes https://github.com/official-stockfish/Stockfish/pull/3761 Bench: 6334068 --- src/movepick.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movepick.h b/src/movepick.h index 2932d9a8..7d78886f 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -86,7 +86,7 @@ enum StatsType { NoCaptures, Captures }; /// unsuccessful during the current search, and is used for reduction and move /// ordering decisions. It uses 2 tables (one for each color) indexed by /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards -typedef Stats ButterflyHistory; +typedef Stats ButterflyHistory; /// At higher depths LowPlyHistory records successful quiet moves near the root /// and quiet moves which are/were in the PV (ttPv). LowPlyHistory is populated during From 385deefd807b512a1c404f5cc4c2045f265a800d Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 22 Oct 2021 20:01:05 -0700 Subject: [PATCH 190/282] Fix sometimes incorrect key for prefetches STC https://tests.stockfishchess.org/tests/view/61737b4f6ce927be32558401 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 138712 W: 34914 L: 34942 D: 68856 Ptnml(0-2): 421, 14817, 38894, 14817, 407 Very minor tweak since Position::key() depends on the 50 move rule counter. Comments: https://github.com/mstembera/Stockfish/commit/cddde31eed505cdf0c4fc8ff96b89f6e39c797e1 closes https://github.com/official-stockfish/Stockfish/pull/3759 No functional change --- src/position.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/position.cpp b/src/position.cpp index 0686d245..ae1da017 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1013,9 +1013,9 @@ void Position::do_null_move(StateInfo& newSt) { } st->key ^= Zobrist::side; + ++st->rule50; prefetch(TT.first_entry(key())); - ++st->rule50; st->pliesFromNull = 0; sideToMove = ~sideToMove; From 7262fd5d14810b7b495b5038e348a448fda1bcc3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 27 Oct 2021 20:14:41 +0200 Subject: [PATCH 191/282] Stockfish 14.1 Official release version of Stockfish 14.1 Bench: 6334068 --- Today, we have the pleasure to announce Stockfish 14.1. As usual, downloads will be freely available at stockfishchess.org/download [1]. With Stockfish 14.1 our users get access to the strongest chess engine available today. In the period leading up to this release, Stockfish convincingly won several chess engine tournaments, including the TCEC 21 superfinal, the TCEC Cup 9, and the Computer Chess Championship for Fischer Random Chess (Chess960). In the latter tournament, Stockfish was undefeated in 599 out of 600 games played. Compared to Stockfish 14, this release introduces a more advanced NNUE architecture and various search improvements. In self play testing, using a book of balanced openings, Stockfish 14.1 wins three times more game pairs than it loses [2]. At this high level, draws are very common, so the Elo difference to Stockfish 14 is about 17 Elo. The NNUE evaluation method, introduced to top level chess with Stockfish 12 about one year ago [3], has now been adopted by several other strong CPU based chess engines. The Stockfish project builds on a thriving community of enthusiasts (thanks everybody!) that contribute their expertise, time, and resources to build a free and open-source chess engine that is robust, widely available, and very strong. We invite our chess fans to join the fishtest testing framework and programmers to contribute to the project [4]. Stay safe and enjoy chess! The Stockfish team [1] https://stockfishchess.org/download/ [2] https://tests.stockfishchess.org/tests/view/6175c320af70c2be1788fa2b [3] https://github.com/official-stockfish/Stockfish/discussions/3628 [4] https://stockfishchess.org/get-involved/ --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index f9c12337..bb3a641b 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,7 +67,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = ""; +const string Version = "14.1"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 717d6c5ed503543c66eb7a2c10e8a97c88d99862 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 29 Oct 2021 17:45:09 +0200 Subject: [PATCH 192/282] Widen the aspiration window for larger evals passed STC LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 36840 W: 9359 L: 9134 D: 18347 Ptnml(0-2): 111, 4130, 9722, 4337, 120 https://tests.stockfishchess.org/tests/view/617c601301c6d0988731d10a passed LTC LLR: 2.98 (-2.94,2.94) <0.50,3.50> Total: 64824 W: 16377 L: 16043 D: 32404 Ptnml(0-2): 27, 6712, 18618, 7010, 45 https://tests.stockfishchess.org/tests/view/617c720d01c6d0988731d114 closes https://github.com/official-stockfish/Stockfish/pull/3768 Bench: 7683058 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 36329f8f..14de2377 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -380,7 +380,7 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].previousScore; - delta = Value(17); + delta = Value(17) + int(prev) * prev / 16384; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); From a8330d5c3bfeb4c9d5c55083223792e0989bb9c6 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 31 Oct 2021 12:09:40 +0100 Subject: [PATCH 193/282] Do more deeper LMR searches. At expected cut nodes allow at least one ply deeper LMR search for the first seventh moves. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 42880 W: 10964 L: 10738 D: 21178 Ptnml(0-2): 105, 4565, 11883, 4773, 114 https://tests.stockfishchess.org/tests/view/6179abd7a9b1d8fbcc4ee6f4 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 66872 W: 16930 L: 16603 D: 33339 Ptnml(0-2): 36, 6509, 20024, 6826, 41 https://tests.stockfishchess.org/tests/view/617a30fb2fbca9ca65972b5e closes https://github.com/official-stockfish/Stockfish/pull/3770 Bench: 6295536 --- src/search.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 14de2377..c7c8e782 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1209,10 +1209,11 @@ moves_loop: // When in check, search starts here // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). - int deeper = r >= -1 ? 0 - : moveCount <= 5 ? 2 - : PvNode && depth > 6 ? 1 - : 0; + int deeper = r >= -1 ? 0 + : moveCount <= 5 ? 2 + : PvNode && depth > 6 ? 1 + : cutNode && moveCount <= 7 ? 1 + : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); From 0e89d6e7546d26a19a108d047b489d9ba6f7970c Mon Sep 17 00:00:00 2001 From: Michel Van den Bergh Date: Sun, 31 Oct 2021 19:35:30 +0100 Subject: [PATCH 194/282] Do not output to stderr during the build. To help with debugging, the worker sends the output of stderr (suitable truncated) to the action log on the server, in case a build fails. For this to work it is important that there is no spurious output to stderr. closes https://github.com/official-stockfish/Stockfish/pull/3773 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index fea597e7..5c52661b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -751,7 +751,7 @@ profile-build: net config-sanity objclean profileclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) @echo "" @echo "Step 2/4. Running benchmark for pgo-build ..." - $(PGOBENCH) > /dev/null + $(PGOBENCH) 2>&1 | tail -n 4 @echo "" @echo "Step 3/4. Building optimized executable ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean From ef4822aa8d5945d490acca674eb1db8c3c38e9d5 Mon Sep 17 00:00:00 2001 From: xefoci7612 Date: Sun, 10 Oct 2021 14:03:51 +0200 Subject: [PATCH 195/282] Simplify Skill implementation Currently we handle the UCI_Elo with a double randomization. This seems not necessary and a bit involuted. This patch removes the first randomization and unifies the 2 cases. closes https://github.com/official-stockfish/Stockfish/pull/3769 No functional change. --- AUTHORS | 1 + src/search.cpp | 41 +++++++++++++++++++---------------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/AUTHORS b/AUTHORS index 56725e98..35ccdaf5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -194,6 +194,7 @@ tttak Unai Corzo (unaiic) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) +xefoci7612 zz4032 diff --git a/src/search.cpp b/src/search.cpp index c7c8e782..075be835 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -112,14 +112,22 @@ namespace { return thisThread->state; } - // Skill structure is used to implement strength limit + // Skill structure is used to implement strength limit. If we have an uci_elo then + // we convert it to a suitable fractional skill level using anchoring to CCRL Elo + // (goldfish 1.13 = 2000) and a fit through Ordo derived Elo for match (TC 60+0.6) + // results spanning a wide range of k values. struct Skill { - explicit Skill(int l) : level(l) {} - bool enabled() const { return level < 20; } - bool time_to_pick(Depth depth) const { return depth == 1 + level; } + Skill(int skill_level, int uci_elo) { + if (uci_elo) + level = std::clamp(std::pow((uci_elo - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0); + else + level = double(skill_level); + } + bool enabled() const { return level < 20.0; } + bool time_to_pick(Depth depth) const { return depth == 1 + int(level); } Move pick_best(size_t multiPV); - int level; + double level; Move best = MOVE_NONE; }; @@ -243,10 +251,11 @@ void MainThread::search() { Time.availableNodes += Limits.inc[us] - Threads.nodes_searched(); Thread* bestThread = this; + Skill skill = Skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); if ( int(Options["MultiPV"]) == 1 && !Limits.depth - && !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) + && !skill.enabled() && rootMoves[0].pv[0] != MOVE_NONE) bestThread = Threads.get_best_thread(); @@ -311,19 +320,7 @@ void Thread::search() { std::fill(&lowPlyHistory[MAX_LPH - 2][0], &lowPlyHistory.back().back() + 1, 0); size_t multiPV = size_t(Options["MultiPV"]); - - // Pick integer skill levels, but non-deterministically round up or down - // such that the average integer skill corresponds to the input floating point one. - // UCI_Elo is converted to a suitable fractional skill level, using anchoring - // to CCRL Elo (goldfish 1.13 = 2000) and a fit through Ordo derived Elo - // for match (TC 60+0.6) results spanning a wide range of k values. - PRNG rng(now()); - double floatLevel = Options["UCI_LimitStrength"] ? - std::clamp(std::pow((Options["UCI_Elo"] - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0) : - double(Options["Skill Level"]); - int intLevel = int(floatLevel) + - ((floatLevel - int(floatLevel)) * 1024 > rng.rand() % 1024 ? 1 : 0); - Skill skill(intLevel); + Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); // When playing with strength handicap enable MultiPV search that we will // use behind the scenes to retrieve a set of possible moves. @@ -1780,8 +1777,8 @@ moves_loop: // When in check, search starts here // RootMoves are already sorted by score in descending order Value topScore = rootMoves[0].score; int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValueMg); - int weakness = 120 - 2 * level; int maxScore = -VALUE_INFINITE; + double weakness = 120 - 2 * level; // Choose best move. For each move score we add two terms, both dependent on // weakness. One is deterministic and bigger for weaker levels, and one is @@ -1789,8 +1786,8 @@ moves_loop: // When in check, search starts here for (size_t i = 0; i < multiPV; ++i) { // This is our magic formula - int push = ( weakness * int(topScore - rootMoves[i].score) - + delta * (rng.rand() % weakness)) / 128; + int push = int(( weakness * int(topScore - rootMoves[i].score) + + delta * (rng.rand() % int(weakness))) / 128); if (rootMoves[i].score + push >= maxScore) { From 5a223afe4cfdc2cbb0092c86ddc20fd260e1ac0d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 1 Nov 2021 06:28:37 +0100 Subject: [PATCH 196/282] Restore development version No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index bb3a641b..f9c12337 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,7 +67,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = "14.1"; +const string Version = ""; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 11c6cf720d4cdd882bc0f2c36e25910cf77fb57b Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Wed, 27 Oct 2021 18:08:24 -0300 Subject: [PATCH 197/282] More futility pruning Expand maximum allowed eval by 50% in futility pruning, above the VALUE_KNOWN_WIN. STC: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 128208 W: 32534 L: 32192 D: 63482 Ptnml(0-2): 298, 13484, 36216, 13790, 316 https://tests.stockfishchess.org/tests/view/6179c069a9b1d8fbcc4ee716 LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 89816 W: 22645 L: 22265 D: 44906 Ptnml(0-2): 41, 8404, 27650, 8760, 53 https://tests.stockfishchess.org/tests/view/617ad728f411ea45cc39f895 closes https://github.com/official-stockfish/Stockfish/pull/3767 bench: 6804175 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 075be835..a712ce87 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -813,7 +813,7 @@ namespace { if ( !PvNode && depth < 9 && eval - futility_margin(depth, improving) >= beta - && eval < VALUE_KNOWN_WIN) // Do not return unproven wins + && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 8. Null move search with verification search (~40 Elo) From c2b9134c6e7637ea375b4755a6f96dc772c6bb17 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 4 Nov 2021 18:35:01 +0300 Subject: [PATCH 198/282] Do more reductions at Pv nodes with low delta This patch increases reduction for PvNodes that have their delta (difference between beta and alpha) significantly reduced compared to what it was at root. passed STC https://tests.stockfishchess.org/tests/view/617f9063af49befdeee40226 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 220840 W: 55752 L: 55150 D: 109938 Ptnml(0-2): 583, 24982, 58712, 25536, 607 passed LTC https://tests.stockfishchess.org/tests/view/61815de959e71df00dcc42ed LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 79000 W: 19937 L: 19562 D: 39501 Ptnml(0-2): 36, 8190, 22674, 8563, 37 closes https://github.com/official-stockfish/Stockfish/pull/3774 bench: 6717808 --- src/search.cpp | 6 ++++++ src/thread.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index a712ce87..46c95ef0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -625,6 +625,8 @@ namespace { if (alpha >= beta) return alpha; } + else + thisThread->rootDelta = beta - alpha; assert(0 <= ss->ply && ss->ply < MAX_PLY); @@ -1167,6 +1169,10 @@ moves_loop: // When in check, search starts here && bestMoveCount <= 3) r--; + // Increases reduction for PvNodes that have small window + if (PvNode && beta - alpha < thisThread->rootDelta / 4) + r++; + // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv diff --git a/src/thread.h b/src/thread.h index e04d303a..fae866c4 100644 --- a/src/thread.h +++ b/src/thread.h @@ -72,6 +72,7 @@ public: StateInfo rootState; Search::RootMoves rootMoves; Depth rootDepth, completedDepth; + Value rootDelta; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; LowPlyHistory lowPlyHistory; From 45e5e65a28ce7e304c279fabf5f8a83cced73013 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 1 Nov 2021 08:40:33 +0100 Subject: [PATCH 199/282] do not store qsearch positions in TT as exact. in qsearch don't store positions in TT with the exact flag. passed STC: https://tests.stockfishchess.org/tests/view/617f9a29af49befdeee40231 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 155568 W: 39003 L: 39022 D: 77543 Ptnml(0-2): 403, 17854, 41305, 17803, 419 passed LTC: https://tests.stockfishchess.org/tests/view/6180d47259e71df00dcc42a5 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 79640 W: 19993 L: 19910 D: 39737 Ptnml(0-2): 37, 8356, 22957, 8427, 43 closes https://github.com/official-stockfish/Stockfish/pull/3775 Bench: 7531210 --- src/search.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 46c95ef0..6c894c17 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1416,13 +1416,12 @@ moves_loop: // When in check, search starts here Key posKey; Move ttMove, move, bestMove; Depth ttDepth; - Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; + Value bestValue, value, ttValue, futilityValue, futilityBase; bool pvHit, givesCheck, captureOrPromotion; int moveCount; if (PvNode) { - oldAlpha = alpha; // To flag BOUND_EXACT when eval above alpha and no available moves (ss+1)->pv = pv; ss->pv[0] = MOVE_NONE; } @@ -1612,8 +1611,7 @@ moves_loop: // When in check, search starts here // Save gathered info in transposition table tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, - bestValue >= beta ? BOUND_LOWER : - PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, + bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove, ss->staticEval); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); From a0259d8ab9661b7f625474d2cbe18481ef69bbf2 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 5 Nov 2021 13:49:28 +0100 Subject: [PATCH 200/282] Tweak initial aspiration window. Maintain for each root move an exponential average of the search value with a weight ratio of 2:1 (new value vs old values). Then the average score is used as the center of the initial aspiration window instead of the previous score. Stats indicate (see PR) that the deviation for previous score is in general greater than using average score, so later seems a better estimation of the next search value. This is probably the reason this patch succeded besides smoothing the sometimes wild swings in search score. An additional observation is that at higher depth previous score is above but average score below zero. So for average score more/less fail/low highs should be occur than previous score. STC: LLR: 2.97 (-2.94,2.94) <0.00,2.50> Total: 59792 W: 15106 L: 14792 D: 29894 Ptnml(0-2): 144, 6718, 15869, 7010, 155 https://tests.stockfishchess.org/tests/view/61841612d7a085ad008eef06 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 46448 W: 11835 L: 11537 D: 23076 Ptnml(0-2): 21, 4756, 13374, 5050, 23 https://tests.stockfishchess.org/tests/view/618463abd7a085ad008eef3e closes https://github.com/official-stockfish/Stockfish/pull/3776 Bench: 6719976 --- src/search.cpp | 4 +++- src/search.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6c894c17..090a7931 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -376,7 +376,7 @@ void Thread::search() { // Reset aspiration window starting size if (rootDepth >= 4) { - Value prev = rootMoves[pvIdx].previousScore; + Value prev = rootMoves[pvIdx].averageScore; delta = Value(17) + int(prev) * prev / 16384; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); @@ -1280,6 +1280,8 @@ moves_loop: // When in check, search starts here RootMove& rm = *std::find(thisThread->rootMoves.begin(), thisThread->rootMoves.end(), move); + rm.averageScore = rm.averageScore != -VALUE_INFINITE ? (2 * value + rm.averageScore) / 3 : value; + // PV move or new best move? if (moveCount == 1 || value > alpha) { diff --git a/src/search.h b/src/search.h index ba9d0677..7a5d5bdf 100644 --- a/src/search.h +++ b/src/search.h @@ -73,6 +73,7 @@ struct RootMove { Value score = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE; + Value averageScore = -VALUE_INFINITE; int selDepth = 0; int tbRank = 0; Value tbScore; From 7b278aab9f61620b9dba31896b38aeea1eb911e2 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 7 Nov 2021 11:01:03 +0100 Subject: [PATCH 201/282] Reduce use of lazyEval In case the evaluation at root is large, discourage the use of lazyEval. This fixes https://github.com/official-stockfish/Stockfish/issues/3772 or at least improves it significantly. In this case, poor play with large odds can be observed, in extreme cases leading to a loss despite large advantage: r1bq1b1r/ppp3p1/3p1nkp/n3p3/2B1P2N/2NPB3/PPP2PPP/R3K2R b KQ - 5 9 With this patch the poor move is only considered up to depth 13, in master up to depth 28. The patch did not pass at LTC with Elo gainer bounds, but with slightly positive Elo nevertheless (95% LOS). STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 40368 W: 10318 L: 10041 D: 20009 Ptnml(0-2): 103, 4493, 10725, 4750, 113 https://tests.stockfishchess.org/tests/view/61800ad259e71df00dcc420d LTC: LLR: -2.94 (-2.94,2.94) <0.50,3.00> Total: 212288 W: 52997 L: 52692 D: 106599 Ptnml(0-2): 112, 22038, 61549, 22323, 122 https://tests.stockfishchess.org/tests/view/618050d959e71df00dcc426d closes https://github.com/official-stockfish/Stockfish/pull/3780 Bench: 7127040 --- src/evaluate.cpp | 5 ++++- src/search.cpp | 2 +- src/thread.h | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2f1d5067..a503b0c8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -988,7 +988,9 @@ namespace { // Early exit if score is high auto lazy_skip = [&](Value lazyThreshold) { - return abs(mg_value(score) + eg_value(score)) > lazyThreshold + pos.non_pawn_material() / 32; + return abs(mg_value(score) + eg_value(score)) > lazyThreshold + + std::abs(pos.this_thread()->bestValue) * 5 / 4 + + pos.non_pawn_material() / 32; }; if (lazy_skip(LazyThreshold1)) @@ -1126,6 +1128,7 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); pos.this_thread()->trend = SCORE_ZERO; // Reset any dynamic contempt + pos.this_thread()->bestValue = VALUE_ZERO; // Reset bestValue for lazyEval v = Evaluation(pos).value(); diff --git a/src/search.cpp b/src/search.cpp index 090a7931..11d1df32 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -286,7 +286,7 @@ void Thread::search() { // The latter is needed for statScore and killer initialization. Stack stack[MAX_PLY+10], *ss = stack+7; Move pv[MAX_PLY+1]; - Value bestValue, alpha, beta, delta; + Value alpha, beta, delta; Move lastBestMove = MOVE_NONE; Depth lastBestMoveDepth = 0; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); diff --git a/src/thread.h b/src/thread.h index fae866c4..38793739 100644 --- a/src/thread.h +++ b/src/thread.h @@ -64,6 +64,7 @@ public: uint64_t nodesLastExplosive; uint64_t nodesLastNormal; std::atomic nodes, tbHits, bestMoveChanges; + Value bestValue; int selDepth, nmpMinPly; Color nmpColor; ExplosionState state; From c4a1390f4e485b8b6a2cf43504a968a3c8a4317d Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 8 Nov 2021 08:46:43 -0300 Subject: [PATCH 202/282] Simplify away the Reverse Move penalty This simplifies the penalty for reverse move introduced in https://github.com/official-stockfish/Stockfish/pull/2294 . STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 81696 W: 20627 L: 20540 D: 40529 Ptnml(0-2): 221, 9390, 21559, 9437, 241 https://tests.stockfishchess.org/tests/view/618810acd7a085ad008ef1cc LTC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 44136 W: 11021 L: 10890 D: 22225 Ptnml(0-2): 28, 4570, 12746, 4691, 33 https://tests.stockfishchess.org/tests/view/61885686d7a085ad008ef20b closes https://github.com/official-stockfish/Stockfish/pull/3781 bench: 6547978 --- src/search.cpp | 4 ---- src/types.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 11d1df32..f2e11e35 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1756,10 +1756,6 @@ moves_loop: // When in check, search starts here thisThread->mainHistory[us][from_to(move)] << bonus; update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); - // Penalty for reversed move in case of moved piece not being a pawn - if (type_of(pos.moved_piece(move)) != PAWN) - thisThread->mainHistory[us][from_to(reverse_move(move))] << -bonus; - // Update countermove history if (is_ok((ss-1)->currentMove)) { diff --git a/src/types.h b/src/types.h index fd643117..02cd19de 100644 --- a/src/types.h +++ b/src/types.h @@ -470,10 +470,6 @@ constexpr Move make_move(Square from, Square to) { return Move((from << 6) + to); } -constexpr Move reverse_move(Move m) { - return make_move(to_sq(m), from_sq(m)); -} - template constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); From 1a5c21dc56a30f4af3979fb47ec3681dc7077195 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 7 Nov 2021 14:42:46 +0100 Subject: [PATCH 203/282] Tune a few NNUE related scaling parameters passed STC LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 102480 W: 26099 L: 25708 D: 50673 Ptnml(0-2): 282, 11637, 27003, 12044, 274 https://tests.stockfishchess.org/tests/view/618820e3d7a085ad008ef1dd passed LTC LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 165512 W: 41689 L: 41112 D: 82711 Ptnml(0-2): 82, 17255, 47510, 17822, 87 https://tests.stockfishchess.org/tests/view/6188b470d7a085ad008ef239 closes https://github.com/official-stockfish/Stockfish/pull/3784 Bench: 6339548 --- src/evaluate.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a503b0c8..9fdadbb7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,9 +1091,9 @@ Value Eval::evaluate(const Position& pos) { v = Evaluation(pos).value(); // classical else { - int scale = 883 - + 32 * pos.count() - + 32 * pos.non_pawn_material() / 1024; + int scale = 898 + + 24 * pos.count() + + 33 * pos.non_pawn_material() / 1024; v = NNUE::evaluate(pos, true) * scale / 1024; // NNUE @@ -1102,7 +1102,7 @@ Value Eval::evaluate(const Position& pos) { } // Damp down the evaluation linearly when shuffling - v = v * (100 - pos.rule50_count()) / 100; + v = v * (207 - pos.rule50_count()) / 207; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From 9048ac00db12a9ac48bff9b9eb145b30ff88d984 Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Sat, 13 Nov 2021 06:38:52 +0800 Subject: [PATCH 204/282] Fix processor group binding under Windows. Starting with Windows Build 20348 the behavior of the numa API has been changed: https://docs.microsoft.com/en-us/windows/win32/procthread/numa-support Old code only worked because there was probably a limit on how many cores/threads can reside within one NUMA node, and the OS creates extra NUMA nodes when necessary, however the actual mechanism of core binding is done by "Processor Groups"(https://docs.microsoft.com/en-us/windows/win32/procthread/processor-groups). With a newer OS, one NUMA node can have many such "Processor Groups" and we should just consistently use the number of groups to bind the threads instead of deriving the topology from the number of NUMA nodes. This change is required to spread threads on all cores on Windows 11 with a 3990X CPU. It has only 1 NUMA node with 2 groups of 64 threads each. closes https://github.com/official-stockfish/Stockfish/pull/3787 No functional change. --- src/misc.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index f9c12337..0af20e10 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -502,7 +502,7 @@ void bindThisThread(size_t) {} int best_group(size_t idx) { int threads = 0; - int nodes = 0; + int groups = 0; int cores = 0; DWORD returnLength = 0; DWORD byteOffset = 0; @@ -530,8 +530,8 @@ int best_group(size_t idx) { while (byteOffset < returnLength) { - if (ptr->Relationship == RelationNumaNode) - nodes++; + if (ptr->Relationship == RelationGroup) + groups += ptr->Group.MaximumGroupCount; else if (ptr->Relationship == RelationProcessorCore) { @@ -546,23 +546,23 @@ int best_group(size_t idx) { free(buffer); - std::vector groups; + std::vector core_groups; - // Run as many threads as possible on the same node until core limit is - // reached, then move on filling the next node. - for (int n = 0; n < nodes; n++) - for (int i = 0; i < cores / nodes; i++) - groups.push_back(n); + // Run as many threads as possible on the same group until core limit is + // reached, then move on filling the next group. + for (int n = 0; n < groups; n++) + for (int i = 0; i < cores / groups; i++) + core_groups.push_back(n); // In case a core has more than one logical processor (we assume 2) and we // have still threads to allocate, then spread them evenly across available - // nodes. + // groups. for (int t = 0; t < threads - cores; t++) - groups.push_back(t % nodes); + core_groups.push_back(t % groups); // If we still have more threads than the total number of logical processors // then return -1 and let the OS to decide what to do. - return idx < groups.size() ? groups[idx] : -1; + return idx < core_groups.size() ? core_groups[idx] : -1; } From f5df517145890ecee3d855e98470241b68645b87 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 14 Nov 2021 21:35:00 +0300 Subject: [PATCH 205/282] Simplify Pv nodes related logic in LMR Instead of having 2 separate conditions for Pv nodes reductions we can actually write them together. Despite it's not being strictly logically the same bench actually doesn't change up to depth 20, so them interacting is really rare and thus it's just a removal of extra PvNode check most of the time. passed STC: https://tests.stockfishchess.org/tests/view/618ce27cd7a085ad008ef4e9 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 37488 W: 9424 L: 9279 D: 18785 Ptnml(0-2): 90, 3903, 10634, 4006, 111 passed LTC: https://tests.stockfishchess.org/tests/view/618d2585d7a085ad008ef527 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 49968 W: 12449 L: 12331 D: 25188 Ptnml(0-2): 27, 4745, 15309, 4889, 14 closes https://github.com/official-stockfish/Stockfish/pull/3792 Bench: 6339548 --- src/search.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f2e11e35..fabb0ff0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1164,15 +1164,12 @@ moves_loop: // When in check, search starts here { Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); - // Decrease reduction if on the PV (~2 Elo) + // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode - && bestMoveCount <= 3) + && bestMoveCount <= 3 + && beta - alpha >= thisThread->rootDelta / 4) r--; - // Increases reduction for PvNodes that have small window - if (PvNode && beta - alpha < thisThread->rootDelta / 4) - r++; - // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv From a5a89b27c8e3225fb453d603bc4515d32bb351c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 19 Nov 2021 20:04:35 +0100 Subject: [PATCH 206/282] Introduce Optimism Current master implements a scaling of the raw NNUE output value with a formula equivalent to 'eval = alpha * NNUE_output', where the scale factor alpha varies between 1.8 (for early middle game) and 0.9 (for pure endgames). This feature allows Stockfish to keep material on the board when she thinks she has the advantage, and to seek exchanges and simplifications when she thinks she has to defend. This patch slightly offsets the turning point between these two strategies, by adding to Stockfish's evaluation a small "optimism" value before actually doing the scaling. The effect is that SF will play a little bit more risky, trying to keep the tension a little bit longer when she is defending, and keeping even more material on the board when she has an advantage. We note that this patch is similar in spirit to the old "Contempt" idea we used to have in classical Stockfish, but this implementation differs in two key points: a) it has been tested as an Elo-gainer against master; b) the values output by the search are not changed on average by the implementation (in other words, the optimism value changes the tension/exchange strategy, but a displayed value of 1.0 pawn has the same signification before and after the patch). See the old comment https://github.com/official-stockfish/Stockfish/pull/1361#issuecomment-359165141 for some images illustrating the ideas. ------- finished yellow at STC: LLR: -2.94 (-2.94,2.94) <0.00,2.50> Total: 165048 W: 41705 L: 41611 D: 81732 Ptnml(0-2): 565, 18959, 43245, 19327, 428 https://tests.stockfishchess.org/tests/view/61942a3dcd645dc8291c876b passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 121656 W: 30762 L: 30287 D: 60607 Ptnml(0-2): 87, 12558, 35032, 13095, 56 https://tests.stockfishchess.org/tests/view/61962c58cd645dc8291c8877 ------- How to continue from there? a) the shape (slope and amplitude) of the sigmoid used to compute the optimism value could be tweaked to try to gain more Elo, so the parameters of the sigmoid function in line 391 of search.cpp could be tuned with SPSA. Manual tweaking is also possible using this Desmos page: https://www.desmos.com/calculator/jhh83sqq92 b) in a similar vein, with two recents patches affecting the scaling of the NNUE evaluation in evaluate.cpp, now could be a good time to try a round of SPSA tuning of the NNUE network; c) this patch will tend to keep tension in middlegame a little bit longer, so any patch improving the defensive aspect of play via search extensions in risky, tactical positions would be welcome. ------- closes https://github.com/official-stockfish/Stockfish/pull/3797 Bench: 6184852 --- src/evaluate.cpp | 19 +++++++++++++------ src/misc.h | 27 +++++++++++++++++++++++++++ src/search.cpp | 15 ++++++++++----- src/thread.h | 1 + 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9fdadbb7..c1d3d159 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,11 +1091,15 @@ Value Eval::evaluate(const Position& pos) { v = Evaluation(pos).value(); // classical else { - int scale = 898 - + 24 * pos.count() - + 33 * pos.non_pawn_material() / 1024; + int scale = 898 + + 24 * pos.count() + + 33 * pos.non_pawn_material() / 1024; - v = NNUE::evaluate(pos, true) * scale / 1024; // NNUE + Value nnue = NNUE::evaluate(pos, true); // NNUE + Color stm = pos.side_to_move(); + Value optimism = pos.this_thread()->optimism[stm]; + + v = (nnue + optimism) * scale / 1024 - optimism; if (pos.is_chess960()) v += fix_FRC(pos); @@ -1127,8 +1131,11 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); - pos.this_thread()->trend = SCORE_ZERO; // Reset any dynamic contempt - pos.this_thread()->bestValue = VALUE_ZERO; // Reset bestValue for lazyEval + // Reset any global variable used in eval + pos.this_thread()->trend = SCORE_ZERO; + pos.this_thread()->bestValue = VALUE_ZERO; + pos.this_thread()->optimism[WHITE] = VALUE_ZERO; + pos.this_thread()->optimism[BLACK] = VALUE_ZERO; v = Evaluation(pos).value(); diff --git a/src/misc.h b/src/misc.h index 718e5558..c1744130 100644 --- a/src/misc.h +++ b/src/misc.h @@ -138,6 +138,33 @@ private: std::size_t size_ = 0; }; + +/// sigmoid(t, x0, y0, C, P, Q) implements a sigmoid-like function using only integers, +/// with the following properties: +/// +/// - sigmoid is centered in (x0, y0) +/// - sigmoid has amplitude [-P/Q , P/Q] instead of [-1 , +1] +/// - limit is (y0 - P/Q) when t tends to -infinity +/// - limit is (y0 + P/Q) when t tends to +infinity +/// - the slope can be adjusted using C > 0, smaller C giving a steeper sigmoid +/// - the slope of the sigmoid when t = x0 is P/(Q*C) +/// - sigmoid is increasing with t when P > 0 and Q > 0 +/// - to get a decreasing sigmoid, call with -t, or change sign of P +/// - mean value of the sigmoid is y0 +/// +/// Use to draw the sigmoid + +inline int64_t sigmoid(int64_t t, int64_t x0, + int64_t y0, + int64_t C, + int64_t P, + int64_t Q) +{ + assert(C > 0); + return y0 + P * (t-x0) / (Q * (std::abs(t-x0) + C)) ; +} + + /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated /// to the public domain by Sebastiano Vigna (2014). diff --git a/src/search.cpp b/src/search.cpp index fabb0ff0..1dfadd21 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -334,8 +334,10 @@ void Thread::search() { nodesLastExplosive = nodes; nodesLastNormal = nodes; - state = EXPLOSION_NONE; - trend = SCORE_ZERO; + state = EXPLOSION_NONE; + trend = SCORE_ZERO; + optimism[ us] = Value(25); + optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -381,11 +383,14 @@ void Thread::search() { alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); - // Adjust trend based on root move's previousScore (dynamic contempt) - int tr = 113 * prev / (abs(prev) + 147); - + // Adjust trend and optimism based on root move's previousScore + int tr = sigmoid(prev, 0, 0, 147, 113, 1); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); + + int opt = sigmoid(prev, 0, 25, 147, 14464, 256); + optimism[ us] = Value(opt); + optimism[~us] = -optimism[us]; } // Start with a small aspiration window and, in the case of a fail diff --git a/src/thread.h b/src/thread.h index 38793739..cd206faa 100644 --- a/src/thread.h +++ b/src/thread.h @@ -68,6 +68,7 @@ public: int selDepth, nmpMinPly; Color nmpColor; ExplosionState state; + Value optimism[COLOR_NB]; Position rootPos; StateInfo rootState; From a943b1d28d673814ee1f3de4a2ae4b8e091f1e4c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 21 Nov 2021 20:57:12 +0100 Subject: [PATCH 207/282] Remove appveyor CI retire msvc support and corresponding CI. No active development happens on msvc, and build is much slower or wrong. gcc (mingw) is our toolchain of choice also on windows, and the latter is tested. No functional change --- appveyor.yml | 88 ---------------------------------------------------- 1 file changed, 88 deletions(-) delete mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index ab608409..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,88 +0,0 @@ -version: 1.0.{build} -clone_depth: 50 - -branches: - only: - - master - -# Operating system (build VM template) -os: Visual Studio 2019 - -# Build platform, i.e. x86, x64, AnyCPU. This setting is optional. -platform: - - x86 - - x64 - -# build Configuration, i.e. Debug, Release, etc. -configuration: - - Debug - - Release - -matrix: - # The build fail immediately once one of the job fails - fast_finish: true - -# Scripts that are called at very beginning, before repo cloning -init: - - cmake --version - - msbuild /version - -before_build: - - ps: | - # Get sources - $src = get-childitem -Path *.cpp -Recurse | select -ExpandProperty FullName - $src = $src -join ' ' - $src = $src.Replace("\", "/") - - # Build CMakeLists.txt - $t = 'cmake_minimum_required(VERSION 3.17)', - 'project(Stockfish)', - 'set(CMAKE_CXX_STANDARD 17)', - 'set(CMAKE_CXX_STANDARD_REQUIRED ON)', - 'set (CMAKE_CXX_EXTENSIONS OFF)', - 'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)', - 'set(source_files', $src, ')', - 'add_executable(stockfish ${source_files})' - - # Write CMakeLists.txt withouth BOM - $MyPath = (Get-Item -Path "." -Verbose).FullName + '\CMakeLists.txt' - $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False - [System.IO.File]::WriteAllLines($MyPath, $t, $Utf8NoBomEncoding) - - # Obtain bench reference from git log - $b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1 - $bench = $b -match '\D+(\d+)' | % { $matches[1] } - Write-Host "Reference bench:" $bench - $g = "Visual Studio 16 2019" - If (${env:PLATFORM} -eq 'x64') { $a = "x64" } - If (${env:PLATFORM} -eq 'x86') { $a = "Win32" } - cmake -G "${g}" -A ${a} . - Write-Host "Generated files for: " $g $a - -build_script: - - cmake --build . --config %CONFIGURATION% -- /verbosity:minimal - - ps: | - # Download default NNUE net from fishtest - $nnuenet = Get-Content -Path src\evaluate.h | Select-String -CaseSensitive -Pattern "EvalFileDefaultName" | Select-String -CaseSensitive -Pattern "nn-[a-z0-9]{12}.nnue" - $dummy = $nnuenet -match "(?nn-[a-z0-9]{12}.nnue)" - $nnuenet = $Matches.nnuenet - Write-Host "Default net:" $nnuenet - $nnuedownloadurl = "https://tests.stockfishchess.org/api/nn/$nnuenet" - $nnuefilepath = "src\${env:CONFIGURATION}\$nnuenet" - if (Test-Path -Path $nnuefilepath) { - Write-Host "Already available." - } else { - Write-Host "Downloading $nnuedownloadurl to $nnuefilepath" - Invoke-WebRequest -Uri $nnuedownloadurl -OutFile $nnuefilepath - } - -before_test: - - cd src/%CONFIGURATION% - - stockfish bench 2> out.txt >NUL - - ps: | - # Verify bench number - $s = (gc "./out.txt" | out-string) - $r = ($s -match 'Nodes searched \D+(\d+)' | % { $matches[1] }) - Write-Host "Engine bench:" $r - Write-Host "Reference bench:" $bench - If ($r -ne $bench) { exit 1 } From 7218ec4df9fef1146a451b71f0ed3bfd8123c9f9 Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Sat, 20 Nov 2021 17:57:08 +0800 Subject: [PATCH 208/282] Revert and fix earlier windows NUMA patch revert https://github.com/official-stockfish/Stockfish/commit/9048ac00db12a9ac48bff9b9eb145b30ff88d984 due to core spread problem and fix new OS compatibility with another method. This code assumes that if one NUMA node has more than one processor groups, they are created equal(having equal amount of cores assigned to each of the groups), and also the total number of available cores contained in such groups are equal to the number of available cores within one NUMA node because of how best_node function works. closes https://github.com/official-stockfish/Stockfish/pull/3798 fixes https://github.com/official-stockfish/Stockfish/pull/3787 No functional change. --- src/misc.cpp | 54 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 0af20e10..4dfa9f0c 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -36,6 +36,7 @@ typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY); typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); +typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); } #endif @@ -495,14 +496,14 @@ void bindThisThread(size_t) {} #else -/// best_group() retrieves logical processor information using Windows specific -/// API and returns the best group id for the thread with index idx. Original +/// best_node() retrieves logical processor information using Windows specific +/// API and returns the best node id for the thread with index idx. Original /// code from Texel by Peter Österlund. -int best_group(size_t idx) { +int best_node(size_t idx) { int threads = 0; - int groups = 0; + int nodes = 0; int cores = 0; DWORD returnLength = 0; DWORD byteOffset = 0; @@ -530,8 +531,8 @@ int best_group(size_t idx) { while (byteOffset < returnLength) { - if (ptr->Relationship == RelationGroup) - groups += ptr->Group.MaximumGroupCount; + if (ptr->Relationship == RelationNumaNode) + nodes++; else if (ptr->Relationship == RelationProcessorCore) { @@ -546,23 +547,23 @@ int best_group(size_t idx) { free(buffer); - std::vector core_groups; + std::vector groups; - // Run as many threads as possible on the same group until core limit is - // reached, then move on filling the next group. - for (int n = 0; n < groups; n++) - for (int i = 0; i < cores / groups; i++) - core_groups.push_back(n); + // Run as many threads as possible on the same node until core limit is + // reached, then move on filling the next node. + for (int n = 0; n < nodes; n++) + for (int i = 0; i < cores / nodes; i++) + groups.push_back(n); // In case a core has more than one logical processor (we assume 2) and we // have still threads to allocate, then spread them evenly across available - // groups. + // nodes. for (int t = 0; t < threads - cores; t++) - core_groups.push_back(t % groups); + groups.push_back(t % nodes); // If we still have more threads than the total number of logical processors // then return -1 and let the OS to decide what to do. - return idx < core_groups.size() ? core_groups[idx] : -1; + return idx < groups.size() ? groups[idx] : -1; } @@ -571,22 +572,35 @@ int best_group(size_t idx) { void bindThisThread(size_t idx) { // Use only local variables to be thread-safe - int group = best_group(idx); + int node = best_node(idx); - if (group == -1) + if (node == -1) return; // Early exit if the needed API are not available at runtime HMODULE k32 = GetModuleHandle("Kernel32.dll"); auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity"); + auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2"); if (!fun2 || !fun3) return; - GROUP_AFFINITY affinity; - if (fun2(group, &affinity)) - fun3(GetCurrentThread(), &affinity, nullptr); + if (!fun4) { + GROUP_AFFINITY affinity; + if (fun2(node, &affinity)) + fun3(GetCurrentThread(), &affinity, nullptr); + } else { + // If a numa node has more than one processor group, we assume they are + // sized equal and we spread threads evenly across the groups. + USHORT elements, returnedElements; + elements = GetMaximumProcessorGroupCount(); + GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc( + elements * sizeof(GROUP_AFFINITY)); + if (fun4(node, affinity, elements, &returnedElements)) + fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); + free(affinity); + } } #endif From 092b27a6d0174f619fff9a53099ac9fdc5c2cb4e Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 23 Nov 2021 20:40:32 +0100 Subject: [PATCH 209/282] Less futility pruning. Disable futility pruning at former PV nodes stored in the transposition table. STC: LLR: 2.96 (-2.94,2.94) <0.00,2.50> Total: 102256 W: 25708 L: 25318 D: 51230 Ptnml(0-2): 276, 11511, 27168, 11893, 280 https://tests.stockfishchess.org/tests/view/61990b3135c7c6348cb602db LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 183304 W: 46027 L: 45408 D: 91869 Ptnml(0-2): 96, 19029, 52778, 19658, 91 https://tests.stockfishchess.org/tests/view/619a0d1b35c7c6348cb603bc closes https://github.com/official-stockfish/Stockfish/pull/3804 Bench: 7334766 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1dfadd21..92ec9eca 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -817,7 +817,7 @@ namespace { // Step 7. Futility pruning: child node (~50 Elo). // The depth condition is important for mate finding. - if ( !PvNode + if ( !ss->ttPv && depth < 9 && eval - futility_margin(depth, improving) >= beta && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. From 0ac8aca893dd2052f8433e0b4a3d65073266b00f Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 25 Nov 2021 20:55:52 +0300 Subject: [PATCH 210/282] Use fraction of history heuristics in futility pruning This idea is somewhat of a respin of smth we had in futility pruning and that was simplified away - dependence of it not only on static evaluation of position but also on move history heuristics. Instead of aborting it when they are high there we use fraction of their sum to adjust static eval pruning criteria. passed STC https://tests.stockfishchess.org/tests/view/619bd438c0a4ea18ba95a27d LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 113704 W: 29284 L: 28870 D: 55550 Ptnml(0-2): 357, 12884, 30044, 13122, 445 passed LTC https://tests.stockfishchess.org/tests/view/619cb8f0c0a4ea18ba95a334 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 147136 W: 37307 L: 36770 D: 73059 Ptnml(0-2): 107, 15279, 42265, 15804, 113 closes https://github.com/official-stockfish/Stockfish/pull/3805 bench 6777918 --- src/search.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 92ec9eca..12c89de4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1054,17 +1054,19 @@ moves_loop: // When in check, search starts here } else { + int history = (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)]; + // Continuation history based pruning (~20 Elo) - if (lmrDepth < 5 - && (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] < -3000 * depth + 3000) + if ( lmrDepth < 5 + && history < -3000 * depth + 3000) continue; // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 - && ss->staticEval + 172 + 145 * lmrDepth <= alpha) + && ss->staticEval + 172 + 145 * lmrDepth + history / 256 <= alpha) continue; // Prune moves with negative SEE (~20 Elo) From 9ee58dc7a75d7cdc09935f8782e927bb19700afe Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 24 Nov 2021 22:13:35 +0100 Subject: [PATCH 211/282] Update default net to nn-3678835b1d3d.nnue New net trained with nnue-pytorch, started from the master net on a data set of Leela (T60.binpack+T74.binpck) and Stockfish data (wrongIsRight_nodes5000pv2.binpack), available as a single interleaved binpack: https://drive.google.com/file/d/12uWZIA3F2cNbraAzQNb1jgf3tq_6HkTr/view?usp=sharing The nnue-pytorch branch used is https://github.com/vondele/nnue-pytorch/tree/wdl, which has the new feature to filter positions based on the likelihood of the current evaluation leading to the game outcome. It should make it less likely to try to learn from misevaluated positions. Standard options have been used, starting from the master net: --gpus 1 --threads 4 --num-workers 4 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 12 --features=HalfKAv2_hm^ --lambda=1.0 Testing with games shows neutral Elo at STC, and good performance at LTC: STC: https://tests.stockfishchess.org/tests/view/619eb597c0a4ea18ba95a4dc ELO: -0.44 +-1.8 (95%) LOS: 31.2% Total: 40000 W: 10447 L: 10498 D: 19055 Ptnml(0-2): 254, 4576, 10260, 4787, 123 LTC: https://tests.stockfishchess.org/tests/view/619f6e87c0a4ea18ba95a53f ELO: 3.30 +-1.8 (95%) LOS: 100.0% Total: 33062 W: 8560 L: 8246 D: 16256 Ptnml(0-2): 54, 3358, 9352, 3754, 13 passed LTC SPRT: https://tests.stockfishchess.org/tests/view/61a0864e8967bbf894416e65 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 29376 W: 7663 L: 7396 D: 14317 Ptnml(0-2): 67, 3017, 8205, 3380, 19 closes https://github.com/official-stockfish/Stockfish/pull/3808 Bench: 7011501 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index e2cdb210..e5340c7f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-13406b1dcbe0.nnue" + #define EvalFileDefaultName "nn-3678835b1d3d.nnue" namespace NNUE { From 4bb11e823fb292b8cc81ac873ccca9827af462be Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 26 Nov 2021 22:10:00 +0100 Subject: [PATCH 212/282] Tune NNUE scaling params passed STC: https://tests.stockfishchess.org/tests/view/61a156f89e83391467a2b2cc LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 22816 W: 5896 L: 5646 D: 11274 Ptnml(0-2): 55, 2567, 5961, 2723, 102 passed LTC: https://tests.stockfishchess.org/tests/view/61a1cf3d9e83391467a2b30b LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 17904 W: 4658 L: 4424 D: 8822 Ptnml(0-2): 6, 1821, 5079, 2025, 21 closes https://github.com/official-stockfish/Stockfish/pull/3811 Bench: 7218806 --- src/evaluate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c1d3d159..1dc701cf 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,9 +1091,9 @@ Value Eval::evaluate(const Position& pos) { v = Evaluation(pos).value(); // classical else { - int scale = 898 - + 24 * pos.count() - + 33 * pos.non_pawn_material() / 1024; + int scale = 1049 + + 8 * pos.count() + + 20 * pos.non_pawn_material() / 1024; Value nnue = NNUE::evaluate(pos, true); // NNUE Color stm = pos.side_to_move(); From 8bb5a436b240d22e3e9729a1cd094169941afd7c Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 27 Nov 2021 16:38:37 +0300 Subject: [PATCH 213/282] Adjust usage of history in futility pruning This patch refines 0ac8aca893dd2052f8433e0b4a3d65073266b00f that uses history heuristics in futility pruning. Now it adds main history of the move to in and also increases effect by factor of 2. passed STC https://tests.stockfishchess.org/tests/view/61a156829e83391467a2b2c9 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 68464 W: 17920 L: 17587 D: 32957 Ptnml(0-2): 239, 7711, 18025, 7992, 265 passed LTC https://tests.stockfishchess.org/tests/view/61a1bde99e83391467a2b305 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 26088 W: 6926 L: 6674 D: 12488 Ptnml(0-2): 18, 2619, 7531, 2845, 31 closes https://github.com/official-stockfish/Stockfish/pull/3812 bench 6804653 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 12c89de4..8f9fa62c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1063,10 +1063,12 @@ moves_loop: // When in check, search starts here && history < -3000 * depth + 3000) continue; + history += thisThread->mainHistory[us][from_to(move)]; + // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 - && ss->staticEval + 172 + 145 * lmrDepth + history / 256 <= alpha) + && ss->staticEval + 172 + 145 * lmrDepth + history / 128 <= alpha) continue; // Prune moves with negative SEE (~20 Elo) From af050e5eed43f2d360bc6d38a9d9ef64b6ce6ad8 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 28 Nov 2021 15:19:18 +0300 Subject: [PATCH 214/282] Refine futility pruning for parent nodes This patch is a result of refining of tuning vondele did after new net passed and some hand-made values adjustements - excluding changes in other pruning heuristics and rounding value of history divisor to the nearest power of 2. With this patch futility pruning becomes more aggressive and history influence on it is doubled again. passed STC https://tests.stockfishchess.org/tests/view/61a2c4c1a26505c2278c150d LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 33848 W: 8841 L: 8574 D: 16433 Ptnml(0-2): 100, 3745, 8988, 3970, 121 passed LTC https://tests.stockfishchess.org/tests/view/61a327ffa26505c2278c26d9 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 22272 W: 5856 L: 5614 D: 10802 Ptnml(0-2): 12, 2230, 6412, 2468, 14 closes https://github.com/official-stockfish/Stockfish/pull/3814 bench 6302543 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 8f9fa62c..807d2614 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1068,7 +1068,7 @@ moves_loop: // When in check, search starts here // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 - && ss->staticEval + 172 + 145 * lmrDepth + history / 128 <= alpha) + && ss->staticEval + 142 + 139 * lmrDepth + history / 64 <= alpha) continue; // Prune moves with negative SEE (~20 Elo) From e4a0c6c75950bf27b6dc32490a1102499643126b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 28 Nov 2021 19:26:10 +0100 Subject: [PATCH 215/282] Update default net to nn-4f56ecfca5b7.nnue New net trained with nnue-pytorch, started from a master net on a data set of Leela (T60.binpack+T74.binpck) Stockfish data (wrongIsRight_nodes5000pv2.binpack), and Michael Babigian's conversion of T60 Leela data (including TB7 rescoring) (farseer.binpack) available as a single interleaved binpack: https://drive.google.com/file/d/1_sQoWBl31WAxNXma2v45004CIVltytP8/view?usp=sharing The nnue-pytorch branch used is https://github.com/vondele/nnue-pytorch/tree/wdl passed STC: https://tests.stockfishchess.org/tests/view/61a3cc729f0c43dae1c71f1b LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 49152 W: 12842 L: 12544 D: 23766 Ptnml(0-2): 154, 5542, 12904, 5804, 172 passed LTC: https://tests.stockfishchess.org/tests/view/61a43c6260afd064f2d724f1 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 25528 W: 6676 L: 6425 D: 12427 Ptnml(0-2): 9, 2593, 7315, 2832, 15 closes https://github.com/official-stockfish/Stockfish/pull/3816 Bench: 6885242 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index e5340c7f..77c3b4e7 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-3678835b1d3d.nnue" + #define EvalFileDefaultName "nn-4f56ecfca5b7.nnue" namespace NNUE { From ca3c1c5f3a5f011edd04af856cdd59bdaefe423d Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Tue, 30 Nov 2021 09:22:07 +0800 Subject: [PATCH 216/282] Enable compilation on older Windows systems Improve compatibility of the last NUMA patch when running under older versions of Windows, for instance Windows Server 2003. Reported by user "g3g6" in the following comments: https://github.com/official-stockfish/Stockfish/commit/7218ec4df9fef1146a451b71f0ed3bfd8123c9f9 Closes https://github.com/official-stockfish/Stockfish/pull/3821 No functional change --- src/misc.cpp | 27 ++++++++++++++++----------- src/search.cpp | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 4dfa9f0c..294b7c8f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -37,6 +37,7 @@ typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP, typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY); typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); +typedef WORD(*fun5_t)(); } #endif @@ -514,7 +515,8 @@ int best_node(size_t idx) { if (!fun1) return -1; - // First call to get returnLength. We expect it to fail due to null buffer + // First call to GetLogicalProcessorInformationEx() to get returnLength. + // We expect the call to fail due to null buffer. if (fun1(RelationAll, nullptr, &returnLength)) return -1; @@ -522,7 +524,7 @@ int best_node(size_t idx) { SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength); - // Second call, now we expect to succeed + // Second call to GetLogicalProcessorInformationEx(), now we expect to succeed if (!fun1(RelationAll, buffer, &returnLength)) { free(buffer); @@ -582,23 +584,26 @@ void bindThisThread(size_t idx) { auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity"); auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2"); + auto fun5 = (fun5_t)(void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount"); if (!fun2 || !fun3) return; - if (!fun4) { + if (!fun4 || !fun5) + { GROUP_AFFINITY affinity; - if (fun2(node, &affinity)) - fun3(GetCurrentThread(), &affinity, nullptr); - } else { + if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx + fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity + } + else + { // If a numa node has more than one processor group, we assume they are // sized equal and we spread threads evenly across the groups. USHORT elements, returnedElements; - elements = GetMaximumProcessorGroupCount(); - GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc( - elements * sizeof(GROUP_AFFINITY)); - if (fun4(node, affinity, elements, &returnedElements)) - fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); + elements = fun5(); // GetMaximumProcessorGroupCount + GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc(elements * sizeof(GROUP_AFFINITY)); + if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2 + fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); // SetThreadGroupAffinity free(affinity); } } diff --git a/src/search.cpp b/src/search.cpp index 807d2614..6e41f48d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1063,7 +1063,7 @@ moves_loop: // When in check, search starts here && history < -3000 * depth + 3000) continue; - history += thisThread->mainHistory[us][from_to(move)]; + history += thisThread->mainHistory[us][from_to(move)]; // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck From 282644f1413db0ccb58e2e1793d5007c58ff8ce2 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Fri, 26 Nov 2021 08:03:15 +0100 Subject: [PATCH 217/282] Remove depth dependence and use same limit (2000) as stat_bonus STC: https://tests.stockfishchess.org/tests/view/619df59dc0a4ea18ba95a424 LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 83728 W: 21329 L: 21242 D: 41157 Ptnml(0-2): 297, 9669, 21847, 9752, 299 LTC: https://tests.stockfishchess.org/tests/view/619e64d7c0a4ea18ba95a475 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 79888 W: 20238 L: 20155 D: 39495 Ptnml(0-2): 57, 8391, 22980, 8444, 73 closes https://github.com/official-stockfish/Stockfish/pull/3806 bench: 6792010 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6e41f48d..c4ed7337 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -801,7 +801,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval), -1000, 1000); + int bonus = std::clamp(-16 * int((ss-1)->staticEval + ss->staticEval), -2000, 2000); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } From 64f21ecdae0402bfde4e9f4e61097c99bcae343e Mon Sep 17 00:00:00 2001 From: hengyu Date: Fri, 26 Nov 2021 18:25:03 +0800 Subject: [PATCH 218/282] Small clean-up remove unneeded calculation. closes https://github.com/official-stockfish/Stockfish/pull/3807 No functional change. --- src/nnue/evaluate_nnue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 6e40deab..bd473294 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -234,7 +234,7 @@ namespace Stockfish::Eval::NNUE { { buffer[1] = '0' + cp / 10000; cp %= 10000; buffer[2] = '0' + cp / 1000; cp %= 1000; - buffer[3] = '0' + cp / 100; cp %= 100; + buffer[3] = '0' + cp / 100; buffer[4] = ' '; } else if (cp >= 1000) From 4b86ef8c4f8755850b38f2eca026cb9da20c4d01 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Tue, 30 Nov 2021 21:01:34 +0100 Subject: [PATCH 219/282] Fix typos in comments, adjust readme closes https://github.com/official-stockfish/Stockfish/pull/3822 also adjusts readme as requested in https://github.com/official-stockfish/Stockfish/pull/3816 No functional change --- README.md | 8 ++++++-- src/nnue/nnue_common.h | 2 +- src/syzygy/tbprobe.cpp | 20 ++++++++++---------- src/syzygy/tbprobe.h | 2 +- src/tune.h | 2 +- tests/reprosearch.sh | 2 +- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 79db8170..330d19ed 100644 --- a/README.md +++ b/README.md @@ -175,8 +175,12 @@ on the evaluations of millions of positions at moderate search depth. The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. It can be evaluated efficiently on CPUs, and exploits the fact that only parts of the neural network need to be updated after a typical chess move. -[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional -tools to train and develop the NNUE networks. On CPUs supporting modern vector instructions +[The nodchip repository](https://github.com/nodchip/Stockfish) provided the first version of +the needed tools to train and develop the NNUE networks. Today, more advanced training tools are available +in [the nnue-pytorch repository](https://github.com/glinscott/nnue-pytorch/), while data generation tools +are available in [a dedicated branch](https://github.com/official-stockfish/Stockfish/tree/tools). + +On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation results in much stronger playing strength, even if the nodes per second computed by the engine is somewhat lower (roughly 80% of nps is typical). diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 75ac7862..74eaae17 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -109,7 +109,7 @@ namespace Stockfish::Eval::NNUE { // write_little_endian() is our utility to write an integer (signed or unsigned, any size) // to a stream in little-endian order. We swap the byte order before the write if - // necessary to always write in little endian order, independantly of the byte + // necessary to always write in little endian order, independently of the byte // ordering of the compiling machine. template inline void write_little_endian(std::ostream& stream, IntType value) { diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 96b2970f..41e867c0 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -769,7 +769,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu goto encode_remaining; // With pawns we have finished special treatments } - // In positions withouth pawns, we further flip the squares to ensure leading + // In positions without pawns, we further flip the squares to ensure leading // piece is below RANK_5. if (rank_of(squares[0]) > RANK_4) for (int i = 0; i < size; ++i) @@ -812,7 +812,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be // swapped and still get the same position.) // - // In case we have at least 3 unique pieces (inlcuded kings) we encode them + // In case we have at least 3 unique pieces (included kings) we encode them // together. if (entry->hasUniquePieces) { @@ -827,7 +827,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu + (squares[1] - adjust1)) * 62 + squares[2] - adjust2; - // First piece is on a1-h8 diagonal, second below: map this occurence to + // First piece is on a1-h8 diagonal, second below: map this occurrence to // 6 to differentiate from the above case, rank_of() maps a1-d4 diagonal // to 0...3 and finally MapB1H1H7[] maps the b1-h1-h7 triangle to 0..27. else if (off_A1H8(squares[1])) @@ -857,7 +857,7 @@ encode_remaining: idx *= d->groupIdx[0]; Square* groupSq = squares + d->groupLen[0]; - // Encode remainig pawns then pieces according to square, in ascending order + // Encode remaining pawns then pieces according to square, in ascending order bool remainingPawns = entry->hasPawns && entry->pawnCount[1]; while (d->groupLen[++next]) @@ -885,7 +885,7 @@ encode_remaining: // Group together pieces that will be encoded together. The general rule is that // a group contains pieces of same type and color. The exception is the leading -// group that, in case of positions withouth pawns, can be formed by 3 different +// group that, in case of positions without pawns, can be formed by 3 different // pieces (default) or by the king pair when there is not a unique piece apart // from the kings. When there are pawns, pawns are always first in pieces[]. // @@ -917,7 +917,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) { // // This ensures unique encoding for the whole position. The order of the // groups is a per-table parameter and could not follow the canonical leading - // pawns/pieces -> remainig pawns -> remaining pieces. In particular the + // pawns/pieces -> remaining pawns -> remaining pieces. In particular the // first group is at order[0] position and the remaining pawns, when present, // are at order[1] position. bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides @@ -937,7 +937,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) { d->groupIdx[1] = idx; idx *= Binomial[d->groupLen[1]][48 - d->groupLen[0]]; } - else // Remainig pieces + else // Remaining pieces { d->groupIdx[next] = idx; idx *= Binomial[d->groupLen[next]][freeSquares]; @@ -947,7 +947,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) { d->groupIdx[n] = idx; } -// In Recursive Pairing each symbol represents a pair of childern symbols. So +// In Recursive Pairing each symbol represents a pair of children symbols. So // read d->btree[] symbols data and expand each one in his left and right child // symbol until reaching the leafs that represent the symbol value. uint8_t set_symlen(PairsData* d, Sym s, std::vector& visited) { @@ -1317,7 +1317,7 @@ void Tablebases::init(const std::string& paths) { for (auto p : bothOnDiagonal) MapKK[p.first][p.second] = code++; - // Binomial[] stores the Binomial Coefficents using Pascal rule. There + // Binomial[] stores the Binomial Coefficients using Pascal rule. There // are Binomial[k][n] ways to choose k elements from a set of n elements. Binomial[0][0] = 1; @@ -1337,7 +1337,7 @@ void Tablebases::init(const std::string& paths) { for (int leadPawnsCnt = 1; leadPawnsCnt <= 5; ++leadPawnsCnt) for (File f = FILE_A; f <= FILE_D; ++f) { - // Restart the index at every file because TB table is splitted + // Restart the index at every file because TB table is split // by file, so we can reuse the same index for different files. int idx = 0; diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 56734af9..cf61b767 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -38,7 +38,7 @@ enum WDLScore { // Possible states after a probing operation enum ProbeState { FAIL = 0, // Probe failed (missing file table) - OK = 1, // Probe succesful + OK = 1, // Probe successful CHANGE_STM = -1, // DTZ should check the other side ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move) }; diff --git a/src/tune.h b/src/tune.h index b5c715b3..53d52a65 100644 --- a/src/tune.h +++ b/src/tune.h @@ -84,7 +84,7 @@ class Tune { static Tune& instance() { static Tune t; return t; } // Singleton - // Use polymorphism to accomodate Entry of different types in the same vector + // Use polymorphism to accommodate Entry of different types in the same vector struct EntryBase { virtual ~EntryBase() = default; virtual void init_option() = 0; diff --git a/tests/reprosearch.sh b/tests/reprosearch.sh index c1167f7f..e16ba4ae 100755 --- a/tests/reprosearch.sh +++ b/tests/reprosearch.sh @@ -43,7 +43,7 @@ cat << EOF > repeat.exp expect eof EOF -# to increase the likelyhood of finding a non-reproducible case, +# to increase the likelihood of finding a non-reproducible case, # the allowed number of nodes are varied systematically for i in `seq 1 20` do From 95a2ac1e073a6f7c3af8aeef2fc4fe8cbd6650cf Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 29 Nov 2021 09:30:07 -0300 Subject: [PATCH 220/282] Simplify reduction on rootNode when bestMoveChanges is high The reduction introduced in #3736 also consider on rootNode, so we don't have to reduce again. STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 28736 W: 7494 L: 7329 D: 13913 Ptnml(0-2): 95, 3247, 7503, 3444, 79 https://tests.stockfishchess.org/tests/view/61a3abe01b7fdf52228e74d8 LTC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 47816 W: 12434 L: 12308 D: 23074 Ptnml(0-2): 37, 4972, 13755, 5116, 28 https://tests.stockfishchess.org/tests/view/61a3c3e39f0c43dae1c71d71 closes https://github.com/official-stockfish/Stockfish/pull/3817 bench: 6331638 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c4ed7337..70dc4b21 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1185,8 +1185,8 @@ moves_loop: // When in check, search starts here && !likelyFailLow) r -= 2; - // Increase reduction at root and non-PV nodes when the best move does not change frequently - if ( (rootNode || !PvNode) + // Increase reduction at non-PV nodes when the best move does not change frequently + if ( !PvNode && thisThread->bestMoveChanges <= 2) r++; From c1f9a359e8e319d832ee5a55277dab996dd29d25 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 29 Nov 2021 14:51:35 -0300 Subject: [PATCH 221/282] Correctly reset bestMoveChanges for searches not using time management (e.g. analysis, fixed node game play etc), bestMoveChanges was not reset during search iterations. As LMR uses this quantity, search was somewhat weaker. Tested using fixed node playing games: ``` ./c-chess-cli -each nodes=10000 option.Hash=16 -engine cmd=../Stockfish/src/fix -engine cmd=../Stockfish/src/master -concurrency 6 -openings file=../books/UHO_XXL_+0.90_+1.19.epd -games 10000 Score of Stockfish Fix vs Stockfish Master: 3187 - 3028 - 3785 [0.508] 10000 ./c-chess-cli -each nodes=30000 option.Hash=16 -engine cmd=../Stockfish/src/fix -engine cmd=../Stockfish/src/master -concurrency 6 -openings file=../books/UHO_XXL_+0.90_+1.19.epd -games 10000 Score of Stockfish Fix vs Stockfish Master: 2946 - 2834 - 4220 [0.506] 10000 ``` closes https://github.com/official-stockfish/Stockfish/pull/3818 bench: 5061979 --- src/search.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 70dc4b21..c926c1d2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -477,6 +477,13 @@ void Thread::search() { if (skill.enabled() && skill.time_to_pick(rootDepth)) skill.pick_best(multiPV); + // Use part of the gained time from a previous stable move for the current move + for (Thread* th : Threads) + { + totBestMoveChanges += th->bestMoveChanges; + th->bestMoveChanges = 0; + } + // Do we have time for the next iteration? Can we stop searching now? if ( Limits.use_time_management() && !Threads.stop @@ -489,13 +496,6 @@ void Thread::search() { // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.92 : 0.95; double reduction = (1.47 + mainThread->previousTimeReduction) / (2.32 * timeReduction); - - // Use part of the gained time from a previous stable move for the current move - for (Thread* th : Threads) - { - totBestMoveChanges += th->bestMoveChanges; - th->bestMoveChanges = 0; - } double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; From c9977aa0a89c83bf21651bffd3b6f10c344ccc46 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Wed, 1 Dec 2021 23:36:14 +0100 Subject: [PATCH 222/282] Add AVX-VNNI support for Alder Lake and later. In their infinite wisdom, Intel axed AVX512 from Alder Lake chips (well, not entirely, but we kind of want to use the Gracemont cores for chess!) but still added VNNI support. Confusingly enough, this is not the same as VNNI256 support. This adds a specific AVX-VNNI target that will use this AVX-VNNI mode, by prefixing the VNNI instructions with the appropriate VEX prefix, and avoiding AVX512 usage. This is about 1% faster on P cores: Result of 20 runs ================== base (./clang-bmi2 ) = 3306337 +/- 7519 test (./clang-vnni ) = 3344226 +/- 7388 diff = +37889 +/- 4153 speedup = +0.0115 P(speedup > 0) = 1.0000 But a nice 3% faster on E cores: Result of 20 runs ================== base (./clang-bmi2 ) = 1938054 +/- 28257 test (./clang-vnni ) = 1994606 +/- 31756 diff = +56552 +/- 3735 speedup = +0.0292 P(speedup > 0) = 1.0000 This was measured on Clang 13. GCC 11.2 appears to generate worse code for Alder Lake, though the speedup on the E cores is similar. It is possible to run the engine specifically on the P or E using binding, for example in linux it is possible to use (for an 8 P + 8 E setup like i9-12900K): taskset -c 0-15 ./stockfish taskset -c 16-23 ./stockfish where the first call binds to the P-cores and the second to the E-cores. closes https://github.com/official-stockfish/Stockfish/pull/3824 No functional change --- src/Makefile | 26 ++++++++++++++++++++++++-- src/simd.h | 13 ++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5c52661b..a9333a22 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,6 +78,7 @@ endif # ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3 # sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 +# avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 # vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256 # vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 @@ -100,8 +101,8 @@ endif # explicitly check for the list of supported architectures (as listed with make help), # the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true` ifeq ($(ARCH), $(filter $(ARCH), \ - x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \ - x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ + x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ + x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ armv7 armv7-neon armv8 apple-silicon general-64 general-32)) SUPPORTED_ARCH=true @@ -122,6 +123,7 @@ sse2 = no ssse3 = no sse41 = no avx2 = no +avxvnni = no avx512 = no vnni256 = no vnni512 = no @@ -192,6 +194,17 @@ ifeq ($(findstring -avx2,$(ARCH)),-avx2) avx2 = yes endif +ifeq ($(findstring -avxvnni,$(ARCH)),-avxvnni) + popcnt = yes + sse = yes + sse2 = yes + ssse3 = yes + sse41 = yes + avx2 = yes + avxvnni = yes + pext = yes +endif + ifeq ($(findstring -bmi2,$(ARCH)),-bmi2) popcnt = yes sse = yes @@ -544,6 +557,13 @@ ifeq ($(avx2),yes) endif endif +ifeq ($(avxvnni),yes) + CXXFLAGS += -DUSE_VNNI -DUSE_AVXVNNI + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mavxvnni + endif +endif + ifeq ($(avx512),yes) CXXFLAGS += -DUSE_AVX512 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) @@ -689,6 +709,7 @@ help: @echo "x86-64-vnni512 > x86 64-bit with vnni support 512bit wide" @echo "x86-64-vnni256 > x86 64-bit with vnni support 256bit wide" @echo "x86-64-avx512 > x86 64-bit with avx512 support" + @echo "x86-64-avxvnni > x86 64-bit with avxvnni support" @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support" @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" @@ -837,6 +858,7 @@ config-sanity: net @echo "ssse3: '$(ssse3)'" @echo "sse41: '$(sse41)'" @echo "avx2: '$(avx2)'" + @echo "avxvnni: '$(avxvnni)'" @echo "avx512: '$(avx512)'" @echo "vnni256: '$(vnni256)'" @echo "vnni512: '$(vnni512)'" diff --git a/src/simd.h b/src/simd.h index 584148f1..1ac98067 100644 --- a/src/simd.h +++ b/src/simd.h @@ -46,6 +46,13 @@ #define USE_INLINE_ASM #endif +// Use either the AVX512 or AVX-VNNI version of the VNNI instructions. +#if defined(USE_AVXVNNI) +#define VNNI_PREFIX "%{vex%} " +#else +#define VNNI_PREFIX "" +#endif + namespace Stockfish::Simd { #if defined (USE_AVX512) @@ -208,7 +215,7 @@ namespace Stockfish::Simd { # if defined (USE_VNNI) # if defined (USE_INLINE_ASM) asm( - "vpdpbusd %[b], %[a], %[acc]\n\t" + VNNI_PREFIX "vpdpbusd %[b], %[a], %[acc]\n\t" : [acc]"+v"(acc) : [a]"v"(a), [b]"vm"(b) ); @@ -240,8 +247,8 @@ namespace Stockfish::Simd { # if defined (USE_VNNI) # if defined (USE_INLINE_ASM) asm( - "vpdpbusd %[b0], %[a0], %[acc]\n\t" - "vpdpbusd %[b1], %[a1], %[acc]\n\t" + VNNI_PREFIX "vpdpbusd %[b0], %[a0], %[acc]\n\t" + VNNI_PREFIX "vpdpbusd %[b1], %[a1], %[acc]\n\t" : [acc]"+v"(acc) : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) ); From e4b7403f127a36a35bbace9f833ab43babd98a6c Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 2 Dec 2021 23:41:23 +0300 Subject: [PATCH 223/282] Do more aggressive pruning for some node types This patch allows more aggressive futility/see based pruning for PV nodes with low delta and non-pv nodes. Fixes some white space issues. Passed STC https://tests.stockfishchess.org/tests/view/61a5ed33d16c530b5dcc27cc LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 182088 W: 47121 L: 46584 D: 88383 Ptnml(0-2): 551, 20687, 48037, 21212, 557 Passed LTC https://tests.stockfishchess.org/tests/view/61a74dfdbd5c4360bcded0ac LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 87136 W: 22494 L: 22103 D: 42539 Ptnml(0-2): 38, 8918, 25272, 9295, 45 closes https://github.com/official-stockfish/Stockfish/pull/3828 closes https://github.com/official-stockfish/Stockfish/pull/3829 bench 4332259 --- src/misc.cpp | 8 ++++---- src/misc.h | 2 +- src/search.cpp | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 294b7c8f..b46786df 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -515,7 +515,7 @@ int best_node(size_t idx) { if (!fun1) return -1; - // First call to GetLogicalProcessorInformationEx() to get returnLength. + // First call to GetLogicalProcessorInformationEx() to get returnLength. // We expect the call to fail due to null buffer. if (fun1(RelationAll, nullptr, &returnLength)) return -1; @@ -589,13 +589,13 @@ void bindThisThread(size_t idx) { if (!fun2 || !fun3) return; - if (!fun4 || !fun5) + if (!fun4 || !fun5) { GROUP_AFFINITY affinity; if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity - } - else + } + else { // If a numa node has more than one processor group, we assume they are // sized equal and we spread threads evenly across the groups. diff --git a/src/misc.h b/src/misc.h index c1744130..062b420a 100644 --- a/src/misc.h +++ b/src/misc.h @@ -141,7 +141,7 @@ private: /// sigmoid(t, x0, y0, C, P, Q) implements a sigmoid-like function using only integers, /// with the following properties: -/// +/// /// - sigmoid is centered in (x0, y0) /// - sigmoid has amplitude [-P/Q , P/Q] instead of [-1 , +1] /// - limit is (y0 - P/Q) when t tends to -infinity diff --git a/src/search.cpp b/src/search.cpp index c926c1d2..5e022e66 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1065,6 +1065,8 @@ moves_loop: // When in check, search starts here history += thisThread->mainHistory[us][from_to(move)]; + lmrDepth = std::max(0, lmrDepth - (beta - alpha < thisThread->rootDelta / 4)); + // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 From 327060232a3ded6ed19113f234a2f9ba9bf65b02 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 2 Dec 2021 06:58:40 +0100 Subject: [PATCH 224/282] Update default net to nn-cdf1785602d6.nnue Same process as in https://github.com/official-stockfish/Stockfish/commit/e4a0c6c75950bf27b6dc32490a1102499643126b with the training started from the current master net. passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 38224 W: 10023 L: 9742 D: 18459 Ptnml(0-2): 133, 4328, 9940, 4547, 164 https://tests.stockfishchess.org/tests/view/61a8611e4ed77d629d4e836e passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 115176 W: 29783 L: 29321 D: 56072 Ptnml(0-2): 68, 12039, 32936, 12453, 92 https://tests.stockfishchess.org/tests/view/61a8963e4ed77d629d4e8d9b closes https://github.com/official-stockfish/Stockfish/pull/3830 Bench: 4829419 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 77c3b4e7..873a72dc 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-4f56ecfca5b7.nnue" + #define EvalFileDefaultName "nn-cdf1785602d6.nnue" namespace NNUE { From a6a9d828ab4cc35ca0f64207e2ff818a391d1939 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 4 Dec 2021 09:09:09 -0300 Subject: [PATCH 225/282] Simplifies bestMoveChanges from LMR As bestMoveChanges is only reset on mainThread and it could change how other threads search, a multi-threads test was made. STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 146776 W: 37934 L: 37941 D: 70901 Ptnml(0-2): 477, 15644, 41173, 15597, 497 https://tests.stockfishchess.org/tests/view/61a8f9f34ed77d629d4ea2d6 LTC: LLR: 3.11 (-2.94,2.94) <-2.25,0.25> Total: 114040 W: 29314 L: 29269 D: 55457 Ptnml(0-2): 50, 10584, 35722, 10599, 65 https://tests.stockfishchess.org/tests/view/61a9d4bf9e8855bba1a35c4f (SMP, 8 threads) STC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 23888 W: 6308 L: 6143 D: 11437 Ptnml(0-2): 36, 2557, 6600, 2708, 43 https://tests.stockfishchess.org/tests/view/61ac27a756fcf33bce7d3677 closes https://github.com/official-stockfish/Stockfish/pull/3831 bench: 4829419 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5e022e66..28e16609 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1187,9 +1187,8 @@ moves_loop: // When in check, search starts here && !likelyFailLow) r -= 2; - // Increase reduction at non-PV nodes when the best move does not change frequently - if ( !PvNode - && thisThread->bestMoveChanges <= 2) + // Increase reduction at non-PV nodes + if (!PvNode) r++; // Decrease reduction if opponent's move count is high (~1 Elo) From 18f2b12cd019212b9c7103cc63e3bf64a5be7c3c Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 5 Dec 2021 16:18:33 +0100 Subject: [PATCH 226/282] Tweak time management Use for adjustment of the falling eval time factor now also the difference between previous best average score and current best score. STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 109216 W: 28296 L: 27900 D: 53020 Ptnml(0-2): 312, 11759, 30148, 11999, 390 https://tests.stockfishchess.org/tests/view/61aafa8d1b31b85bcfa29d9c LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 54096 W: 14091 L: 13787 D: 26218 Ptnml(0-2): 29, 5124, 16447, 5410, 38 https://tests.stockfishchess.org/tests/view/61abbbbd56fcf33bce7d1d64 closes https://github.com/official-stockfish/Stockfish/pull/3833 Bench: 4829419 --- src/search.cpp | 4 +++- src/thread.cpp | 1 + src/thread.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 28e16609..99c47431 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -260,6 +260,7 @@ void MainThread::search() { bestThread = Threads.get_best_thread(); bestPreviousScore = bestThread->rootMoves[0].score; + bestPreviousAverageScore = bestThread->rootMoves[0].averageScore; // Send again PV info if we have a new best thread if (bestThread != this) @@ -489,7 +490,8 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (318 + 6 * (mainThread->bestPreviousScore - bestValue) + double fallingEval = (142 + 6 * (mainThread->bestPreviousScore - bestValue) + + 6 * (mainThread->bestPreviousAverageScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; fallingEval = std::clamp(fallingEval, 0.5, 1.5); diff --git a/src/thread.cpp b/src/thread.cpp index da8e1d05..c03079d1 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -162,6 +162,7 @@ void ThreadPool::clear() { main()->callsCnt = 0; main()->bestPreviousScore = VALUE_INFINITE; + main()->bestPreviousAverageScore = VALUE_INFINITE; main()->previousTimeReduction = 1.0; } diff --git a/src/thread.h b/src/thread.h index cd206faa..37c6452b 100644 --- a/src/thread.h +++ b/src/thread.h @@ -95,6 +95,7 @@ struct MainThread : public Thread { double previousTimeReduction; Value bestPreviousScore; + Value bestPreviousAverageScore; Value iterValue[4]; int callsCnt; bool stopOnPonderhit; From 7d44b43b3ceb2eebc756709432a0e291f885a1d2 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 5 Dec 2021 16:29:51 +0100 Subject: [PATCH 227/282] Tweak history initialization Initialize continuation history with a slighlty negative value -71 instead of zero. The idea is, because the most history entries will be later negative anyway, to shift the starting values a little bit in the "correct" direction. Of course the effect of initialization dimishes with greater depth so I had the apprehension that the LTC test would be difficult to pass, but it passed. STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 34520 W: 9076 L: 8803 D: 16641 Ptnml(0-2): 136, 3837, 9047, 4098, 142 https://tests.stockfishchess.org/tests/view/61aa52e39e8855bba1a3776b LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 75568 W: 19620 L: 19254 D: 36694 Ptnml(0-2): 44, 7773, 21796, 8115, 56 https://tests.stockfishchess.org/tests/view/61aa87d39e8855bba1a383a5 closes https://github.com/official-stockfish/Stockfish/pull/3834 Bench: 4674029 --- src/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thread.cpp b/src/thread.cpp index c03079d1..ed3accc5 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -67,7 +67,7 @@ void Thread::clear() { { for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(0); + h->fill(-71); continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); } } From a3d425cf55494a4f86dc52a3d3b24c1657cbbc61 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 6 Dec 2021 03:52:44 +0300 Subject: [PATCH 228/282] Assign extra bonus for previous move that caused a fail low more often This patch allows to assign extra bonus for previous move that caused a fail low not only for PvNodes and cutNodes but also fo some allNodes - namely if the best result we could've got from the search is still far below alpha. passed STC https://tests.stockfishchess.org/tests/view/61aa26a49e8855bba1a36d96 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 73808 W: 19183 L: 18842 D: 35783 Ptnml(0-2): 251, 8257, 19564, 8564, 268 passed LTC https://tests.stockfishchess.org/tests/view/61aa7dc29e8855bba1a3814f LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 142416 W: 36717 L: 36192 D: 69507 Ptnml(0-2): 106, 14799, 40862, 15346, 95 closes https://github.com/official-stockfish/Stockfish/pull/3835 bench 4724181 --- src/search.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 99c47431..edeed9c0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1380,7 +1380,15 @@ moves_loop: // When in check, search starts here // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + (PvNode || cutNode))); + { + //Assign extra bonus if current node is PvNode or cutNode + //or fail low was really bad + bool extraBonus = PvNode + || cutNode + || bestValue < alpha - 94 * depth; + + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); + } if (PvNode) bestValue = std::min(bestValue, maxValue); From b82d93ece484f833c994b40d9eddd959ba20ef92 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 4 Dec 2021 23:18:46 +0100 Subject: [PATCH 229/282] Update default net to nn-63376713ba63.nnue. same data set as previous trained nets, tuned the wdl model slightly for training. https://github.com/vondele/nnue-pytorch/tree/wdlTweak1 passed STC: https://tests.stockfishchess.org/tests/view/61abe9e456fcf33bce7d2834 LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 31720 W: 8385 L: 8119 D: 15216 Ptnml(0-2): 117, 3534, 8273, 3838, 98 passed LTC: https://tests.stockfishchess.org/tests/view/61ac293756fcf33bce7d36cf LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 136136 W: 35255 L: 34741 D: 66140 Ptnml(0-2): 114, 14217, 38894, 14727, 116 closes https://github.com/official-stockfish/Stockfish/pull/3836 Bench: 4667742 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 873a72dc..87cb65f3 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-cdf1785602d6.nnue" + #define EvalFileDefaultName "nn-63376713ba63.nnue" namespace NNUE { From 4766dfc3956f78d853c5e0c4636d6f90fd93df9a Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 2 Dec 2021 12:29:11 +0100 Subject: [PATCH 230/282] Optimize FT activation and affine transform for NEON. This patch optimizes the NEON implementation in two ways. The activation layer after the feature transformer is rewritten to make it easier for the compiler to see through dependencies and unroll. This in itself is a minimal, but a positive improvement. Other architectures could benefit from this too in the future. This is not an algorithmic change. The affine transform for large matrices (first layer after FT) on NEON now utilizes the same optimized code path as >=SSSE3, which makes the memory accesses more sequential and makes better use of the available registers, which allows for code that has longer dependency chains. Benchmarks from Redshift#161, profile-build with apple clang george@Georges-MacBook-Air nets % ./stockfish-b82d93 bench 2>&1 | tail -4 (current master) =========================== Total time (ms) : 2167 Nodes searched : 4667742 Nodes/second : 2154011 george@Georges-MacBook-Air nets % ./stockfish-7377b8 bench 2>&1 | tail -4 (this patch) =========================== Total time (ms) : 1842 Nodes searched : 4667742 Nodes/second : 2534061 This is a solid 18% improvement overall, larger in a bench with NNUE-only, not mixed. Improvement is also observed on armv7-neon (Raspberry Pi, and older phones), around 5% speedup. No changes for architectures other than NEON. closes https://github.com/official-stockfish/Stockfish/pull/3837 No functional changes. --- src/Makefile | 8 +++- src/nnue/layers/affine_transform.h | 64 +++++++++++++++++------------ src/nnue/nnue_feature_transformer.h | 13 ++++-- src/simd.h | 39 ++++++++++++++++++ 4 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/Makefile b/src/Makefile index a9333a22..3cf97873 100644 --- a/src/Makefile +++ b/src/Makefile @@ -128,6 +128,7 @@ avx512 = no vnni256 = no vnni512 = no neon = no +arm_version = 0 STRIP = strip ### 2.2 Architecture specific @@ -275,6 +276,7 @@ ifeq ($(ARCH),armv7) arch = armv7 prefetch = yes bits = 32 + arm_version = 7 endif ifeq ($(ARCH),armv7-neon) @@ -283,6 +285,7 @@ ifeq ($(ARCH),armv7-neon) popcnt = yes neon = yes bits = 32 + arm_version = 7 endif ifeq ($(ARCH),armv8) @@ -290,6 +293,7 @@ ifeq ($(ARCH),armv8) prefetch = yes popcnt = yes neon = yes + arm_version = 8 endif ifeq ($(ARCH),apple-silicon) @@ -297,6 +301,7 @@ ifeq ($(ARCH),apple-silicon) prefetch = yes popcnt = yes neon = yes + arm_version = 8 endif ifeq ($(ARCH),ppc-32) @@ -614,7 +619,7 @@ ifeq ($(mmx),yes) endif ifeq ($(neon),yes) - CXXFLAGS += -DUSE_NEON + CXXFLAGS += -DUSE_NEON=$(arm_version) ifeq ($(KERNEL),Linux) ifneq ($(COMP),ndk) ifneq ($(arch),armv8) @@ -863,6 +868,7 @@ config-sanity: net @echo "vnni256: '$(vnni256)'" @echo "vnni512: '$(vnni512)'" @echo "neon: '$(neon)'" + @echo "arm_version: '$(arm_version)'" @echo "" @echo "Flags:" @echo "CXX: $(CXX)" diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index b2871278..11038d69 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -75,8 +75,7 @@ namespace Stockfish::Eval::NNUE::Layers { const auto inputVector = reinterpret_cast(input); # elif defined(USE_NEON) - static_assert(PaddedInputDimensions % 16 == 0); - constexpr IndexType NumChunks = PaddedInputDimensions / 16; + constexpr IndexType NumChunks = (InputDimensions + 15) / 16; const auto inputVector = reinterpret_cast(input); # endif @@ -181,6 +180,9 @@ namespace Stockfish::Eval::NNUE::Layers { #elif defined (USE_SSSE3) static constexpr const IndexType InputSimdWidth = 16; static constexpr const IndexType MaxNumOutputRegs = 8; +#elif defined (USE_NEON) + static constexpr const IndexType InputSimdWidth = 8; + static constexpr const IndexType MaxNumOutputRegs = 8; #else // The fallback implementation will not have permuted weights. // We define these to avoid a lot of ifdefs later. @@ -270,52 +272,64 @@ namespace Stockfish::Eval::NNUE::Layers { OutputType* output = reinterpret_cast(buffer); #if defined (USE_AVX512) - using vec_t = __m512i; - #define vec_setzero _mm512_setzero_si512 - #define vec_set_32 _mm512_set1_epi32 - #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 + using acc_vec_t = __m512i; + using bias_vec_t = __m128i; + using weight_vec_t = __m512i; + using in_vec_t = __m512i; + #define vec_zero _mm512_setzero_si512() #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2 #define vec_hadd Simd::m512_hadd #define vec_haddx4 Simd::m512_haddx4 #elif defined (USE_AVX2) - using vec_t = __m256i; - #define vec_setzero _mm256_setzero_si256 - #define vec_set_32 _mm256_set1_epi32 - #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + using acc_vec_t = __m256i; + using bias_vec_t = __m128i; + using weight_vec_t = __m256i; + using in_vec_t = __m256i; + #define vec_zero _mm256_setzero_si256() #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 #define vec_hadd Simd::m256_hadd #define vec_haddx4 Simd::m256_haddx4 #elif defined (USE_SSSE3) - using vec_t = __m128i; - #define vec_setzero _mm_setzero_si128 - #define vec_set_32 _mm_set1_epi32 - #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + using acc_vec_t = __m128i; + using bias_vec_t = __m128i; + using weight_vec_t = __m128i; + using in_vec_t = __m128i; + #define vec_zero _mm_setzero_si128() #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 #define vec_hadd Simd::m128_hadd #define vec_haddx4 Simd::m128_haddx4 +#elif defined (USE_NEON) + using acc_vec_t = int32x4_t; + using bias_vec_t = int32x4_t; + using weight_vec_t = int8x8_t; + using in_vec_t = int8x8_t; + #define vec_zero {0} + #define vec_add_dpbusd_32x2 Simd::neon_m128_add_dpbusd_epi32x2 + #define vec_hadd Simd::neon_m128_hadd + #define vec_haddx4 Simd::neon_m128_haddx4 #endif -#if defined (USE_SSSE3) - const vec_t* invec = reinterpret_cast(input); +#if defined (USE_SSSE3) || defined (USE_NEON) + const in_vec_t* invec = reinterpret_cast(input); // Perform accumulation to registers for each big block for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock) { - vec_t acc[NumOutputRegs] = { vec_setzero() }; + acc_vec_t acc[NumOutputRegs] = { vec_zero }; // Each big block has NumOutputRegs small blocks in each "row", one per register. // We process two small blocks at a time to save on one addition without VNNI. for (IndexType smallBlock = 0; smallBlock < NumSmallBlocksPerOutput; smallBlock += 2) { - const vec_t* weightvec = - reinterpret_cast( + const weight_vec_t* weightvec = + reinterpret_cast( weights + bigBlock * BigBlockSize + smallBlock * SmallBlockSize * NumOutputRegs); - const vec_t in0 = invec[smallBlock + 0]; - const vec_t in1 = invec[smallBlock + 1]; + const in_vec_t in0 = invec[smallBlock + 0]; + const in_vec_t in1 = invec[smallBlock + 1]; for (IndexType k = 0; k < NumOutputRegs; ++k) vec_add_dpbusd_32x2(acc[k], in0, weightvec[k], in1, weightvec[k + NumOutputRegs]); @@ -324,8 +338,8 @@ namespace Stockfish::Eval::NNUE::Layers { // Horizontally add all accumulators. if constexpr (NumOutputRegs % 4 == 0) { - __m128i* outputvec = reinterpret_cast<__m128i*>(output); - const __m128i* biasvec = reinterpret_cast(biases); + bias_vec_t* outputvec = reinterpret_cast(output); + const bias_vec_t* biasvec = reinterpret_cast(biases); for (IndexType k = 0; k < NumOutputRegs; k += 4) { @@ -343,9 +357,7 @@ namespace Stockfish::Eval::NNUE::Layers { } } -# undef vec_setzero -# undef vec_set_32 -# undef vec_add_dpbusd_32 +# undef vec_zero # undef vec_add_dpbusd_32x2 # undef vec_hadd # undef vec_haddx4 diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 0297b323..4f6a174a 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -336,10 +336,17 @@ namespace Stockfish::Eval::NNUE { { const IndexType offset = HalfDimensions * p; const auto out = reinterpret_cast(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) + + constexpr IndexType UnrollFactor = 16; + static_assert(UnrollFactor % UnrollFactor == 0); + for (IndexType j = 0; j < NumChunks; j += UnrollFactor) { - int16x8_t sum = reinterpret_cast(accumulation[perspectives[p]])[j]; - out[j] = vmax_s8(vqmovn_s16(sum), Zero); + int16x8_t sums[UnrollFactor]; + for (IndexType i = 0; i < UnrollFactor; ++i) + sums[i] = reinterpret_cast(accumulation[perspectives[p]])[j+i]; + + for (IndexType i = 0; i < UnrollFactor; ++i) + out[j+i] = vmax_s8(vqmovn_s16(sums[i]), Zero); } } return psqt; diff --git a/src/simd.h b/src/simd.h index 1ac98067..ffa54d96 100644 --- a/src/simd.h +++ b/src/simd.h @@ -343,6 +343,45 @@ namespace Stockfish::Simd { #endif +#if defined (USE_NEON) + + [[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) { +# if USE_NEON >= 8 + return vaddvq_s32(s); +# else + return s[0] + s[1] + s[2] + s[3]; +# endif + } + + [[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) { + return neon_m128_reduce_add_epi32(sum) + bias; + } + + [[maybe_unused]] static int32x4_t neon_m128_haddx4( + int32x4_t sum0, int32x4_t sum1, int32x4_t sum2, int32x4_t sum3, + int32x4_t bias) { + + int32x4_t hsums { + neon_m128_reduce_add_epi32(sum0), + neon_m128_reduce_add_epi32(sum1), + neon_m128_reduce_add_epi32(sum2), + neon_m128_reduce_add_epi32(sum3) + }; + return vaddq_s32(hsums, bias); + } + + [[maybe_unused]] static void neon_m128_add_dpbusd_epi32x2( + int32x4_t& acc, + int8x8_t a0, int8x8_t b0, + int8x8_t a1, int8x8_t b1) { + + int16x8_t product = vmull_s8(a0, b0); + product = vmlal_s8(product, a1, b1); + acc = vpadalq_s16(acc, product); + } + +#endif + } #endif // STOCKFISH_SIMD_H_INCLUDED From c228f3196ae9965250f317c8784012c2485228c4 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 7 Dec 2021 19:46:21 +0300 Subject: [PATCH 231/282] Introduce post-lmr extensions This idea is somewhat similar to extentions in LMR but has a different flavour. If result of LMR was really good - thus exceeded alpha by some pretty big given margin, we can extend move after LMR in full depth search with 0 window. The idea is that this move is probably a fail high with somewhat of a big probability so extending it makes a lot of sense passed STC https://tests.stockfishchess.org/tests/view/61ad45ea56fcf33bce7d74b7 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 59680 W: 15531 L: 15215 D: 28934 Ptnml(0-2): 193, 6711, 15734, 6991, 211 passed LTC https://tests.stockfishchess.org/tests/view/61ad9ff356fcf33bce7d8646 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 59104 W: 15321 L: 14992 D: 28791 Ptnml(0-2): 53, 6023, 17065, 6364, 47 closes https://github.com/official-stockfish/Stockfish/pull/3838 bench 4881329 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index edeed9c0..f3a96ae2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1165,6 +1165,8 @@ moves_loop: // When in check, search starts here // Step 15. Make the move pos.do_move(move, st, givesCheck); + bool doDeeperSearch = false; + // Step 16. Late moves reduction / extension (LMR, ~200 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many @@ -1237,6 +1239,7 @@ moves_loop: // When in check, search starts here // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; + doDeeperSearch = value > alpha + 88; didLMR = true; } else @@ -1248,7 +1251,7 @@ moves_loop: // When in check, search starts here // Step 17. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); // If the move passed LMR update its stats if (didLMR && !captureOrPromotion) From 94514199123874c0029afb6e00634f26741d90db Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 8 Dec 2021 12:23:39 +0100 Subject: [PATCH 232/282] Improve transposition table remplacement strategy Increase chance that PV node replaces old entry in transposition table. STC: LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 46744 W: 12108 L: 11816 D: 22820 Ptnml(0-2): 156, 5221, 12344, 5477, 174 https://tests.stockfishchess.org/tests/view/61ae068356fcf33bce7d99d0 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 88464 W: 22912 L: 22513 D: 43039 Ptnml(0-2): 84, 9133, 25393, 9544, 78 https://tests.stockfishchess.org/tests/view/61ae973656fcf33bce7db3e1 closes https://github.com/official-stockfish/Stockfish/pull/3839 Bench: 5292488 --- src/tt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 1f495ca9..4af6c9f1 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -40,9 +40,9 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) move16 = (uint16_t)m; // Overwrite less valuable entries (cheapest checks first) - if (b == BOUND_EXACT + if ( b == BOUND_EXACT || (uint16_t)k != key16 - || d - DEPTH_OFFSET > depth8 - 4) + || d - DEPTH_OFFSET + 2 * pv > depth8 - 4) { assert(d > DEPTH_OFFSET); assert(d < 256 + DEPTH_OFFSET); From 8e8234593190804babd36b8a8862e2bb1fc65bb7 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 9 Dec 2021 19:33:06 +0300 Subject: [PATCH 233/282] Adjust singular extension depth restriction This patch is a modification of original idea by lonfom169 which had a good yellow run - do singular extension search with depth threshold 6 unless this is a PvNode with is a part of a PV line - for them set threshold to 8 instead. Passed STC https://tests.stockfishchess.org/tests/view/61b1080406b4c2dcb1b1128c LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 84352 W: 21917 L: 21555 D: 40880 Ptnml(0-2): 288, 9524, 22185, 9896, 283 Passed LTC https://tests.stockfishchess.org/tests/view/61b1860a06b4c2dcb1b134a1 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 63520 W: 16575 L: 16237 D: 30708 Ptnml(0-2): 27, 6519, 18350, 6817, 47 https://github.com/official-stockfish/Stockfish/pull/3840 bench 4729473 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f3a96ae2..cded8614 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1089,7 +1089,7 @@ moves_loop: // When in check, search starts here // a reduced search on all the other moves but the ttMove and if the // result is lower than ttValue minus a margin, then we will extend the ttMove. if ( !rootNode - && depth >= 7 + && depth >= 6 + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ From 9db6ca8592c2c372cc28d8c463dd9eeca464e893 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 11 Dec 2021 15:23:55 +0100 Subject: [PATCH 234/282] Update Top CPU Contributors closes https://github.com/official-stockfish/Stockfish/pull/3842 No functional change --- Top CPU Contributors.txt | 424 ++++++++++++++++++++------------------- 1 file changed, 221 insertions(+), 203 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index dacc5781..f0ec51f9 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,205 +1,223 @@ -Contributors to Fishtest with >10,000 CPU hours, as of Jun 29, 2021. +Contributors to Fishtest with >10,000 CPU hours, as of 2021-12-11. Thank you! -Username CPU Hours Games played ------------------------------------------------------ -noobpwnftw 27649494 1834734733 -mlang 1426107 89454622 -dew 1380910 82831648 -mibere 703840 46867607 -grandphish2 692707 41737913 -tvijlbrief 669642 42371594 -JojoM 597778 35297180 -TueRens 519226 31823562 -cw 458421 30307421 -fastgm 439667 25950040 -gvreuls 436599 28177460 -crunchy 427035 27344275 -CSU_Dynasty 374765 25106278 -Fisherman 326901 21822979 -ctoks 325477 21767943 -velislav 295343 18844324 -linrock 292789 10624427 -bcross 278584 19488961 -okrout 262818 13803272 -pemo 245982 11376085 -glinscott 217799 13780820 -leszek 212346 12959025 -nordlandia 211692 13484886 -bking_US 198894 11876016 -drabel 196463 13450602 -robal 195473 12375650 -mgrabiak 187226 12016564 -Dantist 183202 10990484 -Thanar 179852 12365359 -vdv 175274 9889046 -spams 157128 10319326 -marrco 150295 9402141 -sqrt2 147963 9724586 -mhoram 141278 8901241 -CoffeeOne 137100 5024116 -vdbergh 137041 8926915 -malala 136182 8002293 -xoto 133702 9156676 -davar 122092 7960001 -dsmith 122059 7570238 -Data 113305 8220352 -BrunoBanani 112960 7436849 -MaZePallas 102823 6633619 -sterni1971 100532 5880772 -ElbertoOne 99028 7023771 -brabos 92118 6186135 -oz 92100 6486640 -psk 89957 5984901 -amicic 89156 5392305 -sunu 88851 6028873 -Vizvezdenec 83761 5344740 -0x3C33 82614 5271253 -BRAVONE 81239 5054681 -racerschmacer 80899 5759262 -cuistot 80300 4606144 -nssy 76497 5259388 -teddybaer 75125 5407666 -Pking_cda 73776 5293873 -jromang 72192 5057715 -solarlight 70517 5028306 -dv8silencer 70287 3883992 -Bobo1239 68515 4652287 -manap 66273 4121774 -skiminki 65088 4023328 -tinker 64333 4268790 -sschnee 60767 3500800 -qurashee 57344 3168264 -robnjr 57262 4053117 -Freja 56938 3733019 -ttruscott 56010 3680085 -rkl 55132 4164467 -renouve 53811 3501516 -finfish 51360 3370515 -eva42 51272 3599691 -rap 49985 3219146 -pb00067 49727 3298270 -ronaldjerum 47654 3240695 -bigpen0r 47653 3335327 -eastorwest 47585 3221629 -biffhero 46564 3111352 -VoyagerOne 45476 3452465 -yurikvelo 44834 3034550 -speedycpu 43842 3003273 -jbwiebe 43305 2805433 -Spprtr 42279 2680153 -DesolatedDodo 42007 2447516 -Antihistamine 41788 2761312 -mhunt 41735 2691355 -homyur 39893 2850481 -gri 39871 2515779 -Fifis 38776 2529121 -oryx 38724 2966648 -SC 37290 2731014 -csnodgrass 36207 2688994 -jmdana 36157 2210661 -strelock 34716 2074055 -rpngn 33951 2057395 -Garf 33922 2751802 -EthanOConnor 33370 2090311 -slakovv 32915 2021889 -manapbk 30987 1810399 -Prcuvu 30377 2170122 -anst 30301 2190091 -jkiiski 30136 1904470 -hyperbolic.tom 29840 2017394 -Pyafue 29650 1902349 -Wolfgang 29260 1658936 -zeryl 28156 1579911 -OuaisBla 27636 1578800 -DMBK 27051 1999456 -chriswk 26902 1868317 -achambord 26582 1767323 -Patrick_G 26276 1801617 -yorkman 26193 1992080 -SFTUser 25182 1675689 -nabildanial 24942 1519409 -Sharaf_DG 24765 1786697 -ncfish1 24411 1520927 -rodneyc 24227 1409514 -agg177 23890 1395014 -JanErik 23408 1703875 -Isidor 23388 1680691 -Norabor 23164 1591830 -cisco2015 22897 1762669 -Zirie 22542 1472937 -team-oh 22272 1636708 -MazeOfGalious 21978 1629593 -sg4032 21947 1643265 -ianh2105 21725 1632562 -xor12 21628 1680365 -dex 21612 1467203 -nesoneg 21494 1463031 -sphinx 21211 1384728 -jjoshua2 21001 1423089 -horst.prack 20878 1465656 -Ente 20865 1477066 -0xB00B1ES 20590 1208666 -j3corre 20405 941444 -Adrian.Schmidt123 20316 1281436 -wei 19973 1745989 -MaxKlaxxMiner 19850 1009176 -rstoesser 19569 1293588 -gopeto 19491 1174952 -eudhan 19274 1283717 -jundery 18445 1115855 -megaman7de 18377 1067540 -iisiraider 18247 1101015 -ville 17883 1384026 -chris 17698 1487385 -purplefishies 17595 1092533 -dju 17353 978595 -DragonLord 17014 1162790 -IgorLeMasson 16064 1147232 -ako027ako 15671 1173203 -chuckstablers 15289 891576 -Nikolay.IT 15154 1068349 -Andrew Grant 15114 895539 -OssumOpossum 14857 1007129 -Karby 14808 867120 -enedene 14476 905279 -bpfliegel 14298 884523 -mpx86 14019 759568 -jpulman 13982 870599 -crocogoat 13803 1117422 -joster 13794 950160 -Nesa92 13786 1114691 -Hjax 13535 915487 -jsys14 13459 785000 -Dark_wizzie 13422 1007152 -mabichito 12903 749391 -thijsk 12886 722107 -AdrianSA 12860 804972 -Flopzee 12698 894821 -fatmurphy 12547 853210 -Rudolphous 12520 832340 -scuzzi 12511 845761 -SapphireBrand 12416 969604 -modolief 12386 896470 -Machariel 12335 810784 -pgontarz 12151 848794 -stocky 11954 699440 -mschmidt 11941 803401 -Maxim 11543 836024 -infinity 11470 727027 -torbjo 11395 729145 -Thomas A. Anderson 11372 732094 -savage84 11358 670860 -d64 11263 789184 -MooTheCow 11237 720174 -snicolet 11106 869170 -ali-al-zhrani 11086 767926 -AndreasKrug 10875 887457 -pirt 10806 836519 -basepi 10637 744851 -michaelrpg 10508 739039 -dzjp 10343 732529 -aga 10302 622975 -ols 10259 570669 -lbraesch 10252 647825 -FormazChar 10059 757283 +Username CPU Hours Games played +------------------------------------------------------------------ +noobpwnftw 29832955 2061725095 +mlang 2345848 162775694 +dew 1559902 93935940 +technologov 1154365 43136020 +grandphish2 988545 60725563 +tvijlbrief 795993 51894442 +TueRens 716909 45503672 +mibere 703840 46867607 +JojoM 675708 41046762 +okrout 648513 49953162 +linrock 563226 16184599 +pemo 520578 26816487 +gvreuls 501232 32662374 +cw 495103 33171295 +fastgm 476917 28660004 +crunchy 427035 27344275 +CSU_Dynasty 407300 27580858 +ctoks 386452 25757397 +oz 343309 25441096 +Fisherman 327231 21829379 +velislav 320318 20642978 +bcross 319459 22485737 +leszek 277442 17495865 +Dantist 229980 14568674 +mgrabiak 225684 14774260 +glinscott 217799 13780820 +nordlandia 211692 13484886 +robal 209847 13427680 +drabel 200340 13727458 +bking_US 198894 11876016 +Thanar 179852 12365359 +vdv 175274 9889046 +mhoram 166293 10796647 +spams 157128 10319326 +marrco 150300 9402229 +sqrt2 147963 9724586 +vdbergh 137186 8938965 +CoffeeOne 137100 5024116 +malala 136182 8002293 +xoto 133759 9159372 +davar 122092 7960001 +dsmith 122059 7570238 +amicic 119659 7937885 +Data 113305 8220352 +BrunoBanani 112960 7436849 +rpngn 109031 7392547 +CypressChess 108321 7759588 +MaZePallas 102823 6633619 +sterni1971 100532 5880772 +sunu 100167 7040199 +ElbertoOne 99028 7023771 +skiminki 98121 6478170 +brabos 92118 6186135 +psk 89957 5984901 +cuistot 88420 5225234 +DesolatedDodo 88356 5779482 +racerschmacer 85711 6119610 +Vizvezdenec 83761 5344740 +0x3C33 82614 5271253 +BRAVONE 81239 5054681 +nssy 76497 5259388 +teddybaer 75125 5407666 +Pking_cda 73776 5293873 +jromang 72192 5057715 +solarlight 70517 5028306 +dv8silencer 70287 3883992 +Bobo1239 68515 4652287 +manap 66273 4121774 +sschnee 64563 3633680 +tinker 64333 4268790 +zeryl 63290 4179159 +qurashee 61208 3429862 +yurikvelo 60387 4169900 +robnjr 57262 4053117 +Freja 56938 3733019 +ttruscott 56010 3680085 +rkl 55132 4164467 +renouve 53811 3501516 +finfish 51360 3370515 +eva42 51272 3599691 +Wolfgang 51248 3218932 +eastorwest 50311 3409935 +rap 49985 3219146 +pb00067 49727 3298270 +bigpen0r 47667 3336927 +ronaldjerum 47654 3240695 +MaxKlaxxMiner 47584 2972142 +biffhero 46564 3111352 +Spprtr 45877 2995437 +Fifis 45843 3088497 +VoyagerOne 45476 3452465 +speedycpu 43842 3003273 +jbwiebe 43305 2805433 +Antihistamine 41788 2761312 +mhunt 41735 2691355 +megaman7de 40060 2625050 +homyur 39893 2850481 +gri 39871 2515779 +oryx 38860 2976488 +SC 37299 2731694 +csnodgrass 36207 2688994 +jmdana 36157 2210661 +Garf 36113 2897580 +strelock 34716 2074055 +EthanOConnor 33370 2090311 +slakovv 32915 2021889 +manapbk 30987 1810399 +DMBK 30675 2383552 +Prcuvu 30377 2170122 +anst 30301 2190091 +jkiiski 30136 1904470 +hyperbolic.tom 29840 2017394 +chuckstablers 29659 2093438 +Pyafue 29650 1902349 +tolkki963 28171 1716386 +OuaisBla 27636 1578800 +armo9494 27224 2221042 +chriswk 26902 1868317 +achambord 26582 1767323 +gopeto 26355 1717722 +Patrick_G 26276 1801617 +yorkman 26193 1992080 +SFTUser 25182 1675689 +nabildanial 24942 1519409 +Sharaf_DG 24765 1786697 +ncfish1 24411 1520927 +rodneyc 24275 1410450 +agg177 23890 1395014 +JanErik 23408 1703875 +Isidor 23388 1680691 +Norabor 23339 1602636 +cisco2015 22897 1762669 +Zirie 22542 1472937 +Ente 22486 1606268 +team-oh 22272 1636708 +MazeOfGalious 21978 1629593 +sg4032 21947 1643265 +ianh2105 21725 1632562 +xor12 21628 1680365 +dex 21612 1467203 +nesoneg 21494 1463031 +sphinx 21211 1384728 +jjoshua2 21001 1423089 +horst.prack 20878 1465656 +0xB00B1ES 20590 1208666 +j3corre 20405 941444 +Adrian.Schmidt123 20316 1281436 +wei 19973 1745989 +rstoesser 19569 1293588 +eudhan 19274 1283717 +vulcan 18871 1729392 +user213718 18590 1271128 +jundery 18445 1115855 +iisiraider 18247 1101015 +ville 17883 1384026 +chris 17698 1487385 +purplefishies 17595 1092533 +dju 17353 978595 +DragonLord 17014 1162790 +IgorLeMasson 16064 1147232 +ako027ako 15671 1173203 +Nikolay.IT 15154 1068349 +Andrew Grant 15114 895539 +OssumOpossum 14857 1007129 +Karby 14808 867120 +enedene 14476 905279 +jsys14 14318 843704 +bpfliegel 14298 884523 +mpx86 14019 759568 +jpulman 13982 870599 +kdave 13933 1045550 +crocogoat 13803 1117422 +joster 13794 950160 +Nesa92 13786 1114691 +mbeier 13650 1044928 +AndreasKrug 13624 1090613 +Hjax 13535 915487 +Dark_wizzie 13422 1007152 +Ulysses 13392 1021264 +Calis007 13267 873236 +Rudolphous 13244 883140 +spcc 13085 917006 +Machariel 13010 863104 +mabichito 12903 749391 +thijsk 12886 722107 +AdrianSA 12860 804972 +Flopzee 12698 894821 +fatmurphy 12547 853210 +scuzzi 12511 845761 +SapphireBrand 12416 969604 +modolief 12386 896470 +fishtester 12320 873882 +Farseer 12249 694108 +pgontarz 12151 848794 +stocky 11954 699440 +mschmidt 11941 803401 +Maxim 11543 836024 +infinity 11470 727027 +aga 11409 695071 +torbjo 11395 729145 +pirt 11392 886291 +Thomas A. Anderson 11372 732094 +savage84 11358 670860 +FormazChar 11304 847663 +d64 11263 789184 +dbernier 11258 805102 +MooTheCow 11237 720174 +snicolet 11106 869170 +ali-al-zhrani 11098 768494 +basepi 10637 744851 +Cubox 10621 826448 +michaelrpg 10509 739239 +OIVAS7572 10420 995586 +dzjp 10343 732529 +infinigon 10319 776158 +Garruk 10314 702629 +ols 10259 570669 +lbraesch 10252 647825 From d579db34a3f7a26cd5502e79dafde2a80614d645 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 10 Dec 2021 18:42:05 +0100 Subject: [PATCH 235/282] Simplify falling eval time factor. Remove the difference to previous best score in falling eval calculation. As compensation double the effect of the difference to previous best average score. STC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 86944 W: 22363 L: 22285 D: 42296 Ptnml(0-2): 273, 9227, 24396, 9301, 275 https://tests.stockfishchess.org/tests/view/61b111ce06b4c2dcb1b11546 LTC: LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 134944 W: 34606 L: 34596 D: 65742 Ptnml(0-2): 66, 12941, 41456, 12935, 74 https://tests.stockfishchess.org/tests/view/61b19ca206b4c2dcb1b13a8b closes https://github.com/official-stockfish/Stockfish/pull/3841 Bench: 4729473 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cded8614..687bf0a9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -490,9 +490,8 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (142 + 6 * (mainThread->bestPreviousScore - bestValue) - + 6 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; + double fallingEval = (142 + 12 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly From ea1ddb6aef8bb6ee3056988af7cfd32b2f52f960 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 12 Dec 2021 17:09:21 +0100 Subject: [PATCH 236/282] Update default net to nn-d93927199b3d.nnue Using the same dataset as before but slightly reduced initial LR as in https://github.com/vondele/nnue-pytorch/tree/tweakLR1 passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 51368 W: 13492 L: 13191 D: 24685 Ptnml(0-2): 168, 5767, 13526, 6042, 181 https://tests.stockfishchess.org/tests/view/61b61f43dffbe89a3580b529 passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 45128 W: 11763 L: 11469 D: 21896 Ptnml(0-2): 24, 4583, 13063, 4863, 31 https://tests.stockfishchess.org/tests/view/61b6612edffbe89a3580c447 closes https://github.com/official-stockfish/Stockfish/pull/3848 Bench: 5121336 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 87cb65f3..5a9260fb 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-63376713ba63.nnue" + #define EvalFileDefaultName "nn-d93927199b3d.nnue" namespace NNUE { From c6edf33f539f160d4a7009f3aac25e7ec5668eda Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 7 Dec 2021 23:03:35 +0100 Subject: [PATCH 237/282] Remove NNUE scaling term remove pawns scaling, probably correlated with piece scaling, and might be less useful with the recent improved nets. Might allow for another tune of the scaling params. passed STC https://tests.stockfishchess.org/tests/view/61afdb2e56fcf33bce7df31a LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 280864 W: 72198 L: 72399 D: 136267 Ptnml(0-2): 854, 32356, 74346, 31889, 987 passed LTC https://tests.stockfishchess.org/tests/view/61b233a606b4c2dcb1b16140 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 400136 W: 102669 L: 103012 D: 194455 Ptnml(0-2): 212, 42005, 116047, 41522, 282 closes https://github.com/official-stockfish/Stockfish/pull/3851 Bench: 4735679 --- src/evaluate.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1dc701cf..24445f48 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,8 +1091,7 @@ Value Eval::evaluate(const Position& pos) { v = Evaluation(pos).value(); // classical else { - int scale = 1049 - + 8 * pos.count() + int scale = 1136 + 20 * pos.non_pawn_material() / 1024; Value nnue = NNUE::evaluate(pos, true); // NNUE From 3bea736a2a63247d242316bd25d8901b1414d9c0 Mon Sep 17 00:00:00 2001 From: farseer Date: Wed, 15 Dec 2021 16:28:16 -0800 Subject: [PATCH 238/282] Update default net to nn-4401e826ebcc.nnue Using data T60 12/1/20 to 11/2/2021, T74 4/22/21 to 7/27/21, T75 6/3/21 to 10/16/21, T76 (half of the randomly interleaved dataset due to a mistake merging) 11/10/21 to 11/21/21, wrongIsRight_nodes5000pv2.binpack, and WrongIsRight-Reloaded.binpack combined and shuffled position by position. Trained with LR=4.375e-4 and WDL filtering enabled: python train.py --smart-fen-skipping --random-fen-skipping 0 --features=HalfKAv2_hm^ --lambda=1.0 --max_epochs=800 --seed 910688689 --batch-size 16384 --progress_bar_refresh_rate 30 --threads 4 --num-workers 4 --gpus 1 --resume-from-model C:\msys64\home\Mike\nnue-pytorch\9b3d.pt E:\trainingdata\T60-T74-T75-T76-WiR-WiRR-PbyP.binpack E:\trainingdata\T60-T74-T75-T76-WiR-WiRR-PbyP.binpack Passed STC LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 41848 W: 10962 L: 10676 D: 20210 Elo +2.16 Ptnml(0-2): 142, 4699, 11016, 4865, 202 https://tests.stockfishchess.org/tests/view/61ba886857a0d0f327c2cfd6 Passed LTC LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 27776 W: 7208 L: 6953 D: 13615 Elo + 3.00 Ptnml(0-2): 14, 2808, 8007, 3027, 32 https://tests.stockfishchess.org/tests/view/61baae4d57a0d0f327c2d96f closes https://github.com/official-stockfish/Stockfish/pull/3856 Bench: 4667591 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 5a9260fb..e258fdb6 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-d93927199b3d.nnue" + #define EvalFileDefaultName "nn-4401e826ebcc.nnue" namespace NNUE { From 0889210262ab5a14bc6fcd261125b919120637a0 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 14 Dec 2021 12:00:45 -0300 Subject: [PATCH 239/282] Simplify away singularQuietLMR While at it, we also update the Elo estimate of reduction at non-PV nodes (source: https://tests.stockfishchess.org/tests/view/61acf97156fcf33bce7d6303 ) STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 243632 W: 62874 L: 63022 D: 117736 Ptnml(0-2): 810, 28024, 64249, 27970, 763 https://tests.stockfishchess.org/tests/view/61b8b1b7dffbe89a35814c0d LTC: LLR: 2.93 (-2.94,2.94) <-2.25,0.25> Total: 91392 W: 23520 L: 23453 D: 44419 Ptnml(0-2): 51, 9568, 26387, 9643, 47 https://tests.stockfishchess.org/tests/view/61b97316dffbe89a35817da7 closes https://github.com/official-stockfish/Stockfish/pull/3854 bench: 4217785 --- src/search.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 687bf0a9..01d97646 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -590,8 +590,7 @@ namespace { Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; bool givesCheck, improving, didLMR, priorCapture; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, - ttCapture, singularQuietLMR; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount, bestMoveCount, improvement; @@ -982,7 +981,7 @@ moves_loop: // When in check, search starts here ss->ply); value = bestValue; - singularQuietLMR = moveCountPruning = false; + moveCountPruning = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal or greater than the current depth, and the result of this search was a fail low. @@ -1106,7 +1105,6 @@ moves_loop: // When in check, search starts here if (value < singularBeta) { extension = 1; - singularQuietLMR = !ttCapture; // Avoid search explosion by limiting the number of double extensions if ( !PvNode @@ -1190,7 +1188,7 @@ moves_loop: // When in check, search starts here && !likelyFailLow) r -= 2; - // Increase reduction at non-PV nodes + // Increase reduction at non-PV nodes (~3 Elo) if (!PvNode) r++; @@ -1198,10 +1196,6 @@ moves_loop: // When in check, search starts here if ((ss-1)->moveCount > 13) r--; - // Decrease reduction if ttMove has been singularly extended (~1 Elo) - if (singularQuietLMR) - r--; - // Increase reduction for cut nodes (~3 Elo) if (cutNode && move != ss->killers[0]) r += 2; From dc5d9bdfee70f4267d9a49ad71e5ee478dd50ca5 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Wed, 15 Dec 2021 08:49:44 +0100 Subject: [PATCH 240/282] Remove lowPly history Seems that after pull request #3731 (Capping stat bonus at 2000) this heuristic is no longer useful. STC: https://tests.stockfishchess.org/tests/view/61b8d0e2dffbe89a35815444 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 30672 W: 7974 L: 7812 D: 14886 Ptnml(0-2): 106, 3436, 8072, 3634, 88 LTC: https://tests.stockfishchess.org/tests/view/61b8e90cdffbe89a35815a67 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 42448 W: 10884 L: 10751 D: 20813 Ptnml(0-2): 23, 4394, 12267, 4507, 33 closes https://github.com/official-stockfish/Stockfish/pull/3853 bench: 4474950 --- src/movepick.cpp | 28 ++++++++++++++++------------ src/movepick.h | 22 ++++++---------------- src/search.cpp | 27 +++++---------------------- src/thread.cpp | 1 - src/thread.h | 1 - 5 files changed, 27 insertions(+), 52 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 20640fe2..6aa1954b 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -56,11 +56,14 @@ namespace { /// ordering is at the current node. /// MovePicker constructor for the main search -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, - const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl) - : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), - ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { - +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, + const CapturePieceToHistory* cph, + const PieceToHistory** ch, + Move cm, + const Move* killers) + : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), + ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) +{ assert(d > 0); stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + @@ -69,9 +72,11 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist /// MovePicker constructor for quiescence search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, - const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) - : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) { - + const CapturePieceToHistory* cph, + const PieceToHistory** ch, + Square rs) + : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) +{ assert(d <= 0); stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + @@ -83,8 +88,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist /// MovePicker constructor for ProbCut: we generate captures with SEE greater /// than or equal to the given threshold. MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) - : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) { - + : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) +{ assert(!pos.checkers()); stage = PROBCUT_TT + !(ttm && pos.capture(ttm) @@ -110,8 +115,7 @@ void MovePicker::score() { + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] - + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] - + (ply < MAX_LPH ? 6 * (*lowPlyHistory)[ply][from_to(m)] : 0); + + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]; else // Type == EVASIONS { diff --git a/src/movepick.h b/src/movepick.h index 7d78886f..426bac89 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -88,12 +88,6 @@ enum StatsType { NoCaptures, Captures }; /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards typedef Stats ButterflyHistory; -/// At higher depths LowPlyHistory records successful quiet moves near the root -/// and quiet moves which are/were in the PV (ttPv). LowPlyHistory is populated during -/// iterative deepening and at each new search the data is shifted down by 2 plies -constexpr int MAX_LPH = 4; -typedef Stats LowPlyHistory; - /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous /// move, see www.chessprogramming.org/Countermove_Heuristic typedef Stats CounterMoveHistory; @@ -123,18 +117,16 @@ class MovePicker { public: MovePicker(const MovePicker&) = delete; MovePicker& operator=(const MovePicker&) = delete; - MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); + MovePicker(const Position&, Move, Depth, const ButterflyHistory*, + const CapturePieceToHistory*, + const PieceToHistory**, + Move, + const Move*); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Square); - MovePicker(const Position&, Move, Depth, const ButterflyHistory*, - const LowPlyHistory*, - const CapturePieceToHistory*, - const PieceToHistory**, - Move, - const Move*, - int); + MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); private: @@ -145,7 +137,6 @@ private: const Position& pos; const ButterflyHistory* mainHistory; - const LowPlyHistory* lowPlyHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; Move ttMove; @@ -154,7 +145,6 @@ private: Square recaptureSquare; Value threshold; Depth depth; - int ply; ExtMove moves[MAX_MOVES]; }; diff --git a/src/search.cpp b/src/search.cpp index 01d97646..2abfbaf2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -141,7 +141,7 @@ namespace { Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); - void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth); + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth); @@ -317,9 +317,6 @@ void Thread::search() { mainThread->iterValue[i] = mainThread->bestPreviousScore; } - std::copy(&lowPlyHistory[2][0], &lowPlyHistory.back().back() + 1, &lowPlyHistory[0][0]); - std::fill(&lowPlyHistory[MAX_LPH - 2][0], &lowPlyHistory.back().back() + 1, 0); - size_t multiPV = size_t(Options["MultiPV"]); Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); @@ -666,14 +663,6 @@ namespace { if (!excludedMove) ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); - // Update low ply history for previous move if we are near root and position is or has been in PV - if ( ss->ttPv - && depth > 12 - && ss->ply - 1 < MAX_LPH - && !priorCapture - && is_ok((ss-1)->currentMove)) - thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); - // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit @@ -689,7 +678,7 @@ namespace { { // Bonus for a quiet ttMove that fails high if (!ttCapture) - update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth); + update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !priorCapture) @@ -973,12 +962,10 @@ moves_loop: // When in check, search starts here Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, - &thisThread->lowPlyHistory, &captureHistory, contHist, countermove, - ss->killers, - ss->ply); + ss->killers); value = bestValue; moveCountPruning = false; @@ -1708,7 +1695,7 @@ moves_loop: // When in check, search starts here if (!pos.capture_or_promotion(bestMove)) { // Increase stats for the best move in case it was a quiet move - update_quiet_stats(pos, ss, bestMove, bonus2, depth); + update_quiet_stats(pos, ss, bestMove, bonus2); // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) @@ -1755,7 +1742,7 @@ moves_loop: // When in check, search starts here // update_quiet_stats() updates move sorting heuristics - void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth) { + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { // Update killers if (ss->killers[0] != move) @@ -1775,10 +1762,6 @@ moves_loop: // When in check, search starts here Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } - - // Update low ply history - if (depth > 11 && ss->ply < MAX_LPH) - thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7); } // When playing with strength handicap, choose best move among a set of RootMoves diff --git a/src/thread.cpp b/src/thread.cpp index ed3accc5..099efbad 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -59,7 +59,6 @@ void Thread::clear() { counterMoves.fill(MOVE_NONE); mainHistory.fill(0); - lowPlyHistory.fill(0); captureHistory.fill(0); for (bool inCheck : { false, true }) diff --git a/src/thread.h b/src/thread.h index 37c6452b..6bc1be65 100644 --- a/src/thread.h +++ b/src/thread.h @@ -77,7 +77,6 @@ public: Value rootDelta; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; - LowPlyHistory lowPlyHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; Score trend; From 939b694bfda27017c34a8cdccc81f2bb2ef44079 Mon Sep 17 00:00:00 2001 From: George Sobala Date: Mon, 13 Dec 2021 15:29:31 +0000 Subject: [PATCH 241/282] Fix for profile-build failure using gcc on MacOS Fixes https://github.com/official-stockfish/Stockfish/issues/3846 , where the profiling SF binary generated by GCC on MacOS would launch but failed to quit. Tested with gcc-8, gcc9, gcc10, gcc-11. The problem can be fixed by adding -fvisibility=hidden to the compiler flags, see for example the following piece of Apple documentation: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/SymbolVisibility.html For instance this now works: make -j8 profile-build ARCH=x86-64-avx2 COMP=gcc COMPCXX=g++-11 No functional change --- src/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Makefile b/src/Makefile index 3cf97873..5e263772 100644 --- a/src/Makefile +++ b/src/Makefile @@ -459,6 +459,9 @@ else ifeq ($(comp),clang) else profile_make = gcc-profile-make profile_use = gcc-profile-use + ifeq ($(KERNEL),Darwin) + EXTRAPROFILEFLAGS = -fvisibility=hidden + endif endif ### Travis CI script uses COMPILER to overwrite CXX @@ -920,12 +923,14 @@ gcc-profile-make: @mkdir -p profdir $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-generate=profdir' \ + EXTRACXXFLAGS+=$(EXTRAPROFILEFLAGS) \ EXTRALDFLAGS='-lgcov' \ all gcc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-use=profdir -fno-peel-loops -fno-tracer' \ + EXTRACXXFLAGS+=$(EXTRAPROFILEFLAGS) \ EXTRALDFLAGS='-lgcov' \ all From 0a318cdddf8b6bdd05c2e0ee9b3b61a031d398ed Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 18 Dec 2021 11:51:19 +0300 Subject: [PATCH 242/282] Adjust reductions based on current node delta and root delta This patch is a follow up of previous 2 patches that introduced more reductions for PV nodes with low delta and more pruning for nodes with low delta. Instead of writing separate heuristics now it adjust reductions based on delta / rootDelta - it allows to remove 3 separate adjustements of pruning/LMR in different places and also makes reduction dependence on delta and rootDelta smoother. Also now it works for all pruning heuristics and not just 2. Passed STC https://tests.stockfishchess.org/tests/view/61ba9b6c57a0d0f327c2d48b LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 79192 W: 20513 L: 20163 D: 38516 Ptnml(0-2): 238, 8900, 21024, 9142, 292 passed LTC https://tests.stockfishchess.org/tests/view/61baf77557a0d0f327c2eb8e LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 158400 W: 41134 L: 40572 D: 76694 Ptnml(0-2): 101, 16372, 45745, 16828, 154 closes https://github.com/official-stockfish/Stockfish/pull/3862 bench 4651538 --- src/search.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2abfbaf2..8a4db351 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -69,9 +69,9 @@ namespace { // Reductions lookup table, initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] - Depth reduction(bool i, Depth d, int mn, bool rangeReduction) { + Depth reduction(bool i, Depth d, int mn, bool rangeReduction, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 534) / 1024 + (!i && r > 904) + rangeReduction; + return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904) + rangeReduction; } constexpr int futility_move_count(bool improving, Depth depth) { @@ -1015,6 +1015,8 @@ moves_loop: // When in check, search starts here // Calculate new depth for this move newDepth = depth - 1; + Value delta = beta - alpha; + // Step 13. Pruning at shallow depth (~200 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) @@ -1024,7 +1026,7 @@ moves_loop: // When in check, search starts here moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, rangeReduction > 2), 0); + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta), 0); if ( captureOrPromotion || givesCheck) @@ -1052,8 +1054,6 @@ moves_loop: // When in check, search starts here history += thisThread->mainHistory[us][from_to(move)]; - lmrDepth = std::max(0, lmrDepth - (beta - alpha < thisThread->rootDelta / 4)); - // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 @@ -1161,12 +1161,11 @@ moves_loop: // When in check, search starts here || !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1))) { - Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); + Depth r = reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta); // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode - && bestMoveCount <= 3 - && beta - alpha >= thisThread->rootDelta / 4) + && bestMoveCount <= 3) r--; // Decrease reduction if position is or has been on the PV @@ -1175,10 +1174,6 @@ moves_loop: // When in check, search starts here && !likelyFailLow) r -= 2; - // Increase reduction at non-PV nodes (~3 Elo) - if (!PvNode) - r++; - // Decrease reduction if opponent's move count is high (~1 Elo) if ((ss-1)->moveCount > 13) r--; From fb7d3ab32ebd7a6514bded459b4aa442276f6cda Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 19 Dec 2021 02:30:45 +0300 Subject: [PATCH 243/282] Reintroduce futility pruning for captures This is a reintroduction of an idea that was simplified away approximately 1 year ago. There are some tweaks to it : a) exclude promotions; b) exclude Pv Nodes from it - Pv Nodes logic for captures is really different from non Pv nodes so it makes a lot of sense; c) use a big grain of capture history - idea is taken from my recent patches in futility pruning. passed STC https://tests.stockfishchess.org/tests/view/61bd90f857a0d0f327c373b7 LLR: 2.96 (-2.94,2.94) <0.00,2.50> Total: 86640 W: 22474 L: 22110 D: 42056 Ptnml(0-2): 268, 9732, 22963, 10082, 275 passed LTC https://tests.stockfishchess.org/tests/view/61be094457a0d0f327c38aa3 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 23240 W: 6079 L: 5838 D: 11323 Ptnml(0-2): 14, 2261, 6824, 2512, 9 https://github.com/official-stockfish/Stockfish/pull/3864 bench 4493723 --- src/search.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 8a4db351..d2ceee9f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1037,6 +1037,16 @@ moves_loop: // When in check, search starts here && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) continue; + // Futility pruning for captures + if ( !pos.empty(to_sq(move)) + && !givesCheck + && !PvNode + && lmrDepth < 6 + && !ss->inCheck + && ss->staticEval + 342 + 238 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 8 < alpha) + continue; + // SEE based pruning if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo) continue; From ca51b456493a4663b2d0f82273f67a3e499244a1 Mon Sep 17 00:00:00 2001 From: George Sobala Date: Mon, 13 Dec 2021 16:05:35 +0000 Subject: [PATCH 244/282] Fixes build failure on Apple M1 Silicon This pull request selectively avoids `-mdynamic-no-pic` for gcc on Apple Silicon (there was no problem with the default clang compiler). fixes https://github.com/official-stockfish/Stockfish/issues/3847 closes https://github.com/official-stockfish/Stockfish/pull/3850 No functional change --- src/Makefile | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5e263772..f00df79f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -522,11 +522,17 @@ ifeq ($(optimize),yes) endif endif - ifeq ($(comp),$(filter $(comp),gcc clang icc)) - ifeq ($(KERNEL),Darwin) - CXXFLAGS += -mdynamic-no-pic - endif - endif + ifeq ($(KERNEL),Darwin) + ifeq ($(comp),$(filter $(comp),clang icc)) + CXXFLAGS += -mdynamic-no-pic + endif + + ifeq ($(comp),gcc) + ifneq ($(arch),arm64) + CXXFLAGS += -mdynamic-no-pic + endif + endif + endif ifeq ($(comp),clang) CXXFLAGS += -fexperimental-new-pass-manager From 74776dbcd57a1bdf2d5328e5750cc1d00244e17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 19 Dec 2021 15:15:30 +0100 Subject: [PATCH 245/282] Simplification in evaluate_nnue.cpp Removes the test on non-pawn-material before applying the positional/materialistic bonus. Passed STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 46904 W: 12197 L: 12059 D: 22648 Ptnml(0-2): 170, 5243, 12479, 5399, 161 https://tests.stockfishchess.org/tests/view/61be57cf57a0d0f327c3999d Passed LTC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 18760 W: 4958 L: 4790 D: 9012 Ptnml(0-2): 14, 1942, 5301, 2108, 15 https://tests.stockfishchess.org/tests/view/61bed1fb57a0d0f327c3afa9 closes https://github.com/official-stockfish/Stockfish/pull/3866 Bench: 4826206 --- src/nnue/evaluate_nnue.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index bd473294..a534753a 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -165,12 +165,11 @@ namespace Stockfish::Eval::NNUE { const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); const auto positional = network[bucket]->propagate(transformedFeatures, buffer)[0]; - // Give more value to positional evaluation when material is balanced - if ( adjusted - && abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)) <= RookValueMg - BishopValueMg) - return static_cast(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale); + // Give more value to positional evaluation when adjusted flag is set + if (adjusted) + return static_cast(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale); else - return static_cast((psqt + positional) / OutputScale); + return static_cast((psqt + positional) / OutputScale); } struct NnueEvalTrace { From 2c30956a13986454dcc5dd8c7daa1b27d5a77a1b Mon Sep 17 00:00:00 2001 From: bmc4 Date: Fri, 17 Dec 2021 21:14:34 -0300 Subject: [PATCH 246/282] Remove Capture Extension This revert the patch #3692, probably can be simplified after the introduction of #3838. Fixed-game test: ELO: -1.41 +-1.8 (95%) LOS: 5.9% Total: 20000 W: 1552 L: 1633 D: 16815 Ptnml(0-2): 38, 1242, 7517, 1169, 34 https://tests.stockfishchess.org/tests/view/61bc1a2057a0d0f327c32a3c STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 44528 W: 11619 L: 11478 D: 21431 Ptnml(0-2): 146, 5020, 11771, 5201, 126 https://tests.stockfishchess.org/tests/view/61bc638c57a0d0f327c338fe LTC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 34136 W: 8847 L: 8704 D: 16585 Ptnml(0-2): 23, 3475, 9925, 3626, 19 https://tests.stockfishchess.org/tests/view/61bcb24257a0d0f327c34813 closes https://github.com/official-stockfish/Stockfish/pull/3863 Bench: 4054695 --- src/search.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d2ceee9f..eb0ac5bb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1123,12 +1123,6 @@ moves_loop: // When in check, search starts here extension = -2; } - // Capture extensions for PvNodes and cutNodes - else if ( (PvNode || cutNode) - && captureOrPromotion - && moveCount != 1) - extension = 1; - // Check extensions else if ( givesCheck && depth > 6 From 22e92d23d24ade1053cf543624d0efd01387e886 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 20 Dec 2021 08:08:09 -0300 Subject: [PATCH 247/282] Remove Capture history pruning Fixed number of games. (book: 8moves_v3.png): ELO: -0.69 +-1.8 (95%) LOS: 22.1% Total: 20000 W: 1592 L: 1632 D: 16776 Ptnml(0-2): 44, 1194, 7566, 1150, 46 https://tests.stockfishchess.org/tests/view/61bb8eb657a0d0f327c30ce8 STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 139976 W: 36039 L: 36036 D: 67901 Ptnml(0-2): 435, 16138, 36885, 16049, 481 https://tests.stockfishchess.org/tests/view/61be731857a0d0f327c39ea2 LTC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 70656 W: 18284 L: 18189 D: 34183 Ptnml(0-2): 34, 7317, 20529, 7416, 32 https://tests.stockfishchess.org/tests/view/61bf39b657a0d0f327c3c37b closes https://github.com/official-stockfish/Stockfish/pull/3867 bench: 4281737 --- src/search.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index eb0ac5bb..988166df 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1031,12 +1031,6 @@ moves_loop: // When in check, search starts here if ( captureOrPromotion || givesCheck) { - // Capture history based pruning when the move doesn't give check - if ( !givesCheck - && lmrDepth < 1 - && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) - continue; - // Futility pruning for captures if ( !pos.empty(to_sq(move)) && !givesCheck From 88f17a814d80cdb32ccf9efb7b56bb36d7bf052b Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 20 Dec 2021 16:20:08 -0300 Subject: [PATCH 248/282] Update Elo estimates for terms in search This updates estimates from 2yr ago #2401, and adds missing terms. All tests run at 10+0.1 (STC), 20000 games, error bars +- 1.8 Elo, book 8moves_v3.png. A table of Elo values with the links to the corresponding tests can be found at the PR closes https://github.com/official-stockfish/Stockfish/pull/3868 Non-functional Change --- src/evaluate.cpp | 2 +- src/search.cpp | 56 ++++++++++++++++++++++++------------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 24445f48..28689f1d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1083,7 +1083,7 @@ Value Eval::evaluate(const Position& pos) { Value v; - // Deciding between classical and NNUE eval: for high PSQ imbalance we use classical, + // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. if ( !useNNUE diff --git a/src/search.cpp b/src/search.cpp index 988166df..f5e9779c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -671,20 +671,20 @@ namespace { && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) { - // If ttMove is quiet, update move sorting heuristics on TT hit + // If ttMove is quiet, update move sorting heuristics on TT hit (~1 Elo) if (ttMove) { if (ttValue >= beta) { - // Bonus for a quiet ttMove that fails high + // Bonus for a quiet ttMove that fails high (~3 Elo) if (!ttCapture) update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); - // Extra penalty for early quiet moves of the previous ply + // Extra penalty for early quiet moves of the previous ply (~0 Elo) if ((ss-1)->moveCount <= 2 && !priorCapture) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); } - // Penalty for a quiet ttMove that fails low + // Penalty for a quiet ttMove that fails low (~1 Elo) else if (!ttCapture) { int penalty = -stat_bonus(depth); @@ -773,7 +773,7 @@ namespace { if (eval == VALUE_DRAW) eval = value_draw(thisThread); - // Can ttValue be used as a better position evaluation? + // ttValue can be used as a better position evaluation (~4 Elo) if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) eval = ttValue; @@ -787,7 +787,7 @@ namespace { tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } - // Use static evaluation difference to improve quiet move ordering + // Use static evaluation difference to improve quiet move ordering (~3 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { int bonus = std::clamp(-16 * int((ss-1)->staticEval + ss->staticEval), -2000, 2000); @@ -804,7 +804,7 @@ namespace { improving = improvement > 0; - // Step 7. Futility pruning: child node (~50 Elo). + // Step 7. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 @@ -812,7 +812,7 @@ namespace { && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; - // Step 8. Null move search with verification search (~40 Elo) + // Step 8. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL && (ss-1)->statScore < 23767 @@ -925,7 +925,7 @@ namespace { ss->ttPv = ttPv; } - // Step 10. If the position is not in TT, decrease depth by 2 or 1 depending on node type + // Step 10. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) if ( PvNode && depth >= 6 && !ttMove) @@ -940,7 +940,7 @@ moves_loop: // When in check, search starts here int rangeReduction = 0; - // Step 11. A small Probcut idea, when we are in check + // Step 11. A small Probcut idea, when we are in check (~0 Elo) probCutBeta = beta + 409; if ( ss->inCheck && !PvNode @@ -1017,12 +1017,12 @@ moves_loop: // When in check, search starts here Value delta = beta - alpha; - // Step 13. Pruning at shallow depth (~200 Elo). Depth conditions are important for mate finding. + // Step 13. Pruning at shallow depth (~98 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) { - // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold + // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~7 Elo) moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search @@ -1031,18 +1031,18 @@ moves_loop: // When in check, search starts here if ( captureOrPromotion || givesCheck) { - // Futility pruning for captures + // Futility pruning for captures (~0 Elo) if ( !pos.empty(to_sq(move)) && !givesCheck && !PvNode && lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 342 + 238 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + && ss->staticEval + 342 + 238 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 8 < alpha) continue; - // SEE based pruning - if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo) + // SEE based pruning (~9 Elo) + if (!pos.see_ge(move, Value(-218) * depth)) continue; } else @@ -1051,28 +1051,28 @@ moves_loop: // When in check, search starts here + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)]; - // Continuation history based pruning (~20 Elo) + // Continuation history based pruning (~2 Elo) if ( lmrDepth < 5 && history < -3000 * depth + 3000) continue; history += thisThread->mainHistory[us][from_to(move)]; - // Futility pruning: parent node (~5 Elo) + // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck && lmrDepth < 8 && ss->staticEval + 142 + 139 * lmrDepth + history / 64 <= alpha) continue; - // Prune moves with negative SEE (~20 Elo) + // Prune moves with negative SEE (~3 Elo) if (!pos.see_ge(move, Value(-21 * lmrDepth * lmrDepth - 21 * lmrDepth))) continue; } } - // Step 14. Extensions (~75 Elo) + // Step 14. Extensions (~66 Elo) - // Singular extension search (~70 Elo). If all moves but one fail low on a + // Singular extension search (~58 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), // then that move is singular and should be extended. To verify this we do // a reduced search on all the other moves but the ttMove and if the @@ -1117,13 +1117,13 @@ moves_loop: // When in check, search starts here extension = -2; } - // Check extensions + // Check extensions (~1 Elo) else if ( givesCheck && depth > 6 && abs(ss->staticEval) > 100) extension = 1; - // Quiet ttMove extensions + // Quiet ttMove extensions (~0 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] @@ -1149,7 +1149,7 @@ moves_loop: // When in check, search starts here bool doDeeperSearch = false; - // Step 16. Late moves reduction / extension (LMR, ~200 Elo) + // Step 16. Late moves reduction / extension (LMR, ~98 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". @@ -1467,7 +1467,7 @@ moves_loop: // When in check, search starts here if ((ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos); - // Can ttValue be used as a better position evaluation? + // ttValue can be used as a better position evaluation (~7 Elo) if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))) bestValue = ttValue; @@ -1522,7 +1522,7 @@ moves_loop: // When in check, search starts here moveCount++; - // Futility pruning and moveCount pruning + // Futility pruning and moveCount pruning (~5 Elo) if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck && futilityBase > -VALUE_KNOWN_WIN @@ -1547,7 +1547,7 @@ moves_loop: // When in check, search starts here } } - // Do not search moves with negative SEE values + // Do not search moves with negative SEE values (~5 Elo) if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !pos.see_ge(move)) continue; @@ -1561,7 +1561,7 @@ moves_loop: // When in check, search starts here [pos.moved_piece(move)] [to_sq(move)]; - // Continuation history based pruning + // Continuation history based pruning (~2 Elo) if ( !captureOrPromotion && bestValue > VALUE_TB_LOSS_IN_MAX_PLY && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold From 0a6168089de8df15339e2c8f6604158d7434b678 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 22 Dec 2021 07:54:10 +0300 Subject: [PATCH 249/282] Fall back to NNUE if classical evaluation is much lower than threshold The idea is that if classical eval returns a value much lower than the threshold of its usage it most likely means that position isn't that simple so we need the more precise NNUE evaluation. passed STC: https://tests.stockfishchess.org/tests/view/61bf3e7557a0d0f327c3c47a LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 108072 W: 28007 L: 27604 D: 52461 Ptnml(0-2): 352, 12147, 28650, 12520, 367 passed LTC: https://tests.stockfishchess.org/tests/view/61c0581657a0d0f327c3fa0c LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 155096 W: 40392 L: 39841 D: 74863 Ptnml(0-2): 88, 15983, 44843, 16558, 76 closes https://github.com/official-stockfish/Stockfish/pull/3869 bench 4310422 --- src/evaluate.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 28689f1d..8bc51695 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1086,10 +1086,17 @@ Value Eval::evaluate(const Position& pos) { // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. + bool classical = false; + if ( !useNNUE || abs(eg_value(pos.psq_score())) * 5 > (850 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) + { v = Evaluation(pos).value(); // classical - else + classical = abs(v) >= 300; + } + + // If result of a classical evaluation is much lower than threshold fall back to NNUE + if (!classical && useNNUE) { int scale = 1136 + 20 * pos.non_pawn_material() / 1024; From 7d82f0d1f4e25892e1901d25cf5cf6f6a2606c2a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 17 Dec 2021 19:34:30 +0100 Subject: [PATCH 250/282] Update default net to nn-ac07bd334b62.nnue Trained with essentially the same data as provided and used by Farseer (mbabigian) for the previous master net. T60T70wIsRightFarseerT60T74T75T76.binpack (99GB): ['T60T70wIsRightFarseer.binpack', 'farseerT74.binpack', 'farseerT75.binpack', 'farseerT76.binpack'] using the trainer branch tweakLR1PR (https://github.com/glinscott/nnue-pytorch/pull/158) and `--gpus 1 --threads 4 --num-workers 4 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 12 --features=HalfKAv2_hm^ --lambda=1.00` options passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 108280 W: 28042 L: 27636 D: 52602 Ptnml(0-2): 328, 12382, 28401, 12614, 415 https://tests.stockfishchess.org/tests/view/61bcd8c257a0d0f327c34fbd passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 259296 W: 66974 L: 66175 D: 126147 Ptnml(0-2): 146, 27096, 74452, 27721, 233 https://tests.stockfishchess.org/tests/view/61bda70957a0d0f327c37817 closes https://github.com/official-stockfish/Stockfish/pull/3870 Bench: 4633875 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index e258fdb6..d0c825eb 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-4401e826ebcc.nnue" + #define EvalFileDefaultName "nn-ac07bd334b62.nnue" namespace NNUE { From 93b14a17d168e87e7f05fc09e3ba93e737b0757e Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 25 Dec 2021 08:54:16 -0300 Subject: [PATCH 251/282] Don't direct prune a move if it's a retake STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 36304 W: 9499 L: 9226 D: 17579 Ptnml(0-2): 96, 4102, 9508, 4325, 121 https://tests.stockfishchess.org/tests/view/61c7069ae68b2a714b6dca27 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 93824 W: 24478 L: 24068 D: 45278 Ptnml(0-2): 70, 9644, 27082, 10038, 78 https://tests.stockfishchess.org/tests/view/61c725fee68b2a714b6dcfa2 closes https://github.com/official-stockfish/Stockfish/pull/3871 Bench: 4106806 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f5e9779c..a7629717 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1503,10 +1503,11 @@ moves_loop: // When in check, search starts here // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS) // will be generated. + Square prevSq = to_sq((ss-1)->currentMove); MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, - to_sq((ss-1)->currentMove)); + prevSq); // Loop through the moves until no moves remain or a beta cutoff occurs while ((move = mp.next_move()) != MOVE_NONE) @@ -1525,6 +1526,7 @@ moves_loop: // When in check, search starts here // Futility pruning and moveCount pruning (~5 Elo) if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck + && to_sq(move) != prevSq && futilityBase > -VALUE_KNOWN_WIN && type_of(move) != PROMOTION) { From 1066119083ffc8a697becbeb75a48f95add1a92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicolet?= Date: Thu, 30 Dec 2021 11:23:40 +0100 Subject: [PATCH 252/282] Tweak optimism with complexity This patch increases the optimism bonus for "complex positions", where the complexity is measured as the absolute value of the difference between material and the sophisticated NNUE evaluation (idea by Joost VandeVondele). Also rename some variables in evaluate() while there. passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 88392 W: 23150 L: 22781 D: 42461 Ptnml(0-2): 318, 9961, 23257, 10354, 306 https://tests.stockfishchess.org/tests/view/61cbbedee68b2a714b6eb110 passed LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 37848 W: 10043 L: 9766 D: 18039 Ptnml(0-2): 26, 3815, 10961, 4100, 22 https://tests.stockfishchess.org/tests/view/61cc0cc3e68b2a714b6ec28c Closes https://github.com/official-stockfish/Stockfish/pull/3875 Follow-up from https://github.com/official-stockfish/Stockfish/commit/a5a89b27c8e3225fb453d603bc4515d32bb351c3 Bench: 4125221 --- src/evaluate.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8bc51695..edff9185 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1082,29 +1082,28 @@ make_v: Value Eval::evaluate(const Position& pos) { Value v; + bool useClassical = false; // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. - - bool classical = false; - if ( !useNNUE || abs(eg_value(pos.psq_score())) * 5 > (850 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) { v = Evaluation(pos).value(); // classical - classical = abs(v) >= 300; + useClassical = abs(v) >= 300; } // If result of a classical evaluation is much lower than threshold fall back to NNUE - if (!classical && useNNUE) + if (useNNUE && !useClassical) { - int scale = 1136 - + 20 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos, true); // NNUE + int scale = 1136 + 20 * pos.non_pawn_material() / 1024; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; + Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); + int complexity = abs(nnue - psq) / 256; + optimism *= (1 + complexity); v = (nnue + optimism) * scale / 1024 - optimism; if (pos.is_chess960()) From 061f98a9e3ae439fac13b4b2dda4be47977994b1 Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Thu, 30 Dec 2021 22:50:20 -0300 Subject: [PATCH 253/282] Smooth out doDeeperSearch Adjust threshold based on the difference between newDepth and LMR depth. With more reduction, bigger fail-high is required in order to perform the deeper search. STC: LLR: 2.96 (-2.94,2.94) <0.00,2.50> Total: 93576 W: 24133 L: 23758 D: 45685 Ptnml(0-2): 260, 10493, 24935, 10812, 288 https://tests.stockfishchess.org/tests/view/61cbb5cee68b2a714b6eaf09 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 109280 W: 28198 L: 27754 D: 53328 Ptnml(0-2): 60, 11225, 31637, 11647, 71 https://tests.stockfishchess.org/tests/view/61cc03fee68b2a714b6ec091 closes https://github.com/official-stockfish/Stockfish/pull/3877 Bench: 4464723 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a7629717..32a6ae61 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -550,7 +550,7 @@ namespace { if ( ss->ply > 10 && search_explosion(thisThread) == MUST_CALM_DOWN && depth > (ss-1)->depth) - depth = (ss-1)->depth; + depth = (ss-1)->depth; constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; @@ -1212,7 +1212,7 @@ moves_loop: // When in check, search starts here // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; - doDeeperSearch = value > alpha + 88; + doDeeperSearch = value > (alpha + 62 + 20 * (newDepth - d)); didLMR = true; } else @@ -1280,7 +1280,7 @@ moves_loop: // When in check, search starts here rm.pv.push_back(*m); // We record how often the best move has been changed in each iteration. - // This information is used for time management and LMR. In MultiPV mode, + // This information is used for time management. In MultiPV mode, // we must take care to only do this for the first PV line. if ( moveCount > 1 && !thisThread->pvIdx) From 0b41887527f1d7264ee5644dfa53e00ab64441b1 Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Sat, 1 Jan 2022 14:09:08 -0300 Subject: [PATCH 254/282] Simplify away rangeReduction Remove rangeReduction, introduced in [#3717](https://github.com/official-stockfish/Stockfish/pull/3717), as it seemingly doesn't bring enough ELO anymore. It might be interesting to add new forms of reduction or tune the reduction formula in the future. STC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 45008 W: 12114 L: 11972 D: 20922 Ptnml(0-2): 174, 5031, 11952, 5173, 174 https://tests.stockfishchess.org/tests/view/61d08b7b069ca917749c9f6f LTC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 30792 W: 8235 L: 8086 D: 14471 Ptnml(0-2): 24, 3162, 8882, 3297, 31 https://tests.stockfishchess.org/tests/view/61d0a6ad069ca917749ca420 closes https://github.com/official-stockfish/Stockfish/pull/3878 Bench: 4048312 --- src/search.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 32a6ae61..2fd8245e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -69,9 +69,9 @@ namespace { // Reductions lookup table, initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] - Depth reduction(bool i, Depth d, int mn, bool rangeReduction, Value delta, Value rootDelta) { + Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904) + rangeReduction; + return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -938,8 +938,6 @@ namespace { moves_loop: // When in check, search starts here - int rangeReduction = 0; - // Step 11. A small Probcut idea, when we are in check (~0 Elo) probCutBeta = beta + 409; if ( ss->inCheck @@ -1026,7 +1024,7 @@ moves_loop: // When in check, search starts here moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta), 0); + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, delta, thisThread->rootDelta), 0); if ( captureOrPromotion || givesCheck) @@ -1159,7 +1157,7 @@ moves_loop: // When in check, search starts here || !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1))) { - Depth r = reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta); + Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode @@ -1206,10 +1204,6 @@ moves_loop: // When in check, search starts here value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - // Range reductions (~3 Elo) - if (ss->staticEval - value < 30 && depth > 7) - rangeReduction++; - // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; doDeeperSearch = value > (alpha + 62 + 20 * (newDepth - d)); From ad926d34c0105d523bfa5cb92cbcf9f337d54c08 Mon Sep 17 00:00:00 2001 From: Brad Knox <64992190+bknox83@users.noreply.github.com> Date: Mon, 3 Jan 2022 20:33:26 -0600 Subject: [PATCH 255/282] Update copyright years Happy New Year! closes https://github.com/official-stockfish/Stockfish/pull/3881 No functional change --- src/Makefile | 2 +- src/benchmark.cpp | 2 +- src/bitbase.cpp | 2 +- src/bitboard.cpp | 2 +- src/bitboard.h | 2 +- src/endgame.cpp | 2 +- src/endgame.h | 2 +- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- src/main.cpp | 2 +- src/material.cpp | 2 +- src/material.h | 2 +- src/misc.cpp | 2 +- src/misc.h | 2 +- src/movegen.cpp | 2 +- src/movegen.h | 2 +- src/movepick.cpp | 2 +- src/movepick.h | 2 +- src/nnue/evaluate_nnue.cpp | 2 +- src/nnue/evaluate_nnue.h | 2 +- src/nnue/features/half_ka_v2_hm.cpp | 2 +- src/nnue/features/half_ka_v2_hm.h | 2 +- src/nnue/layers/affine_transform.h | 2 +- src/nnue/layers/clipped_relu.h | 2 +- src/nnue/layers/input_slice.h | 2 +- src/nnue/nnue_accumulator.h | 2 +- src/nnue/nnue_architecture.h | 2 +- src/nnue/nnue_common.h | 2 +- src/nnue/nnue_feature_transformer.h | 2 +- src/pawns.cpp | 2 +- src/pawns.h | 2 +- src/position.cpp | 2 +- src/position.h | 2 +- src/psqt.cpp | 2 +- src/psqt.h | 2 +- src/search.cpp | 2 +- src/search.h | 2 +- src/simd.h | 2 +- src/syzygy/tbprobe.cpp | 2 +- src/syzygy/tbprobe.h | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- src/thread_win32_osx.h | 2 +- src/timeman.cpp | 2 +- src/timeman.h | 2 +- src/tt.cpp | 2 +- src/tt.h | 2 +- src/tune.cpp | 2 +- src/tune.h | 2 +- src/types.h | 2 +- src/uci.cpp | 2 +- src/uci.h | 2 +- src/ucioption.cpp | 2 +- 53 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/Makefile b/src/Makefile index f00df79f..406f029f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ # Stockfish, a UCI chess playing engine derived from Glaurung 2.1 -# Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) +# Copyright (C) 2004-2022 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 diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 7945a453..02ff2f9d 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 27bf4095..84300baf 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 6b84b51e..fd0ba235 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/bitboard.h b/src/bitboard.h index b29f3e24..2b6e2a69 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/endgame.cpp b/src/endgame.cpp index a44d3a1c..e773e7a9 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/endgame.h b/src/endgame.h index 146111b9..e79f696f 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/evaluate.cpp b/src/evaluate.cpp index edff9185..ab71fa67 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/evaluate.h b/src/evaluate.h index d0c825eb..57a7687d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/main.cpp b/src/main.cpp index 62e0ed52..fad0ef84 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/material.cpp b/src/material.cpp index 9d17af20..1567358a 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/material.h b/src/material.h index 26535a53..3ca169ce 100644 --- a/src/material.h +++ b/src/material.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/misc.cpp b/src/misc.cpp index b46786df..41c59b3f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/misc.h b/src/misc.h index 062b420a..688d00e7 100644 --- a/src/misc.h +++ b/src/misc.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/movegen.cpp b/src/movegen.cpp index 5095bb74..c7a3c29b 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/movegen.h b/src/movegen.h index 3f895f05..bbb35b39 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/movepick.cpp b/src/movepick.cpp index 6aa1954b..694b9222 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/movepick.h b/src/movepick.h index 426bac89..e2cbfcde 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index a534753a..862b2003 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index c7fa4a96..2e4f1f50 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 6face217..07a1d7a1 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index c7b1a68d..1e6da0bf 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 11038d69..4e85a5fe 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index c6f3ccad..0da5e821 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index b6bf1727..8f526b74 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index d41ecf95..600483b5 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 193a197d..8867fac7 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 74eaae17..1bce00ae 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 4f6a174a..f4024dce 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/pawns.cpp b/src/pawns.cpp index 70fb6f23..6e509133 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/pawns.h b/src/pawns.h index 124619d6..af0370fc 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/position.cpp b/src/position.cpp index ae1da017..ec9229ea 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/position.h b/src/position.h index 9f694a79..8dbf1493 100644 --- a/src/position.h +++ b/src/position.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/psqt.cpp b/src/psqt.cpp index 33a3e00c..ca5664c2 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/psqt.h b/src/psqt.h index 7abb1483..4ee0e379 100644 --- a/src/psqt.h +++ b/src/psqt.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/search.cpp b/src/search.cpp index 2fd8245e..86af3918 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/search.h b/src/search.h index 7a5d5bdf..806295a1 100644 --- a/src/search.h +++ b/src/search.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/simd.h b/src/simd.h index ffa54d96..7b9e8fb2 100644 --- a/src/simd.h +++ b/src/simd.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 41e867c0..a1315244 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index cf61b767..c2917fef 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/thread.cpp b/src/thread.cpp index 099efbad..30177a39 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/thread.h b/src/thread.h index 6bc1be65..a6b0b5a0 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index a21674cc..77d1c3c7 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/timeman.cpp b/src/timeman.cpp index 69d1c96f..0400401e 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/timeman.h b/src/timeman.h index b1878d65..a86f0769 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/tt.cpp b/src/tt.cpp index 4af6c9f1..c7118aea 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/tt.h b/src/tt.h index d915d92e..03fe3e14 100644 --- a/src/tt.h +++ b/src/tt.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/tune.cpp b/src/tune.cpp index ac91b606..a885845f 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/tune.h b/src/tune.h index 53d52a65..75ab484a 100644 --- a/src/tune.h +++ b/src/tune.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/types.h b/src/types.h index 02cd19de..a3a873fa 100644 --- a/src/types.h +++ b/src/types.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/uci.cpp b/src/uci.cpp index b3738a4a..741241b3 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/uci.h b/src/uci.h index d3160109..5bb24a4e 100644 --- a/src/uci.h +++ b/src/uci.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 0cafd3e9..922fa34f 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 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 From 2efda17c2ac4253dec7c566268363f4dfd391b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 7 Jan 2022 07:55:50 +0100 Subject: [PATCH 256/282] Update AUTHORS and CPU contributors files closes https://github.com/official-stockfish/Stockfish/pull/3882 No functional change --- AUTHORS | 1 + Top CPU Contributors.txt | 118 ++++++++++++++++++++------------------- 2 files changed, 61 insertions(+), 58 deletions(-) diff --git a/AUTHORS b/AUTHORS index 35ccdaf5..d7e6dc98 100644 --- a/AUTHORS +++ b/AUTHORS @@ -132,6 +132,7 @@ Michael Whiteley (protonspring) Michel Van den Bergh (vdbergh) Miguel Lahoz (miguel-l) Mikael Bäckman (mbootsector) +Mike Babigian (Farseer) Mira Miroslav Fontán (Hexik) Moez Jellouli (MJZ1977) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index f0ec51f9..718d7cca 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,68 +1,69 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2021-12-11. +Contributors to Fishtest with >10,000 CPU hours, as of 2022-01-08. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 29832955 2061725095 -mlang 2345848 162775694 -dew 1559902 93935940 -technologov 1154365 43136020 -grandphish2 988545 60725563 +noobpwnftw 30323785 2111752181 +mlang 2597136 178003354 +dew 1598255 95747056 +technologov 1395130 59347018 +grandphish2 1028906 63396841 tvijlbrief 795993 51894442 -TueRens 716909 45503672 +TueRens 737922 46359276 +okrout 719183 57150314 mibere 703840 46867607 -JojoM 675708 41046762 -okrout 648513 49953162 -linrock 563226 16184599 -pemo 520578 26816487 -gvreuls 501232 32662374 -cw 495103 33171295 -fastgm 476917 28660004 +JojoM 689134 42001146 +linrock 594355 16779359 +pemo 575248 28386103 +gvreuls 509219 33205908 +cw 500695 33575803 +fastgm 479238 28830588 crunchy 427035 27344275 -CSU_Dynasty 407300 27580858 -ctoks 386452 25757397 -oz 343309 25441096 +CSU_Dynasty 410969 27877556 +ctoks 393901 26299629 +oz 354661 26331020 Fisherman 327231 21829379 -velislav 320318 20642978 -bcross 319459 22485737 -leszek 277442 17495865 -Dantist 229980 14568674 -mgrabiak 225684 14774260 +bcross 325119 22871639 +velislav 320581 20663382 +leszek 291605 18475167 +Dantist 239411 15236750 +mgrabiak 229336 15004308 glinscott 217799 13780820 +robal 211837 13563250 nordlandia 211692 13484886 -robal 209847 13427680 -drabel 200340 13727458 +drabel 200377 13730626 bking_US 198894 11876016 Thanar 179852 12365359 -vdv 175274 9889046 -mhoram 166293 10796647 +vdv 175535 9904264 +mhoram 173134 11257113 spams 157128 10319326 marrco 150300 9402229 sqrt2 147963 9724586 -vdbergh 137186 8938965 +vdbergh 137425 8954767 CoffeeOne 137100 5024116 malala 136182 8002293 xoto 133759 9159372 -davar 122092 7960001 +davar 122113 7961971 dsmith 122059 7570238 amicic 119659 7937885 +rpngn 118952 8100045 Data 113305 8220352 BrunoBanani 112960 7436849 -rpngn 109031 7392547 CypressChess 108321 7759588 MaZePallas 102823 6633619 sterni1971 100532 5880772 sunu 100167 7040199 ElbertoOne 99028 7023771 -skiminki 98121 6478170 +skiminki 98123 6478402 +DesolatedDodo 93686 6139198 brabos 92118 6186135 +cuistot 90357 5350988 psk 89957 5984901 -cuistot 88420 5225234 -DesolatedDodo 88356 5779482 -racerschmacer 85711 6119610 +racerschmacer 85712 6119648 Vizvezdenec 83761 5344740 0x3C33 82614 5271253 BRAVONE 81239 5054681 +sschnee 78091 4678078 nssy 76497 5259388 teddybaer 75125 5407666 Pking_cda 73776 5293873 @@ -70,59 +71,59 @@ jromang 72192 5057715 solarlight 70517 5028306 dv8silencer 70287 3883992 Bobo1239 68515 4652287 +zeryl 68203 4516139 manap 66273 4121774 -sschnee 64563 3633680 tinker 64333 4268790 -zeryl 63290 4179159 +yurikvelo 61692 4262042 qurashee 61208 3429862 -yurikvelo 60387 4169900 robnjr 57262 4053117 Freja 56938 3733019 ttruscott 56010 3680085 rkl 55132 4164467 +Wolfgang 54087 3415872 renouve 53811 3501516 finfish 51360 3370515 eva42 51272 3599691 -Wolfgang 51248 3218932 -eastorwest 50311 3409935 +eastorwest 51055 3451203 rap 49985 3219146 pb00067 49727 3298270 +Spprtr 48260 3141959 bigpen0r 47667 3336927 ronaldjerum 47654 3240695 MaxKlaxxMiner 47584 2972142 biffhero 46564 3111352 -Spprtr 45877 2995437 Fifis 45843 3088497 VoyagerOne 45476 3452465 speedycpu 43842 3003273 jbwiebe 43305 2805433 +megaman7de 43042 2823256 Antihistamine 41788 2761312 mhunt 41735 2691355 -megaman7de 40060 2625050 homyur 39893 2850481 gri 39871 2515779 -oryx 38860 2976488 +oryx 38867 2976992 SC 37299 2731694 +Garf 37213 2986270 csnodgrass 36207 2688994 jmdana 36157 2210661 -Garf 36113 2897580 strelock 34716 2074055 EthanOConnor 33370 2090311 slakovv 32915 2021889 +Calis007 32024 2163604 manapbk 30987 1810399 DMBK 30675 2383552 Prcuvu 30377 2170122 anst 30301 2190091 +armo9494 30198 2438202 jkiiski 30136 1904470 +tolkki963 29918 1822290 hyperbolic.tom 29840 2017394 chuckstablers 29659 2093438 Pyafue 29650 1902349 -tolkki963 28171 1716386 +gopeto 28881 1896862 OuaisBla 27636 1578800 -armo9494 27224 2221042 chriswk 26902 1868317 achambord 26582 1767323 -gopeto 26355 1717722 Patrick_G 26276 1801617 yorkman 26193 1992080 SFTUser 25182 1675689 @@ -135,8 +136,8 @@ JanErik 23408 1703875 Isidor 23388 1680691 Norabor 23339 1602636 cisco2015 22897 1762669 +Ente 22810 1628234 Zirie 22542 1472937 -Ente 22486 1606268 team-oh 22272 1636708 MazeOfGalious 21978 1629593 sg4032 21947 1643265 @@ -151,10 +152,11 @@ horst.prack 20878 1465656 j3corre 20405 941444 Adrian.Schmidt123 20316 1281436 wei 19973 1745989 +belzedar94 19818 1434252 +user213718 19608 1334650 rstoesser 19569 1293588 eudhan 19274 1283717 vulcan 18871 1729392 -user213718 18590 1271128 jundery 18445 1115855 iisiraider 18247 1101015 ville 17883 1384026 @@ -163,52 +165,53 @@ purplefishies 17595 1092533 dju 17353 978595 DragonLord 17014 1162790 IgorLeMasson 16064 1147232 +Roady 15677 1121476 ako027ako 15671 1173203 +kdave 15539 1160356 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 OssumOpossum 14857 1007129 +spcc 14838 1034050 Karby 14808 867120 enedene 14476 905279 -jsys14 14318 843704 +fishtester 14411 1016252 +jsys14 14340 844792 bpfliegel 14298 884523 +AndreasKrug 14096 1126301 mpx86 14019 759568 jpulman 13982 870599 -kdave 13933 1045550 +Ulysses 13977 1073410 crocogoat 13803 1117422 joster 13794 950160 Nesa92 13786 1114691 mbeier 13650 1044928 -AndreasKrug 13624 1090613 Hjax 13535 915487 Dark_wizzie 13422 1007152 -Ulysses 13392 1021264 -Calis007 13267 873236 Rudolphous 13244 883140 -spcc 13085 917006 Machariel 13010 863104 mabichito 12903 749391 thijsk 12886 722107 AdrianSA 12860 804972 Flopzee 12698 894821 +infinigon 12638 933684 fatmurphy 12547 853210 scuzzi 12511 845761 SapphireBrand 12416 969604 modolief 12386 896470 -fishtester 12320 873882 Farseer 12249 694108 pgontarz 12151 848794 stocky 11954 699440 mschmidt 11941 803401 Maxim 11543 836024 infinity 11470 727027 +pirt 11434 889369 aga 11409 695071 torbjo 11395 729145 -pirt 11392 886291 Thomas A. Anderson 11372 732094 savage84 11358 670860 FormazChar 11304 847663 +dbernier 11274 806566 d64 11263 789184 -dbernier 11258 805102 MooTheCow 11237 720174 snicolet 11106 869170 ali-al-zhrani 11098 768494 @@ -217,7 +220,6 @@ Cubox 10621 826448 michaelrpg 10509 739239 OIVAS7572 10420 995586 dzjp 10343 732529 -infinigon 10319 776158 -Garruk 10314 702629 +Garruk 10332 703905 ols 10259 570669 lbraesch 10252 647825 From 9ad0ea73825bf463e842fb1fd41b5acaa8a1cfe1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 6 Jan 2022 07:44:41 +0100 Subject: [PATCH 257/282] Tune a few parameters related to evaluation based on a SPSA tune (using Autoselect) https://tests.stockfishchess.org/tests/view/61d5aa63a314fed318a57046 passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 61960 W: 16640 L: 16316 D: 29004 Ptnml(0-2): 278, 6934, 16204, 7314, 250 https://tests.stockfishchess.org/tests/view/61d7fe4af5fd40f357469a8d passed LTC: LLR: 2.97 (-2.94,2.94) <0.50,3.00> Total: 79408 W: 21994 L: 21618 D: 35796 Ptnml(0-2): 106, 7887, 23331, 8285, 95 https://tests.stockfishchess.org/tests/view/61d836b7f5fd40f35746a3d5 closes https://github.com/official-stockfish/Stockfish/pull/3883 Bench: 4266621 --- src/evaluate.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ab71fa67..4f3843f8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -192,8 +192,8 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(3130); - constexpr Value LazyThreshold2 = Value(2204); + constexpr Value LazyThreshold1 = Value(3631); + constexpr Value LazyThreshold2 = Value(2084); constexpr Value SpaceThreshold = Value(11551); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -1087,10 +1087,10 @@ Value Eval::evaluate(const Position& pos) { // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. if ( !useNNUE - || abs(eg_value(pos.psq_score())) * 5 > (850 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) + || abs(eg_value(pos.psq_score())) * 5 > (849 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) { v = Evaluation(pos).value(); // classical - useClassical = abs(v) >= 300; + useClassical = abs(v) >= 298; } // If result of a classical evaluation is much lower than threshold fall back to NNUE @@ -1101,9 +1101,9 @@ Value Eval::evaluate(const Position& pos) { Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); - int complexity = abs(nnue - psq) / 256; + int complexity = 35 * abs(nnue - psq) / 256; - optimism *= (1 + complexity); + optimism = optimism * (44 + complexity) / 32; v = (nnue + optimism) * scale / 1024 - optimism; if (pos.is_chess960()) @@ -1111,7 +1111,7 @@ Value Eval::evaluate(const Position& pos) { } // Damp down the evaluation linearly when shuffling - v = v * (207 - pos.rule50_count()) / 207; + v = v * (208 - pos.rule50_count()) / 208; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From c5a280c0125b71cdf43bf326551bc95bb4dbc014 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 7 Jan 2022 22:57:09 +0100 Subject: [PATCH 258/282] Tune FRC trapped Bishop patch now that fishtest can deal with FRC, retune this correction. Add an additional fen to bench with cornered B and N. passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 49672 W: 7358 L: 7082 D: 35232 Ptnml(0-2): 241, 4329, 15458, 4529, 279 https://tests.stockfishchess.org/tests/view/61d8b7bf9fea7913d9c63cb7 passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 86688 W: 8308 L: 8007 D: 70373 Ptnml(0-2): 92, 4943, 32989, 5212, 108 https://tests.stockfishchess.org/tests/view/61d92dcb9fea7913d9c650ad closes https://github.com/official-stockfish/Stockfish/pull/3884 Bench: 4326560 --- src/benchmark.cpp | 1 + src/evaluate.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 02ff2f9d..e1c025ad 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -87,6 +87,7 @@ const vector Defaults = { // Chess 960 "setoption name UCI_Chess960 value true", "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", + "nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1", "setoption name UCI_Chess960 value false" }; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 4f3843f8..a5c049a8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1069,8 +1069,8 @@ make_v: && pos.piece_on(SQ_G7) == B_PAWN) correction += CorneredBishop; - return pos.side_to_move() == WHITE ? Value(5 * correction) - : -Value(5 * correction); + return pos.side_to_move() == WHITE ? Value(3 * correction) + : -Value(3 * correction); } } // namespace Eval From 44b1ba89a95f394f3e180eb508f2d7798417c86e Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 10 Jan 2022 21:29:25 +0300 Subject: [PATCH 259/282] Adjust pruning constants This patch is a modification of original tuning done by vondele that failed yellow. Value differences are divided by 2. Passed STC https://tests.stockfishchess.org/tests/view/61d918239fea7913d9c64cdf LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 98968 W: 26248 L: 25858 D: 46862 Ptnml(0-2): 392, 11085, 26156, 11443, 408 Passed LTC https://tests.stockfishchess.org/tests/view/61d99e3c9fea7913d9c663e4 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 215232 W: 58191 L: 57492 D: 99549 Ptnml(0-2): 271, 22124, 62138, 22801, 282 closes https://github.com/official-stockfish/Stockfish/pull/3885 bench 4572746 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 86af3918..58873c89 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1040,7 +1040,7 @@ moves_loop: // When in check, search starts here continue; // SEE based pruning (~9 Elo) - if (!pos.see_ge(move, Value(-218) * depth)) + if (!pos.see_ge(move, Value(-217) * depth)) continue; } else @@ -1051,7 +1051,7 @@ moves_loop: // When in check, search starts here // Continuation history based pruning (~2 Elo) if ( lmrDepth < 5 - && history < -3000 * depth + 3000) + && history < -3875 * (depth - 1)) continue; history += thisThread->mainHistory[us][from_to(move)]; @@ -1059,7 +1059,7 @@ moves_loop: // When in check, search starts here // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck && lmrDepth < 8 - && ss->staticEval + 142 + 139 * lmrDepth + history / 64 <= alpha) + && ss->staticEval + 138 + 137 * lmrDepth + history / 64 <= alpha) continue; // Prune moves with negative SEE (~3 Elo) From c5d45d3220f74045aff249c47abd91d8d663b748 Mon Sep 17 00:00:00 2001 From: pschneider1968 <36973164+pschneider1968@users.noreply.github.com> Date: Mon, 27 Dec 2021 21:16:04 +0100 Subject: [PATCH 260/282] Fix Makefile for compilation with clang on Windows use static compilation and added exclusion of -latomic for Clang/MSYS2 as per ppigazzini's suggestion fixes #3872 closes https://github.com/official-stockfish/Stockfish/pull/3873 No functional change --- src/Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Makefile b/src/Makefile index 406f029f..0e889888 100644 --- a/src/Makefile +++ b/src/Makefile @@ -404,12 +404,14 @@ ifeq ($(COMP),clang) ifneq ($(KERNEL),Darwin) ifneq ($(KERNEL),OpenBSD) ifneq ($(KERNEL),FreeBSD) + ifneq ($(findstring MINGW,$(KERNEL)),MINGW) ifneq ($(RTLIB),compiler-rt) LDFLAGS += -latomic endif endif endif endif + endif ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(OS),Android) @@ -420,6 +422,11 @@ ifeq ($(COMP),clang) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif + + ifeq ($(findstring MINGW,$(KERNEL)),MINGW) + LDFLAGS += -static + endif + endif ifeq ($(KERNEL),Darwin) From 7678d63cf2323e51c01e60cdff4ac3d685313790 Mon Sep 17 00:00:00 2001 From: Rui Coelho Date: Thu, 13 Jan 2022 18:30:53 +0000 Subject: [PATCH 261/282] Use complexity in search This patch uses the complexity measure (from #3875) as a heuristic for null move pruning. Hopefully, there may be room to use it in other pruning techniques. I would like to thank vondele and locutus2 for the feedback and suggestions during testing. Passed STC LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 35000 W: 9624 L: 9347 D: 16029 Ptnml(0-2): 156, 3894, 9137, 4143, 170 https://tests.stockfishchess.org/tests/view/61dda784c65bf87d6c45ab80 Passed LTC LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 230776 W: 64227 L: 63454 D: 103095 Ptnml(0-2): 1082, 23100, 66380, 23615, 1211 https://tests.stockfishchess.org/tests/view/61ddd0cf3ddbc32543e72c2b Closes https://github.com/official-stockfish/Stockfish/pull/3890 Bench: 4464962 --- AUTHORS | 1 + src/search.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index d7e6dc98..f49c1db0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -166,6 +166,7 @@ Rodrigo Exterckötter Tjäder Ron Britvich (Britvich) Ronald de Man (syzygy1, syzygy) rqs +Rui Coelho (ruicoelhopedro) Ryan Schmitt Ryan Takker Sami Kiminki (skiminki) diff --git a/src/search.cpp b/src/search.cpp index 58873c89..c81496d1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -589,7 +589,7 @@ namespace { bool givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; - int moveCount, captureCount, quietCount, bestMoveCount, improvement; + int moveCount, captureCount, quietCount, bestMoveCount, improvement, complexity; // Step 1. Initialize node ss->inCheck = pos.checkers(); @@ -760,6 +760,7 @@ namespace { ss->staticEval = eval = VALUE_NONE; improving = false; improvement = 0; + complexity = 0; goto moves_loop; } else if (ss->ttHit) @@ -803,6 +804,7 @@ namespace { : 200; improving = improvement > 0; + complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score()))); // Step 7. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. @@ -818,7 +820,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 + && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 + complexity / 25 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From d11101e4c6cd6aafca06e07d24e1c7026c92f6e7 Mon Sep 17 00:00:00 2001 From: proukornew Date: Fri, 17 Dec 2021 01:30:23 +0300 Subject: [PATCH 262/282] Improve logic on mingw There is no need to point g++, if we explicitly choose mingw. Now for cygwin: make COMP=mingw ARCH=x86-64-modern build closes https://github.com/official-stockfish/Stockfish/pull/3860 No functional change --- src/Makefile | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Makefile b/src/Makefile index 0e889888..90e596af 100644 --- a/src/Makefile +++ b/src/Makefile @@ -368,22 +368,18 @@ endif ifeq ($(COMP),mingw) comp=mingw - ifeq ($(KERNEL),Linux) - ifeq ($(bits),64) - ifeq ($(shell which x86_64-w64-mingw32-c++-posix),) - CXX=x86_64-w64-mingw32-c++ - else - CXX=x86_64-w64-mingw32-c++-posix - endif + ifeq ($(bits),64) + ifeq ($(shell which x86_64-w64-mingw32-c++-posix 2> /dev/null),) + CXX=x86_64-w64-mingw32-c++ else - ifeq ($(shell which i686-w64-mingw32-c++-posix),) - CXX=i686-w64-mingw32-c++ - else - CXX=i686-w64-mingw32-c++-posix - endif + CXX=x86_64-w64-mingw32-c++-posix endif else - CXX=g++ + ifeq ($(shell which i686-w64-mingw32-c++-posix 2> /dev/null),) + CXX=i686-w64-mingw32-c++ + else + CXX=i686-w64-mingw32-c++-posix + endif endif CXXFLAGS += -pedantic -Wextra -Wshadow From 2b0372319d2a6797c49cb24dca5da221a669e36a Mon Sep 17 00:00:00 2001 From: Rui Coelho Date: Mon, 17 Jan 2022 16:51:20 +0000 Subject: [PATCH 263/282] Use average complexity for time management This patch is a variant of the idea by locutus2 (https://tests.stockfishchess.org/tests/view/61e1f24cb1f9959fe5d88168) to adjust the total time depending on the average complexity of the position. Passed STC LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 39664 W: 10765 L: 10487 D: 18412 Ptnml(0-2): 162, 4213, 10837, 4425, 195 https://tests.stockfishchess.org/tests/view/61e2df8b65a644da8c9ea708 Passed LTC LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 127656 W: 34505 L: 34028 D: 59123 Ptnml(0-2): 116, 12435, 38261, 12888, 128 https://tests.stockfishchess.org/tests/view/61e31db5babab931824dff5e closes https://github.com/official-stockfish/Stockfish/pull/3892 Bench: 4464962 --- src/misc.h | 3 +++ src/search.cpp | 8 +++++++- src/thread.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/misc.h b/src/misc.h index 688d00e7..b9626733 100644 --- a/src/misc.h +++ b/src/misc.h @@ -105,6 +105,9 @@ class RunningAverage { bool is_greater(int64_t a, int64_t b) { return b * average > a * PERIOD * RESOLUTION ; } + int64_t value() + { return average / (PERIOD * RESOLUTION); } + private : static constexpr int64_t PERIOD = 4096; static constexpr int64_t RESOLUTION = 1024; diff --git a/src/search.cpp b/src/search.cpp index c81496d1..c9d5da64 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -329,6 +329,7 @@ void Thread::search() { doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0% doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0% + complexityAverage.set(232, 1); nodesLastExplosive = nodes; nodesLastNormal = nodes; @@ -496,7 +497,10 @@ void Thread::search() { double reduction = (1.47 + mainThread->previousTimeReduction) / (2.32 * timeReduction); double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); - double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; + int complexity = mainThread->complexityAverage.value(); + double complexPosition = std::clamp(1.0 + (complexity - 232) / 1750.0, 0.5, 1.5); + + double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; // Cap used time in case of a single legal move for a better viewer experience in tournaments // yielding correct scores and sufficiently fast moves. @@ -806,6 +810,8 @@ namespace { improving = improvement > 0; complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score()))); + thisThread->complexityAverage.update(complexity); + // Step 7. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv diff --git a/src/thread.h b/src/thread.h index a6b0b5a0..c3d38f3c 100644 --- a/src/thread.h +++ b/src/thread.h @@ -61,6 +61,7 @@ public: Material::Table materialTable; size_t pvIdx, pvLast; RunningAverage doubleExtensionAverage[COLOR_NB]; + RunningAverage complexityAverage; uint64_t nodesLastExplosive; uint64_t nodesLastNormal; std::atomic nodes, tbHits, bestMoveChanges; From 48bf1a386f031947d059a0dc26616366b0f2d5d3 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Wed, 19 Jan 2022 16:56:38 +0100 Subject: [PATCH 264/282] Add msys2 Clang x86_64 to GitHub Action matrix Also use Windows Server 2022 virtual environment for msys2 builds. closes https://github.com/official-stockfish/Stockfish/pull/3893 No functional change --- .github/workflows/stockfish.yml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 54b0cb12..33126a11 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -61,8 +61,8 @@ jobs: shell: 'bash {0}' } - { - name: "Windows 2019 Mingw-w64 GCC x86_64", - os: windows-2019, + name: "Windows 2022 Mingw-w64 GCC x86_64", + os: windows-2022, compiler: g++, comp: gcc, run_expensive_tests: false, @@ -73,8 +73,8 @@ jobs: shell: 'msys2 {0}' } - { - name: "Windows 2019 Mingw-w64 GCC i686", - os: windows-2019, + name: "Windows 2022 Mingw-w64 GCC i686", + os: windows-2022, compiler: g++, comp: gcc, run_expensive_tests: false, @@ -84,6 +84,18 @@ jobs: msys_env: 'i686', shell: 'msys2 {0}' } + - { + name: "Windows 2022 Mingw-w64 Clang x86_64", + os: windows-2022, + compiler: clang++, + comp: clang, + run_expensive_tests: false, + run_32bit_tests: false, + run_64bit_tests: true, + msys_sys: 'clang64', + msys_env: 'clang-x86_64', + shell: 'msys2 {0}' + } defaults: run: @@ -105,7 +117,7 @@ jobs: uses: msys2/setup-msys2@v2 with: msystem: ${{matrix.config.msys_sys}} - install: mingw-w64-${{matrix.config.msys_env}}-gcc make git expect + install: mingw-w64-${{matrix.config.msys_env}}-${{matrix.config.comp}} make git expect - name: Download the used network from the fishtest framework run: | From 67062637f48e7d63e750dee63ba9e881ee6d9382 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Mon, 17 Jan 2022 14:03:16 +0100 Subject: [PATCH 265/282] Improve Makefile for Windows native builds A Windows Native Build (WNB) can be done: - on Windows, using a recent mingw-w64 g++/clang compiler distributed by msys2, cygwin and others - on Linux, using mingw-w64 g++ to cross compile Improvements: - check for a WNB in a proper way and set a variable to simplify the code - set the proper EXE for a WNB - use the proper name for the mingw-w64 clang compiler - use the static linking for a WNB - use wine to make a PGO cross compile on Linux (also with Intel SDE) - enable the LTO build for mingw-w64 g++ compiler - set `lto=auto` to use the make's job server, if available, or otherwise to fall back to autodetection of the number of CPU threads - clean up all the temporary LTO files saved in the local directory Tested on: - msys2 MINGW64 (g++), UCRT64 (g++), MINGW32 (g++), CLANG64 (clang) environments - cygwin mingw-w64 g++ - Ubuntu 18.04 & 21.10 mingw-w64 PGO cross compile (also with Intel SDE) closes #3891 No functional change --- src/Makefile | 80 +++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/src/Makefile b/src/Makefile index 90e596af..1b121299 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,11 +19,27 @@ ### Section 1. General Configuration ### ========================================================================== +### Establish the operating system name +KERNEL = $(shell uname -s) +ifeq ($(KERNEL),Linux) + OS = $(shell uname -o) +endif + +### Target Windows OS +ifeq ($(OS),Windows_NT) + target_windows = yes +else ifeq ($(COMP),mingw) + target_windows = yes + ifeq ($(WINE_PATH),) + WINE_PATH = $(shell which wine) + endif +endif + ### Executable name -ifeq ($(COMP),mingw) -EXE = stockfish.exe +ifeq ($(target_windows),yes) + EXE = stockfish.exe else -EXE = stockfish + EXE = stockfish endif ### Installation dir definitions @@ -32,9 +48,9 @@ BINDIR = $(PREFIX)/bin ### Built-in benchmark for pgo-builds ifeq ($(SDE_PATH),) - PGOBENCH = ./$(EXE) bench + PGOBENCH = $(WINE_PATH) ./$(EXE) bench else - PGOBENCH = $(SDE_PATH) -- ./$(EXE) bench + PGOBENCH = $(SDE_PATH) -- $(WINE_PATH) ./$(EXE) bench endif ### Source and object files @@ -47,12 +63,6 @@ OBJS = $(notdir $(SRCS:.cpp=.o)) VPATH = syzygy:nnue:nnue/features -### Establish the operating system name -KERNEL = $(shell uname -s) -ifeq ($(KERNEL),Linux) - OS = $(shell uname -o) -endif - ### ========================================================================== ### Section 2. High-level Configuration ### ========================================================================== @@ -365,6 +375,10 @@ ifeq ($(COMP),gcc) endif endif +ifeq ($(target_windows),yes) + LDFLAGS += -static +endif + ifeq ($(COMP),mingw) comp=mingw @@ -381,9 +395,7 @@ ifeq ($(COMP),mingw) CXX=i686-w64-mingw32-c++-posix endif endif - CXXFLAGS += -pedantic -Wextra -Wshadow - LDFLAGS += -static endif ifeq ($(COMP),icc) @@ -395,19 +407,19 @@ endif ifeq ($(COMP),clang) comp=clang CXX=clang++ + ifeq ($(target_windows),yes) + CXX=x86_64-w64-mingw32-clang++ + endif + CXXFLAGS += -pedantic -Wextra -Wshadow - ifneq ($(KERNEL),Darwin) - ifneq ($(KERNEL),OpenBSD) - ifneq ($(KERNEL),FreeBSD) - ifneq ($(findstring MINGW,$(KERNEL)),MINGW) + ifeq ($(filter $(KERNEL),Darwin OpenBSD FreeBSD),) + ifeq ($(target_windows),) ifneq ($(RTLIB),compiler-rt) LDFLAGS += -latomic endif endif endif - endif - endif ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(OS),Android) @@ -418,11 +430,6 @@ ifeq ($(COMP),clang) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif - - ifeq ($(findstring MINGW,$(KERNEL)),MINGW) - LDFLAGS += -static - endif - endif ifeq ($(KERNEL),Darwin) @@ -656,9 +663,7 @@ ifeq ($(optimize),yes) ifeq ($(debug), no) ifeq ($(comp),clang) CXXFLAGS += -flto - ifneq ($(findstring MINGW,$(KERNEL)),) - CXXFLAGS += -fuse-ld=lld - else ifneq ($(findstring MSYS,$(KERNEL)),) + ifeq ($(target_windows),yes) CXXFLAGS += -fuse-ld=lld endif LDFLAGS += $(CXXFLAGS) @@ -669,25 +674,17 @@ ifeq ($(debug), no) ifeq ($(gccisclang),) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) -flto=jobserver - ifneq ($(findstring MINGW,$(KERNEL)),) - LDFLAGS += -save-temps - else ifneq ($(findstring MSYS,$(KERNEL)),) - LDFLAGS += -save-temps - endif else CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif -# To use LTO and static linking on windows, the tool chain requires a recent gcc: -# gcc version 10.1 in msys2 or TDM-GCC version 9.2 are known to work, older might not. -# So, only enable it for a cross from Linux by default. +# To use LTO and static linking on Windows, +# the tool chain requires gcc version 10.1 or later. else ifeq ($(comp),mingw) - ifeq ($(KERNEL),Linux) ifneq ($(arch),i386) - CXXFLAGS += -flto - LDFLAGS += $(CXXFLAGS) -flto=jobserver - endif + CXXFLAGS += -flto=auto + LDFLAGS += $(CXXFLAGS) -save-temps endif endif endif @@ -843,8 +840,9 @@ profileclean: @rm -rf profdir @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s @rm -f stockfish.profdata *.profraw - @rm -f stockfish.exe.lto_wrapper_args - @rm -f stockfish.exe.ltrans.out + @rm -f stockfish.*args* + @rm -f stockfish.*lt* + @rm -f stockfish.res @rm -f ./-lstdc++.res default: From 77cf5704b6deda52171dafeb2fae370273ebd797 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 20 Jan 2022 17:49:27 +0100 Subject: [PATCH 266/282] Revert -flto=auto on mingw causes issues on some installations (glinscott/fishtest#1255). closes https://github.com/official-stockfish/Stockfish/pull/3898 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 1b121299..5f3e739f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -683,7 +683,7 @@ ifeq ($(debug), no) # the tool chain requires gcc version 10.1 or later. else ifeq ($(comp),mingw) ifneq ($(arch),i386) - CXXFLAGS += -flto=auto + CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) -save-temps endif endif From 9083050be692b2d9a4f281e78b967755e00cfc39 Mon Sep 17 00:00:00 2001 From: "J. Oster" Date: Sat, 15 Jan 2022 18:18:52 +0100 Subject: [PATCH 267/282] Simplify limiting extensions. Replace the current method for limiting extensions to avoid search getting stuck with a much simpler method. the test position in https://github.com/official-stockfish/Stockfish/commit/73018a03375b4b72ee482eb5a4a2152d7e4f0aac can still be searched without stuck search. fixes #3815 where the search now makes progress with rootDepth shows robust behavior in a d10 search for 1M positions. passed STC https://tests.stockfishchess.org/tests/view/61e303e3babab931824dfb18 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 57568 W: 15449 L: 15327 D: 26792 Ptnml(0-2): 243, 6211, 15779, 6283, 268 passed LTC https://tests.stockfishchess.org/tests/view/61e3586cbabab931824e091c LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 128200 W: 34632 L: 34613 D: 58955 Ptnml(0-2): 124, 12559, 38710, 12588, 119 closes https://github.com/official-stockfish/Stockfish/pull/3899 Bench: 4550528 --- src/search.cpp | 152 +++++++++++++++++++------------------------------ src/thread.h | 7 +-- 2 files changed, 59 insertions(+), 100 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c9d5da64..792c9729 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -88,30 +88,6 @@ namespace { return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); } - // Check if the current thread is in a search explosion - ExplosionState search_explosion(Thread* thisThread) { - - uint64_t nodesNow = thisThread->nodes; - bool explosive = thisThread->doubleExtensionAverage[WHITE].is_greater(2, 100) - || thisThread->doubleExtensionAverage[BLACK].is_greater(2, 100); - - if (explosive) - thisThread->nodesLastExplosive = nodesNow; - else - thisThread->nodesLastNormal = nodesNow; - - if ( explosive - && thisThread->state == EXPLOSION_NONE - && nodesNow - thisThread->nodesLastNormal > 6000000) - thisThread->state = MUST_CALM_DOWN; - - if ( thisThread->state == MUST_CALM_DOWN - && nodesNow - thisThread->nodesLastExplosive > 6000000) - thisThread->state = EXPLOSION_NONE; - - return thisThread->state; - } - // Skill structure is used to implement strength limit. If we have an uci_elo then // we convert it to a suitable fractional skill level using anchoring to CCRL Elo // (goldfish 1.13 = 2000) and a fit through Ordo derived Elo for match (TC 60+0.6) @@ -327,16 +303,11 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0% - doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0% complexityAverage.set(232, 1); - nodesLastExplosive = nodes; - nodesLastNormal = nodes; - state = EXPLOSION_NONE; - trend = SCORE_ZERO; - optimism[ us] = Value(25); - optimism[~us] = -optimism[us]; + trend = SCORE_ZERO; + optimism[ us] = Value(25); + optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -548,14 +519,6 @@ namespace { template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { - Thread* thisThread = pos.this_thread(); - - // Step 0. Limit search explosion - if ( ss->ply > 10 - && search_explosion(thisThread) == MUST_CALM_DOWN - && depth > (ss-1)->depth) - depth = (ss-1)->depth; - constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; const Depth maxNextDepth = rootNode ? depth : depth + 1; @@ -596,6 +559,7 @@ namespace { int moveCount, captureCount, quietCount, bestMoveCount, improvement, complexity; // Step 1. Initialize node + Thread* thisThread = pos.this_thread(); ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); @@ -643,9 +607,6 @@ namespace { ss->depth = depth; Square prevSq = to_sq((ss-1)->currentMove); - // Update the running average statistics for double extensions - thisThread->doubleExtensionAverage[us].update(ss->depth > (ss-1)->depth); - // Initialize statScore to zero for the grandchildren of the current position. // So statScore is shared between all grandchildren and only the first grandchild // starts with statScore = 0. Later grandchildren start with the last calculated @@ -1077,65 +1038,68 @@ moves_loop: // When in check, search starts here } // Step 14. Extensions (~66 Elo) - - // Singular extension search (~58 Elo). If all moves but one fail low on a - // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), - // then that move is singular and should be extended. To verify this we do - // a reduced search on all the other moves but the ttMove and if the - // result is lower than ttValue minus a margin, then we will extend the ttMove. - if ( !rootNode - && depth >= 6 + 2 * (PvNode && tte->is_pv()) - && move == ttMove - && !excludedMove // Avoid recursive singular search - /* && ttValue != VALUE_NONE Already implicit in the next condition */ - && abs(ttValue) < VALUE_KNOWN_WIN - && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 3) + // We take care to not overdo to avoid search getting stuck. + if (ss->ply < thisThread->rootDepth * 2) { - Value singularBeta = ttValue - 3 * depth; - Depth singularDepth = (depth - 1) / 2; - - ss->excludedMove = move; - value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); - ss->excludedMove = MOVE_NONE; - - if (value < singularBeta) + // Singular extension search (~58 Elo). If all moves but one fail low on a + // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), + // then that move is singular and should be extended. To verify this we do + // a reduced search on all the other moves but the ttMove and if the + // result is lower than ttValue minus a margin, then we will extend the ttMove. + if ( !rootNode + && depth >= 6 + 2 * (PvNode && tte->is_pv()) + && move == ttMove + && !excludedMove // Avoid recursive singular search + /* && ttValue != VALUE_NONE Already implicit in the next condition */ + && abs(ttValue) < VALUE_KNOWN_WIN + && (tte->bound() & BOUND_LOWER) + && tte->depth() >= depth - 3) { - extension = 1; + Value singularBeta = ttValue - 3 * depth; + Depth singularDepth = (depth - 1) / 2; - // Avoid search explosion by limiting the number of double extensions - if ( !PvNode - && value < singularBeta - 75 - && ss->doubleExtensions <= 6) - extension = 2; + ss->excludedMove = move; + value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); + ss->excludedMove = MOVE_NONE; + + if (value < singularBeta) + { + extension = 1; + + // Avoid search explosion by limiting the number of double extensions + if ( !PvNode + && value < singularBeta - 75 + && ss->doubleExtensions <= 6) + extension = 2; + } + + // Multi-cut pruning + // Our ttMove is assumed to fail high, and now we failed high also on a reduced + // search without the ttMove. So we assume this expected Cut-node is not singular, + // that multiple moves fail high, and we can prune the whole subtree by returning + // a soft bound. + else if (singularBeta >= beta) + return singularBeta; + + // If the eval of ttMove is greater than beta, we reduce it (negative extension) + else if (ttValue >= beta) + extension = -2; } - // Multi-cut pruning - // Our ttMove is assumed to fail high, and now we failed high also on a reduced - // search without the ttMove. So we assume this expected Cut-node is not singular, - // that multiple moves fail high, and we can prune the whole subtree by returning - // a soft bound. - else if (singularBeta >= beta) - return singularBeta; + // Check extensions (~1 Elo) + else if ( givesCheck + && depth > 6 + && abs(ss->staticEval) > 100) + extension = 1; - // If the eval of ttMove is greater than beta, we reduce it (negative extension) - else if (ttValue >= beta) - extension = -2; + // Quiet ttMove extensions (~0 Elo) + else if ( PvNode + && move == ttMove + && move == ss->killers[0] + && (*contHist[0])[movedPiece][to_sq(move)] >= 10000) + extension = 1; } - // Check extensions (~1 Elo) - else if ( givesCheck - && depth > 6 - && abs(ss->staticEval) > 100) - extension = 1; - - // Quiet ttMove extensions (~0 Elo) - else if ( PvNode - && move == ttMove - && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 10000) - extension = 1; - // Add extension to new depth newDepth += extension; ss->doubleExtensions = (ss-1)->doubleExtensions + (extension == 2); diff --git a/src/thread.h b/src/thread.h index c3d38f3c..594a8ea2 100644 --- a/src/thread.h +++ b/src/thread.h @@ -60,16 +60,11 @@ public: Pawns::Table pawnsTable; Material::Table materialTable; size_t pvIdx, pvLast; - RunningAverage doubleExtensionAverage[COLOR_NB]; RunningAverage complexityAverage; - uint64_t nodesLastExplosive; - uint64_t nodesLastNormal; std::atomic nodes, tbHits, bestMoveChanges; - Value bestValue; int selDepth, nmpMinPly; Color nmpColor; - ExplosionState state; - Value optimism[COLOR_NB]; + Value bestValue, optimism[COLOR_NB]; Position rootPos; StateInfo rootState; From bddd38c45e1f1457e1435cd066036e98f54d7397 Mon Sep 17 00:00:00 2001 From: pschneider1968 <36973164+pschneider1968@users.noreply.github.com> Date: Fri, 21 Jan 2022 14:11:53 +0100 Subject: [PATCH 268/282] Fix Makefile for Android NDK cross-compile For cross-compiling to Android on windows, the Makefile needs some tweaks. Tested with Android NDK 23.1.7779620 and 21.4.7075529, using Windows 10 with clean MSYS2 environment (i.e. no MINGW/GCC/Clang toolchain in PATH) and Fedora 35, with build target: build ARCH=armv8 COMP=ndk The resulting binary runs fine inside Droidfish on my Samsung Galaxy Note20 Ultra and Samsung Galaxy Tab S7+ Other builds tested to exclude regressions: MINGW64/Clang64 build on Windows; MINGW64 cross build, native Clang and GCC builds on Fedora. wiki docs https://github.com/glinscott/fishtest/wiki/Cross-compiling-Stockfish-for-Android-on-Windows-and-Linux closes https://github.com/official-stockfish/Stockfish/pull/3901 No functional change --- src/Makefile | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5f3e739f..9666f138 100644 --- a/src/Makefile +++ b/src/Makefile @@ -27,7 +27,9 @@ endif ### Target Windows OS ifeq ($(OS),Windows_NT) - target_windows = yes + ifneq ($(COMP),ndk) + target_windows = yes + endif else ifeq ($(COMP),mingw) target_windows = yes ifeq ($(WINE_PATH),) @@ -451,11 +453,19 @@ ifeq ($(COMP),ndk) ifeq ($(arch),armv7) CXX=armv7a-linux-androideabi16-clang++ CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon - STRIP=arm-linux-androideabi-strip + ifneq ($(shell which arm-linux-androideabi-strip 2>/dev/null),) + STRIP=arm-linux-androideabi-strip + else + STRIP=llvm-strip + endif endif ifeq ($(arch),armv8) CXX=aarch64-linux-android21-clang++ - STRIP=aarch64-linux-android-strip + ifneq ($(shell which aarch64-linux-android-strip 2>/dev/null),) + STRIP=aarch64-linux-android-strip + else + STRIP=llvm-strip + endif endif LDFLAGS += -static-libstdc++ -pie -lm -latomic endif @@ -801,7 +811,7 @@ strip: install: -mkdir -p -m 755 $(BINDIR) -cp $(EXE) $(BINDIR) - -strip $(BINDIR)/$(EXE) + $(STRIP) $(BINDIR)/$(EXE) # clean all clean: objclean profileclean @@ -833,7 +843,7 @@ net: # clean binaries and objects objclean: - @rm -f $(EXE) *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o + @rm -f stockfish stockfish.exe *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o # clean auxiliary profiling files profileclean: From 8b4afcf8f7a38ee3060f39d563aad5956fe723d7 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 25 Jan 2022 02:22:03 +0300 Subject: [PATCH 269/282] Scale child node futility pruning with previous move history. Idea is to do more futility pruning if previous move has bad histories and less if it has good histories. passed STC https://tests.stockfishchess.org/tests/view/61e3757fbabab931824e0db7 LLR: 2.96 (-2.94,2.94) <0.00,2.50> Total: 156816 W: 42282 L: 41777 D: 72757 Ptnml(0-2): 737, 17775, 40913, 18212, 771 passed LTC https://tests.stockfishchess.org/tests/view/61e43496928632f7813a5535 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 349968 W: 94612 L: 93604 D: 161752 Ptnml(0-2): 300, 35934, 101550, 36858, 342 closes https://github.com/official-stockfish/Stockfish/pull/3903 bench 4720954 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 792c9729..c3360ad1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -777,7 +777,8 @@ namespace { // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 - && eval - futility_margin(depth, improving) >= beta + && eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta + && eval >= beta && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; From 90d051952f4fce415f09f316e24e1701aafa7a92 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 29 Jan 2022 06:39:40 +0300 Subject: [PATCH 270/282] Do stats updates after LMR for captures Since captures that are in LMR use continuation histories of corresponding quiet moves it makes sense to update this histories if this capture passes LMR by analogy to existing logic for quiet moves. Passed STC https://tests.stockfishchess.org/tests/view/61f367eef7fba9f1a4f1318b LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 208464 W: 56006 L: 55407 D: 97051 Ptnml(0-2): 964, 23588, 54655, 23935, 1090 Passed LTC https://tests.stockfishchess.org/tests/view/61f41e34f7fba9f1a4f15241 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 69144 W: 18793 L: 18441 D: 31910 Ptnml(0-2): 65, 6982, 20142, 7302, 81 closes https://github.com/official-stockfish/Stockfish/pull/3910 bench 4637392 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c3360ad1..8d5ad979 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1194,11 +1194,14 @@ moves_loop: // When in check, search starts here value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); // If the move passed LMR update its stats - if (didLMR && !captureOrPromotion) + if (didLMR) { int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); + if (captureOrPromotion) + bonus /= 4; + update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } } From 50200de5af09db3a9143082e4e66baef11b4be2a Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 31 Jan 2022 07:25:10 +0300 Subject: [PATCH 271/282] Cleanup and update CPU contributors closes https://github.com/official-stockfish/Stockfish/pull/3917 No functional change --- Top CPU Contributors.txt | 113 ++++++++++++++++++++------------------- src/types.h | 5 -- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 718d7cca..4bc96cde 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,90 +1,91 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2022-01-08. +Contributors to Fishtest with >10,000 CPU hours, as of 2022-02-05. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 30323785 2111752181 -mlang 2597136 178003354 -dew 1598255 95747056 -technologov 1395130 59347018 -grandphish2 1028906 63396841 +noobpwnftw 30730952 2158431735 +mlang 2729669 187335452 +technologov 1696847 74478658 +dew 1635640 97483012 +grandphish2 1062754 64955639 tvijlbrief 795993 51894442 -TueRens 737922 46359276 -okrout 719183 57150314 +okrout 773704 63465204 +TueRens 766198 47770388 mibere 703840 46867607 -JojoM 689134 42001146 -linrock 594355 16779359 -pemo 575248 28386103 -gvreuls 509219 33205908 -cw 500695 33575803 -fastgm 479238 28830588 +JojoM 703005 42689868 +pemo 634102 29868807 +linrock 626939 17408017 +gvreuls 517442 33605006 +cw 503905 33850487 +fastgm 482847 29004732 crunchy 427035 27344275 -CSU_Dynasty 410969 27877556 -ctoks 393901 26299629 -oz 354661 26331020 +CSU_Dynasty 415864 28116776 +ctoks 403102 26737127 +oz 357710 26490208 +bcross 331095 23165889 Fisherman 327231 21829379 -bcross 325119 22871639 -velislav 320581 20663382 -leszek 291605 18475167 -Dantist 239411 15236750 -mgrabiak 229336 15004308 +velislav 321708 20729264 +leszek 303654 19063973 +Dantist 251015 15843226 +mgrabiak 231973 15162494 glinscott 217799 13780820 -robal 211837 13563250 +robal 213960 13665726 nordlandia 211692 13484886 -drabel 200377 13730626 +drabel 200914 13755384 bking_US 198894 11876016 +mhoram 180229 11610075 Thanar 179852 12365359 -vdv 175535 9904264 -mhoram 173134 11257113 +vdv 175544 9904472 spams 157128 10319326 marrco 150300 9402229 sqrt2 147963 9724586 -vdbergh 137425 8954767 +vdbergh 137429 8955089 CoffeeOne 137100 5024116 malala 136182 8002293 xoto 133759 9159372 -davar 122113 7961971 +rpngn 131285 8657757 +davar 122661 7996937 dsmith 122059 7570238 amicic 119659 7937885 -rpngn 118952 8100045 Data 113305 8220352 BrunoBanani 112960 7436849 CypressChess 108321 7759588 MaZePallas 102823 6633619 sterni1971 100532 5880772 sunu 100167 7040199 +DesolatedDodo 99038 6414626 ElbertoOne 99028 7023771 skiminki 98123 6478402 -DesolatedDodo 93686 6139198 brabos 92118 6186135 -cuistot 90357 5350988 +cuistot 90358 5351004 psk 89957 5984901 racerschmacer 85712 6119648 Vizvezdenec 83761 5344740 +sschnee 83003 4840890 0x3C33 82614 5271253 BRAVONE 81239 5054681 -sschnee 78091 4678078 nssy 76497 5259388 teddybaer 75125 5407666 Pking_cda 73776 5293873 +zeryl 73335 4774257 jromang 72192 5057715 solarlight 70517 5028306 dv8silencer 70287 3883992 Bobo1239 68515 4652287 -zeryl 68203 4516139 manap 66273 4121774 tinker 64333 4268790 -yurikvelo 61692 4262042 +yurikvelo 63371 4335060 qurashee 61208 3429862 robnjr 57262 4053117 +Wolfgang 57014 3561352 Freja 56938 3733019 ttruscott 56010 3680085 rkl 55132 4164467 -Wolfgang 54087 3415872 renouve 53811 3501516 finfish 51360 3370515 eva42 51272 3599691 -eastorwest 51055 3451203 +Calis007 51182 3131552 +eastorwest 51058 3451555 rap 49985 3219146 pb00067 49727 3298270 Spprtr 48260 3141959 @@ -92,11 +93,11 @@ bigpen0r 47667 3336927 ronaldjerum 47654 3240695 MaxKlaxxMiner 47584 2972142 biffhero 46564 3111352 +megaman7de 45992 2952006 Fifis 45843 3088497 VoyagerOne 45476 3452465 speedycpu 43842 3003273 jbwiebe 43305 2805433 -megaman7de 43042 2823256 Antihistamine 41788 2761312 mhunt 41735 2691355 homyur 39893 2850481 @@ -109,18 +110,17 @@ jmdana 36157 2210661 strelock 34716 2074055 EthanOConnor 33370 2090311 slakovv 32915 2021889 -Calis007 32024 2163604 +armo9494 32129 2551682 +tolkki963 32114 1932256 manapbk 30987 1810399 DMBK 30675 2383552 Prcuvu 30377 2170122 anst 30301 2190091 -armo9494 30198 2438202 jkiiski 30136 1904470 -tolkki963 29918 1822290 +gopeto 29886 1979118 hyperbolic.tom 29840 2017394 chuckstablers 29659 2093438 Pyafue 29650 1902349 -gopeto 28881 1896862 OuaisBla 27636 1578800 chriswk 26902 1868317 achambord 26582 1767323 @@ -132,11 +132,12 @@ Sharaf_DG 24765 1786697 ncfish1 24411 1520927 rodneyc 24275 1410450 agg177 23890 1395014 +belzedar94 23707 1593860 JanErik 23408 1703875 Isidor 23388 1680691 Norabor 23339 1602636 +Ente 23093 1642458 cisco2015 22897 1762669 -Ente 22810 1628234 Zirie 22542 1472937 team-oh 22272 1636708 MazeOfGalious 21978 1629593 @@ -148,12 +149,12 @@ nesoneg 21494 1463031 sphinx 21211 1384728 jjoshua2 21001 1423089 horst.prack 20878 1465656 +user213718 20783 1379584 0xB00B1ES 20590 1208666 j3corre 20405 941444 Adrian.Schmidt123 20316 1281436 wei 19973 1745989 -belzedar94 19818 1434252 -user213718 19608 1334650 +Roady 19848 1335928 rstoesser 19569 1293588 eudhan 19274 1283717 vulcan 18871 1729392 @@ -163,24 +164,24 @@ ville 17883 1384026 chris 17698 1487385 purplefishies 17595 1092533 dju 17353 978595 +kdave 17183 1242754 DragonLord 17014 1162790 +thirdlife 16996 447356 +spcc 16932 1130940 +fishtester 16644 1123000 +Ulysses 16490 1184400 IgorLeMasson 16064 1147232 -Roady 15677 1121476 ako027ako 15671 1173203 -kdave 15539 1160356 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 OssumOpossum 14857 1007129 -spcc 14838 1034050 Karby 14808 867120 +AndreasKrug 14608 1152093 enedene 14476 905279 -fishtester 14411 1016252 jsys14 14340 844792 bpfliegel 14298 884523 -AndreasKrug 14096 1126301 mpx86 14019 759568 jpulman 13982 870599 -Ulysses 13977 1073410 crocogoat 13803 1117422 joster 13794 950160 Nesa92 13786 1114691 @@ -188,12 +189,13 @@ mbeier 13650 1044928 Hjax 13535 915487 Dark_wizzie 13422 1007152 Rudolphous 13244 883140 +MarcusTullius 13221 843169 Machariel 13010 863104 mabichito 12903 749391 thijsk 12886 722107 AdrianSA 12860 804972 +infinigon 12807 937332 Flopzee 12698 894821 -infinigon 12638 933684 fatmurphy 12547 853210 scuzzi 12511 845761 SapphireBrand 12416 969604 @@ -202,19 +204,21 @@ Farseer 12249 694108 pgontarz 12151 848794 stocky 11954 699440 mschmidt 11941 803401 +dbernier 11609 818636 Maxim 11543 836024 +pirt 11516 894513 infinity 11470 727027 -pirt 11434 889369 aga 11409 695071 torbjo 11395 729145 Thomas A. Anderson 11372 732094 savage84 11358 670860 -FormazChar 11304 847663 -dbernier 11274 806566 +markkulix 11331 739098 +FormazChar 11308 847735 d64 11263 789184 MooTheCow 11237 720174 snicolet 11106 869170 ali-al-zhrani 11098 768494 +whelanh 11067 235676 basepi 10637 744851 Cubox 10621 826448 michaelrpg 10509 739239 @@ -223,3 +227,4 @@ dzjp 10343 732529 Garruk 10332 703905 ols 10259 570669 lbraesch 10252 647825 +Jackfish 10098 682338 diff --git a/src/types.h b/src/types.h index a3a873fa..cf42bc9f 100644 --- a/src/types.h +++ b/src/types.h @@ -173,11 +173,6 @@ enum Bound { BOUND_EXACT = BOUND_UPPER | BOUND_LOWER }; -enum ExplosionState { - EXPLOSION_NONE, - MUST_CALM_DOWN -}; - enum Value : int { VALUE_ZERO = 0, VALUE_DRAW = 0, From e178a09c47e340e5183d9bd2d331741aa837ba8a Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Mon, 31 Jan 2022 14:37:45 +0100 Subject: [PATCH 272/282] Drop sse from target "x86-32" have maximal compatibility on legacy target arch, now supporting AMD Athlon The old behavior can anyway be selected by the user if needed, for example make -j profile-build ARCH=x86-32 sse=yes fixes #3904 closes https://github.com/official-stockfish/Stockfish/pull/3918 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 9666f138..eff8baca 100644 --- a/src/Makefile +++ b/src/Makefile @@ -152,7 +152,7 @@ ifeq ($(findstring x86,$(ARCH)),x86) ifeq ($(findstring x86-32,$(ARCH)),x86-32) arch = i386 bits = 32 - sse = yes + sse = no mmx = yes else arch = x86_64 From 95d7369e54f20715345cf5408040f3c7d1ec8415 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 4 Feb 2022 22:42:41 +0300 Subject: [PATCH 273/282] Introduce movecount pruning for quiet check evasions in qsearch Idea of this patch is that we usually don't consider quiet check evasions as "good" ones and prefer capture based ones instead. So it makes sense to think that if in qsearch 2 quiet check evasions failed to produce anything good 3rd and further ones wouldn't be good either. passed STC https://tests.stockfishchess.org/tests/view/61fc1b1ed508ec6a1c9f397c LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 58800 W: 15947 L: 15626 D: 27227 Ptnml(0-2): 273, 6568, 15462, 6759, 338 passed LTC https://tests.stockfishchess.org/tests/view/61fcc56dd508ec6a1c9f5619 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 89544 W: 24208 L: 23810 D: 41526 Ptnml(0-2): 81, 9038, 26134, 9440, 79 closes https://github.com/official-stockfish/Stockfish/pull/3920 bench 4830082 --- src/search.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 8d5ad979..c0f77f8a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1479,6 +1479,8 @@ moves_loop: // When in check, search starts here contHist, prevSq); + int quietCheckEvasions = 0; + // Loop through the moves until no moves remain or a beta cutoff occurs while ((move = mp.next_move()) != MOVE_NONE) { @@ -1540,6 +1542,15 @@ moves_loop: // When in check, search starts here && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) continue; + // movecount pruning for quiet check evasions + if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY + && quietCheckEvasions > 1 + && !captureOrPromotion + && ss->inCheck) + continue; + + quietCheckEvasions += !captureOrPromotion && ss->inCheck; + // Make and search the move pos.do_move(move, st, givesCheck); value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); From 4d3950c6eb80c932af00f6495668d5c5adf3701b Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 5 Feb 2022 04:03:02 +0300 Subject: [PATCH 274/282] Reintroduce razoring Razoring was simplified away some years ago, this patch reintroduces it in a slightly different form. Now for low depths if eval is far below alpha we check if qsearch can push it above alpha - and if it can't we return a fail low. passed STC https://tests.stockfishchess.org/tests/view/61fbf968d508ec6a1c9f3274 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 226120 W: 61106 L: 60472 D: 104542 Ptnml(0-2): 1118, 25592, 59080, 26078, 1192 passed LTC https://tests.stockfishchess.org/tests/view/61fcc569d508ec6a1c9f5617 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 113128 W: 30851 L: 30397 D: 51880 Ptnml(0-2): 114, 11483, 32926, 11917, 124 closes https://github.com/official-stockfish/Stockfish/pull/3921 bench 4684080 --- src/search.cpp | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c0f77f8a..6ca3b56e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -773,7 +773,17 @@ namespace { thisThread->complexityAverage.update(complexity); - // Step 7. Futility pruning: child node (~25 Elo). + // Step 7. Razoring. + // If eval is really low check with qsearch if it can exceed alpha, if it can't, + // return a fail low. + if (!PvNode && depth <= 6 && eval < alpha - 400 - 300 * depth * depth) + { + value = qsearch(pos, ss, alpha - 1, alpha); + if (value < alpha) + return value; + } + + // Step 8. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 @@ -782,7 +792,7 @@ namespace { && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; - // Step 8. Null move search with verification search (~22 Elo) + // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL && (ss-1)->statScore < 23767 @@ -834,7 +844,7 @@ namespace { probCutBeta = beta + 209 - 44 * improving; - // Step 9. ProbCut (~4 Elo) + // Step 10. ProbCut (~4 Elo) // If we have a good enough capture and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode @@ -895,7 +905,7 @@ namespace { ss->ttPv = ttPv; } - // Step 10. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) + // Step 11. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) if ( PvNode && depth >= 6 && !ttMove) @@ -908,7 +918,7 @@ namespace { moves_loop: // When in check, search starts here - // Step 11. A small Probcut idea, when we are in check (~0 Elo) + // Step 12. A small Probcut idea, when we are in check (~0 Elo) probCutBeta = beta + 409; if ( ss->inCheck && !PvNode @@ -945,7 +955,7 @@ moves_loop: // When in check, search starts here && (tte->bound() & BOUND_UPPER) && tte->depth() >= depth; - // Step 12. Loop through all pseudo-legal moves until no moves remain + // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { @@ -985,7 +995,7 @@ moves_loop: // When in check, search starts here Value delta = beta - alpha; - // Step 13. Pruning at shallow depth (~98 Elo). Depth conditions are important for mate finding. + // Step 14. Pruning at shallow depth (~98 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) @@ -1038,7 +1048,7 @@ moves_loop: // When in check, search starts here } } - // Step 14. Extensions (~66 Elo) + // Step 15. Extensions (~66 Elo) // We take care to not overdo to avoid search getting stuck. if (ss->ply < thisThread->rootDepth * 2) { @@ -1115,12 +1125,12 @@ moves_loop: // When in check, search starts here [movedPiece] [to_sq(move)]; - // Step 15. Make the move + // Step 16. Make the move pos.do_move(move, st, givesCheck); bool doDeeperSearch = false; - // Step 16. Late moves reduction / extension (LMR, ~98 Elo) + // Step 17. Late moves reduction / extension (LMR, ~98 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". @@ -1188,7 +1198,7 @@ moves_loop: // When in check, search starts here didLMR = false; } - // Step 17. Full depth search when LMR is skipped or fails high + // Step 18. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); @@ -1218,12 +1228,12 @@ moves_loop: // When in check, search starts here std::min(maxNextDepth, newDepth), false); } - // Step 18. Undo move + // Step 19. Undo move pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // Step 19. Check for a new best move + // Step 20. Check for a new best move // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. @@ -1306,7 +1316,7 @@ moves_loop: // When in check, search starts here return VALUE_DRAW; */ - // Step 20. Check for mate and stalemate + // Step 21. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be a mate or a stalemate. If we are in a singular extension search then // return a fail low score. From 08ac4e9db5d763edb788f3b01ea5c3bac494defa Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 7 Feb 2022 13:32:21 +0300 Subject: [PATCH 275/282] Do less depth reduction in null move pruning for complex positions This patch makes us reduce less depth in null move pruning if complexity is high enough. Thus, null move pruning now depends in two distinct ways on complexity, while being the only search heuristic that exploits complexity so far. passed STC https://tests.stockfishchess.org/tests/view/61fde60fd508ec6a1c9f7754 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 170000 W: 45555 L: 45027 D: 79418 Ptnml(0-2): 760, 19352, 44359, 19658, 871 passed LTC https://tests.stockfishchess.org/tests/view/61fe91febf46cb834cbd5c90 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 145272 W: 39182 L: 38651 D: 67439 Ptnml(0-2): 127, 14864, 42157, 15327, 161 closes https://github.com/official-stockfish/Stockfish/pull/3923 bench 4461945 --- src/search.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6ca3b56e..426f7937 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -776,7 +776,9 @@ namespace { // Step 7. Razoring. // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (!PvNode && depth <= 6 && eval < alpha - 400 - 300 * depth * depth) + if ( !PvNode + && depth <= 6 + && eval < alpha - 400 - 300 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -805,8 +807,8 @@ namespace { { assert(eval - beta >= 0); - // Null move dynamic reduction based on depth and value - Depth R = std::min(int(eval - beta) / 205, 3) + depth / 3 + 4; + // Null move dynamic reduction based on depth, eval and complexity of position + Depth R = std::min(int(eval - beta) / 205, 3) + depth / 3 + 4 - (complexity > 500); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; From b0b31558a236301aa8578030a8a3109856634fa9 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 9 Feb 2022 17:39:21 +0300 Subject: [PATCH 276/282] Big search tuning Most credits for this patch should go to @candirufish. Based on his big search tuning (1M games at 20+0.1s) https://tests.stockfishchess.org/tests/view/61fc7a6ed508ec6a1c9f4b7d with some hand polishing on top of it, which includes : a) correcting trend sigmoid - for some reason original tuning resulted in it being negative. This heuristic was proven to be worth some elo for years so reversing it sign is probably some random artefact; b) remove changes to continuation history based pruning - this heuristic historically was really good at providing green STCs and then failing at LTC miserably if we tried to make it more strict, original tuning was done at short time control and thus it became more strict - which doesn't scale to longer time controls; c) remove changes to improvement - not really indended :). passed STC https://tests.stockfishchess.org/tests/view/6203526e88ae2c84271c2ee2 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 16840 W: 4604 L: 4363 D: 7873 Ptnml(0-2): 82, 1780, 4449, 2033, 76 passed LTC https://tests.stockfishchess.org/tests/view/620376e888ae2c84271c35d4 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 17232 W: 4771 L: 4542 D: 7919 Ptnml(0-2): 14, 1655, 5048, 1886, 13 closes https://github.com/official-stockfish/Stockfish/pull/3926 bench 5030992 --- src/search.cpp | 93 +++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 426f7937..5bc9de7b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(214 * (d - improving)); + return Value(171 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -71,7 +71,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904); + return (r + 1575 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 1011); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -80,7 +80,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((6 * d + 229) * d - 215 , 2000); + return std::min((7 * d + 254) * d - 206 , 1990); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -157,7 +157,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((21.9 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((21.5 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -303,10 +303,10 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(232, 1); + complexityAverage.set(190, 1); trend = SCORE_ZERO; - optimism[ us] = Value(25); + optimism[ us] = Value(34); optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -349,16 +349,16 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].averageScore; - delta = Value(17) + int(prev) * prev / 16384; + delta = Value(16) + int(prev) * prev / 16384; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust trend and optimism based on root move's previousScore - int tr = sigmoid(prev, 0, 0, 147, 113, 1); + int tr = sigmoid(prev, 6, 13, 96, 110, 1); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); - int opt = sigmoid(prev, 0, 25, 147, 14464, 256); + int opt = sigmoid(prev, 7, 21, 94, 14786, 221); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -413,7 +413,7 @@ void Thread::search() { else break; - delta += delta / 4 + 5; + delta += delta / 4 + 3; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } @@ -459,17 +459,17 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (142 + 12 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; + double fallingEval = (87 + 12 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 777.20; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.92 : 0.95; - double reduction = (1.47 + mainThread->previousTimeReduction) / (2.32 * timeReduction); + timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.70 : 0.91; + double reduction = (1.59 + mainThread->previousTimeReduction) / (2.33 * timeReduction); double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::clamp(1.0 + (complexity - 232) / 1750.0, 0.5, 1.5); + double complexPosition = std::clamp(1.0 + (complexity - 312) / 1750.0, 0.5, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; @@ -490,7 +490,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.58) + && Time.elapsed() > totalTime * 0.55) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -788,19 +788,19 @@ namespace { // Step 8. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv - && depth < 9 + && depth < 8 && eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta && eval >= beta - && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. + && eval < 17548) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 23767 + && (ss-1)->statScore < 13706 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 + complexity / 25 + && ss->staticEval >= beta - 19 * depth - improvement / 15 + 200 + complexity / 25 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -844,13 +844,13 @@ namespace { } } - probCutBeta = beta + 209 - 44 * improving; + probCutBeta = beta + 229 - 47 * improving; // Step 10. ProbCut (~4 Elo) // If we have a good enough capture and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode - && depth > 4 + && depth > 3 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY // if value from transposition table is lower than probCutBeta, don't attempt probCut // there and in further interactions with transposition table cutoff depth is set to depth - 3 @@ -871,7 +871,6 @@ namespace { if (move != excludedMove && pos.legal(move)) { assert(pos.capture_or_promotion(move)); - assert(depth >= 5); captureOrPromotion = true; @@ -909,19 +908,19 @@ namespace { // Step 11. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) if ( PvNode - && depth >= 6 + && depth >= 4 && !ttMove) depth -= 2; if ( cutNode - && depth >= 9 + && depth >= 7 && !ttMove) depth--; moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~0 Elo) - probCutBeta = beta + 409; + probCutBeta = beta + 401; if ( ss->inCheck && !PvNode && depth >= 4 @@ -1017,12 +1016,12 @@ moves_loop: // When in check, search starts here && !PvNode && lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 342 + 238 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + && ss->staticEval + 392 + 207 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 8 < alpha) continue; // SEE based pruning (~9 Elo) - if (!pos.see_ge(move, Value(-217) * depth)) + if (!pos.see_ge(move, Value(-200) * depth)) continue; } else @@ -1040,12 +1039,12 @@ moves_loop: // When in check, search starts here // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck - && lmrDepth < 8 - && ss->staticEval + 138 + 137 * lmrDepth + history / 64 <= alpha) + && lmrDepth < 11 + && ss->staticEval + 131 + 137 * lmrDepth + history / 64 <= alpha) continue; // Prune moves with negative SEE (~3 Elo) - if (!pos.see_ge(move, Value(-21 * lmrDepth * lmrDepth - 21 * lmrDepth))) + if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 29 * lmrDepth))) continue; } } @@ -1081,7 +1080,7 @@ moves_loop: // When in check, search starts here // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 75 + && value < singularBeta - 71 && ss->doubleExtensions <= 6) extension = 2; } @@ -1101,15 +1100,15 @@ moves_loop: // When in check, search starts here // Check extensions (~1 Elo) else if ( givesCheck - && depth > 6 - && abs(ss->staticEval) > 100) + && depth > 7 + && abs(ss->staticEval) > 128) extension = 1; // Quiet ttMove extensions (~0 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 10000) + && (*contHist[0])[movedPiece][to_sq(move)] >= 8932) extension = 1; } @@ -1136,8 +1135,8 @@ moves_loop: // When in check, search starts here // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". - if ( depth >= 3 - && moveCount > 1 + 2 * rootNode + if ( depth >= 2 + && moveCount > 1 + rootNode && ( !ss->ttPv || !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1))) @@ -1146,7 +1145,7 @@ moves_loop: // When in check, search starts here // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode - && bestMoveCount <= 3) + && bestMoveCount <= 4) r--; // Decrease reduction if position is or has been on the PV @@ -1156,7 +1155,7 @@ moves_loop: // When in check, search starts here r -= 2; // Decrease reduction if opponent's move count is high (~1 Elo) - if ((ss-1)->moveCount > 13) + if ((ss-1)->moveCount > 7) r--; // Increase reduction for cut nodes (~3 Elo) @@ -1171,18 +1170,18 @@ moves_loop: // When in check, search starts here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4923; + - 4142; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 14721; + r -= ss->statScore / 15328; // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 : moveCount <= 5 ? 2 - : PvNode && depth > 6 ? 1 - : cutNode && moveCount <= 7 ? 1 + : PvNode && depth > 4 ? 1 + : cutNode && moveCount <= 5 ? 1 : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); @@ -1191,7 +1190,7 @@ moves_loop: // When in check, search starts here // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; - doDeeperSearch = value > (alpha + 62 + 20 * (newDepth - d)); + doDeeperSearch = value > (alpha + 80 + 20 * (newDepth - d)); didLMR = true; } else @@ -1212,7 +1211,7 @@ moves_loop: // When in check, search starts here : -stat_bonus(newDepth); if (captureOrPromotion) - bonus /= 4; + bonus /= 5; update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } @@ -1343,7 +1342,7 @@ moves_loop: // When in check, search starts here //or fail low was really bad bool extraBonus = PvNode || cutNode - || bestValue < alpha - 94 * depth; + || bestValue < alpha - 99 * depth; update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); } @@ -1474,7 +1473,7 @@ moves_loop: // When in check, search starts here if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 155; + futilityBase = bestValue + 127; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From cb9c2594fcedc881ae8f8bfbfdf130cf89840e4c Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 27 Nov 2021 15:17:02 +0100 Subject: [PATCH 277/282] Update architecture to "SFNNv4". Update network to nn-6877cd24400e.nnue. Architecture: The diagram of the "SFNNv4" architecture: https://user-images.githubusercontent.com/8037982/153455685-cbe3a038-e158-4481-844d-9d5fccf5c33a.png The most important architectural changes are the following: * 1024x2 [activated] neurons are pairwise, elementwise multiplied (not quite pairwise due to implementation details, see diagram), which introduces a non-linearity that exhibits similar benefits to previously tested sigmoid activation (quantmoid4), while being slightly faster. * The following layer has therefore 2x less inputs, which we compensate by having 2 more outputs. It is possible that reducing the number of outputs might be beneficial (as we had it as low as 8 before). The layer is now 1024->16. * The 16 outputs are split into 15 and 1. The 1-wide output is added to the network output (after some necessary scaling due to quantization differences). The 15-wide is activated and follows the usual path through a set of linear layers. The additional 1-wide output is at least neutral, but has shown a slightly positive trend in training compared to networks without it (all 16 outputs through the usual path), and allows possibly an additional stage of lazy evaluation to be introduced in the future. Additionally, the inference code was rewritten and no longer uses a recursive implementation. This was necessitated by the splitting of the 16-wide intermediate result into two, which was impossible to do with the old implementation with ugly hacks. This is hopefully overall for the better. First session: The first session was training a network from scratch (random initialization). The exact trainer used was slightly different (older) from the one used in the second session, but it should not have a measurable effect. The purpose of this session is to establish a strong network base for the second session. Small deviations in strength do not harm the learnability in the second session. The training was done using the following command: python3 train.py \ /home/sopel/nnue/nnue-pytorch-training/data/nodes5000pv2_UHO.binpack \ /home/sopel/nnue/nnue-pytorch-training/data/nodes5000pv2_UHO.binpack \ --gpus "$3," \ --threads 4 \ --num-workers 4 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=1.0 \ --gamma=0.992 \ --lr=8.75e-4 \ --max_epochs=400 \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$2 Every 20th net was saved and its playing strength measured against some baseline at 25k nodes per move with pure NNUE evaluation (modified binary). The exact setup is not important as long as it's consistent. The purpose is to sift good candidates from bad ones. The dataset can be found https://drive.google.com/file/d/1UQdZN_LWQ265spwTBwDKo0t1WjSJKvWY/view Second session: The second training session was done starting from the best network (as determined by strength testing) from the first session. It is important that it's resumed from a .pt model and NOT a .ckpt model. The conversion can be performed directly using serialize.py The LR schedule was modified to use gamma=0.995 instead of gamma=0.992 and LR=4.375e-4 instead of LR=8.75e-4 to flatten the LR curve and allow for longer training. The training was then running for 800 epochs instead of 400 (though it's possibly mostly noise after around epoch 600). The training was done using the following command: The training was done using the following command: python3 train.py \ /data/sopel/nnue/nnue-pytorch-training/data/T60T70wIsRightFarseerT60T74T75T76.binpack \ /data/sopel/nnue/nnue-pytorch-training/data/T60T70wIsRightFarseerT60T74T75T76.binpack \ --gpus "$3," \ --threads 4 \ --num-workers 4 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=1.0 \ --gamma=0.995 \ --lr=4.375e-4 \ --max_epochs=800 \ --resume-from-model /data/sopel/nnue/nnue-pytorch-training/data/exp295/nn-epoch399.pt \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$run_id In particular note that we now use lambda=1.0 instead of lambda=0.8 (previous nets), because tests show that WDL-skipping introduced by vondele performs better with lambda=1.0. Nets were being saved every 20th epoch. In total 16 runs were made with these settings and the best nets chosen according to playing strength at 25k nodes per move with pure NNUE evaluation - these are the 4 nets that have been put on fishtest. The dataset can be found either at ftp://ftp.chessdb.cn/pub/sopel/data_sf/T60T70wIsRightFarseerT60T74T75T76.binpack in its entirety (download might be painfully slow because hosted in China) or can be assembled in the following way: Get the https://github.com/official-stockfish/Stockfish/blob/5640ad48ae5881223b868362c1cbeb042947f7b4/script/interleave_binpacks.py script. Download T60T70wIsRightFarseer.binpack https://drive.google.com/file/d/1_sQoWBl31WAxNXma2v45004CIVltytP8/view Download farseerT74.binpack http://trainingdata.farseer.org/T74-May13-End.7z Download farseerT75.binpack http://trainingdata.farseer.org/T75-June3rd-End.7z Download farseerT76.binpack http://trainingdata.farseer.org/T76-Nov10th-End.7z Run python3 interleave_binpacks.py T60T70wIsRightFarseer.binpack farseerT74.binpack farseerT75.binpack farseerT76.binpack T60T70wIsRightFarseerT60T74T75T76.binpack Tests: STC: https://tests.stockfishchess.org/tests/view/6203fb85d71106ed12a407b7 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 16952 W: 4775 L: 4521 D: 7656 Ptnml(0-2): 133, 1818, 4318, 2076, 131 LTC: https://tests.stockfishchess.org/tests/view/62041e68d71106ed12a40e85 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 14944 W: 4138 L: 3907 D: 6899 Ptnml(0-2): 21, 1499, 4202, 1728, 22 closes https://github.com/official-stockfish/Stockfish/pull/3927 Bench: 4919707 --- src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 17 +-- src/nnue/layers/affine_transform.h | 91 +++++------- src/nnue/layers/clipped_relu.h | 35 ++--- src/nnue/layers/input_slice.h | 73 ---------- src/nnue/nnue_architecture.h | 107 +++++++++++--- src/nnue/nnue_feature_transformer.h | 210 +++++++++++++--------------- 7 files changed, 235 insertions(+), 300 deletions(-) delete mode 100644 src/nnue/layers/input_slice.h diff --git a/src/evaluate.h b/src/evaluate.h index 57a7687d..1934c9bd 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-ac07bd334b62.nnue" + #define EvalFileDefaultName "nn-6877cd24400e.nnue" namespace NNUE { diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 862b2003..0fd58462 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -148,22 +148,18 @@ namespace Stockfish::Eval::NNUE { #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformedFeaturesUnaligned[ FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; - char bufferUnaligned[Network::BufferSize + alignment]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); - auto* buffer = align_ptr_up(&bufferUnaligned[0]); #else alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; - alignas(alignment) char buffer[Network::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); - ASSERT_ALIGNED(buffer, alignment); const std::size_t bucket = (pos.count() - 1) / 4; const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); - const auto positional = network[bucket]->propagate(transformedFeatures, buffer)[0]; + const auto positional = network[bucket]->propagate(transformedFeatures); // Give more value to positional evaluation when adjusted flag is set if (adjusted) @@ -190,27 +186,20 @@ namespace Stockfish::Eval::NNUE { #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformedFeaturesUnaligned[ FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; - char bufferUnaligned[Network::BufferSize + alignment]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); - auto* buffer = align_ptr_up(&bufferUnaligned[0]); #else alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; - alignas(alignment) char buffer[Network::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); - ASSERT_ALIGNED(buffer, alignment); NnueEvalTrace t{}; t.correctBucket = (pos.count() - 1) / 4; for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) { - const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); - const auto output = network[bucket]->propagate(transformedFeatures, buffer); - - int materialist = psqt; - int positional = output[0]; + const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket); + const auto positional = network[bucket]->propagate(transformedFeatures); t.psqt[bucket] = static_cast( materialist / OutputScale ); t.positional[bucket] = static_cast( positional / OutputScale ); diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 4e85a5fe..22451915 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -63,19 +63,17 @@ namespace Stockfish::Eval::NNUE::Layers { { # if defined(USE_SSE2) // At least a multiple of 16, with SSE2. - static_assert(PaddedInputDimensions % 16 == 0); - constexpr IndexType NumChunks = PaddedInputDimensions / 16; + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; const __m128i Zeros = _mm_setzero_si128(); const auto inputVector = reinterpret_cast(input); # elif defined(USE_MMX) - static_assert(InputDimensions % 8 == 0); - constexpr IndexType NumChunks = InputDimensions / 8; + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / 8; const __m64 Zeros = _mm_setzero_si64(); const auto inputVector = reinterpret_cast(input); # elif defined(USE_NEON) - constexpr IndexType NumChunks = (InputDimensions + 15) / 16; + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; const auto inputVector = reinterpret_cast(input); # endif @@ -150,24 +148,27 @@ namespace Stockfish::Eval::NNUE::Layers { } #endif - template + template class AffineTransform; // A specialization for large inputs. - template - class AffineTransform= 2*64-1)>> { + template + class AffineTransform(InDims, MaxSimdWidth) >= 2*64)>> { public: // Input/output type - using InputType = typename PreviousLayer::OutputType; + using InputType = std::uint8_t; using OutputType = std::int32_t; - static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType InputDimensions = PreviousLayer::OutputDimensions; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = OutDims; static constexpr IndexType PaddedInputDimensions = ceil_to_multiple(InputDimensions, MaxSimdWidth); + static constexpr IndexType PaddedOutputDimensions = + ceil_to_multiple(OutputDimensions, MaxSimdWidth); + + using OutputBuffer = OutputType[PaddedOutputDimensions]; static_assert(PaddedInputDimensions >= 128, "Something went wrong. This specialization should not have been chosen."); @@ -202,20 +203,12 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(OutputDimensions % NumOutputRegs == 0); - // Size of forward propagation buffer used in this layer - static constexpr std::size_t SelfBufferSize = - ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); - - // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t BufferSize = - PreviousLayer::BufferSize + SelfBufferSize; - // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value() { + static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { std::uint32_t hashValue = 0xCC03DAE4u; hashValue += OutputDimensions; - hashValue ^= PreviousLayer::get_hash_value() >> 1; - hashValue ^= PreviousLayer::get_hash_value() << 31; + hashValue ^= prevHash >> 1; + hashValue ^= prevHash << 31; return hashValue; } @@ -242,7 +235,6 @@ namespace Stockfish::Eval::NNUE::Layers { // Read network parameters bool read_parameters(std::istream& stream) { - if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); @@ -254,7 +246,6 @@ namespace Stockfish::Eval::NNUE::Layers { // Write network parameters bool write_parameters(std::ostream& stream) const { - if (!previousLayer.write_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) write_little_endian(stream, biases[i]); @@ -266,10 +257,7 @@ namespace Stockfish::Eval::NNUE::Layers { // Forward propagation const OutputType* propagate( - const TransformedFeatureType* transformedFeatures, char* buffer) const { - const auto input = previousLayer.propagate( - transformedFeatures, buffer + SelfBufferSize); - OutputType* output = reinterpret_cast(buffer); + const InputType* input, OutputType* output) const { #if defined (USE_AVX512) using acc_vec_t = __m512i; @@ -312,7 +300,6 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined (USE_SSSE3) || defined (USE_NEON) const in_vec_t* invec = reinterpret_cast(input); - // Perform accumulation to registers for each big block for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock) { @@ -377,26 +364,28 @@ namespace Stockfish::Eval::NNUE::Layers { using BiasType = OutputType; using WeightType = std::int8_t; - PreviousLayer previousLayer; - alignas(CacheLineSize) BiasType biases[OutputDimensions]; alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; }; - template - class AffineTransform> { + template + class AffineTransform(InDims, MaxSimdWidth) < 2*64)>> { public: // Input/output type - using InputType = typename PreviousLayer::OutputType; + // Input/output type + using InputType = std::uint8_t; using OutputType = std::int32_t; - static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType InputDimensions = - PreviousLayer::OutputDimensions; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = OutDims; + static constexpr IndexType PaddedInputDimensions = - ceil_to_multiple(InputDimensions, MaxSimdWidth); + ceil_to_multiple(InputDimensions, MaxSimdWidth); + static constexpr IndexType PaddedOutputDimensions = + ceil_to_multiple(OutputDimensions, MaxSimdWidth); + + using OutputBuffer = OutputType[PaddedOutputDimensions]; static_assert(PaddedInputDimensions < 128, "Something went wrong. This specialization should not have been chosen."); @@ -405,20 +394,12 @@ namespace Stockfish::Eval::NNUE::Layers { static constexpr const IndexType InputSimdWidth = SimdWidth; #endif - // Size of forward propagation buffer used in this layer - static constexpr std::size_t SelfBufferSize = - ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); - - // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t BufferSize = - PreviousLayer::BufferSize + SelfBufferSize; - // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value() { + static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { std::uint32_t hashValue = 0xCC03DAE4u; hashValue += OutputDimensions; - hashValue ^= PreviousLayer::get_hash_value() >> 1; - hashValue ^= PreviousLayer::get_hash_value() << 31; + hashValue ^= prevHash >> 1; + hashValue ^= prevHash << 31; return hashValue; } @@ -441,7 +422,6 @@ namespace Stockfish::Eval::NNUE::Layers { // Read network parameters bool read_parameters(std::istream& stream) { - if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) @@ -452,7 +432,6 @@ namespace Stockfish::Eval::NNUE::Layers { // Write network parameters bool write_parameters(std::ostream& stream) const { - if (!previousLayer.write_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) write_little_endian(stream, biases[i]); @@ -463,10 +442,7 @@ namespace Stockfish::Eval::NNUE::Layers { } // Forward propagation const OutputType* propagate( - const TransformedFeatureType* transformedFeatures, char* buffer) const { - const auto input = previousLayer.propagate( - transformedFeatures, buffer + SelfBufferSize); - const auto output = reinterpret_cast(buffer); + const InputType* input, OutputType* output) const { #if defined (USE_AVX2) using vec_t = __m256i; @@ -491,12 +467,11 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined (USE_SSSE3) const auto inputVector = reinterpret_cast(input); - static_assert(InputDimensions % 8 == 0); static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); if constexpr (OutputDimensions % OutputSimdWidth == 0) { - constexpr IndexType NumChunks = InputDimensions / 4; + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / 4; constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; const auto input32 = reinterpret_cast(input); @@ -555,8 +530,6 @@ namespace Stockfish::Eval::NNUE::Layers { using BiasType = OutputType; using WeightType = std::int8_t; - PreviousLayer previousLayer; - alignas(CacheLineSize) BiasType biases[OutputDimensions]; alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; }; diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 0da5e821..ffd2e3b7 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -26,51 +26,41 @@ namespace Stockfish::Eval::NNUE::Layers { // Clipped ReLU - template + template class ClippedReLU { public: // Input/output type - using InputType = typename PreviousLayer::OutputType; + using InputType = std::int32_t; using OutputType = std::uint8_t; - static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType InputDimensions = PreviousLayer::OutputDimensions; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = InputDimensions; static constexpr IndexType PaddedOutputDimensions = ceil_to_multiple(OutputDimensions, 32); - // Size of forward propagation buffer used in this layer - static constexpr std::size_t SelfBufferSize = - ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); - - // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t BufferSize = - PreviousLayer::BufferSize + SelfBufferSize; + using OutputBuffer = OutputType[PaddedOutputDimensions]; // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value() { + static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { std::uint32_t hashValue = 0x538D24C7u; - hashValue += PreviousLayer::get_hash_value(); + hashValue += prevHash; return hashValue; } // Read network parameters - bool read_parameters(std::istream& stream) { - return previousLayer.read_parameters(stream); + bool read_parameters(std::istream&) { + return true; } // Write network parameters - bool write_parameters(std::ostream& stream) const { - return previousLayer.write_parameters(stream); + bool write_parameters(std::ostream&) const { + return true; } // Forward propagation const OutputType* propagate( - const TransformedFeatureType* transformedFeatures, char* buffer) const { - const auto input = previousLayer.propagate( - transformedFeatures, buffer + SelfBufferSize); - const auto output = reinterpret_cast(buffer); + const InputType* input, OutputType* output) const { #if defined(USE_AVX2) if constexpr (InputDimensions % SimdWidth == 0) { @@ -191,9 +181,6 @@ namespace Stockfish::Eval::NNUE::Layers { return output; } - - private: - PreviousLayer previousLayer; }; } // namespace Stockfish::Eval::NNUE::Layers diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h deleted file mode 100644 index 8f526b74..00000000 --- a/src/nnue/layers/input_slice.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 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 . -*/ - -// NNUE evaluation function layer InputSlice definition - -#ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED -#define NNUE_LAYERS_INPUT_SLICE_H_INCLUDED - -#include "../nnue_common.h" - -namespace Stockfish::Eval::NNUE::Layers { - -// Input layer -template -class InputSlice { - public: - // Need to maintain alignment - static_assert(Offset % MaxSimdWidth == 0, ""); - - // Output type - using OutputType = TransformedFeatureType; - - // Output dimensionality - static constexpr IndexType OutputDimensions = OutDims; - - // Size of forward propagation buffer used from the input layer to this layer - static constexpr std::size_t BufferSize = 0; - - // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value() { - std::uint32_t hashValue = 0xEC42E90Du; - hashValue ^= OutputDimensions ^ (Offset << 10); - return hashValue; - } - - // Read network parameters - bool read_parameters(std::istream& /*stream*/) { - return true; - } - - // Write network parameters - bool write_parameters(std::ostream& /*stream*/) const { - return true; - } - - // Forward propagation - const OutputType* propagate( - const TransformedFeatureType* transformedFeatures, - char* /*buffer*/) const { - return transformedFeatures + Offset; - } - - private: -}; - -} // namespace Stockfish::Eval::NNUE::Layers - -#endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 8867fac7..725b40fb 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -25,35 +25,106 @@ #include "features/half_ka_v2_hm.h" -#include "layers/input_slice.h" #include "layers/affine_transform.h" #include "layers/clipped_relu.h" +#include "../misc.h" + namespace Stockfish::Eval::NNUE { - // Input features used in evaluation function - using FeatureSet = Features::HalfKAv2_hm; +// Input features used in evaluation function +using FeatureSet = Features::HalfKAv2_hm; - // Number of input feature dimensions after conversion - constexpr IndexType TransformedFeatureDimensions = 1024; - constexpr IndexType PSQTBuckets = 8; - constexpr IndexType LayerStacks = 8; +// Number of input feature dimensions after conversion +constexpr IndexType TransformedFeatureDimensions = 1024; +constexpr IndexType PSQTBuckets = 8; +constexpr IndexType LayerStacks = 8; - namespace Layers { +struct Network +{ + static constexpr int FC_0_OUTPUTS = 15; + static constexpr int FC_1_OUTPUTS = 32; - // Define network structure - using InputLayer = InputSlice; - using HiddenLayer1 = ClippedReLU>; - using HiddenLayer2 = ClippedReLU>; - using OutputLayer = AffineTransform; + Layers::AffineTransform fc_0; + Layers::ClippedReLU ac_0; + Layers::AffineTransform fc_1; + Layers::ClippedReLU ac_1; + Layers::AffineTransform fc_2; - } // namespace Layers + // Hash value embedded in the evaluation file + static constexpr std::uint32_t get_hash_value() { + // input slice hash + std::uint32_t hashValue = 0xEC42E90Du; + hashValue ^= TransformedFeatureDimensions * 2; - using Network = Layers::OutputLayer; + hashValue = decltype(fc_0)::get_hash_value(hashValue); + hashValue = decltype(ac_0)::get_hash_value(hashValue); + hashValue = decltype(fc_1)::get_hash_value(hashValue); + hashValue = decltype(ac_1)::get_hash_value(hashValue); + hashValue = decltype(fc_2)::get_hash_value(hashValue); - static_assert(TransformedFeatureDimensions % MaxSimdWidth == 0, ""); - static_assert(Network::OutputDimensions == 1, ""); - static_assert(std::is_same::value, ""); + return hashValue; + } + + // Read network parameters + bool read_parameters(std::istream& stream) { + if (!fc_0.read_parameters(stream)) return false; + if (!ac_0.read_parameters(stream)) return false; + if (!fc_1.read_parameters(stream)) return false; + if (!ac_1.read_parameters(stream)) return false; + if (!fc_2.read_parameters(stream)) return false; + return true; + } + + // Read network parameters + bool write_parameters(std::ostream& stream) const { + if (!fc_0.write_parameters(stream)) return false; + if (!ac_0.write_parameters(stream)) return false; + if (!fc_1.write_parameters(stream)) return false; + if (!ac_1.write_parameters(stream)) return false; + if (!fc_2.write_parameters(stream)) return false; + return true; + } + + std::int32_t propagate(const TransformedFeatureType* transformedFeatures) + { + constexpr uint64_t alignment = CacheLineSize; + + struct Buffer + { + alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; + alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; + alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; + alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; + alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out; + }; + +#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) + char bufferRaw[sizeof(Buffer) + alignment]; + char* bufferRawAligned = align_ptr_up(&bufferRaw[0]); + Buffer& buffer = *(new (bufferRawAligned) Buffer); +#else + alignas(alignment) Buffer buffer; +#endif + + fc_0.propagate(transformedFeatures, buffer.fc_0_out); + ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out); + fc_1.propagate(buffer.ac_0_out, buffer.fc_1_out); + ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); + fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); + + // buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) + const IndexType offset = (HalfDimensions / 2) * p; + +#if defined(USE_AVX512) + + constexpr IndexType OutputChunkSize = 512 / 8; + static_assert((HalfDimensions / 2) % OutputChunkSize == 0); + constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; + + const __m512i Zero = _mm512_setzero_si512(); + const __m512i One = _mm512_set1_epi16(127); + const __m512i Control = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); + + const __m512i* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const __m512i* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + __m512i* out = reinterpret_cast< __m512i*>(output + offset); + + for (IndexType j = 0; j < NumOutputChunks; j += 1) { - __m512i sum0 = _mm512_load_si512(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 0]); - __m512i sum1 = _mm512_load_si512(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 1]); + const __m512i sum0a = _mm512_max_epi16(_mm512_min_epi16(in0[j * 2 + 0], One), Zero); + const __m512i sum0b = _mm512_max_epi16(_mm512_min_epi16(in0[j * 2 + 1], One), Zero); + const __m512i sum1a = _mm512_max_epi16(_mm512_min_epi16(in1[j * 2 + 0], One), Zero); + const __m512i sum1b = _mm512_max_epi16(_mm512_min_epi16(in1[j * 2 + 1], One), Zero); - _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, - _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); + const __m512i pa = _mm512_srli_epi16(_mm512_mullo_epi16(sum0a, sum1a), 7); + const __m512i pb = _mm512_srli_epi16(_mm512_mullo_epi16(sum0b, sum1b), 7); + + out[j] = _mm512_permutexvar_epi64(Control, _mm512_packs_epi16(pa, pb)); } - } - return psqt; - #elif defined(USE_AVX2) +#elif defined(USE_AVX2) - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; - constexpr int Control = 0b11011000; - const __m256i Zero = _mm256_setzero_si256(); + constexpr IndexType OutputChunkSize = 256 / 8; + static_assert((HalfDimensions / 2) % OutputChunkSize == 0); + constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - auto out = reinterpret_cast<__m256i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) + const __m256i Zero = _mm256_setzero_si256(); + const __m256i One = _mm256_set1_epi16(127); + constexpr int Control = 0b11011000; + + const __m256i* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const __m256i* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + __m256i* out = reinterpret_cast< __m256i*>(output + offset); + + for (IndexType j = 0; j < NumOutputChunks; j += 1) { - __m256i sum0 = _mm256_load_si256(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 0]); - __m256i sum1 = _mm256_load_si256(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 1]); + const __m256i sum0a = _mm256_max_epi16(_mm256_min_epi16(in0[j * 2 + 0], One), Zero); + const __m256i sum0b = _mm256_max_epi16(_mm256_min_epi16(in0[j * 2 + 1], One), Zero); + const __m256i sum1a = _mm256_max_epi16(_mm256_min_epi16(in1[j * 2 + 0], One), Zero); + const __m256i sum1b = _mm256_max_epi16(_mm256_min_epi16(in1[j * 2 + 1], One), Zero); - _mm256_store_si256(&out[j], _mm256_permute4x64_epi64( - _mm256_max_epi8(_mm256_packs_epi16(sum0, sum1), Zero), Control)); + const __m256i pa = _mm256_srli_epi16(_mm256_mullo_epi16(sum0a, sum1a), 7); + const __m256i pb = _mm256_srli_epi16(_mm256_mullo_epi16(sum0b, sum1b), 7); + + out[j] = _mm256_permute4x64_epi64(_mm256_packs_epi16(pa, pb), Control); } - } - return psqt; - #elif defined(USE_SSE2) +#elif defined(USE_SSE2) - #ifdef USE_SSE41 - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; - const __m128i Zero = _mm_setzero_si128(); - #else - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; - const __m128i k0x80s = _mm_set1_epi8(-128); - #endif + constexpr IndexType OutputChunkSize = 128 / 8; + static_assert((HalfDimensions / 2) % OutputChunkSize == 0); + constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - auto out = reinterpret_cast<__m128i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) + const __m128i Zero = _mm_setzero_si128(); + const __m128i One = _mm_set1_epi16(127); + + const __m128i* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const __m128i* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + __m128i* out = reinterpret_cast< __m128i*>(output + offset); + + for (IndexType j = 0; j < NumOutputChunks; j += 1) { - __m128i sum0 = _mm_load_si128(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 0]); - __m128i sum1 = _mm_load_si128(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 1]); - const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); + const __m128i sum0a = _mm_max_epi16(_mm_min_epi16(in0[j * 2 + 0], One), Zero); + const __m128i sum0b = _mm_max_epi16(_mm_min_epi16(in0[j * 2 + 1], One), Zero); + const __m128i sum1a = _mm_max_epi16(_mm_min_epi16(in1[j * 2 + 0], One), Zero); + const __m128i sum1b = _mm_max_epi16(_mm_min_epi16(in1[j * 2 + 1], One), Zero); - #ifdef USE_SSE41 - _mm_store_si128(&out[j], _mm_max_epi8(packedbytes, Zero)); - #else - _mm_store_si128(&out[j], _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)); - #endif + const __m128i pa = _mm_srli_epi16(_mm_mullo_epi16(sum0a, sum1a), 7); + const __m128i pb = _mm_srli_epi16(_mm_mullo_epi16(sum0b, sum1b), 7); + + out[j] = _mm_packs_epi16(pa, pb); } - } - return psqt; - #elif defined(USE_MMX) +#elif defined(USE_NEON) - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; - const __m64 k0x80s = _mm_set1_pi8(-128); + constexpr IndexType OutputChunkSize = 128 / 8; + static_assert((HalfDimensions / 2) % OutputChunkSize == 0); + constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - auto out = reinterpret_cast<__m64*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) + const int16x8_t Zero = vdupq_n_s16(0); + const int16x8_t One = vdupq_n_s16(127); + + const int16x8_t* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const int16x8_t* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + int8x16_t* out = reinterpret_cast< int8x16_t*>(output + offset); + + for (IndexType j = 0; j < NumOutputChunks; j += 1) { - __m64 sum0 = *(&reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); - __m64 sum1 = *(&reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); - const __m64 packedbytes = _mm_packs_pi16(sum0, sum1); - out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); + const int16x8_t sum0a = vmaxq_s16(vminq_s16(in0[j * 2 + 0], One), Zero); + const int16x8_t sum0b = vmaxq_s16(vminq_s16(in0[j * 2 + 1], One), Zero); + const int16x8_t sum1a = vmaxq_s16(vminq_s16(in1[j * 2 + 0], One), Zero); + const int16x8_t sum1b = vmaxq_s16(vminq_s16(in1[j * 2 + 1], One), Zero); + + const int8x8_t pa = vshrn_n_s16(vmulq_s16(sum0a, sum1a), 7); + const int8x8_t pb = vshrn_n_s16(vmulq_s16(sum0b, sum1b), 7); + + out[j] = vcombine_s8(pa, pb); } - } - _mm_empty(); - return psqt; - #elif defined(USE_NEON) +#else - constexpr IndexType NumChunks = HalfDimensions / (SimdWidth / 2); - const int8x8_t Zero = {0}; - - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - const auto out = reinterpret_cast(&output[offset]); - - constexpr IndexType UnrollFactor = 16; - static_assert(UnrollFactor % UnrollFactor == 0); - for (IndexType j = 0; j < NumChunks; j += UnrollFactor) - { - int16x8_t sums[UnrollFactor]; - for (IndexType i = 0; i < UnrollFactor; ++i) - sums[i] = reinterpret_cast(accumulation[perspectives[p]])[j+i]; - - for (IndexType i = 0; i < UnrollFactor; ++i) - out[j+i] = vmax_s8(vqmovn_s16(sums[i]), Zero); + for (IndexType j = 0; j < HalfDimensions / 2; ++j) { + BiasType sum0 = accumulation[static_cast(perspectives[p])][j + 0]; + BiasType sum1 = accumulation[static_cast(perspectives[p])][j + HalfDimensions / 2]; + sum0 = std::max(0, std::min(127, sum0)); + sum1 = std::max(0, std::min(127, sum1)); + output[offset + j] = static_cast(sum0 * sum1 / 128); } + +#endif } + return psqt; - #else - - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - for (IndexType j = 0; j < HalfDimensions; ++j) - { - BiasType sum = accumulation[perspectives[p]][j]; - output[offset + j] = static_cast(std::max(0, std::min(127, sum))); - } - } - return psqt; - - #endif - } // end of function transform() From 3ec6e1d2450183ed4975cf569b5a1286cb9d8369 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 12 Feb 2022 20:08:45 +0300 Subject: [PATCH 278/282] Big search tuning (version 2) One more tuning - this one includes newly introduced heuristics and some other parameters that were not included in previous one. Result of 400k games at 20+0.2 "as is". Tuning is continuing since there is probably a lot more elo to gain. STC: https://tests.stockfishchess.org/tests/view/620782edd71106ed12a497d1 LLR: 2.99 (-2.94,2.94) <0.00,2.50> Total: 38504 W: 10260 L: 9978 D: 18266 Ptnml(0-2): 142, 4249, 10230, 4447, 184 LTC: https://tests.stockfishchess.org/tests/view/6207a243d71106ed12a49d07 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 25176 W: 6793 L: 6546 D: 11837 Ptnml(0-2): 20, 2472, 7360, 2713, 23 closes https://github.com/official-stockfish/Stockfish/pull/3931 Bench: 4784796 --- src/search.cpp | 98 +++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5bc9de7b..0b98bb27 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(171 * (d - improving)); + return Value(147 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -71,7 +71,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1575 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 1011); + return (r + 1627 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 992); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -80,7 +80,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((7 * d + 254) * d - 206 , 1990); + return std::min((8 * d + 281) * d - 241 , 1949); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -157,7 +157,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((21.5 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((21.14 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -303,10 +303,10 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(190, 1); + complexityAverage.set(211, 1); trend = SCORE_ZERO; - optimism[ us] = Value(34); + optimism[ us] = Value(33); optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -349,16 +349,16 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].averageScore; - delta = Value(16) + int(prev) * prev / 16384; + delta = Value(19) + int(prev) * prev / 18321; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust trend and optimism based on root move's previousScore - int tr = sigmoid(prev, 6, 13, 96, 110, 1); + int tr = sigmoid(prev, 4, 11, 92, 119, 1); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); - int opt = sigmoid(prev, 7, 21, 94, 14786, 221); + int opt = sigmoid(prev, 9, 18, 115, 12250, 187); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -413,7 +413,7 @@ void Thread::search() { else break; - delta += delta / 4 + 3; + delta += delta / 4 + 2; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } @@ -459,17 +459,17 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (87 + 12 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 777.20; + double fallingEval = (66 + 12 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 809.70; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.70 : 0.91; - double reduction = (1.59 + mainThread->previousTimeReduction) / (2.33 * timeReduction); + timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.73 : 0.94; + double reduction = (1.66 + mainThread->previousTimeReduction) / (2.35 * timeReduction); double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::clamp(1.0 + (complexity - 312) / 1750.0, 0.5, 1.5); + double complexPosition = std::clamp(1.0 + (complexity - 293) / 1525.0, 0.5, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; @@ -490,7 +490,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.55) + && Time.elapsed() > totalTime * 0.49) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -766,7 +766,7 @@ namespace { // margin and the improving flag are used in various pruning heuristics. improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 200; + : 184; improving = improvement > 0; complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score()))); @@ -778,7 +778,7 @@ namespace { // return a fail low. if ( !PvNode && depth <= 6 - && eval < alpha - 400 - 300 * depth * depth) + && eval < alpha - 486 - 314 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -791,16 +791,16 @@ namespace { && depth < 8 && eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta && eval >= beta - && eval < 17548) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. + && eval < 22266) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 13706 + && (ss-1)->statScore < 15075 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 19 * depth - improvement / 15 + 200 + complexity / 25 + && ss->staticEval >= beta - 18 * depth - improvement / 19 + 215 + complexity / 30 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -808,7 +808,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 205, 3) + depth / 3 + 4 - (complexity > 500); + Depth R = std::min(int(eval - beta) / 184, 4) + depth / 3 + 4 - (complexity > 799); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -844,13 +844,13 @@ namespace { } } - probCutBeta = beta + 229 - 47 * improving; + probCutBeta = beta + 204 - 52 * improving; // Step 10. ProbCut (~4 Elo) // If we have a good enough capture and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode - && depth > 3 + && depth > 4 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY // if value from transposition table is lower than probCutBeta, don't attempt probCut // there and in further interactions with transposition table cutoff depth is set to depth - 3 @@ -908,12 +908,12 @@ namespace { // Step 11. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) if ( PvNode - && depth >= 4 + && depth >= 3 && !ttMove) depth -= 2; if ( cutNode - && depth >= 7 + && depth >= 8 && !ttMove) depth--; @@ -923,7 +923,7 @@ moves_loop: // When in check, search starts here probCutBeta = beta + 401; if ( ss->inCheck && !PvNode - && depth >= 4 + && depth >= 2 && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3 @@ -1014,14 +1014,14 @@ moves_loop: // When in check, search starts here if ( !pos.empty(to_sq(move)) && !givesCheck && !PvNode - && lmrDepth < 6 + && lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 392 + 207 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] - + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 8 < alpha) + && ss->staticEval + 424 + 138 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; // SEE based pruning (~9 Elo) - if (!pos.see_ge(move, Value(-200) * depth)) + if (!pos.see_ge(move, Value(-214) * depth)) continue; } else @@ -1040,11 +1040,11 @@ moves_loop: // When in check, search starts here // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck && lmrDepth < 11 - && ss->staticEval + 131 + 137 * lmrDepth + history / 64 <= alpha) + && ss->staticEval + 147 + 125 * lmrDepth + history / 64 <= alpha) continue; // Prune moves with negative SEE (~3 Elo) - if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 29 * lmrDepth))) + if (!pos.see_ge(move, Value(-23 * lmrDepth * lmrDepth - 31 * lmrDepth))) continue; } } @@ -1067,7 +1067,7 @@ moves_loop: // When in check, search starts here && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - 3 * depth; + Value singularBeta = ttValue - 4 * depth; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; @@ -1080,8 +1080,8 @@ moves_loop: // When in check, search starts here // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 71 - && ss->doubleExtensions <= 6) + && value < singularBeta - 52 + && ss->doubleExtensions <= 8) extension = 2; } @@ -1100,15 +1100,15 @@ moves_loop: // When in check, search starts here // Check extensions (~1 Elo) else if ( givesCheck - && depth > 7 - && abs(ss->staticEval) > 128) + && depth > 8 + && abs(ss->staticEval) > 81) extension = 1; // Quiet ttMove extensions (~0 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 8932) + && (*contHist[0])[movedPiece][to_sq(move)] >= 7546) extension = 1; } @@ -1145,7 +1145,7 @@ moves_loop: // When in check, search starts here // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode - && bestMoveCount <= 4) + && bestMoveCount <= 3) r--; // Decrease reduction if position is or has been on the PV @@ -1170,18 +1170,18 @@ moves_loop: // When in check, search starts here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4142; + - 4123; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 15328; + r -= ss->statScore / 17417; // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 : moveCount <= 5 ? 2 - : PvNode && depth > 4 ? 1 - : cutNode && moveCount <= 5 ? 1 + : PvNode && depth > 3 ? 1 + : cutNode && moveCount <= 7 ? 1 : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); @@ -1190,7 +1190,7 @@ moves_loop: // When in check, search starts here // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; - doDeeperSearch = value > (alpha + 80 + 20 * (newDepth - d)); + doDeeperSearch = value > (alpha + 76 + 11 * (newDepth - d)); didLMR = true; } else @@ -1211,7 +1211,7 @@ moves_loop: // When in check, search starts here : -stat_bonus(newDepth); if (captureOrPromotion) - bonus /= 5; + bonus /= 6; update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } @@ -1335,14 +1335,14 @@ moves_loop: // When in check, search starts here quietsSearched, quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low - else if ( (depth >= 3 || PvNode) + else if ( (depth >= 4 || PvNode) && !priorCapture) { //Assign extra bonus if current node is PvNode or cutNode //or fail low was really bad bool extraBonus = PvNode || cutNode - || bestValue < alpha - 99 * depth; + || bestValue < alpha - 71 * depth; update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); } @@ -1473,7 +1473,7 @@ moves_loop: // When in check, search starts here if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 127; + futilityBase = bestValue + 139; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From 84b1940fcae95bb0a641dda9e85cb96f8c21cd22 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 17 Feb 2022 10:54:07 +0300 Subject: [PATCH 279/282] Tune search at very long time control This patch is a result of tuning done by user @candirufish after 150k games. Since the tuned values were really interesting and touched heuristics that are known for their non-linear scaling I decided to run limited games LTC match, even if the STC test was really bad (which was expected). After seeing the results of the LTC match, I also run a VLTC (very long time control) SPRTtest, which passed. The main difference is in extensions: this patch allows much more singular/double extensions, both in terms of allowing them at lower depths and with lesser margins. Failed STC: https://tests.stockfishchess.org/tests/view/620d66643ec80158c0cd3b46 LLR: -2.94 (-2.94,2.94) <0.00,2.50> Total: 4968 W: 1194 L: 1398 D: 2376 Ptnml(0-2): 47, 633, 1294, 497, 13 Performed well at LTC in a fixed-length match: https://tests.stockfishchess.org/tests/view/620d66823ec80158c0cd3b4a ELO: 3.36 +-1.8 (95%) LOS: 100.0% Total: 30000 W: 7966 L: 7676 D: 14358 Ptnml(0-2): 36, 2936, 8755, 3248, 25 Passed VLTC SPRT test: https://tests.stockfishchess.org/tests/view/620da11a26f5b17ec884f939 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 4400 W: 1326 L: 1127 D: 1947 Ptnml(0-2): 13, 309, 1348, 526, 4 closes https://github.com/official-stockfish/Stockfish/pull/3937 Bench: 6318903 --- src/search.cpp | 88 +++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0b98bb27..e6420931 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(147 * (d - improving)); + return Value(168 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -71,7 +71,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1627 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 992); + return (r + 1463 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 1010); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -80,7 +80,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((8 * d + 281) * d - 241 , 1949); + return std::min((9 * d + 270) * d - 311 , 2145); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -157,7 +157,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((21.14 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((20.81 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -303,10 +303,10 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(211, 1); + complexityAverage.set(202, 1); trend = SCORE_ZERO; - optimism[ us] = Value(33); + optimism[ us] = Value(39); optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -349,16 +349,16 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].averageScore; - delta = Value(19) + int(prev) * prev / 18321; + delta = Value(16) + int(prev) * prev / 19178; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust trend and optimism based on root move's previousScore - int tr = sigmoid(prev, 4, 11, 92, 119, 1); + int tr = sigmoid(prev, 3, 8, 90, 125, 1); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); - int opt = sigmoid(prev, 9, 18, 115, 12250, 187); + int opt = sigmoid(prev, 8, 17, 144, 13966, 183); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -459,17 +459,17 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (66 + 12 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 809.70; + double fallingEval = (69 + 12 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 781.4; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.73 : 0.94; - double reduction = (1.66 + mainThread->previousTimeReduction) / (2.35 * timeReduction); + timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.63 : 0.73; + double reduction = (1.56 + mainThread->previousTimeReduction) / (2.20 * timeReduction); double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::clamp(1.0 + (complexity - 293) / 1525.0, 0.5, 1.5); + double complexPosition = std::clamp(1.0 + (complexity - 326) / 1618.1, 0.5, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; @@ -490,7 +490,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.49) + && Time.elapsed() > totalTime * 0.43) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -766,7 +766,7 @@ namespace { // margin and the improving flag are used in various pruning heuristics. improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 184; + : 175; improving = improvement > 0; complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score()))); @@ -777,8 +777,8 @@ namespace { // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. if ( !PvNode - && depth <= 6 - && eval < alpha - 486 - 314 * depth * depth) + && depth <= 7 + && eval < alpha - 348 - 258 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -791,16 +791,16 @@ namespace { && depth < 8 && eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta && eval >= beta - && eval < 22266) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. + && eval < 26305) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 15075 + && (ss-1)->statScore < 14695 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 18 * depth - improvement / 19 + 215 + complexity / 30 + && ss->staticEval >= beta - 15 * depth - improvement / 15 + 198 + complexity / 28 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -808,7 +808,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 184, 4) + depth / 3 + 4 - (complexity > 799); + Depth R = std::min(int(eval - beta) / 147, 5) + depth / 3 + 4 - (complexity > 753); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -844,7 +844,7 @@ namespace { } } - probCutBeta = beta + 204 - 52 * improving; + probCutBeta = beta + 179 - 46 * improving; // Step 10. ProbCut (~4 Elo) // If we have a good enough capture and a reduced search returns a value @@ -920,7 +920,7 @@ namespace { moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~0 Elo) - probCutBeta = beta + 401; + probCutBeta = beta + 481; if ( ss->inCheck && !PvNode && depth >= 2 @@ -1014,14 +1014,14 @@ moves_loop: // When in check, search starts here if ( !pos.empty(to_sq(move)) && !givesCheck && !PvNode - && lmrDepth < 7 + && lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 424 + 138 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] - + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) + && ss->staticEval + 281 + 179 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha) continue; // SEE based pruning (~9 Elo) - if (!pos.see_ge(move, Value(-214) * depth)) + if (!pos.see_ge(move, Value(-203) * depth)) continue; } else @@ -1040,11 +1040,11 @@ moves_loop: // When in check, search starts here // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck && lmrDepth < 11 - && ss->staticEval + 147 + 125 * lmrDepth + history / 64 <= alpha) + && ss->staticEval + 122 + 138 * lmrDepth + history / 60 <= alpha) continue; // Prune moves with negative SEE (~3 Elo) - if (!pos.see_ge(move, Value(-23 * lmrDepth * lmrDepth - 31 * lmrDepth))) + if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 20 * lmrDepth))) continue; } } @@ -1059,7 +1059,7 @@ moves_loop: // When in check, search starts here // a reduced search on all the other moves but the ttMove and if the // result is lower than ttValue minus a margin, then we will extend the ttMove. if ( !rootNode - && depth >= 6 + 2 * (PvNode && tte->is_pv()) + && depth >= 4 + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ @@ -1067,7 +1067,7 @@ moves_loop: // When in check, search starts here && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - 4 * depth; + Value singularBeta = ttValue - 3 * depth; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; @@ -1080,7 +1080,7 @@ moves_loop: // When in check, search starts here // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 52 + && value < singularBeta - 26 && ss->doubleExtensions <= 8) extension = 2; } @@ -1100,15 +1100,15 @@ moves_loop: // When in check, search starts here // Check extensions (~1 Elo) else if ( givesCheck - && depth > 8 - && abs(ss->staticEval) > 81) + && depth > 9 + && abs(ss->staticEval) > 71) extension = 1; // Quiet ttMove extensions (~0 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 7546) + && (*contHist[0])[movedPiece][to_sq(move)] >= 5491) extension = 1; } @@ -1170,18 +1170,18 @@ moves_loop: // When in check, search starts here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4123; + - 4334; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 17417; + r -= ss->statScore / 15914; // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 - : moveCount <= 5 ? 2 - : PvNode && depth > 3 ? 1 - : cutNode && moveCount <= 7 ? 1 + : moveCount <= 4 ? 2 + : PvNode && depth > 4 ? 1 + : cutNode && moveCount <= 8 ? 1 : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); @@ -1190,7 +1190,7 @@ moves_loop: // When in check, search starts here // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; - doDeeperSearch = value > (alpha + 76 + 11 * (newDepth - d)); + doDeeperSearch = value > (alpha + 78 + 11 * (newDepth - d)); didLMR = true; } else @@ -1342,7 +1342,7 @@ moves_loop: // When in check, search starts here //or fail low was really bad bool extraBonus = PvNode || cutNode - || bestValue < alpha - 71 * depth; + || bestValue < alpha - 70 * depth; update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); } @@ -1473,7 +1473,7 @@ moves_loop: // When in check, search starts here if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 139; + futilityBase = bestValue + 118; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From 2da1d1bf571e3fd2e1d6cf56b76a7504de1a9453 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Sun, 6 Feb 2022 19:20:30 +0100 Subject: [PATCH 280/282] Add ARM NDK to Github Actions matrix - set the variable only for the required tests to keep simple the yml file - use NDK 21.x until will be fixed the Stockfish static build problem with NDK 23.x - set the test for armv7, armv7-neon, armv8 builds: - use armv7a-linux-androideabi21-clang++ compiler for armv7 armv7-neon - enforce a static build - silence the Warning for the unused compilation flag "-pie" with the static build, otherwise the Github workflow stops - use qemu to bench the build and get the signature Many thanks to @pschneider1968 that made all the hard work with NDK :) closes https://github.com/official-stockfish/Stockfish/pull/3924 No functional change --- .github/workflows/stockfish.yml | 68 +++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 33126a11..f1741ed8 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -5,6 +5,7 @@ on: - master - tools - github_ci + - github_ci_armv7 pull_request: branches: - master @@ -20,6 +21,12 @@ jobs: strategy: matrix: config: + # set the variable for the required tests: + # run_expensive_tests: true + # run_32bit_tests: true + # run_64bit_tests: true + # run_armv8_tests: true + # run_armv7_tests: true - { name: "Ubuntu 20.04 GCC", os: ubuntu-20.04, @@ -35,18 +42,31 @@ jobs: os: ubuntu-20.04, compiler: clang++, comp: clang, - run_expensive_tests: false, run_32bit_tests: true, run_64bit_tests: true, shell: 'bash {0}' } + - { + name: "Ubuntu 20.04 NDK armv8", + os: ubuntu-20.04, + compiler: aarch64-linux-android21-clang++, + comp: ndk, + run_armv8_tests: true, + shell: 'bash {0}' + } + - { + name: "Ubuntu 20.04 NDK armv7", + os: ubuntu-20.04, + compiler: armv7a-linux-androideabi21-clang++, + comp: ndk, + run_armv7_tests: true, + shell: 'bash {0}' + } - { name: "MacOS 10.15 Apple Clang", os: macos-10.15, compiler: clang++, comp: clang, - run_expensive_tests: false, - run_32bit_tests: false, run_64bit_tests: true, shell: 'bash {0}' } @@ -55,8 +75,6 @@ jobs: os: macos-10.15, compiler: g++-10, comp: gcc, - run_expensive_tests: false, - run_32bit_tests: false, run_64bit_tests: true, shell: 'bash {0}' } @@ -65,8 +83,6 @@ jobs: os: windows-2022, compiler: g++, comp: gcc, - run_expensive_tests: false, - run_32bit_tests: false, run_64bit_tests: true, msys_sys: 'mingw64', msys_env: 'x86_64', @@ -77,9 +93,7 @@ jobs: os: windows-2022, compiler: g++, comp: gcc, - run_expensive_tests: false, run_32bit_tests: true, - run_64bit_tests: false, msys_sys: 'mingw32', msys_env: 'i686', shell: 'msys2 {0}' @@ -89,8 +103,6 @@ jobs: os: windows-2022, compiler: clang++, comp: clang, - run_expensive_tests: false, - run_32bit_tests: false, run_64bit_tests: true, msys_sys: 'clang64', msys_env: 'clang-x86_64', @@ -110,7 +122,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt update - sudo apt install expect valgrind g++-multilib + sudo apt install expect valgrind g++-multilib qemu-user - name: Setup msys and install required packages if: runner.os == 'Windows' @@ -130,6 +142,7 @@ jobs: - name: Check compiler run: | + export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin $COMPILER -v - name: Test help target @@ -251,6 +264,37 @@ jobs: make clean make -j2 ARCH=x86-64-vnni256 build + # armv8 tests + + - name: Test armv8 build + if: ${{ matrix.config.run_armv8_tests }} + run: | + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv8 build + ../tests/signature.sh $benchref + + # armv7 tests + + - name: Test armv7 build + if: ${{ matrix.config.run_armv7_tests }} + run: | + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv7 build + ../tests/signature.sh $benchref + + - name: Test armv7-neon build + if: ${{ matrix.config.run_armv7_tests }} + run: | + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv7-neon build + ../tests/signature.sh $benchref + # Other tests - name: Check perft and search reproducibility From abef3e86f42fd4953d28cc7c3381601475d11346 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 20 Feb 2022 17:36:19 +0100 Subject: [PATCH 281/282] Fix clang warning on unused variable mark variable as used. fixes https://github.com/official-stockfish/Stockfish/issues/3900 closes https://github.com/official-stockfish/Stockfish/pull/3941 No functional change --- src/evaluate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a5c049a8..923564cb 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -108,6 +108,7 @@ namespace Eval { MemoryBuffer buffer(const_cast(reinterpret_cast(gEmbeddedNNUEData)), size_t(gEmbeddedNNUESize)); + (void) gEmbeddedNNUEEnd; // Silence warning on unused variable istream stream(&buffer); if (load_eval(eval_file, stream)) From 27139dedac14af400f5b18e2ab50aca3f8cf0e33 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 19 Feb 2022 18:24:11 +0300 Subject: [PATCH 282/282] Adjust usage of LMR for 2nd move in move ordering Current master prohibits usage of LMR for 2nd move at rootNode. This patch also disables LMR for 2nd move not only at rootNode but also at first PvNode that is a reply to rootNode. passed STC: https://tests.stockfishchess.org/tests/view/620e8c9026f5b17ec885143a LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 54096 W: 14305 L: 13996 D: 25795 Ptnml(0-2): 209, 6075, 14192, 6342, 230 passed LTC: https://tests.stockfishchess.org/tests/view/620eb327b1792e8985f81fb8 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 110864 W: 29602 L: 29156 D: 52106 Ptnml(0-2): 112, 11147, 32455, 11619, 99 closes https://github.com/official-stockfish/Stockfish/pull/3940 bench 6820724 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e6420931..6785ba4c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1136,7 +1136,7 @@ moves_loop: // When in check, search starts here // been searched. In general we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". if ( depth >= 2 - && moveCount > 1 + rootNode + && moveCount > 1 + (PvNode && ss->ply <= 1) && ( !ss->ttPv || !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1)))