Skip to content

Commit 3ed4fad

Browse files
authored
Add files via upload
1 parent fe1202d commit 3ed4fad

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+10813
-0
lines changed

src/analyzer/analyzer.cc

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
/*
2+
This file is part of Leela Chess Zero.
3+
Copyright (C) 2018 The LCZero Authors
4+
5+
Leela Chess is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
Leela Chess is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with Leela Chess. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#include "analyzer/analyzer.h"
20+
#include "analyzer/table.h"
21+
#include "mcts/search.h"
22+
#include "neural/factory.h"
23+
#include "neural/loader.h"
24+
#include "utils/optionsparser.h"
25+
#include "utils/string.h"
26+
27+
#include <algorithm>
28+
#include <cmath>
29+
#include <iostream>
30+
#include <sstream>
31+
#include <string>
32+
#include <vector>
33+
34+
namespace lczero {
35+
namespace {
36+
const char* kTsvReportStr = "Filename of the tab-separated report file";
37+
const char* kTxtReportStr = "Filename of the text report file";
38+
const char* kMovesStr = "Moves in UCI format, space separated";
39+
const char* kMovesToAnalyzeStr = "Number of (last) moves to analyze";
40+
const char* kNodesStr = "(comma separated) How many nodes to calculate";
41+
const char* kTrainExamplesStr =
42+
"How many examples of training data to generate";
43+
const char* kWeightsStr = "Network weights file path";
44+
const char* kNnBackendStr = "NN backend to use";
45+
const char* kNnBackendOptionsStr = "NN backend parameters";
46+
47+
const char* kAutoDiscover = "<autodiscover>";
48+
} // namespace
49+
50+
Analyzer::Analyzer() {
51+
options_parser_.AddContext("play");
52+
options_parser_.AddContext("training");
53+
play_options_ = &options_parser_.GetOptionsDict("play");
54+
play_options_ = &options_parser_.GetOptionsDict("training");
55+
56+
options_parser_.Add<StringOption>(kTsvReportStr, "tsv-report");
57+
options_parser_.Add<StringOption>(kTxtReportStr, "txt-report");
58+
options_parser_.Add<StringOption>(kMovesStr, "moves");
59+
options_parser_.Add<IntOption>(kMovesToAnalyzeStr, 1, 999, "num-moves") = 4;
60+
options_parser_.Add<StringOption>(kNodesStr, "nodes-list") =
61+
"10,50,100,200,400,600,800,1200,1600,5000,10000";
62+
options_parser_.Add<IntOption>(kTrainExamplesStr, 1, 999,
63+
"training-examples") = 10;
64+
options_parser_.Add<StringOption>(kWeightsStr, "weights", 'w') =
65+
kAutoDiscover;
66+
67+
// TODO(mooskagh) Move all that to network factory.
68+
const auto backends = NetworkFactory::Get()->GetBackendsList();
69+
options_parser_.Add<ChoiceOption>(kNnBackendStr, backends, "backend") =
70+
backends.empty() ? "<none>" : backends[0];
71+
options_parser_.Add<StringOption>(kNnBackendOptionsStr, "backend-opts");
72+
73+
Search::PopulateUciParams(&options_parser_);
74+
75+
// Overriding default options of search.
76+
auto defaults = options_parser_.GetMutableDefaultsOptions();
77+
defaults->Set<int>(Search::kMiniBatchSizeStr, 1); // Minibatch = 1
78+
defaults->Set<bool>(Search::kSmartPruningStr, false); // No smart pruning
79+
defaults->Set<bool>(Search::kVerboseStatsStr, true); // Verbose stats
80+
}
81+
82+
void Analyzer::GatherStats(Table3d* table, const Node* root_node,
83+
std::string& col, bool flip) {
84+
uint64_t total_n = 0;
85+
float factor = play_options_->Get<float>(Search::kCpuctStr) *
86+
std::sqrt(std::max(root_node->GetN(), 1u));
87+
const float parent_q =
88+
-root_node->GetQ(0) - play_options_->Get<float>(Search::kFpuReductionStr);
89+
90+
for (Node* node : root_node->Children()) {
91+
const auto n = node->GetNStarted();
92+
total_n += n;
93+
const auto u = factor * node->GetU();
94+
const auto q = node->GetQ(parent_q);
95+
const auto move = node->GetMove(flip).as_string();
96+
table->Add3dVal(col, move, "N", std::to_string(n));
97+
table->Add3dVal(col, move, "U", std::to_string(u));
98+
table->Add3dVal(col, move, "Q", std::to_string(q));
99+
table->Add3dVal(col, move, "U+Q", std::to_string(u + q));
100+
}
101+
102+
for (Node* node : root_node->Children()) {
103+
auto n = node->GetNStarted();
104+
table->Add3dVal(col, node->GetMove(flip).as_string(), "N%",
105+
std::to_string(static_cast<double>(n) / total_n));
106+
}
107+
}
108+
109+
void Analyzer::RunOnePosition(const std::vector<Move>& moves) {
110+
NNCache cache(200000);
111+
NodeTree tree;
112+
tree.ResetToPosition(ChessBoard::kStartingFen, moves);
113+
114+
auto nodeses = ParseIntList(play_options_->Get<std::string>(kNodesStr));
115+
std::sort(nodeses.begin(), nodeses.end());
116+
117+
Table3d table;
118+
std::vector<std::string> cols;
119+
120+
// Run search in increasing number of nodes.
121+
for (int nodes : nodeses) {
122+
WriteToLog("Nodes: " + std::to_string(nodes));
123+
SearchLimits limits;
124+
limits.visits = nodes;
125+
126+
Search search(tree, network_.get(),
127+
std::bind(&Analyzer::OnBestMove, this, std::placeholders::_1),
128+
std::bind(&Analyzer::OnInfo, this, std::placeholders::_1),
129+
limits, *play_options_, &cache);
130+
131+
search.RunBlocking(1);
132+
133+
auto col = std::to_string(nodes);
134+
cols.push_back(col);
135+
GatherStats(&table, tree.GetCurrentHead(), col, tree.IsBlackToMove());
136+
table.AddColVal(col, "bestmove", search.GetBestMove().first.as_string());
137+
}
138+
139+
// Fetch MCTS-agnostic per-move stats P and V.
140+
std::vector<const Node*> nodes;
141+
for (Node* iter : tree.GetCurrentHead()->Children()) {
142+
nodes.emplace_back(iter);
143+
}
144+
std::sort(nodes.begin(), nodes.end(), [](const Node* a, const Node* b) {
145+
return a->GetNStarted() > b->GetNStarted();
146+
});
147+
std::vector<std::string> rows;
148+
for (const Node* node : nodes) {
149+
auto move = node->GetMove(tree.IsBlackToMove()).as_string();
150+
rows.push_back(move);
151+
table.AddRowVal(move, "P", std::to_string(node->GetP()));
152+
table.AddRowVal(move, "V", std::to_string(node->GetV()));
153+
}
154+
155+
// Dump table to log.
156+
auto lines = table.RenderTable(cols, rows, {"N", "N%", "U", "Q", "U+Q"},
157+
{"P", "V"}, {"bestmove"});
158+
for (const auto& line : lines) WriteToTsvLog(line);
159+
}
160+
161+
void Analyzer::Run() {
162+
if (!options_parser_.ProcessAllFlags()) return;
163+
164+
// Open log files.
165+
if (!play_options_->Get<std::string>(kTxtReportStr).empty()) {
166+
log_.open(play_options_->Get<std::string>(kTxtReportStr).c_str());
167+
}
168+
if (!play_options_->Get<std::string>(kTsvReportStr).empty()) {
169+
tsvlog_.open(play_options_->Get<std::string>(kTsvReportStr).c_str());
170+
}
171+
172+
// Load network
173+
InitializeNetwork();
174+
175+
// Parse moves.
176+
std::vector<std::string> moves_str =
177+
StrSplitAtWhitespace(play_options_->Get<std::string>(kMovesStr));
178+
std::vector<Move> moves;
179+
for (const auto& move : moves_str) moves.emplace_back(move);
180+
181+
for (int i = 0; i < play_options_->Get<int>(kMovesToAnalyzeStr); ++i) {
182+
// Write position to logs.
183+
std::string initial_pos;
184+
if (moves_str.empty()) {
185+
WriteToLog("Position: startpos");
186+
WriteToTsvLog({"Startpos."});
187+
} else {
188+
WriteToLog("Position: moves " + StrJoin(moves_str));
189+
WriteToTsvLog({"Moves " + StrJoin(moves_str)});
190+
}
191+
192+
// Run Mcts at different depths.
193+
RunOnePosition(moves);
194+
195+
// Run training several times.
196+
197+
if (moves.empty()) break;
198+
moves.pop_back();
199+
moves_str.pop_back();
200+
WriteToTsvLog({});
201+
}
202+
203+
// DumpFlags();
204+
}
205+
206+
void Analyzer::InitializeNetwork() {
207+
std::string network_path = play_options_->Get<std::string>(kWeightsStr);
208+
std::string backend = play_options_->Get<std::string>(kNnBackendStr);
209+
std::string backend_options =
210+
play_options_->Get<std::string>(kNnBackendOptionsStr);
211+
212+
std::string net_path = network_path;
213+
if (net_path == kAutoDiscover) {
214+
net_path = DiscoveryWeightsFile();
215+
}
216+
Weights weights = LoadWeightsFromFile(net_path);
217+
218+
OptionsDict network_options = OptionsDict::FromString(
219+
backend_options, &options_parser_.GetOptionsDict());
220+
221+
network_ = NetworkFactory::Get()->Create(backend, weights, network_options);
222+
}
223+
224+
void Analyzer::WriteToLog(const std::string& line) const {
225+
std::cout << line << std::endl;
226+
if (log_) log_ << line << std::endl;
227+
}
228+
229+
void Analyzer::WriteToTsvLog(const std::vector<std::string>& line) const {
230+
if (!tsvlog_) return;
231+
bool first = true;
232+
for (const auto& chunk : line) {
233+
if (first) {
234+
first = false;
235+
} else {
236+
tsvlog_ << "\t";
237+
}
238+
tsvlog_ << chunk;
239+
}
240+
tsvlog_ << std::endl;
241+
}
242+
243+
void Analyzer::OnBestMove(const BestMoveInfo& move) const {
244+
WriteToLog("BestMove: " + move.bestmove.as_string());
245+
}
246+
247+
void Analyzer::OnInfo(const ThinkingInfo& info) const {
248+
std::string res = "Info";
249+
if (info.depth >= 0) res += " depth " + std::to_string(info.depth);
250+
if (info.seldepth >= 0) res += " seldepth " + std::to_string(info.seldepth);
251+
if (info.time >= 0) res += " time " + std::to_string(info.time);
252+
if (info.nodes >= 0) res += " nodes " + std::to_string(info.nodes);
253+
if (info.score) res += " score cp " + std::to_string(*info.score);
254+
if (info.nps >= 0) res += " nps " + std::to_string(info.nps);
255+
256+
if (!info.pv.empty()) {
257+
res += " pv";
258+
for (const auto& move : info.pv) res += " " + move.as_string();
259+
}
260+
if (!info.comment.empty()) res += " string " + info.comment;
261+
WriteToLog(res);
262+
}
263+
264+
} // namespace lczero

src/analyzer/analyzer.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
This file is part of Leela Chess Zero.
3+
Copyright (C) 2018 The LCZero Authors
4+
5+
Leela Chess is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
Leela Chess is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with Leela Chess. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#pragma once
20+
21+
#include <fstream>
22+
#include "analyzer/table.h"
23+
#include "chess/board.h"
24+
#include "chess/callbacks.h"
25+
#include "mcts/node.h"
26+
#include "neural/network.h"
27+
#include "utils/optionsparser.h"
28+
29+
namespace lczero {
30+
31+
class Analyzer {
32+
public:
33+
Analyzer();
34+
void Run();
35+
36+
private:
37+
void RunOnePosition(const std::vector<Move>& position);
38+
39+
void WriteToLog(const std::string& line) const;
40+
void WriteToTsvLog(const std::vector<std::string>& line) const;
41+
42+
void InitializeNetwork();
43+
void OnBestMove(const BestMoveInfo& move) const;
44+
void OnInfo(const ThinkingInfo& info) const;
45+
void GatherStats(Table3d* table, const Node* root_node, std::string& col,
46+
bool flip);
47+
48+
std::unique_ptr<Network> network_;
49+
OptionsParser options_parser_;
50+
const OptionsDict* play_options_;
51+
// const OptionsDict* training_options_;
52+
mutable std::ofstream log_;
53+
mutable std::ofstream tsvlog_;
54+
};
55+
56+
} // namespace lczero

0 commit comments

Comments
 (0)