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