Exporting the currently loaded network file
[stockfish] / src / nnue / evaluate_nnue.cpp
1 /*
2   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3   Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
4
5   Stockfish 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   Stockfish 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 this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 // Code for calculating NNUE evaluation function
20
21 #include <iostream>
22 #include <set>
23
24 #include "../evaluate.h"
25 #include "../position.h"
26 #include "../misc.h"
27 #include "../uci.h"
28 #include "../types.h"
29
30 #include "evaluate_nnue.h"
31
32 namespace Stockfish::Eval::NNUE {
33
34   // Input feature converter
35   LargePagePtr<FeatureTransformer> featureTransformer;
36
37   // Evaluation function
38   AlignedPtr<Network> network;
39
40   // Evaluation function file name
41   std::string fileName;
42   std::string netDescription;
43
44   namespace Detail {
45
46   // Initialize the evaluation function parameters
47   template <typename T>
48   void initialize(AlignedPtr<T>& pointer) {
49
50     pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
51     std::memset(pointer.get(), 0, sizeof(T));
52   }
53
54   template <typename T>
55   void initialize(LargePagePtr<T>& pointer) {
56
57     static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
58     pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
59     std::memset(pointer.get(), 0, sizeof(T));
60   }
61
62   // Read evaluation function parameters
63   template <typename T>
64   bool read_parameters(std::istream& stream, T& reference) {
65
66     std::uint32_t header;
67     header = read_little_endian<std::uint32_t>(stream);
68     if (!stream || header != T::get_hash_value()) return false;
69     return reference.read_parameters(stream);
70   }
71
72   // Write evaluation function parameters
73   template <typename T>
74   bool write_parameters(std::ostream& stream, const T& reference) {
75
76     write_little_endian<std::uint32_t>(stream, T::get_hash_value());
77     return reference.write_parameters(stream);
78   }
79
80   }  // namespace Detail
81
82   // Initialize the evaluation function parameters
83   void initialize() {
84
85     Detail::initialize(featureTransformer);
86     Detail::initialize(network);
87   }
88
89   // Read network header
90   bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc)
91   {
92     std::uint32_t version, size;
93
94     version     = read_little_endian<std::uint32_t>(stream);
95     *hashValue = read_little_endian<std::uint32_t>(stream);
96     size        = read_little_endian<std::uint32_t>(stream);
97     if (!stream || version != Version) return false;
98     desc->resize(size);
99     stream.read(&(*desc)[0], size);
100     return !stream.fail();
101   }
102
103   // Write network header
104   bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc)
105   {
106     write_little_endian<std::uint32_t>(stream, Version);
107     write_little_endian<std::uint32_t>(stream, hashValue);
108     write_little_endian<std::uint32_t>(stream, desc.size());
109     stream.write(&desc[0], desc.size());
110     return !stream.fail();
111   }
112
113   // Read network parameters
114   bool read_parameters(std::istream& stream) {
115
116     std::uint32_t hashValue;
117     if (!read_header(stream, &hashValue, &netDescription)) return false;
118     if (hashValue != HashValue) return false;
119     if (!Detail::read_parameters(stream, *featureTransformer)) return false;
120     if (!Detail::read_parameters(stream, *network)) return false;
121     return stream && stream.peek() == std::ios::traits_type::eof();
122   }
123
124   // Write network parameters
125   bool write_parameters(std::ostream& stream) {
126
127     if (!write_header(stream, HashValue, netDescription)) return false;
128     if (!Detail::write_parameters(stream, *featureTransformer)) return false;
129     if (!Detail::write_parameters(stream, *network)) return false;
130     return (bool)stream;
131   }
132
133   // Evaluation function. Perform differential calculation.
134   Value evaluate(const Position& pos) {
135
136     // We manually align the arrays on the stack because with gcc < 9.3
137     // overaligning stack variables with alignas() doesn't work correctly.
138
139     constexpr uint64_t alignment = CacheLineSize;
140
141 #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
142     TransformedFeatureType transformedFeaturesUnaligned[
143       FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
144     char bufferUnaligned[Network::BufferSize + alignment];
145
146     auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
147     auto* buffer = align_ptr_up<alignment>(&bufferUnaligned[0]);
148 #else
149     alignas(alignment)
150       TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
151     alignas(alignment) char buffer[Network::BufferSize];
152 #endif
153
154     ASSERT_ALIGNED(transformedFeatures, alignment);
155     ASSERT_ALIGNED(buffer, alignment);
156
157     featureTransformer->transform(pos, transformedFeatures);
158     const auto output = network->propagate(transformedFeatures, buffer);
159
160     return static_cast<Value>(output[0] / OutputScale);
161   }
162
163   // Load eval, from a file stream or a memory stream
164   bool load_eval(std::string name, std::istream& stream) {
165
166     initialize();
167     fileName = name;
168     return read_parameters(stream);
169   }
170
171   // Save eval, to a file stream or a memory stream
172   bool save_eval(std::ostream& stream) {
173
174     if (fileName.empty())
175       return false;
176
177     return write_parameters(stream);
178   }
179
180 } // namespace Stockfish::Eval::NNUE