25
25
#include < fstream>
26
26
#include < iomanip>
27
27
#include < iostream>
28
+ #include < optional>
28
29
#include < sstream>
29
30
#include < unordered_map>
30
31
#include < vector>
34
35
#include " nnue/evaluate_nnue.h"
35
36
#include " nnue/nnue_architecture.h"
36
37
#include " position.h"
37
- #include " thread.h"
38
38
#include " types.h"
39
39
#include " uci.h"
40
+ #include " ucioption.h"
40
41
41
42
// Macro to embed the default efficiently updatable neural network (NNUE) file
42
43
// data in the engine binary (using incbin.h, by Dale Weiler).
@@ -62,10 +63,6 @@ namespace Polyfish {
62
63
63
64
namespace Eval {
64
65
65
- std::unordered_map<NNUE::NetSize, EvalFile> EvalFiles = {
66
- {NNUE::Big, {" EvalFile" , EvalFileDefaultNameBig, " None" }},
67
- {NNUE::Small, {" EvalFileSmall" , EvalFileDefaultNameSmall, " None" }}};
68
-
69
66
70
67
// Tries to load a NNUE network at startup time, or when the engine
71
68
// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
@@ -74,37 +71,44 @@ std::unordered_map<NNUE::NetSize, EvalFile> EvalFiles = {
74
71
// network may be embedded in the binary), in the active working directory and
75
72
// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
76
73
// variable to have the engine search in a special directory in their distro.
77
- void NNUE::init () {
74
+ NNUE::EvalFiles NNUE::load_networks (const std::string& rootDirectory,
75
+ const OptionsMap& options,
76
+ NNUE::EvalFiles evalFiles) {
78
77
79
- for (auto & [netSize, evalFile] : EvalFiles )
78
+ for (auto & [netSize, evalFile] : evalFiles )
80
79
{
81
80
// Replace with
82
- // Options [evalFile.option_name ]
81
+ // options [evalFile.optionName ]
83
82
// once fishtest supports the uci option EvalFileSmall
84
- std::string user_eval_file = Options [evalFile.option_name ];
83
+ std::string user_eval_file = options [evalFile.optionName ];
85
84
86
85
if (user_eval_file.empty ())
87
- user_eval_file = evalFile.default_name ;
86
+ user_eval_file = evalFile.defaultName ;
88
87
89
88
#if defined(DEFAULT_NNUE_DIRECTORY)
90
- std::vector<std::string> dirs = {" <internal>" , " " , CommandLine::binaryDirectory ,
89
+ std::vector<std::string> dirs = {" <internal>" , " " , rootDirectory ,
91
90
stringify (DEFAULT_NNUE_DIRECTORY)};
92
91
#else
93
- std::vector<std::string> dirs = {" <internal>" , " " , CommandLine::binaryDirectory };
92
+ std::vector<std::string> dirs = {" <internal>" , " " , rootDirectory };
94
93
#endif
95
94
96
95
for (const std::string& directory : dirs)
97
96
{
98
- if (evalFile.selected_name != user_eval_file)
97
+ if (evalFile.current != user_eval_file)
99
98
{
100
99
if (directory != " <internal>" )
101
100
{
102
101
std::ifstream stream (directory + user_eval_file, std::ios::binary);
103
- if (NNUE::load_eval (user_eval_file, stream, netSize))
104
- evalFile.selected_name = user_eval_file;
102
+ auto description = NNUE::load_eval (stream, netSize);
103
+
104
+ if (description.has_value ())
105
+ {
106
+ evalFile.current = user_eval_file;
107
+ evalFile.netDescription = description.value ();
108
+ }
105
109
}
106
110
107
- if (directory == " <internal>" && user_eval_file == evalFile.default_name )
111
+ if (directory == " <internal>" && user_eval_file == evalFile.defaultName )
108
112
{
109
113
// C++ way to prepare a buffer for a memory stream
110
114
class MemoryBuffer : public std ::basic_streambuf<char > {
@@ -123,27 +127,35 @@ void NNUE::init() {
123
127
(void ) gEmbeddedNNUESmallEnd ;
124
128
125
129
std::istream stream (&buffer);
126
- if (NNUE::load_eval (user_eval_file, stream, netSize))
127
- evalFile.selected_name = user_eval_file;
130
+ auto description = NNUE::load_eval (stream, netSize);
131
+
132
+ if (description.has_value ())
133
+ {
134
+ evalFile.current = user_eval_file;
135
+ evalFile.netDescription = description.value ();
136
+ }
128
137
}
129
138
}
130
139
}
131
140
}
141
+
142
+ return evalFiles;
132
143
}
133
144
134
145
// Verifies that the last net used was loaded successfully
135
- void NNUE::verify () {
146
+ void NNUE::verify (const OptionsMap& options,
147
+ const std::unordered_map<Eval::NNUE::NetSize, EvalFile>& evalFiles) {
136
148
137
- for (const auto & [netSize, evalFile] : EvalFiles )
149
+ for (const auto & [netSize, evalFile] : evalFiles )
138
150
{
139
151
// Replace with
140
- // Options [evalFile.option_name ]
152
+ // options [evalFile.optionName ]
141
153
// once fishtest supports the uci option EvalFileSmall
142
- std::string user_eval_file = Options [evalFile.option_name ];
154
+ std::string user_eval_file = options [evalFile.optionName ];
143
155
if (user_eval_file.empty ())
144
- user_eval_file = evalFile.default_name ;
156
+ user_eval_file = evalFile.defaultName ;
145
157
146
- if (evalFile.selected_name != user_eval_file)
158
+ if (evalFile.current != user_eval_file)
147
159
{
148
160
std::string msg1 =
149
161
" Network evaluation parameters compatible with the engine must be available." ;
@@ -153,7 +165,7 @@ void NNUE::verify() {
153
165
" including the directory name, to the network file." ;
154
166
std::string msg4 = " The default net can be downloaded from: "
155
167
" https://tests.stockfishchess.org/api/nn/"
156
- + evalFile.default_name ;
168
+ + evalFile.defaultName ;
157
169
std::string msg5 = " The engine will be terminated now." ;
158
170
159
171
sync_cout << " info string ERROR: " << msg1 << sync_endl;
@@ -181,7 +193,7 @@ int Eval::simple_eval(const Position& pos, Color c) {
181
193
182
194
// Evaluate is the evaluator for the outer world. It returns a static evaluation
183
195
// of the position from the point of view of the side to move.
184
- Value Eval::evaluate (const Position& pos) {
196
+ Value Eval::evaluate (const Position& pos, int optimism ) {
185
197
186
198
assert (!pos.checkers ());
187
199
@@ -202,8 +214,6 @@ Value Eval::evaluate(const Position& pos) {
202
214
Value nnue = smallNet ? NNUE::evaluate<NNUE::Small>(pos, true , &nnueComplexity)
203
215
: NNUE::evaluate<NNUE::Big>(pos, true , &nnueComplexity);
204
216
205
- int optimism = pos.this_thread ()->optimism [stm];
206
-
207
217
// Blend optimism and eval with nnue complexity and material imbalance
208
218
optimism += optimism * (nnueComplexity + std::abs (simpleEval - nnue)) / 512 ;
209
219
nnue -= nnue * (nnueComplexity + std::abs (simpleEval - nnue)) / 32768 ;
@@ -230,12 +240,6 @@ std::string Eval::trace(Position& pos) {
230
240
if (pos.checkers ())
231
241
return " Final evaluation: none (in check)" ;
232
242
233
- // Reset any global variable used in eval
234
- pos.this_thread ()->bestValue = VALUE_ZERO;
235
- pos.this_thread ()->rootSimpleEval = VALUE_ZERO;
236
- pos.this_thread ()->optimism [WHITE] = VALUE_ZERO;
237
- pos.this_thread ()->optimism [BLACK] = VALUE_ZERO;
238
-
239
243
std::stringstream ss;
240
244
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision (2 );
241
245
ss << ' \n ' << NNUE::trace (pos) << ' \n ' ;
@@ -247,7 +251,7 @@ std::string Eval::trace(Position& pos) {
247
251
v = pos.side_to_move () == WHITE ? v : -v;
248
252
ss << " NNUE evaluation " << 0.01 * UCI::to_cp (v) << " (white side)\n " ;
249
253
250
- v = evaluate (pos);
254
+ v = evaluate (pos, VALUE_ZERO );
251
255
v = pos.side_to_move () == WHITE ? v : -v;
252
256
ss << " Final evaluation " << 0.01 * UCI::to_cp (v) << " (white side)" ;
253
257
ss << " [with scaled NNUE, ...]" ;
0 commit comments