From 754fc8a8b5ca7466926d54465eeb1df4d4a481ac Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 19 May 2021 01:24:51 +0200 Subject: [PATCH] Remove Tempo The Tempo variable was introduced 10 years ago in our search because the classical evaluation function was antisymmetrical in White and Black by design to gain speed: Eval(White to play) = -Eval(Black to play) Nowadays our neural networks know which side is to play in a position when they evaluate a position and are trained on real games, so the neural network encodes the advantage of moving as an output of search. This patch shows that the Tempo variable is not necessary anymore. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 33512 W: 2805 L: 2709 D: 27998 Ptnml(0-2): 80, 2209, 12095, 2279, 93 https://tests.stockfishchess.org/tests/view/60a44ceace8ea25a3ef03d30 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 53920 W: 1807 L: 1760 D: 50353 Ptnml(0-2): 16, 1617, 23650, 1658, 19 https://tests.stockfishchess.org/tests/view/60a477f0ce8ea25a3ef03d49 We also tried a match (20000 games) at STC using purely classical, result was neutral: https://tests.stockfishchess.org/tests/view/60a4eebcce8ea25a3ef03db5 Note: there are two locations left in search.cpp where we assume antisymmetry of evaluation (in relation with a speed optimization for null moves in lines 770 and 1439), but as the values are just used for heuristic pruning this approximation should not hurt too much because the order of magnitude is still true most of the time. closes https://github.com/official-stockfish/Stockfish/pull/3481 Bench: 4015864 --- src/evaluate.cpp | 4 ++-- src/search.cpp | 6 +++--- src/timeman.cpp | 8 -------- src/timeman.h | 1 - src/types.h | 1 - 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 256bd994..543644ee 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1058,7 +1058,7 @@ make_v: v = (v / 16) * 16; // Side to move point of view - v = (pos.side_to_move() == WHITE ? v : -v) + Tempo; + v = (pos.side_to_move() == WHITE ? v : -v); return v; } @@ -1119,7 +1119,7 @@ Value Eval::evaluate(const Position& pos) { int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE; + Value nnue = NNUE::evaluate(pos) * scale / 1024; if (pos.is_chess960()) nnue += fix_FRC(pos); diff --git a/src/search.cpp b/src/search.cpp index 6e1d2b53..359a774f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -767,7 +767,7 @@ namespace { if ((ss-1)->currentMove != MOVE_NULL) ss->staticEval = eval = evaluate(pos); else - ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo; + ss->staticEval = eval = -(ss-1)->staticEval; // Save static evaluation into transposition table tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); @@ -776,7 +776,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval - 2 * Tempo), -1000, 1000); + int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval), -1000, 1000); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } @@ -1436,7 +1436,7 @@ moves_loop: // When in check, search starts from here // and addition of two tempos ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) - : -(ss-1)->staticEval + 2 * Tempo; + : -(ss-1)->staticEval; // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) diff --git a/src/timeman.cpp b/src/timeman.cpp index 3236b6e9..f742d1e4 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -94,14 +94,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { optimumTime = TimePoint(optScale * timeLeft); maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); - if (Stockfish::Search::Limits.use_time_management()) - { - int strength = std::log( std::max(1, int(optimumTime * Threads.size() / 10))) * 60; - tempoNNUE = std::clamp( (strength + 264) / 24, 18, 30); - } - else - tempoNNUE = 28; // default for no time given - if (Options["Ponder"]) optimumTime += optimumTime / 4; } diff --git a/src/timeman.h b/src/timeman.h index 4ac0b4be..b1878d65 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -37,7 +37,6 @@ public: TimePoint(Threads.nodes_searched()) : now() - startTime; } int64_t availableNodes; // When in 'nodes as time' mode - int tempoNNUE; private: TimePoint startTime; diff --git a/src/types.h b/src/types.h index efebce1a..0bd4a1c4 100644 --- a/src/types.h +++ b/src/types.h @@ -191,7 +191,6 @@ enum Value : int { BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, QueenValueMg = 2538, QueenValueEg = 2682, - Tempo = 28, MidgameLimit = 15258, EndgameLimit = 3915 }; -- 2.39.2