]> git.sesse.net Git - stockfish/blob - src/nnue/evaluate_nnue.cpp
Unify naming convention of the NNUE code
[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
43   namespace Detail {
44
45   // Initialize the evaluation function parameters
46   template <typename T>
47   void initialize(AlignedPtr<T>& pointer) {
48
49     pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
50     std::memset(pointer.get(), 0, sizeof(T));
51   }
52
53   template <typename T>
54   void initialize(LargePagePtr<T>& pointer) {
55
56     static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
57     pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
58     std::memset(pointer.get(), 0, sizeof(T));
59   }
60
61   // Read evaluation function parameters
62   template <typename T>
63   bool read_parameters(std::istream& stream, T& reference) {
64
65     std::uint32_t header;
66     header = read_little_endian<std::uint32_t>(stream);
67     if (!stream || header != T::get_hash_value()) return false;
68     return reference.read_parameters(stream);
69   }
70
71   }  // namespace Detail
72
73   // Initialize the evaluation function parameters
74   void initialize() {
75
76     Detail::initialize(featureTransformer);
77     Detail::initialize(network);
78   }
79
80   // Read network header
81   bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* architecture)
82   {
83     std::uint32_t version, size;
84
85     version     = read_little_endian<std::uint32_t>(stream);
86     *hashValue = read_little_endian<std::uint32_t>(stream);
87     size        = read_little_endian<std::uint32_t>(stream);
88     if (!stream || version != Version) return false;
89     architecture->resize(size);
90     stream.read(&(*architecture)[0], size);
91     return !stream.fail();
92   }
93
94   // Read network parameters
95   bool read_parameters(std::istream& stream) {
96
97     std::uint32_t hashValue;
98     std::string architecture;
99     if (!read_header(stream, &hashValue, &architecture)) return false;
100     if (hashValue != HashValue) return false;
101     if (!Detail::read_parameters(stream, *featureTransformer)) return false;
102     if (!Detail::read_parameters(stream, *network)) return false;
103     return stream && stream.peek() == std::ios::traits_type::eof();
104   }
105
106   // Evaluation function. Perform differential calculation.
107   Value evaluate(const Position& pos) {
108
109     // We manually align the arrays on the stack because with gcc < 9.3
110     // overaligning stack variables with alignas() doesn't work correctly.
111
112     constexpr uint64_t alignment = CacheLineSize;
113
114 #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
115     TransformedFeatureType transformedFeaturesUnaligned[
116       FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
117     char bufferUnaligned[Network::BufferSize + alignment];
118
119     auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
120     auto* buffer = align_ptr_up<alignment>(&bufferUnaligned[0]);
121 #else
122     alignas(alignment)
123       TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
124     alignas(alignment) char buffer[Network::BufferSize];
125 #endif
126
127     ASSERT_ALIGNED(transformedFeatures, alignment);
128     ASSERT_ALIGNED(buffer, alignment);
129
130     featureTransformer->transform(pos, transformedFeatures);
131     const auto output = network->propagate(transformedFeatures, buffer);
132
133     return static_cast<Value>(output[0] / OutputScale);
134   }
135
136   // Load eval, from a file stream or a memory stream
137   bool load_eval(std::string name, std::istream& stream) {
138
139     initialize();
140     fileName = name;
141     return read_parameters(stream);
142   }
143
144 } // namespace Stockfish::Eval::NNUE