2222#include < cassert>
2323#include < cmath>
2424#include < cstdlib>
25- #include < fstream>
2625#include < iomanip>
2726#include < iostream>
28- #include < optional>
2927#include < sstream>
30- #include < unordered_map>
31- #include < vector>
3228
33- #include " incbin/incbin.h"
34- #include " misc.h"
35- #include " nnue/evaluate_nnue.h"
36- #include " nnue/nnue_architecture.h"
29+ #include " nnue/network.h"
30+ #include " nnue/nnue_misc.h"
3731#include " position.h"
3832#include " types.h"
3933#include " uci.h"
40- #include " ucioption.h"
41-
42- // Macro to embed the default efficiently updatable neural network (NNUE) file
43- // data in the engine binary (using incbin.h, by Dale Weiler).
44- // This macro invocation will declare the following three variables
45- // const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data
46- // const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end
47- // const unsigned int gEmbeddedNNUESize; // the size of the embedded file
48- // Note that this does not work in Microsoft Visual Studio.
49- #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF)
50- INCBIN (EmbeddedNNUEBig, EvalFileDefaultNameBig);
51- INCBIN (EmbeddedNNUESmall, EvalFileDefaultNameSmall);
52- #else
53- const unsigned char gEmbeddedNNUEBigData [1 ] = {0x0 };
54- const unsigned char * const gEmbeddedNNUEBigEnd = &gEmbeddedNNUEBigData [1 ];
55- const unsigned int gEmbeddedNNUEBigSize = 1 ;
56- const unsigned char gEmbeddedNNUESmallData [1 ] = {0x0 };
57- const unsigned char * const gEmbeddedNNUESmallEnd = &gEmbeddedNNUESmallData [1 ];
58- const unsigned int gEmbeddedNNUESmallSize = 1 ;
59- #endif
60-
6134
6235namespace Stockfish {
6336
64- namespace Eval {
65-
66-
67- // Tries to load a NNUE network at startup time, or when the engine
68- // receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
69- // The name of the NNUE network is always retrieved from the EvalFile option.
70- // We search the given network in three locations: internally (the default
71- // network may be embedded in the binary), in the active working directory and
72- // in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
73- // variable to have the engine search in a special directory in their distro.
74- NNUE::EvalFiles NNUE::load_networks (const std::string& rootDirectory,
75- const OptionsMap& options,
76- NNUE::EvalFiles evalFiles) {
77-
78- for (auto & [netSize, evalFile] : evalFiles)
79- {
80- std::string user_eval_file = options[evalFile.optionName ];
81-
82- if (user_eval_file.empty ())
83- user_eval_file = evalFile.defaultName ;
84-
85- #if defined(DEFAULT_NNUE_DIRECTORY)
86- std::vector<std::string> dirs = {" <internal>" , " " , rootDirectory,
87- stringify (DEFAULT_NNUE_DIRECTORY)};
88- #else
89- std::vector<std::string> dirs = {" <internal>" , " " , rootDirectory};
90- #endif
91-
92- for (const std::string& directory : dirs)
93- {
94- if (evalFile.current != user_eval_file)
95- {
96- if (directory != " <internal>" )
97- {
98- std::ifstream stream (directory + user_eval_file, std::ios::binary);
99- auto description = NNUE::load_eval (stream, netSize);
100-
101- if (description.has_value ())
102- {
103- evalFile.current = user_eval_file;
104- evalFile.netDescription = description.value ();
105- }
106- }
107-
108- if (directory == " <internal>" && user_eval_file == evalFile.defaultName )
109- {
110- // C++ way to prepare a buffer for a memory stream
111- class MemoryBuffer : public std ::basic_streambuf<char > {
112- public:
113- MemoryBuffer (char * p, size_t n) {
114- setg (p, p, p + n);
115- setp (p, p + n);
116- }
117- };
118-
119- MemoryBuffer buffer (
120- const_cast <char *>(reinterpret_cast <const char *>(
121- netSize == Small ? gEmbeddedNNUESmallData : gEmbeddedNNUEBigData )),
122- size_t (netSize == Small ? gEmbeddedNNUESmallSize : gEmbeddedNNUEBigSize ));
123- (void ) gEmbeddedNNUEBigEnd ; // Silence warning on unused variable
124- (void ) gEmbeddedNNUESmallEnd ;
125-
126- std::istream stream (&buffer);
127- auto description = NNUE::load_eval (stream, netSize);
128-
129- if (description.has_value ())
130- {
131- evalFile.current = user_eval_file;
132- evalFile.netDescription = description.value ();
133- }
134- }
135- }
136- }
137- }
138-
139- return evalFiles;
140- }
141-
142- // Verifies that the last net used was loaded successfully
143- void NNUE::verify (const OptionsMap& options,
144- const std::unordered_map<Eval::NNUE::NetSize, EvalFile>& evalFiles) {
145-
146- for (const auto & [netSize, evalFile] : evalFiles)
147- {
148- std::string user_eval_file = options[evalFile.optionName ];
149-
150- if (user_eval_file.empty ())
151- user_eval_file = evalFile.defaultName ;
152-
153- if (evalFile.current != user_eval_file)
154- {
155- std::string msg1 =
156- " Network evaluation parameters compatible with the engine must be available." ;
157- std::string msg2 =
158- " The network file " + user_eval_file + " was not loaded successfully." ;
159- std::string msg3 = " The UCI option EvalFile might need to specify the full path, "
160- " including the directory name, to the network file." ;
161- std::string msg4 = " The default net can be downloaded from: "
162- " https://tests.stockfishchess.org/api/nn/"
163- + evalFile.defaultName ;
164- std::string msg5 = " The engine will be terminated now." ;
165-
166- sync_cout << " info string ERROR: " << msg1 << sync_endl;
167- sync_cout << " info string ERROR: " << msg2 << sync_endl;
168- sync_cout << " info string ERROR: " << msg3 << sync_endl;
169- sync_cout << " info string ERROR: " << msg4 << sync_endl;
170- sync_cout << " info string ERROR: " << msg5 << sync_endl;
171-
172- exit (EXIT_FAILURE);
173- }
174-
175- sync_cout << " info string NNUE evaluation using " << user_eval_file << sync_endl;
176- }
177- }
178- }
179-
18037// Returns a static, purely materialistic evaluation of the position from
18138// the point of view of the given color. It can be divided by PawnValue to get
18239// an approximation of the material advantage on the board in terms of pawns.
@@ -188,7 +45,7 @@ int Eval::simple_eval(const Position& pos, Color c) {
18845
18946// Evaluate is the evaluator for the outer world. It returns a static evaluation
19047// of the position from the point of view of the side to move.
191- Value Eval::evaluate (const Position& pos, int optimism) {
48+ Value Eval::evaluate (const Eval::NNUE::Networks& networks, const Position& pos, int optimism) {
19249
19350 assert (!pos.checkers ());
19451
@@ -198,8 +55,8 @@ Value Eval::evaluate(const Position& pos, int optimism) {
19855
19956 int nnueComplexity;
20057
201- Value nnue = smallNet ? NNUE:: evaluate<NNUE::Small> (pos, true , &nnueComplexity, psqtOnly)
202- : NNUE:: evaluate<NNUE::Big> (pos, true , &nnueComplexity, false );
58+ Value nnue = smallNet ? networks. small . evaluate (pos, true , &nnueComplexity, psqtOnly)
59+ : networks. big . evaluate (pos, true , &nnueComplexity, false );
20360
20461 // Blend optimism and eval with nnue complexity and material imbalance
20562 optimism += optimism * (nnueComplexity + std::abs (simpleEval - nnue)) / 512 ;
@@ -222,23 +79,22 @@ Value Eval::evaluate(const Position& pos, int optimism) {
22279// a string (suitable for outputting to stdout) that contains the detailed
22380// descriptions and values of each evaluation term. Useful for debugging.
22481// Trace scores are from white's point of view
225- std::string Eval::trace (Position& pos) {
82+ std::string Eval::trace (Position& pos, const Eval::NNUE::Networks& networks ) {
22683
22784 if (pos.checkers ())
22885 return " Final evaluation: none (in check)" ;
22986
23087 std::stringstream ss;
23188 ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision (2 );
232- ss << ' \n ' << NNUE::trace (pos) << ' \n ' ;
89+ ss << ' \n ' << NNUE::trace (pos, networks ) << ' \n ' ;
23390
23491 ss << std::showpoint << std::showpos << std::fixed << std::setprecision (2 ) << std::setw (15 );
23592
236- Value v;
237- v = NNUE::evaluate<NNUE::Big>(pos, false );
238- v = pos.side_to_move () == WHITE ? v : -v;
93+ Value v = networks.big .evaluate (pos, false );
94+ v = pos.side_to_move () == WHITE ? v : -v;
23995 ss << " NNUE evaluation " << 0.01 * UCI::to_cp (v) << " (white side)\n " ;
24096
241- v = evaluate (pos, VALUE_ZERO);
97+ v = evaluate (networks, pos, VALUE_ZERO);
24298 v = pos.side_to_move () == WHITE ? v : -v;
24399 ss << " Final evaluation " << 0.01 * UCI::to_cp (v) << " (white side)" ;
244100 ss << " [with scaled NNUE, ...]" ;
0 commit comments