From a2da83237c378d75f4a8e6be255960657720b3e1 Mon Sep 17 00:00:00 2001 From: Naphthalin <40385638+Naphthalin@users.noreply.github.com> Date: Tue, 13 Aug 2019 15:28:23 +0200 Subject: [PATCH 01/11] update build-cl.cmd (#923) --- build-cl.cmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-cl.cmd b/build-cl.cmd index 762c2c8ac4..785a439629 100644 --- a/build-cl.cmd +++ b/build-cl.cmd @@ -6,7 +6,8 @@ set MSBuild="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBui rem call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 -meson.py build --backend vs2017 --buildtype release ^ +rem change to '-Dblas=true' to also build the blas backend with mkl +meson build --backend vs2017 --buildtype release -Dblas=false ^ -Dmkl_include="C:\Program Files (x86)\IntelSWTools\compilers_and_libraries\windows\mkl\include" ^ -Dmkl_libdirs="C:\Program Files (x86)\IntelSWTools\compilers_and_libraries\windows\mkl\lib\intel64" ^ -Dopencl_libdirs="C:\Program Files (x86)\AMD APP SDK\3.0\lib\x86_64" ^ @@ -28,4 +29,3 @@ cd build %MSBuild% /p:Configuration=Release /p:Platform=x64 ^ /p:PreferredToolArchitecture=x64 lc0@exe.vcxproj ^ /filelogger - From be3ccd7328e5d25510a845e8779b2d7cd7e005ed Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere <38748663+ddobbelaere@users.noreply.github.com> Date: Thu, 15 Aug 2019 09:38:34 +0200 Subject: [PATCH 02/11] Add git short revision to version string. (#920) * Add git short revision to dev version string. * Address review comments. * Updated comment. * Don't let build fail if git is not installed. * Print warning if git is not installed. * Clarified warning. * Address review comments. * Updated handling of submodules + added warning. * Check for dirty working directory. * Extract revision after automatically downloading submodules to prevent 'dirty' flag. * Fix build error. * Print build identifier as message. * Revert "Fix build error." This reverts commit 37cec6fe71e93a88298d0c104f63daa990c8e3b5. * Properly fix build error. --- meson.build | 42 +++++++++++++++++++++++++++++++++++------- src/version.cc | 7 ++++--- src/version.h | 3 ++- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/meson.build b/meson.build index b75c9a241e..f570bb0745 100644 --- a/meson.build +++ b/meson.build @@ -66,20 +66,48 @@ endif gen = generator(protoc, output: ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'], arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/libs/lczero-common', '--cpp_out=@BUILD_DIR@', '@INPUT@']) +# Handle submodules. +git = find_program('git', required: false) if run_command('checkdir.py', 'libs/lczero-common/proto').returncode() != 0 - if run_command('git', 'status').returncode() == 0 - message('updating git submodule libs/lczero-common') - run_command('git', 'submodule', 'update', '--init', '--recursive') + if git.found() + if run_command(git, 'status').returncode() == 0 + message('updating git submodule libs/lczero-common') + run_command(git, 'submodule', 'update', '--init', '--recursive') + else + message('cloning lczero-common.git into libs/lczero-common') + run_command(git, 'clone', '--depth=1', + 'https://github.com/LeelaChessZero/lczero-common.git', + 'libs/lczero-common/') + endif else - message('cloning lczero-common.git into libs/lczero-common') - run_command('git', 'clone', '--depth=1', - 'https://github.com/LeelaChessZero/lczero-common.git', - 'libs/lczero-common/') + error('Please install git to automatically fetch submodules or download the archives manually from GitHub.') endif endif files += gen.process('libs/lczero-common/proto/net.proto', preserve_path_from : meson.current_source_dir() + '/libs/lczero-common/') + +# Extract git short revision. +short_rev = 'unknown' +if git.found() + r = run_command(git, 'rev-parse', '--short', 'HEAD') + if r.returncode() == 0 + # Now let's check if the working directory is clean. + if run_command(git, 'diff-index', '--quiet', 'HEAD').returncode() == 0 + short_rev = r.stdout().strip() + else + short_rev = 'dirty' + warning('Cannot extract valid git short revision from dirty working directory.') + endif + else + warning('Failed to parse short revision. Use git clone instead of downloading the archive from GitHub.') + endif +endif + +# Construct build identifier. +build_identifier = 'git.' + short_rev +add_project_arguments('-DBUILD_IDENTIFIER="' + build_identifier + '"', language : 'cpp') +message('Using build identifier "' + build_identifier + '".') ############################################################################# ## Main files diff --git a/src/version.cc b/src/version.cc index 9c4e2d0138..b460a5ab89 100644 --- a/src/version.cc +++ b/src/version.cc @@ -31,9 +31,10 @@ std::uint32_t GetVersionInt(int major, int minor, int patch) { } std::string GetVersionStr(int major, int minor, int patch, - const std::string& postfix) { + const std::string& postfix, + const std::string& build_id) { auto v = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); - if (postfix.empty()) return v; - return v + "-" + postfix; + if (postfix.empty()) return v + "+" + build_id; + return v + "-" + postfix + "+" + build_id; } diff --git a/src/version.h b/src/version.h index ee07ebb406..cc9eff3108 100644 --- a/src/version.h +++ b/src/version.h @@ -39,4 +39,5 @@ std::uint32_t GetVersionInt(int major = LC0_VERSION_MAJOR, std::string GetVersionStr(int major = LC0_VERSION_MAJOR, int minor = LC0_VERSION_MINOR, int patch = LC0_VERSION_PATCH, - const std::string& postfix = LC0_VERSION_POSTFIX); + const std::string& postfix = LC0_VERSION_POSTFIX, + const std::string& build_id = BUILD_IDENTIFIER); From aabec70fb0e3366b2d53797ba882b4d018c5ad0a Mon Sep 17 00:00:00 2001 From: borg323 <39573933+borg323@users.noreply.github.com> Date: Thu, 15 Aug 2019 13:32:18 +0300 Subject: [PATCH 03/11] minimize max fastmath error considering float32 accuracy (#917) * minimize max fastmath error considering fp32 accuracy * revert to older FastLog2() algorithm to ensure monotonicity --- src/utils/fastmath.h | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/utils/fastmath.h b/src/utils/fastmath.h index c81523e5c8..d54d37decb 100644 --- a/src/utils/fastmath.h +++ b/src/utils/fastmath.h @@ -29,12 +29,16 @@ #include + + namespace lczero { // These stunts are performed by trained professionals, do not try this at home. // Fast approximate log2(x). Does no range checking. -// The approximation used here is log2(2^N*(1+f)) ~ N+f*(1.342671-0.342671*f) -// where N is the integer and f the fractional part, f>=0. +// The approximation used here is log2(2^N*(1+f)) ~ N+f*(1+k-k*f) where N is the +// exponent and f the fraction (mantissa), f>=0. The constant k is used to tune +// the approximation accuracy. In the final version some constants were slightly +// modified for better accuracy with 32 bit floating point math. inline float FastLog2(const float a) { uint32_t tmp; std::memcpy(&tmp, &a, sizeof(float)); @@ -42,17 +46,22 @@ inline float FastLog2(const float a) { tmp = (tmp & 0x7fffff) | (0x7f << 23); float out; std::memcpy(&out, &tmp, sizeof(float)); - return out * (2.028011f - 0.342671f * out) - 128.68534f + expb; + out -= 1.0f; + // Minimize max absolute error. + return out * (1.3465552f - 0.34655523f * out) - 127 + expb; } // Fast approximate 2^x. Does only limited range checking. -// The approximation used here is 2^(N+f) ~ 2^N*(1+f*(0.656366+0.343634*f)) -// where N is the integer and f the fractional part, f>=0. +// The approximation used here is 2^(N+f) ~ 2^N*(1+f*(1-k+k*f)) where N is the +// integer and f the fractional part, f>=0. The constant k is used to tune the +// approximation accuracy. In the final version some constants were slightly +// modified for better accuracy with 32 bit floating point math. inline float FastPow2(const float a) { if (a < -126) return 0.0; int32_t exp = floor(a); float out = a - exp; - out = 1.0f + out * (0.656366f + 0.343634f * out); + // Minimize max relative error. + out = 1.0f + out * (0.6602339f + 0.33976606f * out); int32_t tmp; std::memcpy(&tmp, &out, sizeof(float)); tmp += static_cast(static_cast(exp) << 23); From 8f13ece9698888e5b156c7b2f3d3300832bda004 Mon Sep 17 00:00:00 2001 From: Ed Lee Date: Sat, 17 Aug 2019 15:04:54 -0700 Subject: [PATCH 04/11] Add hidden options for controlling Dirichlet noise Alpha and Epsilon. (#267) * Add hidden options for controlling Dirichlet noise Alpha and Epsilon. * Restore --noise treating as --noise-epsilon=0.25. --- src/mcts/params.cc | 18 +++++++++++++++++- src/mcts/params.h | 8 ++++++-- src/mcts/search.cc | 5 +++-- src/selfplay/tournament.cc | 2 +- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/mcts/params.cc b/src/mcts/params.cc index 1abce86784..3054da5d85 100644 --- a/src/mcts/params.cc +++ b/src/mcts/params.cc @@ -93,6 +93,15 @@ const OptionId SearchParams::kNoiseId{ "engine to discover new ideas during training by exploring moves which are " "known to be bad. Not normally used during play.", 'n'}; +const OptionId SearchParams::kNoiseEpsilonId{ + "noise-epsilon", "DirichletNoiseEpsilon", + "Amount of Dirichlet noise to combine with root priors. This allows the " + "engine to discover new ideas during training by exploring moves which are " + "known to be bad. Not normally used during play."}; +const OptionId SearchParams::kNoiseAlphaId{ + "noise-alpha", "DirichletNoiseAlpha", + "Alpha of Dirichlet noise to control the sharpness of move probabilities. " + "Larger values result in flatter / more evenly distributed values."}; const OptionId SearchParams::kVerboseStatsId{ "verbose-move-stats", "VerboseMoveStats", "Display Q, V, N, U and P values of every move candidate after each move."}; @@ -203,6 +212,8 @@ void SearchParams::Populate(OptionsParser* options) { options->Add(kTemperatureVisitOffsetId, -1000.0f, 1000.0f) = 0.0f; options->Add(kNoiseId) = false; + options->Add(kNoiseEpsilonId, 0.0f, 1.0f) = 0.0f; + options->Add(kNoiseAlphaId, 0.0f, 10000000.0f) = 0.3f; options->Add(kVerboseStatsId) = false; options->Add(kLogLiveStatsId) = false; options->Add(kSmartPruningFactorId, 0.0f, 10.0f) = 1.33f; @@ -228,6 +239,8 @@ void SearchParams::Populate(OptionsParser* options) { options->Add(kKLDGainAverageInterval, 1, 10000000) = 100; options->Add(kMinimumKLDGainPerNode, 0.0f, 1.0f) = 0.0f; + options->HideOption(kNoiseEpsilonId); + options->HideOption(kNoiseAlphaId); options->HideOption(kLogLiveStatsId); } @@ -236,7 +249,10 @@ SearchParams::SearchParams(const OptionsDict& options) kCpuct(options.Get(kCpuctId.GetId())), kCpuctBase(options.Get(kCpuctBaseId.GetId())), kCpuctFactor(options.Get(kCpuctFactorId.GetId())), - kNoise(options.Get(kNoiseId.GetId())), + kNoiseEpsilon(options.Get(kNoiseId.GetId()) + ? 0.25f + : options.Get(kNoiseEpsilonId.GetId())), + kNoiseAlpha(options.Get(kNoiseAlphaId.GetId())), kSmartPruningFactor(options.Get(kSmartPruningFactorId.GetId())), kFpuAbsolute(options.Get(kFpuStrategyId.GetId()) == "absolute"), diff --git a/src/mcts/params.h b/src/mcts/params.h index 14f1189435..a342ca1a10 100644 --- a/src/mcts/params.h +++ b/src/mcts/params.h @@ -70,7 +70,8 @@ class SearchParams { return options_.Get(kTemperatureWinpctCutoffId.GetId()); } - bool GetNoise() const { return kNoise; } + float GetNoiseEpsilon() const { return kNoiseEpsilon; } + float GetNoiseAlpha() const { return kNoiseAlpha; } bool GetVerboseStats() const { return options_.Get(kVerboseStatsId.GetId()); } @@ -112,6 +113,8 @@ class SearchParams { static const OptionId kTemperatureWinpctCutoffId; static const OptionId kTemperatureVisitOffsetId; static const OptionId kNoiseId; + static const OptionId kNoiseEpsilonId; + static const OptionId kNoiseAlphaId; static const OptionId kVerboseStatsId; static const OptionId kLogLiveStatsId; static const OptionId kSmartPruningFactorId; @@ -143,7 +146,8 @@ class SearchParams { const float kCpuct; const float kCpuctBase; const float kCpuctFactor; - const bool kNoise; + const float kNoiseEpsilon; + const float kNoiseAlpha; const float kSmartPruningFactor; const bool kFpuAbsolute; const float kFpuValue; diff --git a/src/mcts/search.cc b/src/mcts/search.cc index 31e70e30fd..613b6f1648 100644 --- a/src/mcts/search.cc +++ b/src/mcts/search.cc @@ -1262,8 +1262,9 @@ void SearchWorker::FetchSingleNodeResult(NodeToProcess* node_to_process, for (auto edge : node->Edges()) edge.edge()->SetP(edge.GetP() * scale); } // Add Dirichlet noise if enabled and at root. - if (params_.GetNoise() && node == search_->root_node_) { - ApplyDirichletNoise(node, 0.25, 0.3); + if (params_.GetNoiseEpsilon() && node == search_->root_node_) { + ApplyDirichletNoise(node, params_.GetNoiseEpsilon(), + params_.GetNoiseAlpha()); } } diff --git a/src/selfplay/tournament.cc b/src/selfplay/tournament.cc index c4b2ecd0c1..01bfd90c9c 100644 --- a/src/selfplay/tournament.cc +++ b/src/selfplay/tournament.cc @@ -97,7 +97,7 @@ void SelfPlayTournament::PopulateOptions(OptionsParser* options) { defaults->Set(SearchParams::kOutOfOrderEvalId.GetId(), false); defaults->Set(SearchParams::kSmartPruningFactorId.GetId(), 0.0f); defaults->Set(SearchParams::kTemperatureId.GetId(), 1.0f); - defaults->Set(SearchParams::kNoiseId.GetId(), true); + defaults->Set(SearchParams::kNoiseEpsilonId.GetId(), 0.25f); defaults->Set(SearchParams::kFpuValueId.GetId(), 0.0f); defaults->Set(SearchParams::kHistoryFillId.GetId(), "no"); defaults->Set(NetworkFactory::kBackendId.GetId(), From db42c607a9e436e8f8934c03c652e47479983d7e Mon Sep 17 00:00:00 2001 From: borg323 <39573933+borg323@users.noreply.github.com> Date: Thu, 22 Aug 2019 20:37:12 +0300 Subject: [PATCH 05/11] more descriptive zip file names (#922) --- appveyor.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a1a7b91103..ac296701a4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,19 +5,19 @@ image: - Visual Studio 2017 environment: matrix: - - NAME: cuda - - NAME: opencl - - NAME: blas + - NAME: gpu-nvidia-cuda + - NAME: gpu-opencl + - NAME: cpu-openblas clone_folder: c:\projects\lc0 install: - cmd: set CUDA=false - cmd: set OPENCL=false - cmd: set BLAS=false - cmd: set GTEST=false -- cmd: IF %NAME%==cuda set CUDA=true -- cmd: IF %NAME%==opencl set OPENCL=true -- cmd: IF %NAME%==blas set BLAS=true -- cmd: IF %NAME%==blas set GTEST=true +- cmd: IF %NAME%==gpu-nvidia-cuda set CUDA=true +- cmd: IF %NAME%==gpu-opencl set OPENCL=true +- cmd: IF %NAME%==cpu-openblas set BLAS=true +- cmd: IF %NAME%==cpu-openblas set GTEST=true - cmd: IF %BLAS%==true IF NOT EXIST C:\cache\OpenBLAS appveyor DownloadFile https://sjeng.org/ftp/OpenBLAS-0.3.3-win-oldthread.zip - cmd: IF %BLAS%==true IF NOT EXIST C:\cache\OpenBLAS 7z x OpenBLAS-0.3.3-win-oldthread.zip -oC:\cache\OpenBLAS - cmd: IF %OPENCL%==true nuget install opencl-nug -Version 0.777.77 -OutputDirectory C:\cache From d5dfef567f67932b6d94aa4d46298a60bce6ad32 Mon Sep 17 00:00:00 2001 From: borg323 <39573933+borg323@users.noreply.github.com> Date: Thu, 29 Aug 2019 22:26:51 +0300 Subject: [PATCH 06/11] to build opencl also check the headers (#921) * also check opencl headers * make opencl_include a string option --- meson.build | 2 +- meson_options.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index f570bb0745..c0f924fba7 100644 --- a/meson.build +++ b/meson.build @@ -339,7 +339,7 @@ if get_option('build_backends') deps += [ opencl_framework ] has_opencl = true - elif opencl_lib.found() + elif opencl_lib.found() and cc.has_header('CL/opencl.h', args: '-I' + get_option('opencl_include')) deps += [ opencl_lib ] has_opencl = true diff --git a/meson_options.txt b/meson_options.txt index 872e2e8a6d..0c783209e4 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -14,9 +14,9 @@ option('openblas_include', description: 'Paths to openblas include directories') option('opencl_include', - type: 'array', - value: ['/usr/include/'], - description: 'Paths to OpenCL include directories') + type: 'string', + value: '/usr/include/', + description: 'Path to OpenCL include directory') option('tensorflow_libdir', type: 'array', From ec1acac569d14afd2efc90c44cea8dd45f62f62c Mon Sep 17 00:00:00 2001 From: fischerandom <7761375+fischerandom@users.noreply.github.com> Date: Tue, 3 Sep 2019 17:31:55 +0200 Subject: [PATCH 07/11] Fix macOS meson.build (#909) --- meson.build | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index c0f924fba7..651e0d4979 100644 --- a/meson.build +++ b/meson.build @@ -235,7 +235,6 @@ if get_option('build_backends') has_blas = true elif get_option('accelerate') and accelerate_lib.found() - includes += include_directories('/System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/Headers') deps += [ accelerate_lib ] has_blas = true @@ -360,7 +359,9 @@ if get_option('build_backends') 'src/neural/shared/winograd_filter.cc', ] - includes += include_directories(get_option('opencl_include')) + if not opencl_framework.found() + includes += include_directories(get_option('opencl_include')) + endif files += opencl_files has_backends = true From bdf1d97cfab9c3d305b80feafafa0213384b4abb Mon Sep 17 00:00:00 2001 From: Ed Lee Date: Fri, 13 Sep 2019 10:35:55 -0700 Subject: [PATCH 08/11] Only short-circuit search with a non-0 SmartPruningFactor. (#809) --- src/mcts/search.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mcts/search.cc b/src/mcts/search.cc index 613b6f1648..68387d3d41 100644 --- a/src/mcts/search.cc +++ b/src/mcts/search.cc @@ -982,7 +982,8 @@ SearchWorker::NodeToProcess SearchWorker::PickNodeToExtend( second_best_edge.Reset(); } - if (is_root_node && possible_moves <= 1 && !search_->limits_.infinite) { + if (is_root_node && possible_moves <= 1 && !search_->limits_.infinite && + params_.GetSmartPruningFactor()) { // If there is only one move theoretically possible within remaining time, // output it. Mutex::Lock counters_lock(search_->counters_mutex_); From 32ec754890c826b473a483ccb7b674408732d6de Mon Sep 17 00:00:00 2001 From: Hace Date: Fri, 13 Sep 2019 20:16:38 +0200 Subject: [PATCH 09/11] verify location of kings and rooks when reading fens (#943) --- src/chess/board.cc | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/chess/board.cc b/src/chess/board.cc index 3c0b6383c8..21f5b9b7a3 100644 --- a/src/chess/board.cc +++ b/src/chess/board.cc @@ -982,16 +982,28 @@ void ChessBoard::SetFromFen(const std::string& fen, int* no_capture_ply, for (char c : castlings) { switch (c) { case 'K': - castlings_.set_we_can_00(); + if (our_king_.as_string() == "e1" && our_pieces_.get(0, 7) && + rooks_.get(0, 7)) { + castlings_.set_we_can_00(); + } break; case 'k': - castlings_.set_they_can_00(); + if (their_king_.as_string() == "e8" && their_pieces_.get(7, 7) && + rooks_.get(7, 7)) { + castlings_.set_they_can_00(); + } break; case 'Q': - castlings_.set_we_can_000(); + if (our_king_.as_string() == "e1" && our_pieces_.get(0, 0) && + rooks_.get(0, 0)) { + castlings_.set_we_can_000(); + } break; case 'q': - castlings_.set_they_can_000(); + if (their_king_.as_string() == "e8" && their_pieces_.get(7, 0) && + rooks_.get(7, 0)) { + castlings_.set_they_can_000(); + } break; default: throw Exception("Bad fen string: " + fen); From 226cc1b45a5d0f26467050390a5a66134e6481f2 Mon Sep 17 00:00:00 2001 From: Alexis Olson Date: Sat, 14 Sep 2019 01:22:03 -0500 Subject: [PATCH 10/11] Transform Q into logit space when determining Q+U best child (#925) * Efficient and informative depth computation. * Update comments and replace tabs with spaces * Certainty Propagation efficient propagation of certainty, two-fold draw scoring, mate display and more. =1 suitable for training =2 for play Currently negabound search depth is one. Improves play in positions with many certain positions (nrear endgame TBs, mates). Sees repetitions faster and scores positions more accurately. * Fixes compiler warnings/errors on -pedantic. * Resolve Merge Conflicts 2 * Merge: unexpected behaviour when go infinite fixed * Speed fix. Reading non-cached parameters was slow. Now using cached version. Increasing threads (e.g. 4 or 6) will get to masters speed now. Further speed fixes (move generator) possible.... * Speed fix. Used reserve in pseudo legal move generation. If compiled with lto, this yields a speed up by 30-50% in backend=random. In order to fully use CP please use 4 threads+. Changed default temporarily to 4 threads with this commit, to collect more scaling data. * Fix for CP=2, CP=2 (default for play) is now more conservative and adds instant play of certain winning moves and avoidance of loosing moves regardless of visits. CP=3 now adds advanced pruning. * Bugfixes, codecleanup minor changes: - exposed depth parameter (0 is no-look-ahead) - only two modes CP=1 for training and CP=2 for play Todo: - change option from int to choiceoption - use info.mate to communicate mate scores * Rename ClearEdge * change build-cuda to latest * use optional info.mate to display mate scores * display 0.00 for tablebase draws when syzygy filtered * Finalize this WIP PR: - Certainty Propagation is a bool option now, just on or off (default = off). - Cleanup code and comments - Threads default = 2, but if certainty propagation is turned on please use 4 threads. * Prefer terminal wins over certain wins, to avoid delaying mate. Prefer certain losses over terminal losses. * basic certainty propagation - part 2 of PR 487 * fix off by one mate count * Make Two Fold Draw Scoring optional to make everybody happy * fixed typos, comments and formatting * fixed more typos and renamed GetCertaintyStatus to GetCertaintyState * ignore .gitignore * Cosmetic update and comment update. * fix .gitignore * remove redundant checks and code formatting * certain moves without children are reset (n_ = 0), only correct root filtered tb scores if drawn (compatible with new option kSyzygyFastPlayId), comment cleanup. * Streamline node Q getter (now as fast as master - works with PR487!), updated all syzygy comments. TODO: evalposition parameters * merge with master2 * Replace parameter by reference in EvalPosition. Use CertaintyRessult (struct of gameresult and trigger) as return value. * Assignment of Q merge conflict solved * Use parent Q as a default score instead of 0 for unvisited pv. * Add trade penalty * DynamicTP Q >= -0.2 * Update params.cc Better default params * PhasedTP PhasedTP at -0.25 winrate to 0. Less TP if losing. * Update params.cc * Update params.cc Some opening randomization on first 2 ply * Disable certain "draw" reporting as it triggers for won positions too. * Update params.cc * Update search.cc help TP have time to work * Update search.cc * Update search.cc * Update params.cc * Update meson.build meson bug fixed in 51 * Update appveyor.yml * Update meson.build * Update meson.build * Update appveyor.yml * Scale U by Logit function of Q Graph can be seen here: https://www.desmos.com/calculator/mncok1dmp3 * Handle limits at -1, 0, 1 * Syntax fix attempt * Fix undeclared identifier * I can't C++ :( * Remove empty clause * Use proper inverse function to rescale to [-1,1] * Optimization using tanh sum property * Move fpu to U term Because we want `tanh(U + logit(Q) - U)` not `tanh(U + logit(Q-fpu))`. * Remove divide by zero logic for simplification Since `tanhU * Q + 1` should never be zero * Adjust fpu term on tanhU * Move fpu inside tanh * Change sign per @jkormu. * Use `FastTanh` per @borg323 suggestion * Define FastTanh outside of loop * Add FastTanh * Remove FastTanh definition Added to fastmath.h instead * GetVisitsToReachU - target and q in logit space * Include fastmath reference * Typo * Add LogitQEnabled parameter * Add LogitQEnabled parameter * Add kLogitQEnabled to options * Add logit_q boolean argument to GetVisitsToReachU * Add LogitQ switch * D -> d * Default LogitQEnabled = true * Insert PR18 logic * Add FastInvSqrt * Update with PR918 logic * Scale by 1-epsilon to avoid infinity Maximum transformed Q is about 9.557. * Default kLogitQEnabledId = true (PR925 on) * Remove tanh inverse GetVisitsToReachU also needs to be updated * Adjust to match tanh removal in search.cc * Replace FastTanh with FastLogit * Simplify with FastLogit * Simplify including FastLogit * Update Q+U verbose stats * Revert verbose text display to "Q+U" Logit is still applied in calculating the value but Nibbler gives me a constant 1.00000 when I change the text. * Merge PR889 * Add hidden options for controlling Dirichlet noise Alpha and Epsilon. (#267) * Add hidden options for controlling Dirichlet noise Alpha and Epsilon. * Restore --noise treating as --noise-epsilon=0.25. * Prefer terminal wins (and avoid loss) when picking best children. * Prefer shorter wins and longer losses. * Prefer mate score over cp when known. * Convert struct to class. * Convert class to EdgeAndNode methods / anonymous function. * Compute terminal distance sorting only for uci info and best move. * Revert "Merge branch 'PR925+PR918' into patch-1" This reverts commit f649672da79c704009bb33b7fb1b3dde89fd9568, reversing changes made to 06fa9089e0095c020ca09d294e9d1421b7015981. * Revert "Merge branch 'master' into patch-1" This reverts commit 00d256330c779a34966651d5bd15f5526929ad68, reversing changes made to 4d7598857f8d4cf6d67cb2efe2b720a4e1f197ac. * Separate logit logic from old logic * Remove namespace wrappers * Lambda functions * Add semicolons * Try inline functions * Simplify * Handle N == 0 separately * Update to match search.cc better Do we need to worry about fpu here? * Syntax: arrow to dot * 0.5 float * 0.99999994 float * 0.99999994 float * Logit Q overridden to be false * Simplify nested ternary clauses * Stick logit logic into GetQ * Make 2nd GetQ argument optional defaulted to false * Use new GetQ with logit boolean argument * Missing semicolon * Removed extra parenthesis * Fix GetQ in sort section * Another missing semicolon * Use already defined logit_q variable * Shorten parameter name and update description * Shorten LogitQ parameter name * Shorten LogitQ parameter name * Shorten LogitQ parameter name * Shorten LogitQ parameter name * Edit and comment on scaling constant --- src/mcts/node.h | 12 ++++++++---- src/mcts/params.cc | 6 ++++++ src/mcts/params.h | 3 +++ src/mcts/search.cc | 19 ++++++++++++------- src/selfplay/tournament.cc | 1 + src/utils/fastmath.h | 5 +++++ 6 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/mcts/node.h b/src/mcts/node.h index 24488fa5d7..d77c2544d7 100644 --- a/src/mcts/node.h +++ b/src/mcts/node.h @@ -37,6 +37,7 @@ #include "chess/position.h" #include "neural/encoder.h" #include "neural/writer.h" +#include "utils/fastmath.h" #include "utils/mutex.h" namespace lczero { @@ -336,8 +337,11 @@ class EdgeAndNode { Node* node() const { return node_; } // Proxy functions for easier access to node/edge. - float GetQ(float default_q) const { - return (node_ && node_->GetN() > 0) ? node_->GetQ() : default_q; + float GetQ(float default_q, bool logit_q = false) const { + return (node_ && node_->GetN() > 0) ? + // Scale Q slightly to avoid logit(1) = infinity + (logit_q ? FastLogit(0.9999999f * node_->GetQ()) : node_->GetQ()) : + default_q; } float GetD() const { return (node_ && node_->GetN() > 0) ? node_->GetD() : 0.0f; @@ -363,8 +367,8 @@ class EdgeAndNode { } int GetVisitsToReachU(float target_score, float numerator, - float default_q) const { - const auto q = GetQ(default_q); + float default_q, bool logit_q) const { + const auto q = GetQ(default_q, logit_q); if (q >= target_score) return std::numeric_limits::max(); const auto n1 = GetNStarted() + 1; return std::max( diff --git a/src/mcts/params.cc b/src/mcts/params.cc index 3054da5d85..1a229edf75 100644 --- a/src/mcts/params.cc +++ b/src/mcts/params.cc @@ -49,6 +49,10 @@ const OptionId SearchParams::kMaxPrefetchBatchId{ "When the engine cannot gather a large enough batch for immediate use, try " "to prefetch up to X positions which are likely to be useful soon, and put " "them into cache."}; +const OptionId SearchParams::kLogitQId{ + "logit-q", "LogitQ", + "Apply logit to Q when determining Q+U best child. This makes the U term " + "less dominant when Q is near -1 or +1."}; const OptionId SearchParams::kCpuctId{ "cpuct", "CPuct", "cpuct_init constant from \"UCT search\" algorithm. Higher values promote " @@ -201,6 +205,7 @@ void SearchParams::Populate(OptionsParser* options) { // Many of them are overridden with training specific values in tournament.cc. options->Add(kMiniBatchSizeId, 1, 1024) = 256; options->Add(kMaxPrefetchBatchId, 0, 1024) = 32; + options->Add(kLogitQId) = true; options->Add(kCpuctId, 0.0f, 100.0f) = 3.0f; options->Add(kCpuctBaseId, 1.0f, 1000000000.0f) = 19652.0f; options->Add(kCpuctFactorId, 0.0f, 1000.0f) = 2.0f; @@ -246,6 +251,7 @@ void SearchParams::Populate(OptionsParser* options) { SearchParams::SearchParams(const OptionsDict& options) : options_(options), + kLogitQ(options.Get(kLogitQId.GetId())), kCpuct(options.Get(kCpuctId.GetId())), kCpuctBase(options.Get(kCpuctBaseId.GetId())), kCpuctFactor(options.Get(kCpuctFactorId.GetId())), diff --git a/src/mcts/params.h b/src/mcts/params.h index a342ca1a10..b5b448d4e4 100644 --- a/src/mcts/params.h +++ b/src/mcts/params.h @@ -48,6 +48,7 @@ class SearchParams { int GetMaxPrefetchBatch() const { return options_.Get(kMaxPrefetchBatchId.GetId()); } + bool GetLogitQ() const { return kLogitQ; } float GetCpuct() const { return kCpuct; } float GetCpuctBase() const { return kCpuctBase; } float GetCpuctFactor() const { return kCpuctFactor; } @@ -103,6 +104,7 @@ class SearchParams { // Search parameter IDs. static const OptionId kMiniBatchSizeId; static const OptionId kMaxPrefetchBatchId; + static const OptionId kLogitQId; static const OptionId kCpuctId; static const OptionId kCpuctBaseId; static const OptionId kCpuctFactorId; @@ -143,6 +145,7 @@ class SearchParams { // 2. Parameter has to stay the say during the search. // TODO(crem) Some of those parameters can be converted to be dynamic after // trivial search optimiations. + const bool kLogitQ; const float kCpuct; const float kCpuctBase; const float kCpuctFactor; diff --git a/src/mcts/search.cc b/src/mcts/search.cc index 68387d3d41..4f721a205b 100644 --- a/src/mcts/search.cc +++ b/src/mcts/search.cc @@ -212,16 +212,19 @@ std::vector Search::GetVerboseStats(Node* node, const float fpu = GetFpu(params_, node, node == root_node_); const float cpuct = ComputeCpuct(params_, node->GetN()); const float U_coeff = - cpuct * std::sqrt(std::max(node->GetChildrenVisits(), 1u)); + cpuct * std::sqrt(std::max(node->GetChildrenVisits(), 1u)); + const bool logit_q = params_.GetLogitQ(); std::vector edges; for (const auto& edge : node->Edges()) edges.push_back(edge); std::sort( edges.begin(), edges.end(), - [&fpu, &U_coeff](EdgeAndNode a, EdgeAndNode b) { - return std::forward_as_tuple(a.GetN(), a.GetQ(fpu) + a.GetU(U_coeff)) < - std::forward_as_tuple(b.GetN(), b.GetQ(fpu) + b.GetU(U_coeff)); + [&fpu, &U_coeff, &logit_q](EdgeAndNode a, EdgeAndNode b) { + return std::forward_as_tuple( + a.GetN(), a.GetQ(fpu, logit_q) + a.GetU(U_coeff)) < + std::forward_as_tuple( + b.GetN(), b.GetQ(fpu, logit_q) + b.GetU(U_coeff)); }); std::vector infos; @@ -250,7 +253,8 @@ std::vector Search::GetVerboseStats(Node* node, << ") "; oss << "(Q+U: " << std::setw(8) << std::setprecision(5) - << edge.GetQ(fpu) + edge.GetU(U_coeff) << ") "; + << edge.GetQ(fpu, logit_q) + edge.GetU(U_coeff) + << ") "; oss << "(V: "; optional v; @@ -955,7 +959,7 @@ SearchWorker::NodeToProcess SearchWorker::PickNodeToExtend( } ++possible_moves; } - const float Q = child.GetQ(fpu); + const float Q = child.GetQ(fpu, params_.GetLogitQ()); const float score = child.GetU(puct_mult) + Q; if (score > best) { second_best = best; @@ -970,7 +974,8 @@ SearchWorker::NodeToProcess SearchWorker::PickNodeToExtend( if (second_best_edge) { int estimated_visits_to_change_best = - best_edge.GetVisitsToReachU(second_best, puct_mult, fpu); + best_edge.GetVisitsToReachU(second_best, puct_mult, fpu, + params_.GetLogitQ()); // Only cache for n-2 steps as the estimate created by GetVisitsToReachU // has potential rounding errors and some conservative logic that can push // it up to 2 away from the real value. diff --git a/src/selfplay/tournament.cc b/src/selfplay/tournament.cc index 01bfd90c9c..dea95279ea 100644 --- a/src/selfplay/tournament.cc +++ b/src/selfplay/tournament.cc @@ -103,6 +103,7 @@ void SelfPlayTournament::PopulateOptions(OptionsParser* options) { defaults->Set(NetworkFactory::kBackendId.GetId(), "multiplexing"); defaults->Set(SearchParams::kStickyEndgamesId.GetId(), false); + defaults->Set(SearchParams::kLogitQId.GetId(), false); } SelfPlayTournament::SelfPlayTournament(const OptionsDict& options, diff --git a/src/utils/fastmath.h b/src/utils/fastmath.h index d54d37decb..9d1e2cd3fe 100644 --- a/src/utils/fastmath.h +++ b/src/utils/fastmath.h @@ -77,4 +77,9 @@ inline float FastLog(const float a) { // Fast approximate exp(x). Does only limited range checking. inline float FastExp(const float a) { return FastPow2(1.442695040f * a); } +// Fast logit for more readable code. +inline float FastLogit(const float a) { + return 0.5f * FastLog((1.0f + a) / (1.0f - a)); +} + } // namespace lczero From 90124a1f516e3f5b1a1036346ff1303c0bd65b3c Mon Sep 17 00:00:00 2001 From: Alexander Lyashuk Date: Sat, 14 Sep 2019 10:56:09 +0200 Subject: [PATCH 11/11] Disable logit Q+U by default. (#947) --- src/mcts/node.h | 13 +++++++------ src/mcts/params.cc | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/mcts/node.h b/src/mcts/node.h index d77c2544d7..280b44f8b6 100644 --- a/src/mcts/node.h +++ b/src/mcts/node.h @@ -338,10 +338,11 @@ class EdgeAndNode { // Proxy functions for easier access to node/edge. float GetQ(float default_q, bool logit_q = false) const { - return (node_ && node_->GetN() > 0) ? - // Scale Q slightly to avoid logit(1) = infinity - (logit_q ? FastLogit(0.9999999f * node_->GetQ()) : node_->GetQ()) : - default_q; + return (node_ && node_->GetN() > 0) + ? + // Scale Q slightly to avoid logit(1) = infinity. + (logit_q ? FastLogit(0.9999999f * node_->GetQ()) : node_->GetQ()) + : default_q; } float GetD() const { return (node_ && node_->GetN() > 0) ? node_->GetD() : 0.0f; @@ -366,8 +367,8 @@ class EdgeAndNode { return numerator * GetP() / (1 + GetNStarted()); } - int GetVisitsToReachU(float target_score, float numerator, - float default_q, bool logit_q) const { + int GetVisitsToReachU(float target_score, float numerator, float default_q, + bool logit_q) const { const auto q = GetQ(default_q, logit_q); if (q >= target_score) return std::numeric_limits::max(); const auto n1 = GetNStarted() + 1; diff --git a/src/mcts/params.cc b/src/mcts/params.cc index 1a229edf75..6b2522370c 100644 --- a/src/mcts/params.cc +++ b/src/mcts/params.cc @@ -205,7 +205,7 @@ void SearchParams::Populate(OptionsParser* options) { // Many of them are overridden with training specific values in tournament.cc. options->Add(kMiniBatchSizeId, 1, 1024) = 256; options->Add(kMaxPrefetchBatchId, 0, 1024) = 32; - options->Add(kLogitQId) = true; + options->Add(kLogitQId) = false; options->Add(kCpuctId, 0.0f, 100.0f) = 3.0f; options->Add(kCpuctBaseId, 1.0f, 1000000000.0f) = 19652.0f; options->Add(kCpuctFactorId, 0.0f, 1000.0f) = 2.0f;