From 6c197c3964ca0c637ff1f646dc7e6653b1bb4b45 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Sat, 11 Jul 2020 16:25:34 +0200 Subject: [PATCH 01/45] Corrects a functional change in a cleanup patch. This corrects a functional change in https://github.com/official-stockfish/Stockfish/commit/ddcbacd04d1c860e808202ce8c1206c8acdca627 changing evaluation of KPPvK. Bench remains unchanged at low depth With this patch, 8/8/5k1p/8/7p/7K/8/8 b - - 1 11 is again correctly evaluated as a draw. closes https://github.com/official-stockfish/Stockfish/pull/2807 Bench: 4366686 --- src/endgame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 40f49dce..a8ceb648 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -589,8 +589,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Bitboard strongPawns = pos.pieces(strongSide, PAWN); // If all pawns are ahead of the king on a single rook file, it's a draw. - if (!((strongPawns & ~FileABB) || (strongPawns & ~FileHBB)) && - !(strongPawns & ~passed_pawn_span(weakSide, weakKing))) + if ( !(strongPawns & ~(FileABB | FileHBB)) + && !(strongPawns & ~passed_pawn_span(weakSide, weakKing))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; From c3092c54bc6fb837137365fc60eb57bd188deaca Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 12 Jul 2020 13:58:00 -0700 Subject: [PATCH 02/45] Multiple lazy stages. An extension of the lazy eval idea: when the score is sufficiently large we now skip more granular parts of the eval. Inspired by an original patch by Moez Jellouli https://tests.stockfishchess.org/tests/view/5f03b2a159f6f03532894529 Credit to him! STC https://tests.stockfishchess.org/tests/view/5f0a862c59f6f03532894924 LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 13504 W: 2684 L: 2472 D: 8348 Ptnml(0-2): 229, 1496, 3111, 1666, 250 LTC https://tests.stockfishchess.org/tests/view/5f0ac0e159f6f0353289495b LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 31312 W: 3926 L: 3677 D: 23709 Ptnml(0-2): 185, 2773, 9509, 2986, 203 closes https://github.com/official-stockfish/Stockfish/pull/2814 bench: 4541608 --- src/evaluate.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ca6ea5c4..dbb725d4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -74,7 +74,8 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold = Value(1400); + constexpr Value LazyThreshold1 = Value(1400); + constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -786,7 +787,7 @@ namespace { && pos.non_pawn_material(BLACK) == RookValueMg && pos.count(strongSide) - pos.count(~strongSide) <= 1 && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) - && (attackedBy[~strongSide][KING] & pos.pieces(~strongSide, PAWN))) + && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) sf = 36; else if (pos.count() == 1) sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK) @@ -837,9 +838,12 @@ namespace { score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK); // Early exit if score is high - Value v = (mg_value(score) + eg_value(score)) / 2; - if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64) - return pos.side_to_move() == WHITE ? v : -v; + auto lazy_skip = [&](Value lazyThreshold) { + return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64; + }; + + if (lazy_skip(LazyThreshold1)) + goto make_v; // Main evaluation begins here initialize(); @@ -856,12 +860,17 @@ namespace { // More complex interactions that require fully populated attack bitboards score += king< WHITE>() - king< BLACK>() - + threats() - threats() - + passed< WHITE>() - passed< BLACK>() + + passed< WHITE>() - passed< BLACK>(); + + if (lazy_skip(LazyThreshold2)) + goto make_v; + + score += threats() - threats() + space< WHITE>() - space< BLACK>(); +make_v: // Derive single value from mg and eg parts of score - v = winnable(score); + Value v = winnable(score); // In case of tracing add all remaining individual evaluation terms if (T) From d89730d5c8dcf10eb9e1d91a81f903d9fc3c949a Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 13 Jul 2020 20:30:58 +0300 Subject: [PATCH 03/45] Do not overwrite valuable TT data after probcut. This patch allows an engine to write probcut data only in case the probcut search depth is greater than transposition table depth. passed STC https://tests.stockfishchess.org/tests/view/5f0b52e959f6f035328949a6 LLR: 2.97 (-2.94,2.94) {-0.50,1.50} Total: 52544 W: 10145 L: 9880 D: 32519 Ptnml(0-2): 853, 6097, 12121, 6334, 867 passed LTC https://tests.stockfishchess.org/tests/view/5f0bd94c59f6f035328949f3 LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 49576 W: 6164 L: 5863 D: 37549 Ptnml(0-2): 297, 4371, 15218, 4538, 364 closes https://github.com/official-stockfish/Stockfish/pull/2815 bench 4578298 --- src/search.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6cf2f90d..17ccab92 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -925,9 +925,12 @@ namespace { if (value >= probcutBeta) { - tte->save(posKey, value_to_tt(value, ss->ply), ttPv, - BOUND_LOWER, - depth - 3, move, ss->staticEval); + if ( !(ttHit + && tte->depth() >= depth - 3 + && ttValue != VALUE_NONE)) + tte->save(posKey, value_to_tt(value, ss->ply), ttPv, + BOUND_LOWER, + depth - 3, move, ss->staticEval); return value; } } From f0abde241d39ee4507778bf41b392492c5391652 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sat, 25 Jul 2020 07:32:19 -0600 Subject: [PATCH 04/45] Remove conditional_more_than_two(). This is a functional simplification that removes the conditional_more_than_two() function, which was quite strange and kooky. Note the very minor change to the bench value. See this thread for relevant comments on the passing branch: protonspring/Stockfish@d89730d...ff35b50 STC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 59760 W: 11411 L: 11311 D: 37038 Ptnml(0-2): 992, 6863, 14044, 7015, 966 https://tests.stockfishchess.org/tests/view/5f179988c09435d870cb9b9a LTC LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 45208 W: 5553 L: 5497 D: 34158 Ptnml(0-2): 315, 4081, 13761, 4127, 320 https://tests.stockfishchess.org/tests/view/5f184847c09435d870cb9bee closes https://github.com/official-stockfish/Stockfish/pull/2826 Bench: 4578290 --- src/bitboard.h | 6 ------ src/evaluate.cpp | 12 +++++++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 15ec4153..8c95de8c 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -130,12 +130,6 @@ constexpr bool more_than_one(Bitboard b) { return b & (b - 1); } -/// Counts the occupation of the bitboard depending on the occupation of SQ_A1 -/// as in `b & (1ULL << SQ_A1) ? more_than_two(b) : more_than_one(b)` - -constexpr bool conditional_more_than_two(Bitboard b) { - return b & (b - 1) & (b - 2); -} constexpr bool opposite_colors(Square s1, Square s2) { return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index dbb725d4..d16648a8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -310,13 +310,15 @@ namespace { if (Pt == BISHOP || Pt == KNIGHT) { - // Bonus if piece is on an outpost square or can reach one + // Bonus if the piece is on an outpost square or can reach one + // Reduced bonus for knights (BadOutpost) if few relevant targets bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); + Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN); + if ( Pt == KNIGHT - && bb & s & ~CenterFiles - && !(b & pos.pieces(Them) & ~pos.pieces(PAWN)) - && !conditional_more_than_two( - pos.pieces(Them) & ~pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide))) + && bb & s & ~CenterFiles // on a side outpost + && !(b & targets) // no relevant attacks + && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide)))) score += BadOutpost; else if (bb & s) score += Outpost[Pt == BISHOP]; From 62d3106caa2f5acf5ba32500cc19912b8f10612c Mon Sep 17 00:00:00 2001 From: UnaiCorzo Date: Sat, 25 Jul 2020 22:30:05 +0200 Subject: [PATCH 05/45] Remove late irreversible move extension We simplify away the late irreversible move extension, which does not seem to be necessary in the current master. STC LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 38584 W: 7464 L: 7342 D: 23778 Ptnml(0-2): 581, 4328, 9365, 4424, 594 https://tests.stockfishchess.org/tests/view/5f1c9669c09435d870cb9de9 LTC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 27840 W: 3417 L: 3353 D: 21070 Ptnml(0-2): 120, 2315, 8994, 2363, 128 https://tests.stockfishchess.org/tests/view/5f1d2e22c09435d870cb9e21 closes https://github.com/official-stockfish/Stockfish/pull/2836 bench: 4829420 --- src/search.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 17ccab92..6ec4d803 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1067,7 +1067,7 @@ moves_loop: // When in check, search starts from here // 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. + // result is lower than ttValue minus a margin, then we will extend the ttMove. if ( depth >= 6 && move == ttMove && !rootNode @@ -1131,12 +1131,6 @@ moves_loop: // When in check, search starts from here if (type_of(move) == CASTLING) extension = 1; - // Late irreversible move extension - if ( move == ttMove - && pos.rule50_count() > 80 - && (captureOrPromotion || type_of(movedPiece) == PAWN)) - extension = 2; - // Add extension to new depth newDepth += extension; From 33f3cfae0093b934563e1eca78486261f18e4650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicolet?= Date: Tue, 28 Jul 2020 10:08:09 +0200 Subject: [PATCH 06/45] Improve handling of queen imbalance We double the bonus for potential threats by minors and rooks against our queen, in case of "queen vs pieces imbalance". Hopefully this will improve a little bit the evaluation for this well-known Stockfish weakness. passed STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 72976 W: 14003 L: 13710 D: 45263 Ptnml(0-2): 1218, 8370, 17094, 8513, 1293 https://tests.stockfishchess.org/tests/view/5efa50eb020eec13834a977d passed LTC: LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 22232 W: 2779 L: 2560 D: 16893 Ptnml(0-2): 129, 1885, 6896, 2050, 156 https://tests.stockfishchess.org/tests/view/5f1fdd2dc09435d870cb9f13 closes https://github.com/official-stockfish/Stockfish/pull/2864 Bench: 4367349 --- src/evaluate.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d16648a8..b34d82f6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -579,17 +579,21 @@ namespace { // Bonus for threats on the next moves against enemy queen if (pos.count(Them) == 1) { + bool queenImbalance = pos.count() == 1; + Square s = pos.square(Them); - safe = mobilityArea[Us] & ~stronglyProtected; + safe = mobilityArea[Us] + & ~pos.pieces(Us, PAWN) + & ~stronglyProtected; b = attackedBy[Us][KNIGHT] & attacks_bb(s); - score += KnightOnQueen * popcount(b & safe); + score += KnightOnQueen * popcount(b & safe) * (1 + queenImbalance); b = (attackedBy[Us][BISHOP] & attacks_bb(s, pos.pieces())) | (attackedBy[Us][ROOK ] & attacks_bb(s, pos.pieces())); - score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]); + score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance); } if (T) From 9587eeeb5ed29f834d4f956b92e0e732877c47a7 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 30 Jul 2020 18:56:11 +0200 Subject: [PATCH 07/45] Tweak cutnode reduction Less reduction for second move at non-check CUT node with depth <= 10. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 38680 W: 7490 L: 7245 D: 23945 Ptnml(0-2): 643, 4441, 8967, 4606, 683 https://tests.stockfishchess.org/tests/view/5f21e1782f7e63962b99f451 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 71976 W: 9003 L: 8636 D: 54337 Ptnml(0-2): 440, 6414, 21972, 6663, 499 https://tests.stockfishchess.org/tests/view/5f2245762f7e63962b99f4bd closes https://github.com/official-stockfish/Stockfish/pull/2868 Bench: 4746616 --- src/search.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 6ec4d803..91ac60ad 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1167,6 +1167,13 @@ moves_loop: // When in check, search starts from here { Depth r = reduction(improving, depth, moveCount); + // Decrease reduction at non-check cut nodes for second move at low depths + if ( cutNode + && depth <= 10 + && moveCount <= 2 + && !ss->inCheck) + r--; + // Decrease reduction if the ttHit running average is large if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; From 84f3e867903f62480c33243dd0ecbffd342796fc Mon Sep 17 00:00:00 2001 From: nodchip Date: Wed, 5 Aug 2020 17:11:15 +0200 Subject: [PATCH 08/45] Add NNUE evaluation This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish. Both the NNUE and the classical evaluations are available, and can be used to assign a value to a position that is later used in alpha-beta (PVS) search to find the best move. The classical evaluation computes this value as a function of various chess concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation computes this value with a neural network based on basic inputs. The network is optimized and trained on the evalutions 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. This patch is the result of contributions of various authors, from various communities, including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather, rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler, dorzechowski, and vondele. This new evaluation needed various changes to fishtest and the corresponding infrastructure, for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged. The first networks have been provided by gekkehenker and sergiovieri, with the latter net (nn-97f742aaefcd.nnue) being the current default. The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option, provided the `EvalFile` option points the the network file (depending on the GUI, with full path). The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest: 60000 @ 10+0.1 th 1 https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c ELO: 92.77 +-2.1 (95%) LOS: 100.0% Total: 60000 W: 24193 L: 8543 D: 27264 Ptnml(0-2): 609, 3850, 9708, 10948, 4885 40000 @ 20+0.2 th 8 https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58 ELO: 89.47 +-2.0 (95%) LOS: 100.0% Total: 40000 W: 12756 L: 2677 D: 24567 Ptnml(0-2): 74, 1583, 8550, 7776, 2017 At the same time, the impact on the classical evaluation remains minimal, causing no significant regression: sprt @ 10+0.1 th 1 https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b LLR: 2.94 (-2.94,2.94) {-6.00,-4.00} Total: 34936 W: 6502 L: 6825 D: 21609 Ptnml(0-2): 571, 4082, 8434, 3861, 520 sprt @ 60+0.6 th 1 https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d LLR: 2.93 (-2.94,2.94) {-6.00,-4.00} Total: 10088 W: 1232 L: 1265 D: 7591 Ptnml(0-2): 49, 914, 3170, 843, 68 The needed networks can be found at https://tests.stockfishchess.org/nns It is recommended to use the default one as indicated by the `EvalFile` UCI option. Guidelines for testing new nets can be found at https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests Integration has been discussed in various issues: https://github.com/official-stockfish/Stockfish/issues/2823 https://github.com/official-stockfish/Stockfish/issues/2728 The integration branch will be closed after the merge: https://github.com/official-stockfish/Stockfish/pull/2825 https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip closes https://github.com/official-stockfish/Stockfish/pull/2912 This will be an exciting time for computer chess, looking forward to seeing the evolution of this approach. Bench: 4746616 --- .travis.yml | 31 +- AUTHORS | 17 +- Readme.md => README.md | 129 ++++--- appveyor.yml | 17 +- src/Makefile | 227 +++++++++++-- src/benchmark.cpp | 4 +- src/bitbase.cpp | 4 +- src/bitboard.cpp | 4 +- src/bitboard.h | 4 +- src/endgame.cpp | 4 +- src/endgame.h | 4 +- src/evaluate.cpp | 114 +++++-- src/evaluate.h | 24 +- src/main.cpp | 5 +- src/material.cpp | 4 +- src/material.h | 4 +- src/misc.cpp | 62 +++- src/misc.h | 6 +- src/movegen.cpp | 4 +- src/movegen.h | 4 +- src/movepick.cpp | 4 +- src/movepick.h | 4 +- src/nnue/architectures/halfkp_256x2-32-32.h | 54 +++ src/nnue/evaluate_nnue.cpp | 178 ++++++++++ src/nnue/evaluate_nnue.h | 48 +++ src/nnue/features/feature_set.h | 135 ++++++++ src/nnue/features/features_common.h | 45 +++ src/nnue/features/half_kp.cpp | 92 +++++ src/nnue/features/half_kp.h | 67 ++++ src/nnue/features/index_list.h | 64 ++++ src/nnue/layers/affine_transform.h | 215 ++++++++++++ src/nnue/layers/clipped_relu.h | 186 ++++++++++ src/nnue/layers/input_slice.h | 68 ++++ src/nnue/nnue_accumulator.h | 39 +++ src/nnue/nnue_architecture.h | 38 +++ src/nnue/nnue_common.h | 77 +++++ src/nnue/nnue_feature_transformer.h | 355 ++++++++++++++++++++ src/pawns.cpp | 4 +- src/pawns.h | 4 +- src/position.cpp | 108 +++++- src/position.h | 42 ++- src/psqt.cpp | 4 +- src/search.cpp | 6 +- src/search.h | 4 +- src/syzygy/tbprobe.cpp | 3 +- src/syzygy/tbprobe.h | 3 +- src/thread.cpp | 4 +- src/thread.h | 4 +- src/thread_win32_osx.h | 4 +- src/timeman.cpp | 4 +- src/timeman.h | 4 +- src/tt.cpp | 4 +- src/tt.h | 4 +- src/tune.cpp | 4 +- src/tune.h | 4 +- src/types.h | 129 ++++++- src/uci.cpp | 22 +- src/uci.h | 4 +- src/ucioption.cpp | 9 +- 59 files changed, 2474 insertions(+), 245 deletions(-) rename Readme.md => README.md (79%) create mode 100644 src/nnue/architectures/halfkp_256x2-32-32.h create mode 100644 src/nnue/evaluate_nnue.cpp create mode 100644 src/nnue/evaluate_nnue.h create mode 100644 src/nnue/features/feature_set.h create mode 100644 src/nnue/features/features_common.h create mode 100644 src/nnue/features/half_kp.cpp create mode 100644 src/nnue/features/half_kp.h create mode 100644 src/nnue/features/index_list.h create mode 100644 src/nnue/layers/affine_transform.h create mode 100644 src/nnue/layers/clipped_relu.h create mode 100644 src/nnue/layers/input_slice.h create mode 100644 src/nnue/nnue_accumulator.h create mode 100644 src/nnue/nnue_architecture.h create mode 100644 src/nnue/nnue_common.h create mode 100644 src/nnue/nnue_feature_transformer.h diff --git a/.travis.yml b/.travis.yml index e2ae61be..d563a1e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: cpp -dist: xenial +dist: bionic matrix: include: @@ -7,7 +7,6 @@ matrix: compiler: gcc addons: apt: - sources: ['ubuntu-toolchain-r-test'] packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl'] env: - COMPILER=g++-8 @@ -17,23 +16,23 @@ matrix: compiler: clang addons: apt: - sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0'] - packages: ['clang-6.0', 'llvm-6.0-dev', 'g++-multilib', 'valgrind', 'expect', 'curl'] + packages: ['clang-10', 'llvm-10-dev', 'g++-multilib', 'valgrind', 'expect', 'curl'] env: - - COMPILER=clang++-6.0 + - COMPILER=clang++-10 - COMP=clang - - LDFLAGS=-fuse-ld=lld - os: osx + osx_image: xcode12 compiler: gcc env: - COMPILER=g++ - COMP=gcc - os: osx + osx_image: xcode12 compiler: clang env: - - COMPILER=clang++ V='Apple LLVM 9.4.1' # Apple LLVM version 9.1.0 (clang-902.0.39.2) + - COMPILER=clang++ - COMP=clang branches: @@ -48,26 +47,34 @@ script: - 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 + # # Verify bench number against various builds - export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" - make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref - - make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref - - make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref + - 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 build && ../tests/signature.sh $benchref; fi # # Check perft and reproducible search + - export CXXFLAGS="-Werror" + - make clean && make -j2 ARCH=x86-64 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 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 # - # Use g++-8 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc - - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi - - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 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 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi diff --git a/AUTHORS b/AUTHORS index f08d71d3..2e080e61 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,10 +1,17 @@ -# List of authors for Stockfish, as of March 30, 2020 +# List of authors for Stockfish, as of August 4, 2020 +# Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) Marco Costalba (mcostalba) Joona Kiiski (zamar) Gary Linscott (glinscott) +# Authors and inventors of NNUE, training, NNUE port +Yu Nasu (ynasu87) +Motohiro Isozaki (yaneurao) +Hisayori Noda (nodchip) + +# all other authors of the code in alphabetical order Aditya (absimaldata) Adrian Petrescu (apetresc) Ajith Chandy Jose (ajithcj) @@ -36,6 +43,7 @@ Dariusz Orzechowski David Zar Daylen Yang (daylen) DiscanX +Dominik Schlösser (domschl) double-beep Eduardo Cáceres (eduherminio) Eelco de Groot (KingDefender) @@ -115,7 +123,8 @@ Nick Pelling (nickpelling) Nicklas Persson (NicklasPersson) Niklas Fiekas (niklasf) Nikolay Kostov (NikolayIT) -Nguyen Pham +Nguyen Pham (nguyenpham) +Norman Schmidt (FireFather) Ondrej Mosnáček (WOnder93) Oskar Werkelin Ahlin Pablo Vazquez @@ -135,6 +144,7 @@ Richard Lloyd Rodrigo Exterckötter Tjäder Ron Britvich (Britvich) Ronald de Man (syzygy1, syzygy) +rqs Ryan Schmitt Ryan Takker Sami Kiminki (skiminki) @@ -143,6 +153,7 @@ Sergei Antonov (saproj) Sergei Ivanov (svivanov72) sf-x Shane Booth (shane31) +Shawn Varghese (xXH4CKST3RXx) Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) Steinar Gunderson (sesse) @@ -155,9 +166,11 @@ Tom Vijlbrief (tomtor) Tomasz Sobczyk (Sopel97) Torsten Franz (torfranz, tfranzer) Tracey Emery (basepr1me) +tttak Unai Corzo (unaiic) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) +zz4032 # Additionally, we acknowledge the authors and maintainers of fishtest, diff --git a/Readme.md b/README.md similarity index 79% rename from Readme.md rename to README.md index 823518d1..f71a8b34 100644 --- a/Readme.md +++ b/README.md @@ -4,7 +4,13 @@ [![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 -derived from Glaurung 2.1. It is not a complete chess program and requires a +derived from Glaurung 2.1. It features two evaluation functions, the classical +evaluation based on handcrafted terms, and the NNUE evaluation based on +efficiently updateable neural networks. The classical evaluation runs efficiently +on most 64bit CPU architectures, while the NNUE evaluation benefits strongly from the +vector intrinsics available on modern CPUs (avx2 or similar). + +Stockfish is not a complete chess program and requires a UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably. Read the documentation for your GUI of choice for information about how to use @@ -22,21 +28,20 @@ This distribution of Stockfish consists of the following files: * src, a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. +To use the NNUE evaluation an additional data file with neural network parameters +needs to be downloaded. The filename for the default set can be found as the default +value of the `EvalFile` UCI option, with the format +`nn-[SHA256 first 12 digits].nnue` (e.g. nn-c157e0a5755b.nnue). This file can be downloaded from +``` +https://tests.stockfishchess.org/api/nn/[filename] +``` +replacing `[filename]` as needed. -## UCI parameters + +## UCI options Currently, Stockfish has the following UCI options: - * #### Debug Log File - Write all communication to and from the engine into a text file. - - * #### Contempt - A positive value for contempt favors middle game positions and avoids draws. - - * #### 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. - * #### Threads The number of CPU threads used for searching a position. For best performance, set this equal to the number of CPU cores available. @@ -44,9 +49,6 @@ Currently, Stockfish has the following UCI options: * #### Hash The size of the hash table in MB. It is recommended to set Hash after setting Threads. - * #### Clear Hash - Clear the hash table. - * #### Ponder Let Stockfish ponder its next move while the opponent is thinking. @@ -54,10 +56,32 @@ Currently, Stockfish has the following UCI options: Output the N best lines (principal variations, PVs) when searching. Leave at 1 for best performance. - * #### Skill Level - Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength). - Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a - weaker move will be played. + * #### Use NNUE + Toggle between the NNUE and classical evaluation functions. If set to "true", + the network parameters must be availabe to load from file (see also EvalFile). + + * #### EvalFile + The name of the file of the NNUE evaluation parameters. Depending on the GUI the + filename should include the full path to the folder/directory that contains the file. + + * #### 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. + + * #### UCI_AnalyseMode + An option handled by your GUI. + + * #### UCI_Chess960 + An option handled by your GUI. If true, Stockfish will play Chess960. + + * #### UCI_ShowWDL + If enabled, show approximate WDL statistics as part of the engine output. + These WDL numbers model expected game outcomes for a given evaluation and + game ply for engine self-play at fishtest LTC conditions (60+0.6s per game). * #### UCI_LimitStrength Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level. @@ -66,28 +90,10 @@ Currently, Stockfish has the following UCI options: If enabled by UCI_LimitStrength, aim for an engine strength of the given Elo. This Elo rating has been calibrated at a time control of 60s+0.6s and anchored to CCRL 40/4. - * #### UCI_ShowWDL - If enabled, show approximate WDL statistics as part of the engine output. - These WDL numbers model expected game outcomes for a given evaluation and - game ply for engine self-play at fishtest LTC conditions (60+0.6s per game). - - * #### 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. - - * #### Slow Mover - Lower values will make Stockfish take less time in games, higher values will - make it think longer. - - * #### nodestime - Tells the engine to use nodes searched instead of wall time to account for - elapsed time. Useful for engine testing. - - * #### UCI_Chess960 - An option handled by your GUI. If true, Stockfish will play Chess960. - - * #### UCI_AnalyseMode - An option handled by your GUI. + * #### Skill Level + Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength). + Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a + weaker move will be played. * #### SyzygyPath Path to the folders/directories storing the Syzygy tablebase files. Multiple @@ -114,6 +120,47 @@ Currently, Stockfish has the following UCI options: Limit Syzygy tablebase probing to positions with at most this many pieces left (including kings and pawns). + * #### 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. + + * #### Slow Mover + Lower values will make Stockfish take less time in games, higher values will + make it think longer. + + * #### nodestime + Tells the engine to use nodes searched instead of wall time to account for + elapsed time. Useful for engine testing. + + * #### Clear Hash + Clear the hash table. + + * #### Debug Log File + Write all communication to and from the engine into a text file. + +## classical and NNUE evaluation + +Both approaches assign a value to a position that is used in alpha-beta (PVS) search +to find the best move. The classical evaluation computes this value as a function +of various chess concepts, handcrafted by experts, tested and tuned using fishtest. +The NNUE evaluation computes this value with a neural network based on basic +inputs (e.g. piece positions only). The network is optimized and trained +on the evalutions 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 (avx2 and similar), the NNUE evaluation +results in stronger playing strength, even if the nodes per second computed by the engine +is somewhat lower (roughly 60% of nps is typical). + +Note that the NNUE evaluation depends on the Stockfish binary and the network parameter +file (see EvalFile). Not every parameter file is compatible with a given Stockfish binary. +The default value of the EvalFile UCI option is the name of a network that is guaranteed +to be compatible with that binary. ## What to expect from Syzygybases? diff --git a/appveyor.yml b/appveyor.yml index 21f3bbe3..d356ba2f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,10 +4,9 @@ clone_depth: 50 branches: only: - master - - appveyor # Operating system (build VM template) -os: Visual Studio 2017 +os: Visual Studio 2019 # Build platform, i.e. x86, x64, AnyCPU. This setting is optional. platform: @@ -36,8 +35,11 @@ before_build: $src = $src.Replace("\", "/") # Build CMakeLists.txt - $t = 'cmake_minimum_required(VERSION 3.8)', + $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})' @@ -51,10 +53,11 @@ before_build: $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 15 2017" - If (${env:PLATFORM} -eq 'x64') { $g = $g + ' Win64' } - cmake -G "${g}" . - Write-Host "Generated files for: " $g + $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 diff --git a/src/Makefile b/src/Makefile index c3660a20..4741e722 100644 --- a/src/Makefile +++ b/src/Makefile @@ -38,11 +38,12 @@ PGOBENCH = ./$(EXE) bench ### Source and object files 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 + 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 OBJS = $(notdir $(SRCS:.cpp=.o)) -VPATH = syzygy +VPATH = syzygy:nnue:nnue/features ### Establish the operating system name KERNEL = $(shell uname -s) @@ -67,7 +68,14 @@ endif # prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction # popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction # sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions +# sse3 = yes/no --- -msse3 --- Use Intel Streaming SIMD Extensions 3 +# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3 +# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 +# sse42 = yes/no --- -msse4.2 --- Use Intel Streaming SIMD Extensions 4.2 +# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 # pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction +# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 +# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture # # 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 @@ -81,7 +89,15 @@ bits = 64 prefetch = no popcnt = no sse = no +sse3 = no +ssse3 = no +sse41 = no +sse42 = no +avx2 = no pext = no +avx512 = no +neon = no +ARCH = x86-64-modern ### 2.2 Architecture specific ifeq ($(ARCH),general-32) @@ -111,11 +127,70 @@ ifeq ($(ARCH),x86-64) sse = yes endif +ifeq ($(ARCH),x86-64-sse3) + arch = x86_64 + prefetch = yes + sse = yes + sse3 = yes +endif + +ifeq ($(ARCH),x86-64-sse3-popcnt) + arch = x86_64 + prefetch = yes + sse = yes + sse3 = yes + popcnt = yes +endif + +ifeq ($(ARCH),x86-64-ssse3) + arch = x86_64 + prefetch = yes + sse = yes + sse3 = yes + ssse3 = yes +endif + +ifeq ($(ARCH),x86-64-sse41) + arch = x86_64 + prefetch = yes + popcnt = yes + sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes +endif + ifeq ($(ARCH),x86-64-modern) arch = x86_64 prefetch = yes popcnt = yes sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes +endif + +ifeq ($(ARCH),x86-64-sse42) + arch = x86_64 + prefetch = yes + popcnt = yes + sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes + sse42 = yes +endif + +ifeq ($(ARCH),x86-64-avx2) + arch = x86_64 + prefetch = yes + popcnt = yes + sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes + sse42 = yes + avx2 = yes endif ifeq ($(ARCH),x86-64-bmi2) @@ -123,9 +198,28 @@ ifeq ($(ARCH),x86-64-bmi2) prefetch = yes popcnt = yes sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes + sse42 = yes + avx2 = yes pext = yes endif +ifeq ($(ARCH),x86-64-avx512) + arch = x86_64 + prefetch = yes + popcnt = yes + sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes + sse42 = yes + avx2 = yes + pext = yes + avx512 = yes +endif + ifeq ($(ARCH),armv7) arch = armv7 prefetch = yes @@ -136,6 +230,14 @@ ifeq ($(ARCH),armv8) arch = armv8-a prefetch = yes popcnt = yes + neon = yes +endif + +ifeq ($(ARCH),apple-silicon) + arch = arm64 + prefetch = yes + popcnt = yes + neon = yes endif ifeq ($(ARCH),ppc-32) @@ -154,8 +256,8 @@ endif ### ========================================================================== ### 3.1 Selecting compiler (default = gcc) -CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++11 $(EXTRACXXFLAGS) -DEPENDFLAGS += -std=c++11 +CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS) +DEPENDFLAGS += -std=c++17 LDFLAGS += $(EXTRALDFLAGS) ifeq ($(COMP),) @@ -249,8 +351,8 @@ endif endif ifeq ($(KERNEL),Darwin) - CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.9 - LDFLAGS += -arch $(arch) -mmacosx-version-min=10.9 + CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.15 + LDFLAGS += -arch $(arch) -mmacosx-version-min=10.15 endif ### Travis CI script uses COMPILER to overwrite CXX @@ -283,8 +385,8 @@ endif ### 3.2.2 Debugging with undefined behavior sanitizers ifneq ($(sanitize),no) - CXXFLAGS += -g3 -fsanitize=$(sanitize) -fuse-ld=gold - LDFLAGS += -fsanitize=$(sanitize) -fuse-ld=gold + CXXFLAGS += -g3 -fsanitize=$(sanitize) + LDFLAGS += -fsanitize=$(sanitize) endif ### 3.3 Optimization @@ -322,7 +424,7 @@ endif ### 3.6 popcnt ifeq ($(popcnt),yes) - ifeq ($(arch),$(filter $(arch),ppc64 armv8-a)) + ifeq ($(arch),$(filter $(arch),ppc64 armv8-a arm64)) CXXFLAGS += -DUSE_POPCNT else ifeq ($(comp),icc) CXXFLAGS += -msse3 -DUSE_POPCNT @@ -331,11 +433,61 @@ ifeq ($(popcnt),yes) endif endif +ifeq ($(avx2),yes) + CXXFLAGS += -DUSE_AVX2 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mavx2 + endif +endif + +ifeq ($(avx512),yes) + CXXFLAGS += -DUSE_AVX512 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mavx512bw + endif +endif + +ifeq ($(sse42),yes) + CXXFLAGS += -DUSE_SSE42 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -msse4.2 + endif +endif + +ifeq ($(sse41),yes) + CXXFLAGS += -DUSE_SSE41 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -msse4.1 + endif +endif + +ifeq ($(ssse3),yes) + CXXFLAGS += -DUSE_SSSE3 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mssse3 + endif +endif + +ifeq ($(sse3),yes) + CXXFLAGS += -DUSE_SSE3 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -msse3 + endif +endif + +ifeq ($(neon),yes) + CXXFLAGS += -DUSE_NEON +endif + +ifeq ($(arch),x86_64) + CXXFLAGS += -DUSE_SSE2 +endif + ### 3.7 pext ifeq ($(pext),yes) CXXFLAGS += -DUSE_PEXT ifeq ($(comp),$(filter $(comp),gcc clang mingw)) - CXXFLAGS += -msse4 -mbmi2 + CXXFLAGS += -mbmi2 endif endif @@ -381,15 +533,23 @@ help: @echo "Supported targets:" @echo "" @echo "build > Standard build" - @echo "profile-build > PGO build" + @echo "profile-build > Standard build with PGO" @echo "strip > Strip executable" @echo "install > Install executable" @echo "clean > Clean up" + @echo "net > Download the default nnue net" @echo "" @echo "Supported archs:" @echo "" - @echo "x86-64-bmi2 > x86 64-bit with pext support (also enables SSE4)" - @echo "x86-64-modern > x86 64-bit with popcnt support (also enables SSE3)" + @echo "x86-64-avx512 > x86 64-bit with avx512 support" + @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" + @echo "x86-64-avx2 > x86 64-bit with avx2 support" + @echo "x86-64-sse42 > x86 64-bit with sse42 support" + @echo "x86-64-modern > x86 64-bit with sse41 support (x86-64-sse41)" + @echo "x86-64-sse41 > x86 64-bit with sse41 support" + @echo "x86-64-ssse3 > x86 64-bit with ssse3 support" + @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support" + @echo "x86-64-sse3 > x86 64-bit with sse3 support" @echo "x86-64 > x86 64-bit generic" @echo "x86-32 > x86 32-bit (also enables SSE)" @echo "x86-32-old > x86 32-bit fall back for old hardware" @@ -397,6 +557,7 @@ help: @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" @echo "armv8 > ARMv8 64-bit" + @echo "apple-silicon > Apple silicon ARM64" @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" @echo "" @@ -409,17 +570,20 @@ help: @echo "" @echo "Simple examples. If you don't know what to do, you likely want to run: " @echo "" - @echo "make build ARCH=x86-64 (This is for 64-bit systems)" - @echo "make build ARCH=x86-32 (This is for 32-bit systems)" + @echo "make -j build ARCH=x86-64 (This is for 64-bit systems)" + @echo "make -j build ARCH=x86-32 (This is for 32-bit systems)" @echo "" @echo "Advanced examples, for experienced users: " @echo "" - @echo "make build ARCH=x86-64 COMP=clang" - @echo "make profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8" + @echo "make -j build ARCH=x86-64-modern COMP=clang" + @echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8" @echo "" + @echo "The selected architecture $(ARCH) enables the following configuration: " + @echo "" + @$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity -.PHONY: help build profile-build strip install clean objclean profileclean \ +.PHONY: help build profile-build strip install clean net objclean profileclean \ config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ clang-profile-use clang-profile-make @@ -453,14 +617,21 @@ install: clean: objclean profileclean @rm -f .depend *~ core +net: + $(eval nnuenet := $(shell grep EvalFile ucioption.cpp | grep Option | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) + @echo "Default net: $(nnuenet)" + $(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet)) + $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -sL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) + @if test -f "$(nnuenet)"; then echo "Already available."; else echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet); fi + # clean binaries and objects objclean: - @rm -f $(EXE) *.o ./syzygy/*.o + @rm -f $(EXE) *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o # clean auxiliary profiling files profileclean: @rm -rf profdir - @rm -f bench.txt *.gcda *.gcno + @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda @rm -f stockfish.profdata *.profraw default: @@ -485,7 +656,14 @@ config-sanity: @echo "prefetch: '$(prefetch)'" @echo "popcnt: '$(popcnt)'" @echo "sse: '$(sse)'" + @echo "sse3: '$(sse3)'" + @echo "ssse3: '$(ssse3)'" + @echo "sse41: '$(sse41)'" + @echo "sse42: '$(sse42)'" + @echo "avx2: '$(avx2)'" @echo "pext: '$(pext)'" + @echo "avx512: '$(avx512)'" + @echo "neon: '$(neon)'" @echo "" @echo "Flags:" @echo "CXX: $(CXX)" @@ -499,12 +677,19 @@ config-sanity: @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ - test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a" + test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a" || test "$(arch)" = "arm64" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" @test "$(sse)" = "yes" || test "$(sse)" = "no" + @test "$(sse3)" = "yes" || test "$(sse3)" = "no" + @test "$(ssse3)" = "yes" || test "$(ssse3)" = "no" + @test "$(sse41)" = "yes" || test "$(sse41)" = "no" + @test "$(sse42)" = "yes" || test "$(sse42)" = "no" + @test "$(avx2)" = "yes" || test "$(avx2)" = "no" @test "$(pext)" = "yes" || test "$(pext)" = "no" + @test "$(avx512)" = "yes" || test "$(avx512)" = "no" + @test "$(neon)" = "yes" || test "$(neon)" = "no" @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" $(EXE): $(OBJS) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 3299f373..6041d642 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 7e27eb96..bbe8e9a7 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 0bf7eef9..f531010c 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 8c95de8c..a899d879 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 a8ceb648..c8be2198 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 fd1aba2d..1351d88a 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 b34d82f6..f43c62d6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -20,15 +18,50 @@ #include #include +#include #include // For std::memset #include #include +#include #include "bitboard.h" #include "evaluate.h" #include "material.h" #include "pawns.h" #include "thread.h" +#include "uci.h" + +namespace Eval { + + bool useNNUE; + std::string eval_file_loaded="None"; + + void init_NNUE() { + + useNNUE = Options["Use NNUE"]; + std::string eval_file = std::string(Options["EvalFile"]); + if (useNNUE && eval_file_loaded != eval_file) + if (Eval::NNUE::load_eval_file(eval_file)) + eval_file_loaded = eval_file; + } + + void verify_NNUE() { + + std::string eval_file = std::string(Options["EvalFile"]); + if (useNNUE && eval_file_loaded != eval_file) + { + std::cerr << "Use of NNUE evaluation, but the file " << eval_file << " was not loaded successfully. " + << "These network evaluation parameters must be available, compatible with this version of the code. " + << "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file." << std::endl; + std::exit(EXIT_FAILURE); + } + + if (useNNUE) + sync_cout << "info string NNUE evaluation using " << eval_file << " enabled." << sync_endl; + else + sync_cout << "info string classical evaluation enabled." << sync_endl; + } +} namespace Trace { @@ -906,47 +939,62 @@ make_v: /// evaluation of the position from the point of view of the side to move. Value Eval::evaluate(const Position& pos) { - return Evaluation(pos).value(); -} + if (Eval::useNNUE) + return NNUE::evaluate(pos); + else + return Evaluation(pos).value(); +} /// trace() is like evaluate(), but instead of returning a value, it returns /// a string (suitable for outputting to stdout) that contains the detailed /// 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) { if (pos.checkers()) - return "Total evaluation: none (in check)"; - - std::memset(scores, 0, sizeof(scores)); - - pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt - - Value v = Evaluation(pos).value(); - - v = pos.side_to_move() == WHITE ? v : -v; // Trace scores are from white's point of view + return "Final evaluation: none (in check)"; std::stringstream ss; - 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); + ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); + + Value v; + + if (Eval::useNNUE) + { + v = NNUE::evaluate(pos); + } + else + { + std::memset(scores, 0, sizeof(scores)); + + pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt + + 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 << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; diff --git a/src/evaluate.h b/src/evaluate.h index 7c8a2a6f..e808068d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -29,9 +27,23 @@ class Position; namespace Eval { -std::string trace(const Position& pos); + std::string trace(const Position& pos); + Value evaluate(const Position& pos); -Value evaluate(const Position& pos); -} + extern bool useNNUE; + extern std::string eval_file_loaded; + void init_NNUE(); + void verify_NNUE(); + + namespace NNUE { + + Value evaluate(const Position& pos); + Value compute_eval(const Position& pos); + void update_eval(const Position& pos); + bool load_eval_file(const std::string& evalFile); + + } // namespace NNUE + +} // namespace Eval #endif // #ifndef EVALUATE_H_INCLUDED diff --git a/src/main.cpp b/src/main.cpp index fafefee2..fbad6622 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -46,6 +44,7 @@ int main(int argc, char* argv[]) { Endgames::init(); Threads.set(size_t(Options["Threads"])); Search::clear(); // After threads are up + Eval::init_NNUE(); UCI::loop(argc, argv); diff --git a/src/material.cpp b/src/material.cpp index bb25d3ca..0ef9926f 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 21647f23..80d01655 100644 --- a/src/material.h +++ b/src/material.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 2bc05c5b..3d7c75e5 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -46,6 +44,7 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); #include #include #include +#include #if defined(__linux__) && !defined(__ANDROID__) #include @@ -147,10 +146,8 @@ const string engine_info(bool to_uci) { ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); } - ss << (Is64Bit ? " 64" : "") - << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : "")) - << (to_uci ? "\nid author ": " by ") - << "T. Romstad, M. Costalba, J. Kiiski, G. Linscott"; + ss << (to_uci ? "\nid author ": " by ") + << "the Stockfish developers (see AUTHORS file)"; return ss.str(); } @@ -215,7 +212,33 @@ const std::string compiler_info() { compiler += " on unknown system"; #endif - compiler += "\n __VERSION__ macro expands to: "; + compiler += "\nCompilation settings include: "; + compiler += (Is64Bit ? " 64bit" : " 32bit"); + #if defined(USE_AVX512) + compiler += " AVX512"; + #endif + #if defined(USE_AVX2) + compiler += " AVX2"; + #endif + #if defined(USE_SSE42) + compiler += " SSE42"; + #endif + #if defined(USE_SSE41) + compiler += " SSE41"; + #endif + #if defined(USE_SSSE3) + compiler += " SSSE3"; + #endif + #if defined(USE_SSE3) + compiler += " SSE3"; + #endif + compiler += (HasPext ? " BMI2" : ""); + compiler += (HasPopCnt ? " POPCNT" : ""); + #if !defined(NDEBUG) + compiler += " DEBUG"; + #endif + + compiler += "\n__VERSION__ macro expands to: "; #ifdef __VERSION__ compiler += __VERSION__; #else @@ -293,6 +316,29 @@ void prefetch(void* addr) { #endif +/// Wrappers for systems where the c++17 implementation doesn't guarantee the availability of aligned_alloc. +/// Memory allocated with std_aligned_alloc must be freed with std_aligned_free. +/// + +void* std_aligned_alloc(size_t alignment, size_t size) { +#if defined(__APPLE__) + return aligned_alloc(alignment, size); +#elif defined(_WIN32) + return _mm_malloc(size, alignment); +#else + return std::aligned_alloc(alignment, size); +#endif +} + +void std_aligned_free(void* ptr) { +#if defined(__APPLE__) + free(ptr); +#elif defined(_WIN32) + _mm_free(ptr); +#else + free(ptr); +#endif +} /// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages. /// The returned pointer is the aligned one, while the mem argument is the one that needs diff --git a/src/misc.h b/src/misc.h index 373f1b77..eb4e05c0 100644 --- a/src/misc.h +++ b/src/misc.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -33,6 +31,8 @@ const std::string engine_info(bool to_uci = false); const 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); +void std_aligned_free(void* ptr); void* aligned_ttmem_alloc(size_t size, void*& mem); void aligned_ttmem_free(void* mem); // nop if mem == nullptr diff --git a/src/movegen.cpp b/src/movegen.cpp index 4ff12fc6..d74df4c3 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 c2e7c3f1..fb616d00 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 5775f810..96a44449 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 aaff388f..f080935a 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h new file mode 100644 index 00000000..9216bd41 --- /dev/null +++ b/src/nnue/architectures/halfkp_256x2-32-32.h @@ -0,0 +1,54 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 Eval::NNUE { + +// Input features used in evaluation function +using RawFeatures = Features::FeatureSet< + Features::HalfKP>; + +// Number of input feature dimensions after conversion +constexpr IndexType kTransformedFeatureDimensions = 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 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 new file mode 100644 index 00000000..af0894b2 --- /dev/null +++ b/src/nnue/evaluate_nnue.cpp @@ -0,0 +1,178 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 . +*/ + +// Code for calculating NNUE evaluation function + +#include +#include +#include + +#include "../evaluate.h" +#include "../position.h" +#include "../misc.h" +#include "../uci.h" + +#include "evaluate_nnue.h" + +ExtPieceSquare kpp_board_index[PIECE_NB] = { + // convention: W - us, B - them + // viewed from other side, W and B are reversed + { PS_NONE, PS_NONE }, + { PS_W_PAWN, PS_B_PAWN }, + { PS_W_KNIGHT, PS_B_KNIGHT }, + { PS_W_BISHOP, PS_B_BISHOP }, + { PS_W_ROOK, PS_B_ROOK }, + { PS_W_QUEEN, PS_B_QUEEN }, + { PS_W_KING, PS_B_KING }, + { PS_NONE, PS_NONE }, + { PS_NONE, PS_NONE }, + { PS_B_PAWN, PS_W_PAWN }, + { PS_B_KNIGHT, PS_W_KNIGHT }, + { PS_B_BISHOP, PS_W_BISHOP }, + { PS_B_ROOK, PS_W_ROOK }, + { PS_B_QUEEN, PS_W_QUEEN }, + { PS_B_KING, PS_W_KING }, + { PS_NONE, PS_NONE } +}; + + +namespace Eval::NNUE { + + // Input feature converter + AlignedPtr feature_transformer; + + // Evaluation function + AlignedPtr network; + + // Evaluation function file name + std::string fileName; + + namespace Detail { + + // Initialize the evaluation function parameters + template + void Initialize(AlignedPtr& pointer) { + + pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); + std::memset(pointer.get(), 0, sizeof(T)); + } + + // Read evaluation function parameters + template + bool ReadParameters(std::istream& stream, const AlignedPtr& pointer) { + + std::uint32_t header; + stream.read(reinterpret_cast(&header), sizeof(header)); + if (!stream || header != T::GetHashValue()) return false; + return pointer->ReadParameters(stream); + } + + } // namespace Detail + + // Initialize the evaluation function parameters + void Initialize() { + + Detail::Initialize(feature_transformer); + Detail::Initialize(network); + } + + // Read network header + bool ReadHeader(std::istream& stream, + std::uint32_t* hash_value, std::string* architecture) { + + std::uint32_t version, size; + stream.read(reinterpret_cast(&version), sizeof(version)); + stream.read(reinterpret_cast(hash_value), sizeof(*hash_value)); + stream.read(reinterpret_cast(&size), sizeof(size)); + if (!stream || version != kVersion) return false; + architecture->resize(size); + stream.read(&(*architecture)[0], size); + return !stream.fail(); + } + + // Read network parameters + bool ReadParameters(std::istream& stream) { + + std::uint32_t hash_value; + 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; + return stream && stream.peek() == std::ios::traits_type::eof(); + } + + // Proceed with the difference calculation if possible + static void UpdateAccumulatorIfPossible(const Position& pos) { + + feature_transformer->UpdateAccumulatorIfPossible(pos); + } + + // Calculate the evaluation value + static Value ComputeScore(const Position& pos, bool refresh) { + + auto& accumulator = pos.state()->accumulator; + if (!refresh && accumulator.computed_score) { + return accumulator.score; + } + + alignas(kCacheLineSize) TransformedFeatureType + transformed_features[FeatureTransformer::kBufferSize]; + feature_transformer->Transform(pos, transformed_features, refresh); + alignas(kCacheLineSize) char buffer[Network::kBufferSize]; + const auto output = network->Propagate(transformed_features, buffer); + + auto score = static_cast(output[0] / FV_SCALE); + + accumulator.score = score; + accumulator.computed_score = true; + return accumulator.score; + } + + // Load the evaluation function file + bool load_eval_file(const std::string& evalFile) { + + Initialize(); + fileName = evalFile; + + std::ifstream stream(evalFile, std::ios::binary); + + const bool result = ReadParameters(stream); + + return result; + } + + // Evaluation function. Perform differential calculation. + Value evaluate(const Position& pos) { + Value v = ComputeScore(pos, false); + v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + + return v; + } + + // Evaluation function. Perform full calculation. + Value compute_eval(const Position& pos) { + return ComputeScore(pos, true); + } + + // Proceed with the difference calculation if possible + void update_eval(const Position& pos) { + UpdateAccumulatorIfPossible(pos); + } + +} // namespace Eval::NNUE diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h new file mode 100644 index 00000000..5f0d1855 --- /dev/null +++ b/src/nnue/evaluate_nnue.h @@ -0,0 +1,48 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 . +*/ + +// header used in NNUE evaluation function + +#ifndef NNUE_EVALUATE_NNUE_H_INCLUDED +#define NNUE_EVALUATE_NNUE_H_INCLUDED + +#include "nnue_feature_transformer.h" + +#include + +namespace Eval::NNUE { + + // Hash value of evaluation function structure + constexpr std::uint32_t kHashValue = + FeatureTransformer::GetHashValue() ^ Network::GetHashValue(); + + // Deleter for automating release of memory area + template + struct AlignedDeleter { + void operator()(T* ptr) const { + ptr->~T(); + std_aligned_free(ptr); + } + }; + + template + using AlignedPtr = std::unique_ptr>; + +} // namespace 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 new file mode 100644 index 00000000..79ca83ae --- /dev/null +++ b/src/nnue/features/feature_set.h @@ -0,0 +1,135 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 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 + kValues = {{First, Remaining...}}; + }; + + // Base class of feature set + template + class FeatureSetBase { + + public: + // Get a list of indices for active features + template + static void AppendActiveIndices( + const Position& pos, TriggerEvent trigger, IndexListType active[2]) { + + for (Color perspective : { WHITE, BLACK }) { + Derived::CollectActiveIndices( + pos, trigger, perspective, &active[perspective]); + } + } + + // Get a list of indices for recently changed features + template + static void AppendChangedIndices( + const PositionType& pos, TriggerEvent trigger, + IndexListType removed[2], IndexListType added[2], bool reset[2]) { + + const auto& dp = pos.state()->dirtyPiece; + if (dp.dirty_num == 0) return; + + for (Color perspective : { WHITE, BLACK }) { + reset[perspective] = false; + switch (trigger) { + case TriggerEvent::kFriendKingMoved: + reset[perspective] = + dp.pieceId[0] == PIECE_ID_KING + perspective; + break; + default: + assert(false); + break; + } + if (reset[perspective]) { + Derived::CollectActiveIndices( + pos, trigger, perspective, &added[perspective]); + } else { + Derived::CollectChangedIndices( + pos, trigger, perspective, + &removed[perspective], &added[perspective]); + } + } + } + }; + + // 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 kHashValue = FeatureType::kHashValue; + // Number of feature dimensions + static constexpr IndexType kDimensions = FeatureType::kDimensions; + // Maximum number of simultaneously active features + static constexpr IndexType kMaxActiveDimensions = + FeatureType::kMaxActiveDimensions; + // Trigger for full calculation instead of difference calculation + using SortedTriggerSet = + CompileTimeList; + static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues; + + private: + // Get a list of indices for active features + static void CollectActiveIndices( + const Position& pos, const TriggerEvent trigger, const Color perspective, + IndexList* const active) { + if (FeatureType::kRefreshTrigger == trigger) { + FeatureType::AppendActiveIndices(pos, perspective, active); + } + } + + // Get a list of indices for recently changed features + static void CollectChangedIndices( + const Position& pos, const TriggerEvent trigger, const Color perspective, + IndexList* const removed, IndexList* const added) { + + if (FeatureType::kRefreshTrigger == trigger) { + FeatureType::AppendChangedIndices(pos, perspective, removed, added); + } + } + + // Make the base class and the class template that recursively uses itself a friend + friend class FeatureSetBase; + template + friend class FeatureSet; + }; + +} // namespace 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 new file mode 100644 index 00000000..d00a35df --- /dev/null +++ b/src/nnue/features/features_common.h @@ -0,0 +1,45 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 Eval::NNUE::Features { + + class IndexList; + + template + class FeatureSet; + + // Trigger to perform full calculations instead of difference only + enum class TriggerEvent { + kFriendKingMoved // calculate full evaluation when own king moves + }; + + enum class Side { + kFriend // side to move + }; + +} // namespace 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 new file mode 100644 index 00000000..628add6e --- /dev/null +++ b/src/nnue/features/half_kp.cpp @@ -0,0 +1,92 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 HalfKP of NNUE evaluation function + +#include "half_kp.h" +#include "index_list.h" + +namespace Eval::NNUE::Features { + + // Find the index of the feature quantity from the king position and PieceSquare + template + inline IndexType HalfKP::MakeIndex(Square sq_k, PieceSquare p) { + return static_cast(PS_END) * static_cast(sq_k) + p; + } + + // Get pieces information + template + inline void HalfKP::GetPieces( + const Position& pos, Color perspective, + PieceSquare** pieces, Square* sq_target_k) { + + *pieces = (perspective == BLACK) ? + pos.eval_list()->piece_list_fb() : + pos.eval_list()->piece_list_fw(); + const PieceId target = (AssociatedKing == Side::kFriend) ? + static_cast(PIECE_ID_KING + perspective) : + static_cast(PIECE_ID_KING + ~perspective); + *sq_target_k = static_cast(((*pieces)[target] - PS_W_KING) % SQUARE_NB); + } + + // Get a list of indices for active features + template + void HalfKP::AppendActiveIndices( + const Position& pos, Color perspective, IndexList* active) { + + // Do nothing if array size is small to avoid compiler warning + if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return; + + PieceSquare* pieces; + Square sq_target_k; + GetPieces(pos, perspective, &pieces, &sq_target_k); + for (PieceId i = PIECE_ID_ZERO; i < PIECE_ID_KING; ++i) { + if (pieces[i] != PS_NONE) { + active->push_back(MakeIndex(sq_target_k, pieces[i])); + } + } + } + + // Get a list of indices for recently changed features + template + void HalfKP::AppendChangedIndices( + const Position& pos, Color perspective, + IndexList* removed, IndexList* added) { + + PieceSquare* pieces; + Square sq_target_k; + GetPieces(pos, perspective, &pieces, &sq_target_k); + const auto& dp = pos.state()->dirtyPiece; + for (int i = 0; i < dp.dirty_num; ++i) { + if (dp.pieceId[i] >= PIECE_ID_KING) continue; + const auto old_p = static_cast( + dp.old_piece[i].from[perspective]); + if (old_p != PS_NONE) { + removed->push_back(MakeIndex(sq_target_k, old_p)); + } + const auto new_p = static_cast( + dp.new_piece[i].from[perspective]); + if (new_p != PS_NONE) { + added->push_back(MakeIndex(sq_target_k, new_p)); + } + } + } + + template class HalfKP; + +} // namespace Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h new file mode 100644 index 00000000..99842eea --- /dev/null +++ b/src/nnue/features/half_kp.h @@ -0,0 +1,67 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 HalfKP of NNUE evaluation function + +#ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED +#define NNUE_FEATURES_HALF_KP_H_INCLUDED + +#include "../../evaluate.h" +#include "features_common.h" + +namespace Eval::NNUE::Features { + + // Feature HalfKP: Combination of the position of own king + // and the position of pieces other than kings + template + class HalfKP { + + public: + // Feature name + static constexpr const char* kName = "HalfKP(Friend)"; + // Hash value embedded in the evaluation file + static constexpr std::uint32_t kHashValue = + 0x5D69D5B9u ^ (AssociatedKing == Side::kFriend); + // Number of feature dimensions + static constexpr IndexType kDimensions = + static_cast(SQUARE_NB) * static_cast(PS_END); + // Maximum number of simultaneously active features + static constexpr IndexType kMaxActiveDimensions = PIECE_ID_KING; + // Trigger for full calculation instead of difference calculation + static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved; + + // Get a list of indices for active features + static void AppendActiveIndices(const Position& pos, Color perspective, + IndexList* active); + + // Get a list of indices for recently changed features + static void AppendChangedIndices(const Position& pos, Color perspective, + IndexList* removed, IndexList* added); + + // Index of a feature for a given king position and another piece on some square + static IndexType MakeIndex(Square sq_k, PieceSquare p); + + private: + // Get pieces information + static void GetPieces(const Position& pos, Color perspective, + PieceSquare** pieces, Square* sq_target_k); + }; + +} // namespace 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 new file mode 100644 index 00000000..d9ad680a --- /dev/null +++ b/src/nnue/features/index_list.h @@ -0,0 +1,64 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 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 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 new file mode 100644 index 00000000..b585bc87 --- /dev/null +++ b/src/nnue/layers/affine_transform.h @@ -0,0 +1,215 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 layer AffineTransform of NNUE evaluation function + +#ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED +#define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED + +#include +#include "../nnue_common.h" + +namespace Eval::NNUE::Layers { + + // Affine transformation layer + template + class AffineTransform { + 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 kInputDimensions = + PreviousLayer::kOutputDimensions; + static constexpr IndexType kOutputDimensions = OutputDimensions; + static constexpr IndexType kPaddedInputDimensions = + CeilToMultiple(kInputDimensions, kMaxSimdWidth); + + // Size of forward propagation buffer used in this layer + static constexpr std::size_t kSelfBufferSize = + CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize); + + // Size of the forward propagation buffer used from the input layer to this layer + static constexpr std::size_t kBufferSize = + PreviousLayer::kBufferSize + kSelfBufferSize; + + // 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; + } + + // Read network parameters + bool ReadParameters(std::istream& stream) { + if (!previous_layer_.ReadParameters(stream)) return false; + stream.read(reinterpret_cast(biases_), + kOutputDimensions * sizeof(BiasType)); + stream.read(reinterpret_cast(weights_), + kOutputDimensions * kPaddedInputDimensions * + sizeof(WeightType)); + return !stream.fail(); + } + + // Forward propagation + const OutputType* Propagate( + const TransformedFeatureType* transformed_features, char* buffer) const { + const auto input = previous_layer_.Propagate( + transformed_features, buffer + kSelfBufferSize); + const auto output = reinterpret_cast(buffer); + + #if defined(USE_AVX512) + constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2); + const __m512i kOnes = _mm512_set1_epi16(1); + const auto input_vector = reinterpret_cast(input); + + #elif defined(USE_AVX2) + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const __m256i kOnes = _mm256_set1_epi16(1); + const auto input_vector = reinterpret_cast(input); + + #elif defined(USE_SSSE3) + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const __m128i kOnes = _mm_set1_epi16(1); + const auto input_vector = reinterpret_cast(input); + + #elif defined(USE_NEON) + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const auto input_vector = reinterpret_cast(input); + #endif + + for (IndexType i = 0; i < kOutputDimensions; ++i) { + const IndexType offset = i * kPaddedInputDimensions; + + #if defined(USE_AVX512) + __m512i sum = _mm512_setzero_si512(); + const auto row = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + + #if defined(__MINGW32__) || defined(__MINGW64__) + __m512i product = _mm512_maddubs_epi16(_mm512_loadu_si512(&input_vector[j]), _mm512_load_si512(&row[j])); + #else + __m512i product = _mm512_maddubs_epi16(_mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j])); + #endif + + product = _mm512_madd_epi16(product, kOnes); + sum = _mm512_add_epi32(sum, product); + } + output[i] = _mm512_reduce_add_epi32(sum) + biases_[i]; + + // Note: Changing kMaxSimdWidth from 32 to 64 breaks loading existing networks. + // As a result kPaddedInputDimensions may not be an even multiple of 64(512bit) + // and we have to do one more 256bit chunk. + if (kPaddedInputDimensions != kNumChunks * kSimdWidth * 2) + { + const auto iv_256 = reinterpret_cast(input); + const auto row_256 = reinterpret_cast(&weights_[offset]); + int j = kNumChunks * 2; + + #if defined(__MINGW32__) || defined(__MINGW64__) // See HACK comment below in AVX2. + __m256i sum256 = _mm256_maddubs_epi16(_mm256_loadu_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); + #else + __m256i sum256 = _mm256_maddubs_epi16(_mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); + #endif + + sum256 = _mm256_madd_epi16(sum256, _mm256_set1_epi16(1)); + sum256 = _mm256_hadd_epi32(sum256, sum256); + sum256 = _mm256_hadd_epi32(sum256, sum256); + const __m128i lo = _mm256_extracti128_si256(sum256, 0); + const __m128i hi = _mm256_extracti128_si256(sum256, 1); + output[i] += _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi); + } + + #elif defined(USE_AVX2) + __m256i sum = _mm256_setzero_si256(); + const auto row = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m256i product = _mm256_maddubs_epi16( + + #if defined(__MINGW32__) || defined(__MINGW64__) + // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary + // compiled with g++ in MSYS2 crashes here because the output memory is not aligned + // even though alignas is specified. + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&input_vector[j]), _mm256_load_si256(&row[j])); + product = _mm256_madd_epi16(product, kOnes); + sum = _mm256_add_epi32(sum, product); + } + sum = _mm256_hadd_epi32(sum, sum); + sum = _mm256_hadd_epi32(sum, sum); + const __m128i lo = _mm256_extracti128_si256(sum, 0); + const __m128i hi = _mm256_extracti128_si256(sum, 1); + output[i] = _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi) + biases_[i]; + + #elif defined(USE_SSSE3) + __m128i sum = _mm_cvtsi32_si128(biases_[i]); + const auto row = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m128i product = _mm_maddubs_epi16( + _mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j])); + product = _mm_madd_epi16(product, kOnes); + sum = _mm_add_epi32(sum, product); + } + sum = _mm_hadd_epi32(sum, sum); + sum = _mm_hadd_epi32(sum, sum); + output[i] = _mm_cvtsi128_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]); + 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]; + } + output[i] = sum; + #endif + + } + return output; + } + + private: + using BiasType = OutputType; + using WeightType = std::int8_t; + + PreviousLayer previous_layer_; + + alignas(kCacheLineSize) BiasType biases_[kOutputDimensions]; + alignas(kCacheLineSize) + WeightType weights_[kOutputDimensions * kPaddedInputDimensions]; + }; + +} // namespace 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 new file mode 100644 index 00000000..7ade598f --- /dev/null +++ b/src/nnue/layers/clipped_relu.h @@ -0,0 +1,186 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 layer ClippedReLU of NNUE evaluation function + +#ifndef NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED +#define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED + +#include "../nnue_common.h" + +namespace Eval::NNUE::Layers { + + // Clipped ReLU + template + class ClippedReLU { + public: + // Input/output type + using InputType = typename PreviousLayer::OutputType; + using OutputType = std::uint8_t; + static_assert(std::is_same::value, ""); + + // Number of input/output dimensions + static constexpr IndexType kInputDimensions = + PreviousLayer::kOutputDimensions; + static constexpr IndexType kOutputDimensions = kInputDimensions; + + // Size of forward propagation buffer used in this layer + static constexpr std::size_t kSelfBufferSize = + CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize); + + // Size of the forward propagation buffer used from the input layer to this layer + static constexpr std::size_t kBufferSize = + PreviousLayer::kBufferSize + kSelfBufferSize; + + // 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; + } + + // Read network parameters + bool ReadParameters(std::istream& stream) { + return previous_layer_.ReadParameters(stream); + } + + // Forward propagation + const OutputType* Propagate( + const TransformedFeatureType* transformed_features, char* buffer) const { + const auto input = previous_layer_.Propagate( + transformed_features, buffer + kSelfBufferSize); + 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); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m256i*>(output); + for (IndexType i = 0; i < kNumChunks; ++i) { + const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( + + #if defined(__MINGW32__) || defined(__MINGW64__) + // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary + // compiled with g++ in MSYS2 crashes here because the output memory is not aligned + // even though alignas is specified. + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 0]), + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 1])), kWeightScaleBits); + const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 2]), + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 3])), kWeightScaleBits); + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_storeu_si256 + #else + _mm256_store_si256 + #endif + + (&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( + _mm256_packs_epi16(words0, words1), kZero), kOffsets)); + } + constexpr IndexType kStart = kNumChunks * kSimdWidth; + + #elif defined(USE_SSSE3) + constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; + + #ifdef USE_SSE41 + const __m128i kZero = _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) { + const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 0]), + _mm_load_si128(&in[i * 4 + 1])), kWeightScaleBits); + const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 2]), + _mm_load_si128(&in[i * 4 + 3])), kWeightScaleBits); + const __m128i packedbytes = _mm_packs_epi16(words0, words1); + _mm_store_si128(&out[i], + + #ifdef USE_SSE41 + _mm_max_epi8(packedbytes, kZero) + #else + _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) + #endif + + ); + } + constexpr IndexType kStart = kNumChunks * kSimdWidth; + + #elif defined(USE_NEON) + constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2); + const int8x8_t kZero = {0}; + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast(output); + for (IndexType i = 0; i < kNumChunks; ++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); + } + constexpr IndexType kStart = kNumChunks * (kSimdWidth / 2); + #else + constexpr IndexType kStart = 0; + #endif + + for (IndexType i = kStart; i < kInputDimensions; ++i) { + output[i] = static_cast( + std::max(0, std::min(127, input[i] >> kWeightScaleBits))); + } + return output; + } + + private: + PreviousLayer previous_layer_; + }; + +} // namespace 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 new file mode 100644 index 00000000..afca14c8 --- /dev/null +++ b/src/nnue/layers/input_slice.h @@ -0,0 +1,68 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 Eval::NNUE::Layers { + +// Input layer +template +class InputSlice { + public: + // Need to maintain alignment + static_assert(Offset % kMaxSimdWidth == 0, ""); + + // Output type + using OutputType = TransformedFeatureType; + + // Output dimensionality + static constexpr IndexType kOutputDimensions = OutputDimensions; + + // Size of forward propagation buffer used from the input layer to this layer + static constexpr std::size_t kBufferSize = 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; + } + + // Read network parameters + bool ReadParameters(std::istream& /*stream*/) { + return true; + } + + // Forward propagation + const OutputType* Propagate( + const TransformedFeatureType* transformed_features, + char* /*buffer*/) const { + return transformed_features + Offset; + } + + private: +}; + +} // namespace Layers + +#endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h new file mode 100644 index 00000000..2a354a3c --- /dev/null +++ b/src/nnue/nnue_accumulator.h @@ -0,0 +1,39 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Class for difference calculation of NNUE evaluation function + +#ifndef NNUE_ACCUMULATOR_H_INCLUDED +#define NNUE_ACCUMULATOR_H_INCLUDED + +#include "nnue_architecture.h" + +namespace Eval::NNUE { + + // Class that holds the result of affine transformation of input features + struct alignas(32) Accumulator { + std::int16_t + accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; + Value score; + bool computed_accumulation; + bool computed_score; + }; + +} // namespace Eval::NNUE + +#endif // NNUE_ACCUMULATOR_H_INCLUDED diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h new file mode 100644 index 00000000..91cdc4bd --- /dev/null +++ b/src/nnue/nnue_architecture.h @@ -0,0 +1,38 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 . +*/ + +// Input features and network structure used in NNUE evaluation function + +#ifndef NNUE_ARCHITECTURE_H_INCLUDED +#define NNUE_ARCHITECTURE_H_INCLUDED + +// Defines the network structure +#include "architectures/halfkp_256x2-32-32.h" + +namespace Eval::NNUE { + + static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, ""); + static_assert(Network::kOutputDimensions == 1, ""); + static_assert(std::is_same::value, ""); + + // Trigger for full calculation instead of difference calculation + constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers; + +} // namespace Eval::NNUE + +#endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h new file mode 100644 index 00000000..972ef3e5 --- /dev/null +++ b/src/nnue/nnue_common.h @@ -0,0 +1,77 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 . +*/ + +// Constants used in NNUE evaluation function + +#ifndef NNUE_COMMON_H_INCLUDED +#define NNUE_COMMON_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_NEON) +#include +#endif + +namespace Eval::NNUE { + + // Version of the evaluation file + constexpr std::uint32_t kVersion = 0x7AF32F16u; + + // Constant used in evaluation value calculation + constexpr int FV_SCALE = 16; + constexpr int kWeightScaleBits = 6; + + // Size of cache line (in bytes) + constexpr std::size_t kCacheLineSize = 64; + + // SIMD width (in bytes) + #if defined(USE_AVX2) + constexpr std::size_t kSimdWidth = 32; + + #elif defined(USE_SSE2) + constexpr std::size_t kSimdWidth = 16; + + #elif defined(USE_NEON) + constexpr std::size_t kSimdWidth = 16; + #endif + + constexpr std::size_t kMaxSimdWidth = 32; + + // Type of input feature after conversion + using TransformedFeatureType = std::uint8_t; + using IndexType = std::uint32_t; + + // Round n up to be a multiple of base + template + constexpr IntType CeilToMultiple(IntType n, IntType base) { + return (n + base - 1) / base * base; + } + +} // namespace Eval::NNUE + +#endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h new file mode 100644 index 00000000..1cfebbe4 --- /dev/null +++ b/src/nnue/nnue_feature_transformer.h @@ -0,0 +1,355 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 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 that converts the input features of the NNUE evaluation function + +#ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED +#define NNUE_FEATURE_TRANSFORMER_H_INCLUDED + +#include "nnue_common.h" +#include "nnue_architecture.h" +#include "features/index_list.h" + +#include // std::memset() + +namespace Eval::NNUE { + + // Input feature converter + class FeatureTransformer { + + private: + // Number of output dimensions for one side + static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions; + + public: + // Output type + using OutputType = TransformedFeatureType; + + // Number of input/output dimensions + static constexpr IndexType kInputDimensions = RawFeatures::kDimensions; + static constexpr IndexType kOutputDimensions = kHalfDimensions * 2; + + // Size of forward propagation buffer + static constexpr std::size_t kBufferSize = + kOutputDimensions * sizeof(OutputType); + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t GetHashValue() { + return RawFeatures::kHashValue ^ kOutputDimensions; + } + + // Read network parameters + bool ReadParameters(std::istream& stream) { + stream.read(reinterpret_cast(biases_), + kHalfDimensions * sizeof(BiasType)); + stream.read(reinterpret_cast(weights_), + kHalfDimensions * kInputDimensions * sizeof(WeightType)); + return !stream.fail(); + } + + // Proceed with the difference calculation if possible + bool UpdateAccumulatorIfPossible(const Position& pos) const { + const auto now = pos.state(); + if (now->accumulator.computed_accumulation) { + return true; + } + const auto prev = now->previous; + if (prev && prev->accumulator.computed_accumulation) { + UpdateAccumulator(pos); + return true; + } + return false; + } + + // Convert input features + void Transform(const Position& pos, OutputType* output, bool refresh) const { + if (refresh || !UpdateAccumulatorIfPossible(pos)) { + RefreshAccumulator(pos); + } + const auto& accumulation = pos.state()->accumulator.accumulation; + + #if defined(USE_AVX2) + constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + constexpr int kControl = 0b11011000; + const __m256i kZero = _mm256_setzero_si256(); + + #elif defined(USE_SSSE3) + constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + + #ifdef USE_SSE41 + const __m128i kZero = _mm_setzero_si128(); + #else + const __m128i k0x80s = _mm_set1_epi8(-128); + #endif + + #elif defined(USE_NEON) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + const int8x8_t kZero = {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; + + #if defined(USE_AVX2) + auto out = reinterpret_cast<__m256i*>(&output[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m256i sum0 = + + #if defined(__MINGW32__) || defined(__MINGW64__) + // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary + // compiled with g++ in MSYS2 crashes here because the output memory is not aligned + // even though alignas is specified. + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 0]); + __m256i sum1 = + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 1]); + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_storeu_si256 + #else + _mm256_store_si256 + #endif + + (&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( + _mm256_packs_epi16(sum0, sum1), kZero), kControl)); + } + + #elif defined(USE_SSSE3) + auto out = reinterpret_cast<__m128i*>(&output[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m128i sum0 = _mm_load_si128(&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 0]); + __m128i sum1 = _mm_load_si128(&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 1]); + const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); + + _mm_store_si128(&out[j], + + #ifdef USE_SSE41 + _mm_max_epi8(packedbytes, kZero) + #else + _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) + #endif + + ); + } + + #elif defined(USE_NEON) + const auto out = reinterpret_cast(&output[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + int16x8_t sum = reinterpret_cast( + accumulation[perspectives[p]][0])[j]; + out[j] = vmax_s8(vqmovn_s16(sum), kZero); + } + + #else + for (IndexType j = 0; j < kHalfDimensions; ++j) { + BiasType sum = accumulation[static_cast(perspectives[p])][0][j]; + output[offset + j] = static_cast( + std::max(0, std::min(127, sum))); + } + #endif + + } + } + + private: + // Calculate cumulative value without using difference calculation + void RefreshAccumulator(const Position& pos) const { + auto& accumulator = pos.state()->accumulator; + IndexType i = 0; + Features::IndexList active_indices[2]; + RawFeatures::AppendActiveIndices(pos, kRefreshTriggers[i], + active_indices); + for (Color perspective : { WHITE, BLACK }) { + std::memcpy(accumulator.accumulation[perspective][i], biases_, + kHalfDimensions * sizeof(BiasType)); + for (const auto index : active_indices[perspective]) { + const IndexType offset = kHalfDimensions * index; + + #if defined(USE_AVX2) + auto accumulation = reinterpret_cast<__m256i*>( + &accumulator.accumulation[perspective][i][0]); + auto column = reinterpret_cast(&weights_[offset]); + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + for (IndexType j = 0; j < kNumChunks; ++j) { + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_storeu_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadu_si256(&accumulation[j]), column[j])); + #else + accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); + #endif + } + + #elif defined(USE_SSE2) + auto accumulation = reinterpret_cast<__m128i*>( + &accumulator.accumulation[perspective][i][0]); + auto column = reinterpret_cast(&weights_[offset]); + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_NEON) + auto accumulation = reinterpret_cast( + &accumulator.accumulation[perspective][i][0]); + auto column = reinterpret_cast(&weights_[offset]); + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = vaddq_s16(accumulation[j], column[j]); + } + + #else + for (IndexType j = 0; j < kHalfDimensions; ++j) { + accumulator.accumulation[perspective][i][j] += weights_[offset + j]; + } + #endif + + } + } + + accumulator.computed_accumulation = true; + accumulator.computed_score = false; + } + + // Calculate cumulative value using difference calculation + void UpdateAccumulator(const Position& pos) const { + const auto prev_accumulator = pos.state()->previous->accumulator; + auto& accumulator = pos.state()->accumulator; + IndexType i = 0; + Features::IndexList removed_indices[2], added_indices[2]; + bool reset[2]; + RawFeatures::AppendChangedIndices(pos, kRefreshTriggers[i], + removed_indices, added_indices, reset); + for (Color perspective : { WHITE, BLACK }) { + + #if defined(USE_AVX2) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + auto accumulation = reinterpret_cast<__m256i*>( + &accumulator.accumulation[perspective][i][0]); + + #elif defined(USE_SSE2) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + auto accumulation = reinterpret_cast<__m128i*>( + &accumulator.accumulation[perspective][i][0]); + + #elif defined(USE_NEON) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + auto accumulation = reinterpret_cast( + &accumulator.accumulation[perspective][i][0]); + #endif + + if (reset[perspective]) { + std::memcpy(accumulator.accumulation[perspective][i], biases_, + kHalfDimensions * sizeof(BiasType)); + } else { + std::memcpy(accumulator.accumulation[perspective][i], + prev_accumulator.accumulation[perspective][i], + kHalfDimensions * sizeof(BiasType)); + // Difference calculation for the deactivated features + for (const auto index : removed_indices[perspective]) { + const IndexType offset = kHalfDimensions * index; + + #if defined(USE_AVX2) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm256_sub_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_SSE2) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm_sub_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_NEON) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = vsubq_s16(accumulation[j], column[j]); + } + + #else + for (IndexType j = 0; j < kHalfDimensions; ++j) { + accumulator.accumulation[perspective][i][j] -= + weights_[offset + j]; + } + #endif + + } + } + { // Difference calculation for the activated features + for (const auto index : added_indices[perspective]) { + const IndexType offset = kHalfDimensions * index; + + #if defined(USE_AVX2) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_SSE2) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_NEON) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = vaddq_s16(accumulation[j], column[j]); + } + + #else + for (IndexType j = 0; j < kHalfDimensions; ++j) { + accumulator.accumulation[perspective][i][j] += + weights_[offset + j]; + } + #endif + + } + } + } + + accumulator.computed_accumulation = true; + accumulator.computed_score = false; + } + + using BiasType = std::int16_t; + using WeightType = std::int16_t; + + alignas(kCacheLineSize) BiasType biases_[kHalfDimensions]; + alignas(kCacheLineSize) + WeightType weights_[kHalfDimensions * kInputDimensions]; + }; + +} // namespace Eval::NNUE + +#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED diff --git a/src/pawns.cpp b/src/pawns.cpp index 7f8d451a..73682529 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 e6098069..5499826e 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 396bff5f..46e5d78b 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -200,6 +198,9 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); st = si; + // Each piece on board gets a unique ID used to track the piece later + PieceId piece_id, next_piece_id = PIECE_ID_ZERO; + ss >> std::noskipws; // 1. Piece placement @@ -213,7 +214,19 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th else if ((idx = PieceToChar.find(token)) != string::npos) { - put_piece(Piece(idx), sq); + auto pc = Piece(idx); + put_piece(pc, sq); + + if (Eval::useNNUE) + { + // Kings get a fixed ID, other pieces get ID in order of placement + piece_id = + (idx == W_KING) ? PIECE_ID_WKING : + (idx == B_KING) ? PIECE_ID_BKING : + next_piece_id++; + evalList.put_piece(piece_id, sq, pc); + } + ++sq; } } @@ -705,6 +718,14 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ++st->rule50; ++st->pliesFromNull; + // Used by NNUE + st->accumulator.computed_accumulation = false; + st->accumulator.computed_score = false; + PieceId dp0 = PIECE_ID_NONE; + PieceId dp1 = PIECE_ID_NONE; + auto& dp = st->dirtyPiece; + dp.dirty_num = 1; + Color us = sideToMove; Color them = ~us; Square from = from_sq(m); @@ -752,6 +773,16 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { else st->nonPawnMaterial[them] -= PieceValue[MG][captured]; + if (Eval::useNNUE) + { + dp.dirty_num = 2; // 2 pieces moved + dp1 = piece_id_on(capsq); + dp.pieceId[1] = dp1; + dp.old_piece[1] = evalList.piece_with_id(dp1); + evalList.put_piece(dp1, capsq, NO_PIECE); + dp.new_piece[1] = evalList.piece_with_id(dp1); + } + // Update board and piece lists remove_piece(capsq); @@ -787,7 +818,18 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) + { + if (Eval::useNNUE) + { + dp0 = piece_id_on(from); + dp.pieceId[0] = dp0; + dp.old_piece[0] = evalList.piece_with_id(dp0); + evalList.put_piece(dp0, to, pc); + dp.new_piece[0] = evalList.piece_with_id(dp0); + } + move_piece(from, to); + } // If the moving piece is a pawn do some special extra work if (type_of(pc) == PAWN) @@ -810,6 +852,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { remove_piece(to); put_piece(promotion, to); + if (Eval::useNNUE) + { + dp0 = piece_id_on(to); + evalList.put_piece(dp0, to, promotion); + dp.new_piece[0] = evalList.piece_with_id(dp0); + } + // Update hash keys k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; st->pawnKey ^= Zobrist::psq[pc][to]; @@ -901,6 +950,12 @@ void Position::undo_move(Move m) { { move_piece(to, from); // Put the piece back at the source square + if (Eval::useNNUE) + { + PieceId dp0 = st->dirtyPiece.pieceId[0]; + evalList.put_piece(dp0, from, pc); + } + if (st->capturedPiece) { Square capsq = to; @@ -917,6 +972,14 @@ void Position::undo_move(Move m) { } put_piece(st->capturedPiece, capsq); // Restore the captured piece + + if (Eval::useNNUE) + { + PieceId dp1 = st->dirtyPiece.pieceId[1]; + assert(evalList.piece_with_id(dp1).from[WHITE] == PS_NONE); + assert(evalList.piece_with_id(dp1).from[BLACK] == PS_NONE); + evalList.put_piece(dp1, capsq, st->capturedPiece); + } } } @@ -938,6 +1001,34 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + if (Eval::useNNUE) + { + PieceId dp0, dp1; + auto& dp = st->dirtyPiece; + dp.dirty_num = 2; // 2 pieces moved + + if (Do) + { + dp0 = piece_id_on(from); + dp1 = piece_id_on(rfrom); + dp.pieceId[0] = dp0; + dp.old_piece[0] = evalList.piece_with_id(dp0); + evalList.put_piece(dp0, to, make_piece(us, KING)); + dp.new_piece[0] = evalList.piece_with_id(dp0); + dp.pieceId[1] = dp1; + dp.old_piece[1] = evalList.piece_with_id(dp1); + evalList.put_piece(dp1, rto, make_piece(us, ROOK)); + dp.new_piece[1] = evalList.piece_with_id(dp1); + } + else + { + dp0 = piece_id_on(to); + dp1 = piece_id_on(rto); + evalList.put_piece(dp0, from, make_piece(us, KING)); + evalList.put_piece(dp1, rfrom, make_piece(us, ROOK)); + } + } + // Remove both pieces first since squares could overlap in Chess960 remove_piece(Do ? from : to); remove_piece(Do ? rfrom : rto); @@ -955,7 +1046,14 @@ void Position::do_null_move(StateInfo& newSt) { assert(!checkers()); assert(&newSt != st); - std::memcpy(&newSt, st, sizeof(StateInfo)); + if (Eval::useNNUE) + { + std::memcpy(&newSt, st, sizeof(StateInfo)); + st->accumulator.computed_score = false; + } + else + std::memcpy(&newSt, st, offsetof(StateInfo, accumulator)); + newSt.previous = st; st = &newSt; diff --git a/src/position.h b/src/position.h index 8cfa3920..a77050eb 100644 --- a/src/position.h +++ b/src/position.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -27,8 +25,11 @@ #include #include "bitboard.h" +#include "evaluate.h" #include "types.h" +#include "nnue/nnue_accumulator.h" + /// 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 @@ -54,6 +55,10 @@ struct StateInfo { Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; int repetition; + + // Used by NNUE + Eval::NNUE::Accumulator accumulator; + DirtyPiece dirtyPiece; }; @@ -163,6 +168,10 @@ public: bool pos_is_ok() const; void flip(); + // Used by NNUE + StateInfo* state() const; + const EvalList* eval_list() const; + private: // Initialization helpers (used while setting up a position) void set_castling_right(Color c, Square rfrom); @@ -176,6 +185,9 @@ private: template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); + // ID of a piece on a given square + PieceId piece_id_on(Square sq) const; + // Data members Piece board[SQUARE_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; @@ -192,6 +204,9 @@ private: Thread* thisThread; StateInfo* st; bool chess960; + + // List of pieces used in NNUE evaluation function + EvalList evalList; }; namespace PSQT { @@ -426,4 +441,25 @@ inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); } +inline StateInfo* Position::state() const { + + return st; +} + +inline const EvalList* Position::eval_list() const { + + return &evalList; +} + +inline PieceId Position::piece_id_on(Square sq) const +{ + + assert(piece_on(sq) != NO_PIECE); + + PieceId pid = evalList.piece_id_list[sq]; + assert(is_ok(pid)); + + return pid; +} + #endif // #ifndef POSITION_H_INCLUDED diff --git a/src/psqt.cpp b/src/psqt.cpp index 5e8dd2c7..eb36e75e 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 91ac60ad..d1dc4489 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -227,6 +225,8 @@ void MainThread::search() { Time.init(Limits, us, rootPos.game_ply()); TT.new_search(); + Eval::verify_NNUE(); + if (rootMoves.empty()) { rootMoves.emplace_back(MOVE_NONE); diff --git a/src/search.h b/src/search.h index 3e855c8b..2554f3fb 100644 --- a/src/search.h +++ b/src/search.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 95d58945..20215b96 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,7 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch + Copyright (C) 2004-2020 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 df3ca4fe..b998989b 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,7 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch + Copyright (C) 2004-2020 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 a0ee2b25..44aea14e 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 a69e1d10..46da1e34 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 0ef5c981..c4b55a48 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 546eadd2..df4ba9b2 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 9301dc94..5ad72b32 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 34590903..d494c27d 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 e18db8ce..c177ca52 100644 --- a/src/tt.h +++ b/src/tt.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 c1b1c76b..e94f67f8 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 27c3f961..1489fa32 100644 --- a/src/tune.h +++ b/src/tune.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2017 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 c1598561..379859f7 100644 --- a/src/types.h +++ b/src/types.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -203,6 +201,22 @@ enum Piece { PIECE_NB = 16 }; +// An ID used to track the pieces. Max. 32 pieces on board. +enum PieceId { + PIECE_ID_ZERO = 0, + PIECE_ID_KING = 30, + PIECE_ID_WKING = 30, + PIECE_ID_BKING = 31, + PIECE_ID_NONE = 32 +}; + +inline PieceId operator++(PieceId& d, int) { + + PieceId x = d; + d = PieceId(int(d) + 1); + return x; +} + constexpr Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO }, @@ -232,7 +246,8 @@ enum Square : int { SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, SQ_NONE, - SQUARE_NB = 64 + SQUARE_ZERO = 0, + SQUARE_NB = 64 }; enum Direction : int { @@ -255,6 +270,94 @@ enum Rank : int { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB }; +// unique number for each piece type on each square +enum PieceSquare : uint32_t { + 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_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 +}; + +struct ExtPieceSquare { + PieceSquare from[COLOR_NB]; +}; + +// Array for finding the PieceSquare corresponding to the piece on the board +extern ExtPieceSquare kpp_board_index[PIECE_NB]; + +constexpr bool is_ok(PieceId pid); +constexpr Square rotate180(Square sq); + +// Structure holding which tracked piece (PieceId) is where (PieceSquare) +class EvalList { + +public: + // Max. number of pieces without kings is 30 but must be a multiple of 4 in AVX2 + static const int MAX_LENGTH = 32; + + // Array that holds the piece id for the pieces on the board + PieceId piece_id_list[SQUARE_NB]; + + // List of pieces, separate from White and Black POV + PieceSquare* piece_list_fw() const { return const_cast(pieceListFw); } + PieceSquare* piece_list_fb() const { return const_cast(pieceListFb); } + + // Place the piece pc with piece_id on the square sq on the board + void put_piece(PieceId piece_id, Square sq, Piece pc) + { + assert(is_ok(piece_id)); + if (pc != NO_PIECE) + { + pieceListFw[piece_id] = PieceSquare(kpp_board_index[pc].from[WHITE] + sq); + pieceListFb[piece_id] = PieceSquare(kpp_board_index[pc].from[BLACK] + rotate180(sq)); + piece_id_list[sq] = piece_id; + } + else + { + pieceListFw[piece_id] = PS_NONE; + pieceListFb[piece_id] = PS_NONE; + piece_id_list[sq] = piece_id; + } + } + + // Convert the specified piece_id piece to ExtPieceSquare type and return it + ExtPieceSquare piece_with_id(PieceId piece_id) const + { + ExtPieceSquare eps; + eps.from[WHITE] = pieceListFw[piece_id]; + eps.from[BLACK] = pieceListFb[piece_id]; + return eps; + } + +private: + PieceSquare pieceListFw[MAX_LENGTH]; + PieceSquare pieceListFb[MAX_LENGTH]; +}; + +// For differential evaluation of pieces that changed since last turn +struct DirtyPiece { + + // Number of changed pieces + int dirty_num; + + // The ids of changed pieces, max. 2 pieces can change in one move + PieceId pieceId[2]; + + // What changed from the piece with that piece number + ExtPieceSquare old_piece[2]; + ExtPieceSquare new_piece[2]; +}; /// Score enum stores a middlegame and an endgame value in a single integer (enum). /// The least significant 16 bits are used to store the middlegame value and the @@ -280,10 +383,10 @@ inline Value mg_value(Score s) { } #define ENABLE_BASE_OPERATORS_ON(T) \ -constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ -constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ +constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ +constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ constexpr T operator-(T d) { return T(-int(d)); } \ -inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \ +inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \ inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; } #define ENABLE_INCR_OPERATORS_ON(T) \ @@ -302,6 +405,9 @@ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Direction) +ENABLE_INCR_OPERATORS_ON(Piece) +ENABLE_INCR_OPERATORS_ON(PieceSquare) +ENABLE_INCR_OPERATORS_ON(PieceId) ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) @@ -390,6 +496,10 @@ inline Color color_of(Piece pc) { return Color(pc >> 3); } +constexpr bool is_ok(PieceId pid) { + return pid < PIECE_ID_NONE; +} + constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } @@ -426,6 +536,11 @@ constexpr Square to_sq(Move m) { return Square(m & 0x3F); } +// Return relative square when turning the board 180 degrees +constexpr Square rotate180(Square sq) { + return (Square)(sq ^ 0x3F); +} + constexpr int from_to(Move m) { return m & 0xFFF; } diff --git a/src/uci.cpp b/src/uci.cpp index bb57c80b..d6486320 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -78,6 +76,20 @@ namespace { } } + // trace_eval() prints the evaluation for the current position, consistent with the UCI + // options set so far. + + void trace_eval(Position& pos) { + + StateListPtr states(new std::deque(1)); + Position p; + p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main()); + + Eval::verify_NNUE(); + + sync_cout << "\n" << Eval::trace(p) << sync_endl; + } + // setoption() is called when engine receives the "setoption" UCI command. The // function updates the UCI option ("name") to the given value ("value"). @@ -166,7 +178,7 @@ namespace { nodes += Threads.nodes_searched(); } else - sync_cout << "\n" << Eval::trace(pos) << sync_endl; + trace_eval(pos); } else if (token == "setoption") setoption(is); else if (token == "position") position(pos, is, states); @@ -261,7 +273,7 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "flip") pos.flip(); else if (token == "bench") bench(pos, is, states); else if (token == "d") sync_cout << pos << sync_endl; - else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; + else if (token == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; else sync_cout << "Unknown command: " << cmd << sync_endl; diff --git a/src/uci.h b/src/uci.h index ad954d9f..eb0b390b 100644 --- a/src/uci.h +++ b/src/uci.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 ef54ef4e..788aed17 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 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 @@ -42,7 +40,8 @@ void on_hash_size(const Option& o) { TT.resize(size_t(o)); } void on_logger(const Option& o) { start_logger(o); } void on_threads(const Option& o) { Threads.set(size_t(o)); } void on_tb_path(const Option& o) { Tablebases::init(o); } - +void on_use_NNUE(const Option& ) { Eval::init_NNUE(); } +void on_eval_file(const Option& ) { Eval::init_NNUE(); } /// Our case insensitive less() function as required by UCI protocol bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { @@ -79,6 +78,8 @@ void init(OptionsMap& o) { o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); + o["Use NNUE"] << Option(false, on_use_NNUE); + o["EvalFile"] << Option("nn-97f742aaefcd.nnue", on_eval_file); } From 3dca13a958cd0dfea1cdea91da230c5aac9e322f Mon Sep 17 00:00:00 2001 From: MJZ1977 <37274752+MJZ1977@users.noreply.github.com> Date: Thu, 6 Aug 2020 17:39:10 +0200 Subject: [PATCH 09/45] NNUE evaluation threshold The idea is to use NNUE only on quite balanced material positions. This bring a big speedup on research since NNUE eval is slower than classical eval for most of the hardwares and specially on unbalanced positions with LazyEval. STC: https://tests.stockfishchess.org/tests/view/5f2c2680b3ebe5cbfee85b61 LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 3168 W: 560 L: 400 D: 2208 Ptnml(0-2): 21, 294, 819, 404, 46 LTC: https://tests.stockfishchess.org/tests/view/5f2c2ca6b3ebe5cbfee85b69 LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 3200 W: 287 L: 183 D: 2730 Ptnml(0-2): 4, 149, 1191, 251, 5 closes https://github.com/official-stockfish/Stockfish/pull/2916 Bench 4746616 --- src/evaluate.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f43c62d6..09496fdc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -107,9 +107,10 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(1400); - constexpr Value LazyThreshold2 = Value(1300); + constexpr Value LazyThreshold1 = Value(1400); + constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); + constexpr Value NNUEThreshold = Value(500); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -941,9 +942,14 @@ make_v: Value Eval::evaluate(const Position& pos) { if (Eval::useNNUE) - return NNUE::evaluate(pos); - else - return Evaluation(pos).value(); + { + Value balance = pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK); + balance += 200 * (pos.count(WHITE) - pos.count(BLACK)); + // Take NNUE eval only on balanced positions + if (abs(balance) < NNUEThreshold) + return NNUE::evaluate(pos); + } + return Evaluation(pos).value(); } /// trace() is like evaluate(), but instead of returning a value, it returns From 8b8412ef87825d8e341e160585307dc89843b7f6 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 7 Aug 2020 01:08:15 +0200 Subject: [PATCH 10/45] Add tempo also to NNUE eval. STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 10608 W: 1507 L: 1358 D: 7743 Ptnml(0-2): 94, 945, 3074, 1100, 91 https://tests.stockfishchess.org/tests/view/5f2c5921b3ebe5cbfee85b8b LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 7536 W: 556 L: 448 D: 6532 Ptnml(0-2): 9, 383, 2881, 481, 14 https://tests.stockfishchess.org/tests/view/5f2c6f4461e3b6af64881e95 closes https://github.com/official-stockfish/Stockfish/pull/2919 Bench: 4746616 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 09496fdc..015efa48 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -947,7 +947,7 @@ Value Eval::evaluate(const Position& pos) { balance += 200 * (pos.count(WHITE) - pos.count(BLACK)); // Take NNUE eval only on balanced positions if (abs(balance) < NNUEThreshold) - return NNUE::evaluate(pos); + return NNUE::evaluate(pos) + Tempo; } return Evaluation(pos).value(); } From af935365e3e528f445c1c0f48bb43b8cf685719c Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 6 Aug 2020 17:37:54 -0700 Subject: [PATCH 11/45] Tuned pawn values Passed STC: https://tests.stockfishchess.org/tests/view/5f2aa49fa5abc164f05e4d1b LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 40888 W: 7977 L: 7726 D: 25185 Ptnml(0-2): 665, 4806, 9333, 4893, 747 Passed LTC: https://tests.stockfishchess.org/tests/view/5f2b1059b3ebe5cbfee85ae7 LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 51264 W: 6445 L: 6134 D: 38685 Ptnml(0-2): 328, 4564, 15580, 4789, 371 closes https://github.com/official-stockfish/Stockfish/pull/2920 bench: 4314943 --- src/pawns.cpp | 14 +++++++------- src/types.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 73682529..868d0c8e 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -30,21 +30,21 @@ namespace { #define S(mg, eg) make_score(mg, eg) // Pawn penalties - constexpr Score Backward = S( 9, 24); - constexpr Score Doubled = S(11, 56); - constexpr Score Isolated = S( 5, 15); - constexpr Score WeakLever = S( 0, 56); - constexpr Score WeakUnopposed = S(13, 27); + constexpr Score Backward = S( 8, 27); + constexpr Score Doubled = S(11, 55); + constexpr Score Isolated = S( 5, 17); + constexpr Score WeakLever = S( 2, 54); + constexpr Score WeakUnopposed = S(15, 25); // Bonus for blocked pawns at 5th or 6th rank - constexpr Score BlockedPawn[2] = { S(-11, -4), S(-3, 4) }; + constexpr Score BlockedPawn[2] = { S(-13, -4), S(-4, 3) }; constexpr Score BlockedStorm[RANK_NB] = { S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2) }; // Connected pawn bonus - constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; + constexpr int Connected[RANK_NB] = { 0, 7, 8, 11, 24, 45, 85 }; // Strength of pawn shelter for our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. diff --git a/src/types.h b/src/types.h index 379859f7..73da41e2 100644 --- a/src/types.h +++ b/src/types.h @@ -178,7 +178,7 @@ enum Value : int { VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, - PawnValueMg = 124, PawnValueEg = 206, + PawnValueMg = 126, PawnValueEg = 208, KnightValueMg = 781, KnightValueEg = 854, BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, From 7f336dd59b3b1365943d73ee706a9610e18108bb Mon Sep 17 00:00:00 2001 From: UnaiCorzo Date: Tue, 4 Aug 2020 14:32:52 +0200 Subject: [PATCH 12/45] Remove QueenInfiltration STC https://tests.stockfishchess.org/tests/view/5f2955b1a5abc164f05e4c85 LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 29216 W: 5560 L: 5416 D: 18240 Ptnml(0-2): 466, 3329, 6902, 3417, 494 LTC https://tests.stockfishchess.org/tests/view/5f299154a5abc164f05e4ca1 LLR: 2.92 (-2.94,2.94) {-1.50,0.50} Total: 54144 W: 6635 L: 6594 D: 40915 Ptnml(0-2): 372, 4859, 16536, 4966, 339 closes https://github.com/official-stockfish/Stockfish/pull/2910 Bench: 4609008 --- src/evaluate.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 015efa48..d20c7b70 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -181,7 +181,6 @@ namespace { constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); - constexpr Score QueenInfiltration = S( -2, 14); constexpr Score ReachableOutpost = S( 31, 22); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnKingRing = S( 16, 0); @@ -423,10 +422,6 @@ namespace { Bitboard queenPinners; if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners)) score -= WeakQueen; - - // Bonus for queen on weak square in enemy camp - if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s)) - score += QueenInfiltration; } } if (T) From 615d98da2447e79ceceae205e0cd4e878115acc3 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 5 Aug 2020 09:29:27 +0200 Subject: [PATCH 13/45] Do move legality check before pruning. This alllows to simplify the code because the move counter haven't to be decremented later if a move isn't legal. As a side effect now illegal pruned moves doesn't included anymore in move counter. So slightly less pruning and reductions are done. STC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 111016 W: 21106 L: 21077 D: 68833 Ptnml(0-2): 1830, 13083, 25736, 12946, 1913 https://tests.stockfishchess.org/tests/view/5f28816fa5abc164f05e4c26 LTC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 39264 W: 4909 L: 4843 D: 29512 Ptnml(0-2): 263, 3601, 11854, 3635, 279 https://tests.stockfishchess.org/tests/view/5f297902a5abc164f05e4c8e closes https://github.com/official-stockfish/Stockfish/pull/2906 Bench: 4390086 --- src/search.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d1dc4489..2f83f4f4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -986,6 +986,10 @@ moves_loop: // When in check, search starts from here thisThread->rootMoves.begin() + thisThread->pvLast, move)) continue; + // Check for legality + if (!rootNode && !pos.legal(move)) + continue; + ss->moveCount = ++moveCount; if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) @@ -1137,13 +1141,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 (!rootNode && !pos.legal(move)) - { - ss->moveCount = --moveCount; - continue; - } - // Update the current move (this must be done after singular extension search) ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] From 857e045ced9e20f202e15d825e47b3ab8241dcef Mon Sep 17 00:00:00 2001 From: Sergio Vieri Date: Fri, 7 Aug 2020 15:15:04 +0800 Subject: [PATCH 14/45] Update default net to nn-9931db908a9b.nnue Net created at 20200806-1802 passed STC: https://tests.stockfishchess.org/tests/view/5f2d00b461e3b6af64881f21 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 6672 W: 1052 L: 898 D: 4722 Ptnml(0-2): 63, 600, 1868, 730, 75 passed LTC: https://tests.stockfishchess.org/tests/view/5f2d052a61e3b6af64881f29 LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 7576 W: 573 L: 463 D: 6540 Ptnml(0-2): 8, 392, 2889, 480, 19 closes https://github.com/official-stockfish/Stockfish/pull/2923 Bench: 4390086 --- AUTHORS | 1 + src/ucioption.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 2e080e61..07e07297 100644 --- a/AUTHORS +++ b/AUTHORS @@ -151,6 +151,7 @@ Sami Kiminki (skiminki) Sebastian Buchwald (UniQP) Sergei Antonov (saproj) Sergei Ivanov (svivanov72) +Sergio Vieri (sergiovieri) sf-x Shane Booth (shane31) Shawn Varghese (xXH4CKST3RXx) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 788aed17..faeb78ae 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -79,7 +79,7 @@ void init(OptionsMap& o) { o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); o["Use NNUE"] << Option(false, on_use_NNUE); - o["EvalFile"] << Option("nn-97f742aaefcd.nnue", on_eval_file); + o["EvalFile"] << Option("nn-9931db908a9b.nnue", on_eval_file); } From 1c2346538396c1046564ddc411bc9dac72939f8a Mon Sep 17 00:00:00 2001 From: nodchip Date: Fri, 7 Aug 2020 22:34:53 +0900 Subject: [PATCH 15/45] Moved the nnue folder. --- src/{eval => }/nnue/architectures/halfkp-cr-ep_256x2-32-32.h | 0 src/{eval => }/nnue/architectures/halfkp_256x2-32-32.h | 0 src/{eval => }/nnue/architectures/halfkp_384x2-32-32.h | 0 src/{eval => }/nnue/architectures/k-p-cr-ep_256x2-32-32.h | 0 src/{eval => }/nnue/architectures/k-p-cr_256x2-32-32.h | 0 src/{eval => }/nnue/architectures/k-p_256x2-32-32.h | 0 src/{eval => }/nnue/evaluate_nnue.cpp | 0 src/{eval => }/nnue/evaluate_nnue.h | 0 src/{eval => }/nnue/evaluate_nnue_learner.cpp | 0 src/{eval => }/nnue/evaluate_nnue_learner.h | 0 src/{eval => }/nnue/features/castling_right.cpp | 0 src/{eval => }/nnue/features/castling_right.h | 0 src/{eval => }/nnue/features/enpassant.cpp | 0 src/{eval => }/nnue/features/enpassant.h | 0 src/{eval => }/nnue/features/feature_set.h | 0 src/{eval => }/nnue/features/features_common.h | 0 src/{eval => }/nnue/features/half_kp.cpp | 0 src/{eval => }/nnue/features/half_kp.h | 0 src/{eval => }/nnue/features/half_relative_kp.cpp | 0 src/{eval => }/nnue/features/half_relative_kp.h | 0 src/{eval => }/nnue/features/index_list.h | 0 src/{eval => }/nnue/features/k.cpp | 0 src/{eval => }/nnue/features/k.h | 0 src/{eval => }/nnue/features/p.cpp | 0 src/{eval => }/nnue/features/p.h | 0 src/{eval => }/nnue/layers/affine_transform.h | 0 src/{eval => }/nnue/layers/clipped_relu.h | 0 src/{eval => }/nnue/layers/input_slice.h | 0 src/{eval => }/nnue/layers/sum.h | 0 src/{eval => }/nnue/nnue_accumulator.h | 0 src/{eval => }/nnue/nnue_architecture.h | 0 src/{eval => }/nnue/nnue_common.h | 0 src/{eval => }/nnue/nnue_feature_transformer.h | 0 src/{eval => }/nnue/nnue_test_command.cpp | 0 src/{eval => }/nnue/nnue_test_command.h | 0 src/{eval => }/nnue/trainer/features/factorizer.h | 0 src/{eval => }/nnue/trainer/features/factorizer_feature_set.h | 0 src/{eval => }/nnue/trainer/features/factorizer_half_kp.h | 0 src/{eval => }/nnue/trainer/trainer.h | 0 src/{eval => }/nnue/trainer/trainer_affine_transform.h | 0 src/{eval => }/nnue/trainer/trainer_clipped_relu.h | 0 src/{eval => }/nnue/trainer/trainer_feature_transformer.h | 0 src/{eval => }/nnue/trainer/trainer_input_slice.h | 0 src/{eval => }/nnue/trainer/trainer_sum.h | 0 44 files changed, 0 insertions(+), 0 deletions(-) rename src/{eval => }/nnue/architectures/halfkp-cr-ep_256x2-32-32.h (100%) rename src/{eval => }/nnue/architectures/halfkp_256x2-32-32.h (100%) rename src/{eval => }/nnue/architectures/halfkp_384x2-32-32.h (100%) rename src/{eval => }/nnue/architectures/k-p-cr-ep_256x2-32-32.h (100%) rename src/{eval => }/nnue/architectures/k-p-cr_256x2-32-32.h (100%) rename src/{eval => }/nnue/architectures/k-p_256x2-32-32.h (100%) rename src/{eval => }/nnue/evaluate_nnue.cpp (100%) rename src/{eval => }/nnue/evaluate_nnue.h (100%) rename src/{eval => }/nnue/evaluate_nnue_learner.cpp (100%) rename src/{eval => }/nnue/evaluate_nnue_learner.h (100%) rename src/{eval => }/nnue/features/castling_right.cpp (100%) rename src/{eval => }/nnue/features/castling_right.h (100%) rename src/{eval => }/nnue/features/enpassant.cpp (100%) rename src/{eval => }/nnue/features/enpassant.h (100%) rename src/{eval => }/nnue/features/feature_set.h (100%) rename src/{eval => }/nnue/features/features_common.h (100%) rename src/{eval => }/nnue/features/half_kp.cpp (100%) rename src/{eval => }/nnue/features/half_kp.h (100%) rename src/{eval => }/nnue/features/half_relative_kp.cpp (100%) rename src/{eval => }/nnue/features/half_relative_kp.h (100%) rename src/{eval => }/nnue/features/index_list.h (100%) rename src/{eval => }/nnue/features/k.cpp (100%) rename src/{eval => }/nnue/features/k.h (100%) rename src/{eval => }/nnue/features/p.cpp (100%) rename src/{eval => }/nnue/features/p.h (100%) rename src/{eval => }/nnue/layers/affine_transform.h (100%) rename src/{eval => }/nnue/layers/clipped_relu.h (100%) rename src/{eval => }/nnue/layers/input_slice.h (100%) rename src/{eval => }/nnue/layers/sum.h (100%) rename src/{eval => }/nnue/nnue_accumulator.h (100%) rename src/{eval => }/nnue/nnue_architecture.h (100%) rename src/{eval => }/nnue/nnue_common.h (100%) rename src/{eval => }/nnue/nnue_feature_transformer.h (100%) rename src/{eval => }/nnue/nnue_test_command.cpp (100%) rename src/{eval => }/nnue/nnue_test_command.h (100%) rename src/{eval => }/nnue/trainer/features/factorizer.h (100%) rename src/{eval => }/nnue/trainer/features/factorizer_feature_set.h (100%) rename src/{eval => }/nnue/trainer/features/factorizer_half_kp.h (100%) rename src/{eval => }/nnue/trainer/trainer.h (100%) rename src/{eval => }/nnue/trainer/trainer_affine_transform.h (100%) rename src/{eval => }/nnue/trainer/trainer_clipped_relu.h (100%) rename src/{eval => }/nnue/trainer/trainer_feature_transformer.h (100%) rename src/{eval => }/nnue/trainer/trainer_input_slice.h (100%) rename src/{eval => }/nnue/trainer/trainer_sum.h (100%) diff --git a/src/eval/nnue/architectures/halfkp-cr-ep_256x2-32-32.h b/src/nnue/architectures/halfkp-cr-ep_256x2-32-32.h similarity index 100% rename from src/eval/nnue/architectures/halfkp-cr-ep_256x2-32-32.h rename to src/nnue/architectures/halfkp-cr-ep_256x2-32-32.h diff --git a/src/eval/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h similarity index 100% rename from src/eval/nnue/architectures/halfkp_256x2-32-32.h rename to src/nnue/architectures/halfkp_256x2-32-32.h diff --git a/src/eval/nnue/architectures/halfkp_384x2-32-32.h b/src/nnue/architectures/halfkp_384x2-32-32.h similarity index 100% rename from src/eval/nnue/architectures/halfkp_384x2-32-32.h rename to src/nnue/architectures/halfkp_384x2-32-32.h diff --git a/src/eval/nnue/architectures/k-p-cr-ep_256x2-32-32.h b/src/nnue/architectures/k-p-cr-ep_256x2-32-32.h similarity index 100% rename from src/eval/nnue/architectures/k-p-cr-ep_256x2-32-32.h rename to src/nnue/architectures/k-p-cr-ep_256x2-32-32.h diff --git a/src/eval/nnue/architectures/k-p-cr_256x2-32-32.h b/src/nnue/architectures/k-p-cr_256x2-32-32.h similarity index 100% rename from src/eval/nnue/architectures/k-p-cr_256x2-32-32.h rename to src/nnue/architectures/k-p-cr_256x2-32-32.h diff --git a/src/eval/nnue/architectures/k-p_256x2-32-32.h b/src/nnue/architectures/k-p_256x2-32-32.h similarity index 100% rename from src/eval/nnue/architectures/k-p_256x2-32-32.h rename to src/nnue/architectures/k-p_256x2-32-32.h diff --git a/src/eval/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp similarity index 100% rename from src/eval/nnue/evaluate_nnue.cpp rename to src/nnue/evaluate_nnue.cpp diff --git a/src/eval/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h similarity index 100% rename from src/eval/nnue/evaluate_nnue.h rename to src/nnue/evaluate_nnue.h diff --git a/src/eval/nnue/evaluate_nnue_learner.cpp b/src/nnue/evaluate_nnue_learner.cpp similarity index 100% rename from src/eval/nnue/evaluate_nnue_learner.cpp rename to src/nnue/evaluate_nnue_learner.cpp diff --git a/src/eval/nnue/evaluate_nnue_learner.h b/src/nnue/evaluate_nnue_learner.h similarity index 100% rename from src/eval/nnue/evaluate_nnue_learner.h rename to src/nnue/evaluate_nnue_learner.h diff --git a/src/eval/nnue/features/castling_right.cpp b/src/nnue/features/castling_right.cpp similarity index 100% rename from src/eval/nnue/features/castling_right.cpp rename to src/nnue/features/castling_right.cpp diff --git a/src/eval/nnue/features/castling_right.h b/src/nnue/features/castling_right.h similarity index 100% rename from src/eval/nnue/features/castling_right.h rename to src/nnue/features/castling_right.h diff --git a/src/eval/nnue/features/enpassant.cpp b/src/nnue/features/enpassant.cpp similarity index 100% rename from src/eval/nnue/features/enpassant.cpp rename to src/nnue/features/enpassant.cpp diff --git a/src/eval/nnue/features/enpassant.h b/src/nnue/features/enpassant.h similarity index 100% rename from src/eval/nnue/features/enpassant.h rename to src/nnue/features/enpassant.h diff --git a/src/eval/nnue/features/feature_set.h b/src/nnue/features/feature_set.h similarity index 100% rename from src/eval/nnue/features/feature_set.h rename to src/nnue/features/feature_set.h diff --git a/src/eval/nnue/features/features_common.h b/src/nnue/features/features_common.h similarity index 100% rename from src/eval/nnue/features/features_common.h rename to src/nnue/features/features_common.h diff --git a/src/eval/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp similarity index 100% rename from src/eval/nnue/features/half_kp.cpp rename to src/nnue/features/half_kp.cpp diff --git a/src/eval/nnue/features/half_kp.h b/src/nnue/features/half_kp.h similarity index 100% rename from src/eval/nnue/features/half_kp.h rename to src/nnue/features/half_kp.h diff --git a/src/eval/nnue/features/half_relative_kp.cpp b/src/nnue/features/half_relative_kp.cpp similarity index 100% rename from src/eval/nnue/features/half_relative_kp.cpp rename to src/nnue/features/half_relative_kp.cpp diff --git a/src/eval/nnue/features/half_relative_kp.h b/src/nnue/features/half_relative_kp.h similarity index 100% rename from src/eval/nnue/features/half_relative_kp.h rename to src/nnue/features/half_relative_kp.h diff --git a/src/eval/nnue/features/index_list.h b/src/nnue/features/index_list.h similarity index 100% rename from src/eval/nnue/features/index_list.h rename to src/nnue/features/index_list.h diff --git a/src/eval/nnue/features/k.cpp b/src/nnue/features/k.cpp similarity index 100% rename from src/eval/nnue/features/k.cpp rename to src/nnue/features/k.cpp diff --git a/src/eval/nnue/features/k.h b/src/nnue/features/k.h similarity index 100% rename from src/eval/nnue/features/k.h rename to src/nnue/features/k.h diff --git a/src/eval/nnue/features/p.cpp b/src/nnue/features/p.cpp similarity index 100% rename from src/eval/nnue/features/p.cpp rename to src/nnue/features/p.cpp diff --git a/src/eval/nnue/features/p.h b/src/nnue/features/p.h similarity index 100% rename from src/eval/nnue/features/p.h rename to src/nnue/features/p.h diff --git a/src/eval/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h similarity index 100% rename from src/eval/nnue/layers/affine_transform.h rename to src/nnue/layers/affine_transform.h diff --git a/src/eval/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h similarity index 100% rename from src/eval/nnue/layers/clipped_relu.h rename to src/nnue/layers/clipped_relu.h diff --git a/src/eval/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h similarity index 100% rename from src/eval/nnue/layers/input_slice.h rename to src/nnue/layers/input_slice.h diff --git a/src/eval/nnue/layers/sum.h b/src/nnue/layers/sum.h similarity index 100% rename from src/eval/nnue/layers/sum.h rename to src/nnue/layers/sum.h diff --git a/src/eval/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h similarity index 100% rename from src/eval/nnue/nnue_accumulator.h rename to src/nnue/nnue_accumulator.h diff --git a/src/eval/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h similarity index 100% rename from src/eval/nnue/nnue_architecture.h rename to src/nnue/nnue_architecture.h diff --git a/src/eval/nnue/nnue_common.h b/src/nnue/nnue_common.h similarity index 100% rename from src/eval/nnue/nnue_common.h rename to src/nnue/nnue_common.h diff --git a/src/eval/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h similarity index 100% rename from src/eval/nnue/nnue_feature_transformer.h rename to src/nnue/nnue_feature_transformer.h diff --git a/src/eval/nnue/nnue_test_command.cpp b/src/nnue/nnue_test_command.cpp similarity index 100% rename from src/eval/nnue/nnue_test_command.cpp rename to src/nnue/nnue_test_command.cpp diff --git a/src/eval/nnue/nnue_test_command.h b/src/nnue/nnue_test_command.h similarity index 100% rename from src/eval/nnue/nnue_test_command.h rename to src/nnue/nnue_test_command.h diff --git a/src/eval/nnue/trainer/features/factorizer.h b/src/nnue/trainer/features/factorizer.h similarity index 100% rename from src/eval/nnue/trainer/features/factorizer.h rename to src/nnue/trainer/features/factorizer.h diff --git a/src/eval/nnue/trainer/features/factorizer_feature_set.h b/src/nnue/trainer/features/factorizer_feature_set.h similarity index 100% rename from src/eval/nnue/trainer/features/factorizer_feature_set.h rename to src/nnue/trainer/features/factorizer_feature_set.h diff --git a/src/eval/nnue/trainer/features/factorizer_half_kp.h b/src/nnue/trainer/features/factorizer_half_kp.h similarity index 100% rename from src/eval/nnue/trainer/features/factorizer_half_kp.h rename to src/nnue/trainer/features/factorizer_half_kp.h diff --git a/src/eval/nnue/trainer/trainer.h b/src/nnue/trainer/trainer.h similarity index 100% rename from src/eval/nnue/trainer/trainer.h rename to src/nnue/trainer/trainer.h diff --git a/src/eval/nnue/trainer/trainer_affine_transform.h b/src/nnue/trainer/trainer_affine_transform.h similarity index 100% rename from src/eval/nnue/trainer/trainer_affine_transform.h rename to src/nnue/trainer/trainer_affine_transform.h diff --git a/src/eval/nnue/trainer/trainer_clipped_relu.h b/src/nnue/trainer/trainer_clipped_relu.h similarity index 100% rename from src/eval/nnue/trainer/trainer_clipped_relu.h rename to src/nnue/trainer/trainer_clipped_relu.h diff --git a/src/eval/nnue/trainer/trainer_feature_transformer.h b/src/nnue/trainer/trainer_feature_transformer.h similarity index 100% rename from src/eval/nnue/trainer/trainer_feature_transformer.h rename to src/nnue/trainer/trainer_feature_transformer.h diff --git a/src/eval/nnue/trainer/trainer_input_slice.h b/src/nnue/trainer/trainer_input_slice.h similarity index 100% rename from src/eval/nnue/trainer/trainer_input_slice.h rename to src/nnue/trainer/trainer_input_slice.h diff --git a/src/eval/nnue/trainer/trainer_sum.h b/src/nnue/trainer/trainer_sum.h similarity index 100% rename from src/eval/nnue/trainer/trainer_sum.h rename to src/nnue/trainer/trainer_sum.h From bf7d02578e94296667f159a46fe5fdc8e2f3b94b Mon Sep 17 00:00:00 2001 From: nodchip Date: Fri, 7 Aug 2020 22:47:45 +0900 Subject: [PATCH 16/45] Fixed build errors. --- src/evaluate.cpp | 2 +- src/learn/learner.cpp | 2 +- src/nnue/evaluate_nnue.cpp | 8 ++++---- src/nnue/evaluate_nnue_learner.cpp | 14 +++++++------- src/nnue/evaluate_nnue_learner.h | 2 +- src/nnue/features/castling_right.h | 2 +- src/nnue/features/enpassant.h | 2 +- src/nnue/features/features_common.h | 2 +- src/nnue/features/half_kp.h | 2 +- src/nnue/features/half_relative_kp.h | 2 +- src/nnue/features/index_list.h | 2 +- src/nnue/features/k.h | 2 +- src/nnue/features/p.h | 2 +- src/nnue/nnue_test_command.cpp | 4 ++-- src/nnue/trainer/trainer_affine_transform.h | 2 +- src/nnue/trainer/trainer_clipped_relu.h | 2 +- src/nnue/trainer/trainer_feature_transformer.h | 2 +- src/nnue/trainer/trainer_input_slice.h | 2 +- src/nnue/trainer/trainer_sum.h | 2 +- src/position.h | 2 +- src/uci.cpp | 2 +- 21 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 12ecff00..384c081d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -30,7 +30,7 @@ #include "material.h" #include "pawns.h" #include "thread.h" -#include "eval/nnue/evaluate_nnue.h" +#include "nnue/evaluate_nnue.h" namespace Trace { diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index e343fde5..aff32ee8 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -81,7 +81,7 @@ #include "multi_think.h" #if defined(EVAL_NNUE) -#include "../eval/nnue/evaluate_nnue_learner.h" +#include "../nnue/evaluate_nnue_learner.h" #include #endif diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 55e627d0..6a664907 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -5,10 +5,10 @@ #include #include -#include "../../evaluate.h" -#include "../../position.h" -#include "../../misc.h" -#include "../../uci.h" +#include "../evaluate.h" +#include "../position.h" +#include "../misc.h" +#include "../uci.h" #include "evaluate_nnue.h" diff --git a/src/nnue/evaluate_nnue_learner.cpp b/src/nnue/evaluate_nnue_learner.cpp index 3297037d..650f443e 100644 --- a/src/nnue/evaluate_nnue_learner.cpp +++ b/src/nnue/evaluate_nnue_learner.cpp @@ -5,15 +5,15 @@ #include #include -#include "../../learn/learn.h" -#include "../../learn/learning_tools.h" +#include "../learn/learn.h" +#include "../learn/learning_tools.h" -#include "../../position.h" -#include "../../uci.h" -#include "../../misc.h" -#include "../../thread_win32_osx.h" +#include "../position.h" +#include "../uci.h" +#include "../misc.h" +#include "../thread_win32_osx.h" -#include "../evaluate_common.h" +#include "../eval/evaluate_common.h" #include "evaluate_nnue.h" #include "evaluate_nnue_learner.h" diff --git a/src/nnue/evaluate_nnue_learner.h b/src/nnue/evaluate_nnue_learner.h index ace66524..1e4a463e 100644 --- a/src/nnue/evaluate_nnue_learner.h +++ b/src/nnue/evaluate_nnue_learner.h @@ -5,7 +5,7 @@ #if defined(EVAL_LEARN) && defined(EVAL_NNUE) -#include "../../learn/learn.h" +#include "../learn/learn.h" namespace Eval { diff --git a/src/nnue/features/castling_right.h b/src/nnue/features/castling_right.h index 709d4688..3af5b074 100644 --- a/src/nnue/features/castling_right.h +++ b/src/nnue/features/castling_right.h @@ -5,7 +5,7 @@ #if defined(EVAL_NNUE) -#include "../../../evaluate.h" +#include "../../evaluate.h" #include "features_common.h" namespace Eval { diff --git a/src/nnue/features/enpassant.h b/src/nnue/features/enpassant.h index 51880bb4..f77f9c4f 100644 --- a/src/nnue/features/enpassant.h +++ b/src/nnue/features/enpassant.h @@ -5,7 +5,7 @@ #if defined(EVAL_NNUE) -#include "../../../evaluate.h" +#include "../../evaluate.h" #include "features_common.h" namespace Eval { diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h index 8d2ca4a2..2fabbd4f 100644 --- a/src/nnue/features/features_common.h +++ b/src/nnue/features/features_common.h @@ -5,7 +5,7 @@ #if defined(EVAL_NNUE) -#include "../../../evaluate.h" +#include "../../evaluate.h" #include "../nnue_common.h" namespace Eval { diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index cc9cd660..d2e8e521 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -5,7 +5,7 @@ #if defined(EVAL_NNUE) -#include "../../../evaluate.h" +#include "../../evaluate.h" #include "features_common.h" namespace Eval { diff --git a/src/nnue/features/half_relative_kp.h b/src/nnue/features/half_relative_kp.h index 2f967745..49b31499 100644 --- a/src/nnue/features/half_relative_kp.h +++ b/src/nnue/features/half_relative_kp.h @@ -5,7 +5,7 @@ #if defined(EVAL_NNUE) -#include "../../../evaluate.h" +#include "../../evaluate.h" #include "features_common.h" namespace Eval { diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h index 39e66a09..0374b19d 100644 --- a/src/nnue/features/index_list.h +++ b/src/nnue/features/index_list.h @@ -5,7 +5,7 @@ #if defined(EVAL_NNUE) -#include "../../../position.h" +#include "../../position.h" #include "../nnue_architecture.h" namespace Eval { diff --git a/src/nnue/features/k.h b/src/nnue/features/k.h index d7a6f4aa..28431010 100644 --- a/src/nnue/features/k.h +++ b/src/nnue/features/k.h @@ -5,7 +5,7 @@ #if defined(EVAL_NNUE) -#include "../../../evaluate.h" +#include "../../evaluate.h" #include "features_common.h" namespace Eval { diff --git a/src/nnue/features/p.h b/src/nnue/features/p.h index 27a944fa..d07acb59 100644 --- a/src/nnue/features/p.h +++ b/src/nnue/features/p.h @@ -5,7 +5,7 @@ #if defined(EVAL_NNUE) -#include "../../../evaluate.h" +#include "../../evaluate.h" #include "features_common.h" namespace Eval { diff --git a/src/nnue/nnue_test_command.cpp b/src/nnue/nnue_test_command.cpp index b0c57d4c..311c5ded 100644 --- a/src/nnue/nnue_test_command.cpp +++ b/src/nnue/nnue_test_command.cpp @@ -2,8 +2,8 @@ #if defined(ENABLE_TEST_CMD) && defined(EVAL_NNUE) -#include "../../thread.h" -#include "../../uci.h" +#include "../thread.h" +#include "../uci.h" #include "evaluate_nnue.h" #include "nnue_test_command.h" diff --git a/src/nnue/trainer/trainer_affine_transform.h b/src/nnue/trainer/trainer_affine_transform.h index f5b208a3..db56c1c0 100644 --- a/src/nnue/trainer/trainer_affine_transform.h +++ b/src/nnue/trainer/trainer_affine_transform.h @@ -5,7 +5,7 @@ #if defined(EVAL_LEARN) && defined(EVAL_NNUE) -#include "../../../learn/learn.h" +#include "../../learn/learn.h" #include "../layers/affine_transform.h" #include "trainer.h" diff --git a/src/nnue/trainer/trainer_clipped_relu.h b/src/nnue/trainer/trainer_clipped_relu.h index 566ed777..fd7b1a07 100644 --- a/src/nnue/trainer/trainer_clipped_relu.h +++ b/src/nnue/trainer/trainer_clipped_relu.h @@ -5,7 +5,7 @@ #if defined(EVAL_LEARN) && defined(EVAL_NNUE) -#include "../../../learn/learn.h" +#include "../../learn/learn.h" #include "../layers/clipped_relu.h" #include "trainer.h" diff --git a/src/nnue/trainer/trainer_feature_transformer.h b/src/nnue/trainer/trainer_feature_transformer.h index 0139d534..97dbeff4 100644 --- a/src/nnue/trainer/trainer_feature_transformer.h +++ b/src/nnue/trainer/trainer_feature_transformer.h @@ -5,7 +5,7 @@ #if defined(EVAL_LEARN) && defined(EVAL_NNUE) -#include "../../../learn/learn.h" +#include "../../learn/learn.h" #include "../nnue_feature_transformer.h" #include "trainer.h" #include "features/factorizer_feature_set.h" diff --git a/src/nnue/trainer/trainer_input_slice.h b/src/nnue/trainer/trainer_input_slice.h index f5b263d3..33e39244 100644 --- a/src/nnue/trainer/trainer_input_slice.h +++ b/src/nnue/trainer/trainer_input_slice.h @@ -5,7 +5,7 @@ #if defined(EVAL_LEARN) && defined(EVAL_NNUE) -#include "../../../learn/learn.h" +#include "../../learn/learn.h" #include "../layers/input_slice.h" #include "trainer.h" diff --git a/src/nnue/trainer/trainer_sum.h b/src/nnue/trainer/trainer_sum.h index 2efdff67..fb5b1532 100644 --- a/src/nnue/trainer/trainer_sum.h +++ b/src/nnue/trainer/trainer_sum.h @@ -5,7 +5,7 @@ #if defined(EVAL_LEARN) && defined(EVAL_NNUE) -#include "../../../learn/learn.h" +#include "../../learn/learn.h" #include "../layers/sum.h" #include "trainer.h" diff --git a/src/position.h b/src/position.h index 725be527..52cc0254 100644 --- a/src/position.h +++ b/src/position.h @@ -32,7 +32,7 @@ #include "misc.h" #include "types.h" -#include "eval/nnue/nnue_accumulator.h" +#include "nnue/nnue_accumulator.h" /// StateInfo struct stores information needed to restore a Position object to diff --git a/src/uci.cpp b/src/uci.cpp index c775f333..3b0a08a9 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -35,7 +35,7 @@ #include "syzygy/tbprobe.h" #if defined(EVAL_NNUE) && defined(ENABLE_TEST_CMD) -#include "eval/nnue/nnue_test_command.h" +#include "nnue/nnue_test_command.h" #endif using namespace std; From 1abae04cebac252c9728f16a997e0aedaf939277 Mon Sep 17 00:00:00 2001 From: nodchip Date: Fri, 7 Aug 2020 23:00:11 +0900 Subject: [PATCH 17/45] Fixed Makefile. --- src/Makefile | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Makefile b/src/Makefile index 2e6c415d..1e07bba4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -40,15 +40,15 @@ 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 \ eval/evaluate_mir_inv_tools.cpp \ - eval/nnue/evaluate_nnue.cpp \ - eval/nnue/evaluate_nnue_learner.cpp \ - eval/nnue/features/half_kp.cpp \ - eval/nnue/features/half_relative_kp.cpp \ - eval/nnue/features/k.cpp \ - eval/nnue/features/p.cpp \ - eval/nnue/features/castling_right.cpp \ - eval/nnue/features/enpassant.cpp \ - eval/nnue/nnue_test_command.cpp \ + nnue/evaluate_nnue.cpp \ + nnue/evaluate_nnue_learner.cpp \ + nnue/features/half_kp.cpp \ + nnue/features/half_relative_kp.cpp \ + nnue/features/k.cpp \ + nnue/features/p.cpp \ + nnue/features/castling_right.cpp \ + nnue/features/enpassant.cpp \ + nnue/nnue_test_command.cpp \ extra/sfen_packer.cpp \ learn/gensfen2019.cpp \ learn/learner.cpp \ @@ -635,12 +635,12 @@ clean: objclean profileclean # clean binaries and objects objclean: - @rm -f $(EXE) *.o ./syzygy/*.o ./learn/*.o ./extra/*.o ./eval/*.o ./eval/nnue/*.o ./eval/nnue/features/*.o + @rm -f $(EXE) *.o ./syzygy/*.o ./learn/*.o ./extra/*.o ./eval/*.o ./nnue/*.o ./nnue/features/*.o # clean auxiliary profiling files profileclean: @rm -rf profdir - @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./learn/*.gcda ./extra/*.gcda ./eval/*.gcda ./eval/nnue/*.gcda ./eval/nnue/features/*.gcda + @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./learn/*.gcda ./extra/*.gcda ./eval/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda @rm -f stockfish.profdata *.profraw default: From dc5af66eadf3cbe3c3ef106657e561c1aa8ac97f Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 8 Aug 2020 08:24:20 +0200 Subject: [PATCH 18/45] Tweak futility pruning depth. STC https://tests.stockfishchess.org/tests/view/5f2d237161e3b6af64881f43 LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 12712 W: 1823 L: 1664 D: 9225 Ptnml(0-2): 122, 1166, 3627, 1313, 128 LTC https://tests.stockfishchess.org/tests/view/5f2d473061e3b6af64881f6f LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 12104 W: 912 L: 788 D: 10404 Ptnml(0-2): 13, 665, 4582, 769, 23 closes https://github.com/official-stockfish/Stockfish/pull/2930 bench: 4271421 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 2f83f4f4..886ed52c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -816,7 +816,7 @@ namespace { // Step 8. Futility pruning: child node (~50 Elo) if ( !PvNode - && depth < 6 + && depth < 8 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; From 5ccff25df2e8fcbee3d4c1428bbc101afa88e700 Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Fri, 7 Aug 2020 11:24:37 +0200 Subject: [PATCH 19/45] Expand outposts to minors shielded by pawns Allow any pawn in front of a minor piece to replace the pawn protection requirement for outposts. +-------+ +-------+ | . . o | | o . . | o Their pawns | . o x | | o . . | x Our pawns | o N . | | x o B | N,B New (reachable) outpost | . . . | | . _ . | _ Reachable square behind a pawn +-------+ +-------+ N outpost B reaches outpost We want outposts to be secured by pawns against major pieces. If a minor is shielded by any pawn from above, it is rarely at the same time protected by our pawn attacks from below. However, the pawn shield in itself offers some degree of protection. A pawn shield will now suffice to replace the pawn protection for the outpost (and reachable outpost) bonus. This effect stacks with the existing "minor behind pawn" bonus. STC https://tests.stockfishchess.org/tests/view/5f2bcd14b3ebe5cbfee85b2c LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 27248 W: 5353 L: 5119 D: 16776 Ptnml(0-2): 462, 3174, 6185, 3274, 529 LTC https://tests.stockfishchess.org/tests/view/5f2bfef5b3ebe5cbfee85b5a LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 99432 W: 12580 L: 12130 D: 74722 Ptnml(0-2): 696, 8903, 30049, 9391, 677 Closes #2935 Bench: 4143673 --- src/evaluate.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d20c7b70..47b84ee6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -345,7 +345,8 @@ namespace { { // Bonus if the piece is on an outpost square or can reach one // Reduced bonus for knights (BadOutpost) if few relevant targets - bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); + bb = OutpostRanks & (attackedBy[Us][PAWN] | shift(pos.pieces(PAWN))) + & ~pe->pawn_attacks_span(Them); Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN); if ( Pt == KNIGHT From f4c27cda1a6874550fcbf6cf991b0b9abe43ff39 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sat, 8 Aug 2020 03:45:08 +0800 Subject: [PATCH 20/45] Reintroduce late irreversible move extension Reintroduce vondele's late irreversible move extension for fortress keeping. This was removed when we only had classical eval. Now that we have the NNUE net, it seems that this is useful again. STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 5352 W: 787 L: 653 D: 3912 Ptnml(0-2): 34, 451, 1579, 571, 41 https://tests.stockfishchess.org/tests/view/5f2dc8ad61e3b6af64881ff0 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 14416 W: 1013 L: 891 D: 12512 Ptnml(0-2): 15, 722, 5623, 822, 26 https://tests.stockfishchess.org/tests/view/5f2e0e3661e3b6af6488201e closes https://github.com/official-stockfish/Stockfish/pull/2936 Bench: 4154696 --- src/search.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 886ed52c..8be96e29 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,6 +1134,12 @@ moves_loop: // When in check, search starts from here // Castling extension if (type_of(move) == CASTLING) extension = 1; + + // Late irreversible move extension + if ( move == ttMove + && pos.rule50_count() > 80 + && (captureOrPromotion || type_of(movedPiece) == PAWN)) + extension = 2; // Add extension to new depth newDepth += extension; From 910f779eb1f432c3f90fc19c7824840e02cac837 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 8 Aug 2020 05:51:26 +0300 Subject: [PATCH 21/45] Do more futility pruning for parent nodes. This patch increases LMRdepth threshold for futility pruning at parent nodes so it can apply more often. With radical change to evaluation approach it seems that search is really far from optimal state, especially it parts that use static evaluation of position. passed STC https://tests.stockfishchess.org/tests/view/5f2da75661e3b6af64881fd0 LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 8744 W: 1305 L: 1156 D: 6283 Ptnml(0-2): 75, 789, 2500, 928, 80 passed LTC https://tests.stockfishchess.org/tests/view/5f2dcb2a61e3b6af64881ff3 LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 17728 W: 1256 L: 1117 D: 15355 Ptnml(0-2): 22, 961, 6774, 1070, 37 Bench: 4067325 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 8be96e29..4a9bd7de 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1028,7 +1028,7 @@ moves_loop: // When in check, search starts from here continue; // Futility pruning: parent node (~5 Elo) - if ( lmrDepth < 6 + if ( lmrDepth < 8 && !ss->inCheck && ss->staticEval + 284 + 188 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] From 23ecf3d5c6ffbcfbe45acd2afcf503929474a4db Mon Sep 17 00:00:00 2001 From: "U-DESKTOP-3900\\Mark" Date: Fri, 7 Aug 2020 19:53:18 -0400 Subject: [PATCH 22/45] simplified and increased threshold to switch between NNUE and classical STC https://tests.stockfishchess.org/tests/view/5f2deb1661e3b6af6488200f LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 10376 W: 1481 L: 1359 D: 7536 Ptnml(0-2): 91, 953, 2981, 1069, 94 LTC: https://tests.stockfishchess.org/html/live_elo.html?5f2e0a0461e3b6af64882019 LLR: 2.99 (-2.94,2.94) {-1.50,0.50} Total: 5040 W: 375 L: 315 D: 4350 Ptnml(0-2): 7, 263, 1926, 311, 13 closes https://github.com/official-stockfish/Stockfish/pull/2934 Bench: 4067325 --- src/evaluate.cpp | 7 +++---- src/search.cpp | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 47b84ee6..1ae6cb3a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -110,7 +110,7 @@ namespace { constexpr Value LazyThreshold1 = Value(1400); constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); - constexpr Value NNUEThreshold = Value(500); + constexpr Value NNUEThreshold = Value(520); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -939,10 +939,9 @@ Value Eval::evaluate(const Position& pos) { if (Eval::useNNUE) { - Value balance = pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK); - balance += 200 * (pos.count(WHITE) - pos.count(BLACK)); + Value v = eg_value(pos.psq_score()); // Take NNUE eval only on balanced positions - if (abs(balance) < NNUEThreshold) + if (abs(v) < NNUEThreshold) return NNUE::evaluate(pos) + Tempo; } return Evaluation(pos).value(); diff --git a/src/search.cpp b/src/search.cpp index 4a9bd7de..4a993b01 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,7 +1134,7 @@ moves_loop: // When in check, search starts from here // Castling extension if (type_of(move) == CASTLING) extension = 1; - + // Late irreversible move extension if ( move == ttMove && pos.rule50_count() > 80 From 2c9075e91966895390bf1e5818a0498ad4e8af7a Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 16:05:05 +0900 Subject: [PATCH 23/45] Fixed Makefile to fix build. --- src/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 79ce61a3..85f33b0f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -57,7 +57,7 @@ SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp OBJS = $(notdir $(SRCS:.cpp=.o)) -VPATH = syzygy:nnue:nnue/features +VPATH = syzygy:nnue:nnue/features:eval:extra:learn ### Establish the operating system name KERNEL = $(shell uname -s) @@ -745,8 +745,8 @@ icc-profile-use: EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \ all -nnue-learn: config-sanity - $(MAKE) CXXFLAGS='$(CXXFLAGS) -DEVAL_LEARN -DEVAL_NNUE -DUSE_EVAL_HASH -DENABLE_TEST_CMD -DUSE_BLAS -I/mingw64/include/OpenBLAS -fopenmp' LDFLAGS='$(LDFLAGS) -lopenblas -fopenmp' build +learn: config-sanity + $(MAKE) CXXFLAGS='$(CXXFLAGS) -DEVAL_LEARN -DEVAL_NNUE -DENABLE_TEST_CMD -DUSE_BLAS -I/mingw64/include/OpenBLAS -fopenmp' LDFLAGS='$(LDFLAGS) -lopenblas -fopenmp' build .depend: -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null From fa649ba1e2b2f6e47a6ece33448f64150db8563a Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 16:17:55 +0900 Subject: [PATCH 24/45] Removed a compile warning. --- src/misc.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/misc.h b/src/misc.h index e51d5f3f..fe5990e0 100644 --- a/src/misc.h +++ b/src/misc.h @@ -158,13 +158,6 @@ extern void sleep(int ms); // Returns a string that represents the current time. (Used for log output when learning evaluation function) std::string now_string(); -// wrapper for end processing on the way -static void my_exit() -{ - sleep(3000); // It is bad to finish before the error message is output, so put wait. - exit(EXIT_FAILURE); -} - // When compiled with gcc/clang such as msys2, Windows Subsystem for Linux, // In C++ std::ifstream, ::read() is a wrapper for that because it is not possible to read and write files larger than 2GB in one shot. // From 2395833c07a40a3cc83e211025fcb05a3014c9ea Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 16:52:18 +0900 Subject: [PATCH 25/45] Re-added commands for training data generator and trainer. --- src/uci.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++++- src/ucioption.cpp | 16 ++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/uci.cpp b/src/uci.cpp index 24073369..b0d7b6f9 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -32,6 +32,10 @@ #include "uci.h" #include "syzygy/tbprobe.h" +#if defined(EVAL_NNUE) && defined(ENABLE_TEST_CMD) +#include "nnue/nnue_test_command.h" +#endif + using namespace std; extern vector setup_bench(const Position&, istream&); @@ -39,8 +43,44 @@ extern vector setup_bench(const Position&, istream&); // FEN string of the initial position, normal chess const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; -namespace { +// Command to automatically generate a game record +#if defined (EVAL_LEARN) +namespace Learner +{ + // Automatic generation of teacher position + void gen_sfen(Position& pos, istringstream& is); + // Learning from the generated game record + void learn(Position& pos, istringstream& is); + +#if defined(GENSFEN2019) + // Automatic generation command of teacher phase under development + void gen_sfen2019(Position& pos, istringstream& is); +#endif + + // A pair of reader and evaluation value. Returned by Learner::search(),Learner::qsearch(). + typedef std::pair > ValueAndPV; + + ValueAndPV qsearch(Position& pos); + ValueAndPV search(Position& pos, int depth_, size_t multiPV = 1, uint64_t nodesLimit = 0); + +} +#endif + +#if defined(EVAL_NNUE) && defined(ENABLE_TEST_CMD) +void test_cmd(Position& pos, istringstream& is) +{ + // Initialize as it may be searched. + Eval::init_NNUE(); + + std::string param; + is >> param; + + if (param == "nnue") Eval::NNUE::TestCommand(pos, is); +} +#endif + +namespace { // position() is called when engine receives the "position" UCI command. // The function sets up the position described in the given FEN string ("fen") // or the starting position ("startpos") and then makes the moves given in the @@ -218,6 +258,43 @@ namespace { } // namespace +// -------------------- +// Call qsearch(),search() directly for testing +// -------------------- + +#if defined(EVAL_LEARN) +void qsearch_cmd(Position& pos) +{ + cout << "qsearch : "; + auto pv = Learner::qsearch(pos); + cout << "Value = " << pv.first << " , " << UCI::value(pv.first) << " , PV = "; + for (auto m : pv.second) + cout << UCI::move(m, false) << " "; + cout << endl; +} + +void search_cmd(Position& pos, istringstream& is) +{ + string token; + int depth = 1; + int multi_pv = (int)Options["MultiPV"]; + while (is >> token) + { + if (token == "depth") + is >> depth; + if (token == "multipv") + is >> multi_pv; + } + + cout << "search depth = " << depth << " , multi_pv = " << multi_pv << " : "; + auto pv = Learner::search(pos, depth, multi_pv); + cout << "Value = " << pv.first << " , " << UCI::value(pv.first) << " , PV = "; + for (auto m : pv.second) + cout << UCI::move(m, false) << " "; + cout << endl; +} + +#endif /// UCI::loop() waits for a command from stdin, parses it and calls the appropriate /// function. Also intercepts EOF from stdin to ensure gracefully exiting if the @@ -274,6 +351,24 @@ 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; +#if defined (EVAL_LEARN) + else if (token == "gensfen") Learner::gen_sfen(pos, is); + else if (token == "learn") Learner::learn(pos, is); + +#if defined (GENSFEN2019) + // Command to generate teacher phase under development + else if (token == "gensfen2019") Learner::gen_sfen2019(pos, is); +#endif + // Command to call qsearch(),search() directly for testing + else if (token == "qsearch") qsearch_cmd(pos); + else if (token == "search") search_cmd(pos, is); + +#endif + +#if defined(EVAL_NNUE) && defined(ENABLE_TEST_CMD) + // test command + else if (token == "test") test_cmd(pos, is); +#endif else sync_cout << "Unknown command: " << cmd << sync_endl; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index faeb78ae..168e73a9 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -80,6 +80,22 @@ void init(OptionsMap& o) { o["SyzygyProbeLimit"] << Option(7, 0, 7); o["Use NNUE"] << Option(false, on_use_NNUE); o["EvalFile"] << Option("nn-9931db908a9b.nnue", on_eval_file); +#ifdef EVAL_NNUE + // When the evaluation function is loaded at the ucinewgame timing, it is necessary to convert the new evaluation function. + // I want to hit the test eval convert command, but there is no new evaluation function + // It ends abnormally before executing this command. + // Therefore, with this hidden option, you can suppress the loading of the evaluation function when ucinewgame, + // Hit the test eval convert command. + o["SkipLoadingEval"] << Option(false); + // how many moves to use a fixed move + // o["BookMoves"] << Option(16, 0, 10000); +#endif +#if defined(EVAL_LEARN) + // When learning the evaluation function, you can change the folder to save the evaluation function. + // Evalsave by default. This folder shall be prepared in advance. + // Automatically dig a folder under this folder like "0/", "1/", ... and save the evaluation function file there. + o["EvalSaveDir"] << Option("evalsave"); +#endif } From ed4d007e3cfb012671e09723c160dbc2e3d98d54 Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 18:21:38 +0900 Subject: [PATCH 26/45] Fixed a bug that the training data generator crahses on memory allocation. --- src/misc.h | 2 +- src/nnue/trainer/trainer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/misc.h b/src/misc.h index fe5990e0..9ea57fa8 100644 --- a/src/misc.h +++ b/src/misc.h @@ -271,7 +271,7 @@ public: template AlignedAllocator(const AlignedAllocator&) {} - T* allocate(std::size_t n) { return (T*)std_aligned_alloc(n * sizeof(T), alignof(T)); } + T* allocate(std::size_t n) { return (T*)std_aligned_alloc(alignof(T), n * sizeof(T)); } void deallocate(T* p, std::size_t n) { std_aligned_free(p); } }; diff --git a/src/nnue/trainer/trainer.h b/src/nnue/trainer/trainer.h index b42cb4fa..4b467041 100644 --- a/src/nnue/trainer/trainer.h +++ b/src/nnue/trainer/trainer.h @@ -111,7 +111,7 @@ IntType Round(double value) { // make_shared with alignment template std::shared_ptr MakeAlignedSharedPtr(ArgumentTypes&&... arguments) { - const auto ptr = new(std_aligned_alloc(sizeof(T), alignof(T))) + const auto ptr = new(std_aligned_alloc(alignof(T), sizeof(T))) T(std::forward(arguments)...); return std::shared_ptr(ptr, AlignedDeleter()); } From 70d88364fe1f0d55474313ad8b2d5a2b7b28e18f Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 18:22:29 +0900 Subject: [PATCH 27/45] Fixed a bug that the training data generation crashes. --- src/learn/learner.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index eaddbb8a..fea25503 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -635,10 +635,10 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id) // cout << pos; auto v = Eval::evaluate(pos); - // evaluate() returns the evaluation value on the turn side, so - // If it's a turn different from root_color, you must invert v and return it. - if (rootColor != pos.side_to_move()) - v = -v; + // evaluate() returns the evaluation value on the turn side, so + // If it's a turn different from root_color, you must invert v and return it. + if (rootColor != pos.side_to_move()) + v = -v; // Rewind. // Is it C++x14, and isn't there even foreach to turn in reverse? @@ -979,7 +979,7 @@ void gen_sfen(Position&, istringstream& is) << " loop_max = " << loop_max << endl << " eval_limit = " << eval_limit << endl << " thread_num (set by USI setoption) = " << thread_num << endl - << " book_moves (set by USI setoption) = " << Options["BookMoves"] << endl + //<< " book_moves (set by USI setoption) = " << Options["BookMoves"] << endl << " random_move_minply = " << random_move_minply << endl << " random_move_maxply = " << random_move_maxply << endl << " random_move_count = " << random_move_count << endl From b0d28ac3abcac8db4708055b380edc49f63637aa Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 18:23:11 +0900 Subject: [PATCH 28/45] Fixed a bug that the training data generation crashes if eval_limit is high. --- src/learn/learner.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index fea25503..85da2ac3 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -634,11 +634,19 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id) // reach leaf // cout << pos; - auto v = Eval::evaluate(pos); + Value v; + if (pos.checkers()) { + // HACK: If a king is checkmated, Eval::evalute(pos) crashes with an + // assertion. To avoid crashes, return value1 instead. + v = value1; + } + else { + v = Eval::evaluate(pos); // evaluate() returns the evaluation value on the turn side, so // If it's a turn different from root_color, you must invert v and return it. if (rootColor != pos.side_to_move()) v = -v; + } // Rewind. // Is it C++x14, and isn't there even foreach to turn in reverse? From 9a0b20d3fc95e66157db121232d9a025ec86f385 Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 18:24:09 +0900 Subject: [PATCH 29/45] Changed to show if NNUE is used in the training data generator. --- src/learn/learner.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index 85da2ac3..b5821395 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -1002,6 +1002,9 @@ void gen_sfen(Position&, istringstream& is) << " save_every = " << save_every << endl << " random_file_name = " << random_file_name << endl; + // Show if the training data generator uses NNUE. + Eval::verify_NNUE(); + // Create and execute threads as many as Options["Threads"]. { SfenWriter sw(output_file_name, thread_num); From 4f94f29f390c958dc8638cfbf9b4f7d75957fcc6 Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 18:38:02 +0900 Subject: [PATCH 30/45] Revert "Fixed a bug that the training data generation crashes if eval_limit is high." This reverts commit b0d28ac3abcac8db4708055b380edc49f63637aa. --- src/learn/learner.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index b5821395..afc82447 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -634,19 +634,11 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id) // reach leaf // cout << pos; - Value v; - if (pos.checkers()) { - // HACK: If a king is checkmated, Eval::evalute(pos) crashes with an - // assertion. To avoid crashes, return value1 instead. - v = value1; - } - else { - v = Eval::evaluate(pos); + auto v = Eval::evaluate(pos); // evaluate() returns the evaluation value on the turn side, so // If it's a turn different from root_color, you must invert v and return it. if (rootColor != pos.side_to_move()) v = -v; - } // Rewind. // Is it C++x14, and isn't there even foreach to turn in reverse? From 22b85810fe42b6d198ff8732c97c0ed84c3784b6 Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 19:04:08 +0900 Subject: [PATCH 31/45] Re-added the code to skip loading a net file. --- src/nnue/evaluate_nnue.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 66e5ff57..a28a4573 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -184,6 +184,13 @@ namespace Eval::NNUE { bool load_eval_file(const std::string& evalFile) { Initialize(); + + if (Options["SkipLoadingEval"]) + { + std::cout << "info string SkipLoadingEval set to true, Net not loaded!" << std::endl; + return true; + } + fileName = evalFile; std::ifstream stream(evalFile, std::ios::binary); From fcd70a3c814a75484bbe57c066b8dc992a9e659e Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 8 Aug 2020 21:00:19 +0900 Subject: [PATCH 32/45] Updated README.md. Bench: 4067325 --- README.md | 8 +++++--- Readme.md | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 73eec1fb..7a237480 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,10 @@ Stockfish NNUE is a port of a shogi neural network named NNUE (efficiently updat ## Training Guide ### Generating Training Data -Use the "no-nnue.nnue-gen-sfen-from-original-eval" binary. The given example is generation in its simplest form. There are more commands. +To generate training data from the classic eval, use gensfen command with setting "Use NNUE" to "false". The given example is generation in its simplest form. There are more commands. ``` uci +setoption name Use NNUE value false setoption name Threads value x setoption name Hash value y setoption name SyzygyPath value path @@ -27,10 +28,11 @@ This will save a file named "generated_kifu.bin" in the same folder as the binar ### Generating Validation Data The process is the same as the generation of training data, except for the fact that you need to set loop to 1 million, because you don't need a lot of validation data. The depth should be the same as before or slightly higher than the depth of the training data. After generation rename the validation data file to val.bin and drop it in a folder named "validationdata" in the same directory to make it easier. ### Training a Completely New Network -Use the "avx2.halfkp_256x2-32-32.nnue-learn.2020-07-11" binary. Create an empty folder named "evalsave" in the same directory as the binaries. +Use the "learn" binary. Create an empty folder named "evalsave" in the same directory as the binaries. ``` uci setoption name SkipLoadingEval value true +setoption name Use NNUE value true setoption name Threads value x isready learn targetdir trainingdata loop 100 batchsize 1000000 use_draw_in_training 1 use_draw_in_validation 1 eta 1 lambda 1 eval_limit 32000 nn_batch_size 1000 newbob_decay 0.5 eval_save_interval 250000000 loss_output_interval 1000000 mirror_percentage 50 validation_set_file_name validationdata\val.bin @@ -42,7 +44,7 @@ Nets get saved in the "evalsave" folder. - lambda is the amount of weight it puts to eval of learning data vs win/draw/loss results. 1 puts all weight on eval, lambda 0 puts all weight on WDL results. ### Reinforcement Learning -If you would like to do some reinforcement learning on your original network, you must first generate training data using the learn binaries. Make sure that your previously trained network is in the eval folder. Use the commands specified above. Make sure `SkipLoadingEval` is set to false so that the data generated is using the neural net's eval by typing the command `uci setoption name SkipLoadingEval value false` before typing the `isready` command. You should aim to generate less positions than the first run, around 1/10 of the number of positions generated in the first run. The depth should be higher as well. You should also do the same for validation data, with the depth being higher than the last run. +If you would like to do some reinforcement learning on your original network, you must first generate training data using the learn binaries with setting `Use NNUE` to true. Make sure that your previously trained network is in the eval folder. Use the commands specified above. Make sure `SkipLoadingEval` is set to false so that the data generated is using the neural net's eval by typing the command `uci setoption name SkipLoadingEval value false` before typing the `isready` command. You should aim to generate less positions than the first run, around 1/10 of the number of positions generated in the first run. The depth should be higher as well. You should also do the same for validation data, with the depth being higher than the last run. After you have generated the training data, you must move it into your training data folder and delete the older data so that the binary does not accidentally train on the same data again. Do the same for the validation data and name it to val-1.bin to make it less confusing. Make sure the evalsave folder is empty. Then, using the same binary, type in the training commands shown above. Do __NOT__ set `SkipLoadingEval` to true, it must be false or you will get a completely new network, instead of a network trained with reinforcement learning. You should also set eval_save_interval to a number that is lower than the amount of positions in your training data, perhaps also 1/10 of the original value. The validation file should be set to the new validation data, not the old data. diff --git a/Readme.md b/Readme.md index 73eec1fb..7a237480 100644 --- a/Readme.md +++ b/Readme.md @@ -9,9 +9,10 @@ Stockfish NNUE is a port of a shogi neural network named NNUE (efficiently updat ## Training Guide ### Generating Training Data -Use the "no-nnue.nnue-gen-sfen-from-original-eval" binary. The given example is generation in its simplest form. There are more commands. +To generate training data from the classic eval, use gensfen command with setting "Use NNUE" to "false". The given example is generation in its simplest form. There are more commands. ``` uci +setoption name Use NNUE value false setoption name Threads value x setoption name Hash value y setoption name SyzygyPath value path @@ -27,10 +28,11 @@ This will save a file named "generated_kifu.bin" in the same folder as the binar ### Generating Validation Data The process is the same as the generation of training data, except for the fact that you need to set loop to 1 million, because you don't need a lot of validation data. The depth should be the same as before or slightly higher than the depth of the training data. After generation rename the validation data file to val.bin and drop it in a folder named "validationdata" in the same directory to make it easier. ### Training a Completely New Network -Use the "avx2.halfkp_256x2-32-32.nnue-learn.2020-07-11" binary. Create an empty folder named "evalsave" in the same directory as the binaries. +Use the "learn" binary. Create an empty folder named "evalsave" in the same directory as the binaries. ``` uci setoption name SkipLoadingEval value true +setoption name Use NNUE value true setoption name Threads value x isready learn targetdir trainingdata loop 100 batchsize 1000000 use_draw_in_training 1 use_draw_in_validation 1 eta 1 lambda 1 eval_limit 32000 nn_batch_size 1000 newbob_decay 0.5 eval_save_interval 250000000 loss_output_interval 1000000 mirror_percentage 50 validation_set_file_name validationdata\val.bin @@ -42,7 +44,7 @@ Nets get saved in the "evalsave" folder. - lambda is the amount of weight it puts to eval of learning data vs win/draw/loss results. 1 puts all weight on eval, lambda 0 puts all weight on WDL results. ### Reinforcement Learning -If you would like to do some reinforcement learning on your original network, you must first generate training data using the learn binaries. Make sure that your previously trained network is in the eval folder. Use the commands specified above. Make sure `SkipLoadingEval` is set to false so that the data generated is using the neural net's eval by typing the command `uci setoption name SkipLoadingEval value false` before typing the `isready` command. You should aim to generate less positions than the first run, around 1/10 of the number of positions generated in the first run. The depth should be higher as well. You should also do the same for validation data, with the depth being higher than the last run. +If you would like to do some reinforcement learning on your original network, you must first generate training data using the learn binaries with setting `Use NNUE` to true. Make sure that your previously trained network is in the eval folder. Use the commands specified above. Make sure `SkipLoadingEval` is set to false so that the data generated is using the neural net's eval by typing the command `uci setoption name SkipLoadingEval value false` before typing the `isready` command. You should aim to generate less positions than the first run, around 1/10 of the number of positions generated in the first run. The depth should be higher as well. You should also do the same for validation data, with the depth being higher than the last run. After you have generated the training data, you must move it into your training data folder and delete the older data so that the binary does not accidentally train on the same data again. Do the same for the validation data and name it to val-1.bin to make it less confusing. Make sure the evalsave folder is empty. Then, using the same binary, type in the training commands shown above. Do __NOT__ set `SkipLoadingEval` to true, it must be false or you will get a completely new network, instead of a network trained with reinforcement learning. You should also set eval_save_interval to a number that is lower than the amount of positions in your training data, perhaps also 1/10 of the original value. The validation file should be set to the new validation data, not the old data. From 450b60a303b0c59b0cc5dd22d95b9a983dfc4f96 Mon Sep 17 00:00:00 2001 From: mckx00 Date: Sat, 8 Aug 2020 03:07:07 -0700 Subject: [PATCH 33/45] Remove unnecessay legality check Possible after the recent reording pos.legal(move) check https://github.com/official-stockfish/Stockfish/pull/2941 No functional change. --- AUTHORS | 1 + src/search.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 07e07297..21ef3e50 100644 --- a/AUTHORS +++ b/AUTHORS @@ -79,6 +79,7 @@ Jean Gauthier (OuaisBla) Jean-Francois Romang (jromang) Jekaa Jerry Donald Watson (jerrydonaldwatson) +jjoshua2 Jonathan Calovski (Mysseno) Jonathan Dumale (SFisGOD) Joost VandeVondele (vondele) diff --git a/src/search.cpp b/src/search.cpp index 4a993b01..e5d18f77 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1079,8 +1079,7 @@ moves_loop: // When in check, search starts from here /* && ttValue != VALUE_NONE Already implicit in the next condition */ && abs(ttValue) < VALUE_KNOWN_WIN && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 3 - && pos.legal(move)) + && tte->depth() >= depth - 3) { Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2; Depth singularDepth = (depth - 1 + 3 * formerPv) / 2; From 3368d0328591b2741ca32e57cfa0a35a7144fdd1 Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Sat, 8 Aug 2020 12:35:34 +0200 Subject: [PATCH 34/45] update Null Move Pruning parameters STC: https://tests.stockfishchess.org/tests/view/5f2dc38561e3b6af64881fec LLR: 2.99 (-2.94,2.94) {-0.50,1.50} Total: 6120 W: 903 L: 758 D: 4459 Ptnml(0-2): 44, 535, 1775, 644, 62 LTC: https://tests.stockfishchess.org/tests/view/5f2dd55f61e3b6af64882003 LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 7424 W: 577 L: 463 D: 6384 Ptnml(0-2): 16, 375, 2824, 473, 24 closes https://github.com/official-stockfish/Stockfish/pull/2942 bench 4107833 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e5d18f77..9cdc7046 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -827,7 +827,7 @@ namespace { && (ss-1)->statScore < 23824 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 33 * depth - 33 * improving + 112 * ttPv + 311 + && ss->staticEval >= beta - 28 * depth - 28 * improving + 94 * ttPv + 200 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From e663bc533020183c0c52eaf877a91422c9c80742 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 8 Aug 2020 17:43:41 +0300 Subject: [PATCH 35/45] Do more aggressive futility pruning for captures This patch lines up with other patches which use better eval to produce more aggressive cutoffs based on static evaluation of position, it allows more aggressive futility pruning for captures - so now we will be producing them with bigger evaluation of position, so more often. passed STC https://tests.stockfishchess.org/tests/view/5f2da79e61e3b6af64881fd2 LLR: 3.87 (-2.94,2.94) {-0.50,1.50} Total: 27256 W: 3809 L: 3593 D: 19854 Ptnml(0-2): 221, 2578, 7830, 2762, 237 passed LTC https://tests.stockfishchess.org/tests/view/5f2df92061e3b6af64882012 LLR: 4.97 (-2.94,2.94) {0.25,1.75} Total: 43624 W: 3095 L: 2820 D: 37709 Ptnml(0-2): 66, 2410, 16608, 2639, 89 closes https://github.com/official-stockfish/Stockfish/pull/2946 Bench: 4272280 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9cdc7046..201cd974 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1055,7 +1055,7 @@ moves_loop: // When in check, search starts from here && !(PvNode && abs(bestValue) < 2) && PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] && !ss->inCheck - && ss->staticEval + 267 + 391 * lmrDepth + && ss->staticEval + 178 + 261 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; From 6d6267c378aa0aa354e203e5025361d9a4e0d449 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Sat, 8 Aug 2020 12:45:10 +0200 Subject: [PATCH 36/45] Parallelize Link Time Optimization for GCC, CLANG and MINGW This patch tries to run multiple LTO threads in parallel, speeding up the build process of optimized builds if the -j make parameter is used. This mitigates the longer linking times of optimized builds since the integration of the NNUE code. Roughly 2x build speedup. I've tried a similar patch some two years ago but it ran into trouble with old compiler versions then. Since we're on the C++17 standard now these old compilers should be obsolete. closes https://github.com/official-stockfish/Stockfish/pull/2943 No functional change. --- src/Makefile | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index 4741e722..cab7a7e5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -282,6 +282,9 @@ ifeq ($(COMP),gcc) ifneq ($(KERNEL),Darwin) LDFLAGS += -Wl,--no-as-needed endif + + gccversion = $(shell $(CXX) --version) + gccisclang = $(findstring clang,$(gccversion)) endif ifeq ($(COMP),mingw) @@ -496,18 +499,28 @@ endif ### needs access to the optimization flags. ifeq ($(optimize),yes) ifeq ($(debug), no) - ifeq ($(comp),$(filter $(comp),gcc clang)) + ifeq ($(comp),clang) + CXXFLAGS += -flto=thin + LDFLAGS += $(CXXFLAGS) + +# GCC and CLANG use different methods for parallelizing LTO and CLANG pretends to be +# GCC on some systems. + else ifeq ($(comp),gcc) + ifeq ($(gccisclang),) CXXFLAGS += -flto + LDFLAGS += $(CXXFLAGS) -flto=jobserver + else + CXXFLAGS += -flto=thin 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 know to work, older might not. # So, only enable it for a cross from Linux by default. - ifeq ($(comp),mingw) + else ifeq ($(comp),mingw) ifeq ($(KERNEL),Linux) CXXFLAGS += -flto - LDFLAGS += $(CXXFLAGS) + LDFLAGS += $(CXXFLAGS) -flto=jobserver endif endif endif @@ -693,7 +706,7 @@ config-sanity: @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" $(EXE): $(OBJS) - $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +$(CXX) -o $@ $(OBJS) $(LDFLAGS) clang-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ From 1949eb8604853e2ad8f85400590e6a1e2ce7e451 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 8 Aug 2020 22:03:37 +0200 Subject: [PATCH 37/45] Singular extension search tweak Tweak depth. STC https://tests.stockfishchess.org/tests/view/5f2d22ec61e3b6af64881f40 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 17984 W: 2603 L: 2441 D: 12940 Ptnml(0-2): 133, 1751, 5094, 1849, 165 LTC https://tests.stockfishchess.org/tests/view/5f2d5a6a61e3b6af64881f7f LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 85808 W: 5956 L: 5621 D: 74231 Ptnml(0-2): 149, 4748, 32785, 5063, 159 closes https://github.com/official-stockfish/Stockfish/pull/2950 fixes two README.md typos: fixes https://github.com/official-stockfish/Stockfish/issues/2932 bench: 4022669 --- README.md | 4 ++-- src/search.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f71a8b34..7b6ddf4c 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Currently, Stockfish has the following UCI options: * #### Use NNUE Toggle between the NNUE and classical evaluation functions. If set to "true", - the network parameters must be availabe to load from file (see also EvalFile). + the network parameters must be available to load from file (see also EvalFile). * #### EvalFile The name of the file of the NNUE evaluation parameters. Depending on the GUI the @@ -138,7 +138,7 @@ Currently, Stockfish has the following UCI options: * #### Debug Log File Write all communication to and from the engine into a text file. -## classical and NNUE evaluation +## Classical and NNUE evaluation Both approaches assign a value to a position that is used in alpha-beta (PVS) search to find the best move. The classical evaluation computes this value as a function diff --git a/src/search.cpp b/src/search.cpp index 201cd974..37e3ff22 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1072,7 +1072,7 @@ 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 >= 6 + if ( depth >= 7 && move == ttMove && !rootNode && !excludedMove // Avoid recursive singular search From add890a10b8fe03e091520cd0af7383615c6c386 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 8 Aug 2020 22:08:40 +0200 Subject: [PATCH 38/45] LMR search tweak All credit to Vizvezdenec, the original author of the idea. STC https://tests.stockfishchess.org/tests/view/5f2d606a61e3b6af64881f88 LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 8440 W: 1191 L: 1048 D: 6201 Ptnml(0-2): 59, 754, 2467, 865, 75 LTC https://tests.stockfishchess.org/tests/view/5f2d84ad61e3b6af64881fbd LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 21896 W: 1557 L: 1406 D: 18933 Ptnml(0-2): 33, 1185, 8378, 1302, 50 closes https://github.com/official-stockfish/Stockfish/pull/2951 bench: 4084753 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 37e3ff22..0a2519b6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,7 +1159,7 @@ moves_loop: // When in check, search starts from here // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 - && moveCount > 1 + 2 * rootNode + && moveCount > 1 + 2 * rootNode + 2 * (PvNode && abs(bestValue) < 2) && (!rootNode || thisThread->best_move_count(move) == 0) && ( !captureOrPromotion || moveCountPruning From d7a26899a973536ab9d3ce4771d8276d1a4dc55c Mon Sep 17 00:00:00 2001 From: Daniel Dugovic Date: Sat, 8 Aug 2020 15:39:29 -0500 Subject: [PATCH 39/45] Use fallback implementation for C++ aligned_alloc fixes https://github.com/official-stockfish/Stockfish/issues/2921 closes https://github.com/official-stockfish/Stockfish/pull/2927 No functional change --- src/Makefile | 4 ++-- src/misc.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Makefile b/src/Makefile index cab7a7e5..b7585a17 100644 --- a/src/Makefile +++ b/src/Makefile @@ -354,8 +354,8 @@ endif endif ifeq ($(KERNEL),Darwin) - CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.15 - LDFLAGS += -arch $(arch) -mmacosx-version-min=10.15 + CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.13 + LDFLAGS += -arch $(arch) -mmacosx-version-min=10.13 endif ### Travis CI script uses COMPILER to overwrite CXX diff --git a/src/misc.cpp b/src/misc.cpp index 3d7c75e5..05f79b45 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -321,9 +321,9 @@ void prefetch(void* addr) { /// void* std_aligned_alloc(size_t alignment, size_t size) { -#if defined(__APPLE__) +#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) ) return aligned_alloc(alignment, size); -#elif defined(_WIN32) +#elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) return _mm_malloc(size, alignment); #else return std::aligned_alloc(alignment, size); @@ -331,9 +331,9 @@ void* std_aligned_alloc(size_t alignment, size_t size) { } void std_aligned_free(void* ptr) { -#if defined(__APPLE__) +#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) ) free(ptr); -#elif defined(_WIN32) +#elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) _mm_free(ptr); #else free(ptr); From 320fa1b2f082a7db67363e468e7e241d7cedcc64 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 9 Aug 2020 11:05:07 +0200 Subject: [PATCH 40/45] Improve error message on missing net. small rewording, but also print the download url for the default net. closes https://github.com/official-stockfish/Stockfish/pull/2954 No functional change --- src/evaluate.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1ae6cb3a..a642357e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -50,9 +50,13 @@ namespace Eval { std::string eval_file = std::string(Options["EvalFile"]); if (useNNUE && eval_file_loaded != eval_file) { - std::cerr << "Use of NNUE evaluation, but the file " << eval_file << " was not loaded successfully. " - << "These network evaluation parameters must be available, compatible with this version of the code. " - << "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file." << std::endl; + UCI::OptionsMap defaults; + UCI::init(defaults); + + std::cerr << "NNUE evaluation used, but the network file " << eval_file << " was not loaded successfully. " + << "These network evaluation parameters must be available, and compatible with this version of the code. " + << "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file. " + << "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/"+std::string(defaults["EvalFile"]) << std::endl; std::exit(EXIT_FAILURE); } From cd1bb27dd452f336d434a45131bfbe43f8a8c5b3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 9 Aug 2020 19:08:47 +0200 Subject: [PATCH 41/45] Fix aligned_alloc on MinGW introduced with d7a26899a973536ab9d3ce4771d8276d1a4dc55c closes https://github.com/official-stockfish/Stockfish/pull/2959 No functional change. --- src/misc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 05f79b45..bdd7bccb 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -321,7 +321,7 @@ void prefetch(void* addr) { /// void* std_aligned_alloc(size_t alignment, size_t size) { -#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) ) +#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) return aligned_alloc(alignment, size); #elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) return _mm_malloc(size, alignment); @@ -331,7 +331,7 @@ void* std_aligned_alloc(size_t alignment, size_t size) { } void std_aligned_free(void* ptr) { -#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) ) +#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) free(ptr); #elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) _mm_free(ptr); From 2bfde5542919c2ed624b5b62883616e325ccb942 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 9 Aug 2020 21:39:46 +0300 Subject: [PATCH 42/45] Adjust NNUE usage based on number of pawns in position The idea of this patch is that positions are usually more complex and hard to evaluate even if there are more pawns. This patch adjusts NNUE threshold usage depending on number of pawns in position, if pawn count is <3 we use the classical evaluation more often, for pawn count = 3 patch the is non-functional, with pawn count > 3 NNUE evaluation is used more often. passed STC https://tests.stockfishchess.org/tests/view/5f2f02d09081672066536b1f LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 36520 W: 5011 L: 4823 D: 26686 Ptnml(0-2): 299, 3482, 10548, 3594, 337 passed LTC https://tests.stockfishchess.org/tests/view/5f2f4c329081672066536b5c LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 39272 W: 2630 L: 2433 D: 34209 Ptnml(0-2): 53, 2066, 15218, 2229, 70 closes https://github.com/official-stockfish/Stockfish/pull/2960 bench 4084753 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a642357e..ce35c630 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,7 +114,7 @@ namespace { constexpr Value LazyThreshold1 = Value(1400); constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); - constexpr Value NNUEThreshold = Value(520); + constexpr Value NNUEThreshold = Value(460); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -945,7 +945,7 @@ Value Eval::evaluate(const Position& pos) { { Value v = eg_value(pos.psq_score()); // Take NNUE eval only on balanced positions - if (abs(v) < NNUEThreshold) + if (abs(v) < NNUEThreshold + 20 * pos.count()) return NNUE::evaluate(pos) + Tempo; } return Evaluation(pos).value(); From a6e89293df5af35931b61d86b6de3872a981c100 Mon Sep 17 00:00:00 2001 From: Dariusz Orzechowski Date: Sun, 9 Aug 2020 14:32:24 -0700 Subject: [PATCH 43/45] Avoid special casing for MinGW after some testing, no version of MinGW/gcc has been found where this code is still necessary. Probably older code (pre-c++17?) closes https://github.com/official-stockfish/Stockfish/pull/2891 No functional change --- src/nnue/layers/affine_transform.h | 29 +++-------------- src/nnue/layers/clipped_relu.h | 49 ++++------------------------- src/nnue/nnue_feature_transformer.h | 34 ++------------------ 3 files changed, 14 insertions(+), 98 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index b585bc87..ecc3008a 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -104,13 +104,8 @@ namespace Eval::NNUE::Layers { __m512i sum = _mm512_setzero_si512(); const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { - - #if defined(__MINGW32__) || defined(__MINGW64__) - __m512i product = _mm512_maddubs_epi16(_mm512_loadu_si512(&input_vector[j]), _mm512_load_si512(&row[j])); - #else - __m512i product = _mm512_maddubs_epi16(_mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j])); - #endif - + __m512i product = _mm512_maddubs_epi16( + _mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j])); product = _mm512_madd_epi16(product, kOnes); sum = _mm512_add_epi32(sum, product); } @@ -125,12 +120,8 @@ namespace Eval::NNUE::Layers { const auto row_256 = reinterpret_cast(&weights_[offset]); int j = kNumChunks * 2; - #if defined(__MINGW32__) || defined(__MINGW64__) // See HACK comment below in AVX2. - __m256i sum256 = _mm256_maddubs_epi16(_mm256_loadu_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); - #else - __m256i sum256 = _mm256_maddubs_epi16(_mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); - #endif - + __m256i sum256 = _mm256_maddubs_epi16( + _mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); sum256 = _mm256_madd_epi16(sum256, _mm256_set1_epi16(1)); sum256 = _mm256_hadd_epi32(sum256, sum256); sum256 = _mm256_hadd_epi32(sum256, sum256); @@ -144,17 +135,7 @@ namespace Eval::NNUE::Layers { const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { __m256i product = _mm256_maddubs_epi16( - - #if defined(__MINGW32__) || defined(__MINGW64__) - // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary - // compiled with g++ in MSYS2 crashes here because the output memory is not aligned - // even though alignas is specified. - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&input_vector[j]), _mm256_load_si256(&row[j])); + _mm256_load_si256(&input_vector[j]), _mm256_load_si256(&row[j])); product = _mm256_madd_epi16(product, kOnes); sum = _mm256_add_epi32(sum, product); } diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 7ade598f..7e5fcf4a 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -74,50 +74,13 @@ namespace Eval::NNUE::Layers { const auto out = reinterpret_cast<__m256i*>(output); for (IndexType i = 0; i < kNumChunks; ++i) { const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( - - #if defined(__MINGW32__) || defined(__MINGW64__) - // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary - // compiled with g++ in MSYS2 crashes here because the output memory is not aligned - // even though alignas is specified. - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 0]), - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 1])), kWeightScaleBits); + _mm256_load_si256(&in[i * 4 + 0]), + _mm256_load_si256(&in[i * 4 + 1])), kWeightScaleBits); const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 2]), - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 3])), kWeightScaleBits); - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_storeu_si256 - #else - _mm256_store_si256 - #endif - - (&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( + _mm256_load_si256(&in[i * 4 + 2]), + _mm256_load_si256(&in[i * 4 + 3])), kWeightScaleBits); + _mm256_store_si256( + &out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( _mm256_packs_epi16(words0, words1), kZero), kOffsets)); } constexpr IndexType kStart = kNumChunks * kSimdWidth; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 1cfebbe4..f899d761 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -110,36 +110,12 @@ namespace Eval::NNUE { auto out = reinterpret_cast<__m256i*>(&output[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { __m256i sum0 = - - #if defined(__MINGW32__) || defined(__MINGW64__) - // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary - // compiled with g++ in MSYS2 crashes here because the output memory is not aligned - // even though alignas is specified. - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&reinterpret_cast( + _mm256_load_si256(&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 0]); __m256i sum1 = - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&reinterpret_cast( + _mm256_load_si256(&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 1]); - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_storeu_si256 - #else - _mm256_store_si256 - #endif - - (&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( + _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( _mm256_packs_epi16(sum0, sum1), kZero), kControl)); } @@ -202,11 +178,7 @@ namespace Eval::NNUE { auto column = reinterpret_cast(&weights_[offset]); constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); for (IndexType j = 0; j < kNumChunks; ++j) { - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_storeu_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadu_si256(&accumulation[j]), column[j])); - #else accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); - #endif } #elif defined(USE_SSE2) From 27b593a94477a821f80a041320683f805114d4a3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 9 Aug 2020 18:11:38 +0200 Subject: [PATCH 44/45] Fix a data race for NNUE the stateInfo at the rootPos is no longer read-only, as the NNUE accumulator is part of it. Threads can thus not share this object and need their own copy. tested for no regression https://tests.stockfishchess.org/tests/view/5f3022239081672066536bce LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 52800 W: 6843 L: 6802 D: 39155 Ptnml(0-2): 336, 4646, 16399, 4679, 340 closes https://github.com/official-stockfish/Stockfish/pull/2957 fixes https://github.com/official-stockfish/Stockfish/issues/2933 No functional change --- src/Makefile | 4 ++-- src/thread.cpp | 13 +++++-------- src/thread.h | 1 + 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Makefile b/src/Makefile index b7585a17..571172b2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -354,8 +354,8 @@ endif endif ifeq ($(KERNEL),Darwin) - CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.13 - LDFLAGS += -arch $(arch) -mmacosx-version-min=10.13 + CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14 + LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14 endif ### Travis CI script uses COMPILER to overwrite CXX diff --git a/src/thread.cpp b/src/thread.cpp index 44aea14e..1aa66a81 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -204,21 +204,18 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, // We use Position::set() to set root position across threads. But there are // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot - // be deduced from a fen string, so set() clears them and to not lose the info - // we need to backup and later restore setupStates->back(). Note that setupStates - // is shared by threads but is accessed in read-only mode. - StateInfo tmp = setupStates->back(); - + // be deduced from a fen string, so set() clears them and they are set from + // setupStates->back() later. The rootState is per thread, earlier states are shared + // since they are read-only. for (Thread* th : *this) { th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; th->rootDepth = th->completedDepth = 0; th->rootMoves = rootMoves; - th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); + th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th); + th->rootState = setupStates->back(); } - setupStates->back() = tmp; - main()->start_searching(); } diff --git a/src/thread.h b/src/thread.h index 46da1e34..042bc2e9 100644 --- a/src/thread.h +++ b/src/thread.h @@ -65,6 +65,7 @@ public: std::atomic nodes, tbHits, bestMoveChanges; Position rootPos; + StateInfo rootState; Search::RootMoves rootMoves; Depth rootDepth, completedDepth; CounterMoveHistory counterMoves; From 4f97d3446de7eb8e5f3d52558dc66ceb54ef8151 Mon Sep 17 00:00:00 2001 From: nodchip Date: Mon, 10 Aug 2020 08:52:34 +0900 Subject: [PATCH 45/45] Cleaned up source code. --- src/Makefile | 2 +- src/learn/multi_think.h | 1 + src/main.cpp | 20 -------------------- src/misc.h | 5 ----- src/nnue/features/feature_set.h | 4 ++-- src/nnue/layers/affine_transform.h | 2 +- src/nnue/layers/clipped_relu.h | 2 +- src/position.h | 4 ++-- src/search.cpp | 12 ++++++++---- src/search.h | 4 ++++ src/tt.cpp | 4 ---- src/uci.cpp | 3 ++- 12 files changed, 22 insertions(+), 41 deletions(-) diff --git a/src/Makefile b/src/Makefile index 2097fecc..59fb90c5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -442,7 +442,7 @@ endif ### 3.6 popcnt ifeq ($(popcnt),yes) ifeq ($(arch),$(filter $(arch),ppc64 armv8-a arm64)) - CXXFLAGS += -DUSE_POPCNT + CXXFLAGS += -DUSE_POPCNT else ifeq ($(comp),icc) CXXFLAGS += -msse3 -DUSE_POPCNT else diff --git a/src/learn/multi_think.h b/src/learn/multi_think.h index 55edb049..c22b7e8d 100644 --- a/src/learn/multi_think.h +++ b/src/learn/multi_think.h @@ -4,6 +4,7 @@ #if defined(EVAL_LEARN) #include +#include #include "../misc.h" #include "../learn/learn.h" diff --git a/src/main.cpp b/src/main.cpp index e8324186..fbad6622 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,15 +18,6 @@ #include -#ifdef _WIN32 -#include - -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#endif - #include "bitboard.h" #include "endgame.h" #include "position.h" @@ -41,17 +32,6 @@ namespace PSQT { } int main(int argc, char* argv[]) { - // Change the current working directory to the binary directory. So that a - // net file path can be specified with a relative path from the binary - // directory. - // TODO(someone): Implement the logic for other OS. -#ifdef _WIN32 - TCHAR filename[_MAX_PATH]; - ::GetModuleFileName(NULL, filename, sizeof(filename) / sizeof(filename[0])); - std::filesystem::path current_path = filename; - current_path.remove_filename(); - std::filesystem::current_path(current_path); -#endif std::cout << engine_info() << std::endl; diff --git a/src/misc.h b/src/misc.h index 9ea57fa8..ecef028f 100644 --- a/src/misc.h +++ b/src/misc.h @@ -19,7 +19,6 @@ #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED -#include #include #include #include @@ -27,12 +26,8 @@ #include #include #include -#ifndef _MSC_VER -#include -#endif #include "types.h" -#include "thread_win32_osx.h" const std::string engine_info(bool to_uci = false); const std::string compiler_info(); diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index c46da462..ec34a486 100644 --- a/src/nnue/features/feature_set.h +++ b/src/nnue/features/feature_set.h @@ -1,4 +1,4 @@ -/* +/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) @@ -171,7 +171,7 @@ namespace Eval::NNUE::Features { } } - // Get a list of indices whose values ​​have changed from the previous one in the feature quantity + // Get a list of indices whose values have changed from the previous one in the feature quantity template static void CollectChangedIndices( const Position& pos, const TriggerEvent trigger, const Color perspective, diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index cdf26cb8..057de8e1 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -67,7 +67,7 @@ namespace Eval::NNUE::Layers { PreviousLayer::GetStructureString() + ")"; } - // Read network parameters + // Read network parameters bool ReadParameters(std::istream& stream) { if (!previous_layer_.ReadParameters(stream)) return false; stream.read(reinterpret_cast(biases_), diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 8c648526..822e60b0 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -193,7 +193,7 @@ namespace Eval::NNUE::Layers { // Make the learning class a friend friend class Trainer; - PreviousLayer previous_layer_; + PreviousLayer previous_layer_; }; } // namespace Eval::NNUE::Layers diff --git a/src/position.h b/src/position.h index 535bf29e..b5dbaf59 100644 --- a/src/position.h +++ b/src/position.h @@ -1,4 +1,4 @@ -/* +/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) @@ -182,7 +182,7 @@ public: // Do not include gamePly in pack. void sfen_pack(PackedSfen& sfen); - // ª It is slow to go through sfen, so I made a function to set packed sfen directly. + // It is slow to go through sfen, so I made a function to set packed sfen directly. // Equivalent to pos.set(sfen_unpack(data),si,th);. // If there is a problem with the passed phase and there is an error, non-zero is returned. // PackedSfen does not include gamePly so it cannot be restored. If you want to set it, specify it with an argument. diff --git a/src/search.cpp b/src/search.cpp index 7c666772..fe1771a3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -992,7 +992,11 @@ moves_loop: // When in check, search starts from here ss->moveCount = ++moveCount; - if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000 && !Limits.silent) + if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000 +#if defined(EVAL_LEARN) + && !Limits.silent +#endif + ) sync_cout << "info depth " << depth << " currmove " << UCI::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl; @@ -2066,10 +2070,10 @@ namespace Learner // Increase the generation of the substitution table for this thread because it is a new search. //TT.new_search(th->thread_id()); - // ↑ If you call new_search here, it may be a loss because you can't use the previous search result. + // If you call new_search here, it may be a loss because you can't use the previous search result. // Do not do this here, but caller should do TT.new_search(th->thread_id()) for each station ... - // →Because we want to avoid reaching the same final diagram, use the substitution table commonly for all threads when generating teachers. + // Because we want to avoid reaching the same final diagram, use the substitution table commonly for all threads when generating teachers. //#endif } } @@ -2259,7 +2263,7 @@ namespace Learner } // Pass PV_is(ok) to eliminate this PV, there may be NULL_MOVE in the middle. - // → PV should not be NULL_MOVE because it is PV + // PV should not be NULL_MOVE because it is PV // MOVE_WIN has never been thrust. (For now) for (Move move : rootMoves[0].pv) { diff --git a/src/search.h b/src/search.h index 0d4e5399..01d8a4c1 100644 --- a/src/search.h +++ b/src/search.h @@ -86,7 +86,9 @@ struct LimitsType { time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); movestogo = depth = mate = perft = infinite = 0; nodes = 0; +#if defined (EVAL_LEARN) silent = false; +#endif } bool use_time_management() const { @@ -97,9 +99,11 @@ struct LimitsType { TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; int movestogo, depth, mate, perft, infinite; int64_t nodes; +#if defined (EVAL_LEARN) // Silent mode that does not output to the screen (for continuous self-play in process) // Do not output PV at this time. bool silent; +#endif }; extern LimitsType Limits; diff --git a/src/tt.cpp b/src/tt.cpp index 54c7f6b9..d494c27d 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -114,9 +114,6 @@ void TranspositionTable::clear() { /// TTEntry t2 if its replace value is greater than that of t2. TTEntry* TranspositionTable::probe(const Key key, bool& found) const { -#if defined(DISABLE_TT) - return found = false, first_entry(0); -#else TTEntry* const tte = first_entry(key); const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster @@ -141,7 +138,6 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { replace = &tte[i]; return found = false, replace; -#endif } diff --git a/src/uci.cpp b/src/uci.cpp index b0d7b6f9..8972cec9 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -81,6 +81,7 @@ void test_cmd(Position& pos, istringstream& is) #endif namespace { + // position() is called when engine receives the "position" UCI command. // The function sets up the position described in the given FEN string ("fen") // or the starting position ("startpos") and then makes the moves given in the @@ -462,4 +463,4 @@ Move UCI::to_move(const Position& pos, string& str) { return m; return MOVE_NONE; -} \ No newline at end of file +}