]> git.sesse.net Git - stockfish/blob - src/nnue/evaluate_nnue.cpp
Remove EvalList
[stockfish] / src / nnue / evaluate_nnue.cpp
1 /*
2   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3   Copyright (C) 2004-2020 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 <fstream>
22 #include <iostream>
23 #include <set>
24
25 #include "../evaluate.h"
26 #include "../position.h"
27 #include "../misc.h"
28 #include "../uci.h"
29
30 #include "evaluate_nnue.h"
31
32 namespace Eval::NNUE {
33
34   uint32_t kpp_board_index[PIECE_NB][COLOR_NB] = {
35    // convention: W - us, B - them
36    // viewed from other side, W and B are reversed
37       { PS_NONE,     PS_NONE     },
38       { PS_W_PAWN,   PS_B_PAWN   },
39       { PS_W_KNIGHT, PS_B_KNIGHT },
40       { PS_W_BISHOP, PS_B_BISHOP },
41       { PS_W_ROOK,   PS_B_ROOK   },
42       { PS_W_QUEEN,  PS_B_QUEEN  },
43       { PS_W_KING,   PS_B_KING   },
44       { PS_NONE,     PS_NONE     },
45       { PS_NONE,     PS_NONE     },
46       { PS_B_PAWN,   PS_W_PAWN   },
47       { PS_B_KNIGHT, PS_W_KNIGHT },
48       { PS_B_BISHOP, PS_W_BISHOP },
49       { PS_B_ROOK,   PS_W_ROOK   },
50       { PS_B_QUEEN,  PS_W_QUEEN  },
51       { PS_B_KING,   PS_W_KING   },
52       { PS_NONE,     PS_NONE     }
53   };
54
55   // Input feature converter
56   AlignedPtr<FeatureTransformer> feature_transformer;
57
58   // Evaluation function
59   AlignedPtr<Network> network;
60
61   // Evaluation function file name
62   std::string fileName;
63
64   namespace Detail {
65
66   // Initialize the evaluation function parameters
67   template <typename T>
68   void Initialize(AlignedPtr<T>& pointer) {
69
70     pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
71     std::memset(pointer.get(), 0, sizeof(T));
72   }
73
74   // Read evaluation function parameters
75   template <typename T>
76   bool ReadParameters(std::istream& stream, const AlignedPtr<T>& pointer) {
77
78     std::uint32_t header;
79     header = read_little_endian<std::uint32_t>(stream);
80     if (!stream || header != T::GetHashValue()) return false;
81     return pointer->ReadParameters(stream);
82   }
83
84   }  // namespace Detail
85
86   // Initialize the evaluation function parameters
87   void Initialize() {
88
89     Detail::Initialize(feature_transformer);
90     Detail::Initialize(network);
91   }
92
93   // Read network header
94   bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture)
95   {
96     std::uint32_t version, size;
97
98     version     = read_little_endian<std::uint32_t>(stream);
99     *hash_value = read_little_endian<std::uint32_t>(stream);
100     size        = read_little_endian<std::uint32_t>(stream);
101     if (!stream || version != kVersion) return false;
102     architecture->resize(size);
103     stream.read(&(*architecture)[0], size);
104     return !stream.fail();
105   }
106
107   // Read network parameters
108   bool ReadParameters(std::istream& stream) {
109
110     std::uint32_t hash_value;
111     std::string architecture;
112     if (!ReadHeader(stream, &hash_value, &architecture)) return false;
113     if (hash_value != kHashValue) return false;
114     if (!Detail::ReadParameters(stream, feature_transformer)) return false;
115     if (!Detail::ReadParameters(stream, network)) return false;
116     return stream && stream.peek() == std::ios::traits_type::eof();
117   }
118
119   // Proceed with the difference calculation if possible
120   static void UpdateAccumulatorIfPossible(const Position& pos) {
121
122     feature_transformer->UpdateAccumulatorIfPossible(pos);
123   }
124
125   // Calculate the evaluation value
126   static Value ComputeScore(const Position& pos, bool refresh) {
127
128     auto& accumulator = pos.state()->accumulator;
129     if (!refresh && accumulator.computed_score) {
130       return accumulator.score;
131     }
132
133     alignas(kCacheLineSize) TransformedFeatureType
134         transformed_features[FeatureTransformer::kBufferSize];
135     feature_transformer->Transform(pos, transformed_features, refresh);
136     alignas(kCacheLineSize) char buffer[Network::kBufferSize];
137     const auto output = network->Propagate(transformed_features, buffer);
138
139     auto score = static_cast<Value>(output[0] / FV_SCALE);
140
141     accumulator.score = score;
142     accumulator.computed_score = true;
143     return accumulator.score;
144   }
145
146   // Load the evaluation function file
147   bool load_eval_file(const std::string& evalFile) {
148
149     Initialize();
150     fileName = evalFile;
151
152     std::ifstream stream(evalFile, std::ios::binary);
153
154     const bool result = ReadParameters(stream);
155
156     return result;
157   }
158
159   // Evaluation function. Perform differential calculation.
160   Value evaluate(const Position& pos) {
161     return ComputeScore(pos, false);
162   }
163
164   // Evaluation function. Perform full calculation.
165   Value compute_eval(const Position& pos) {
166     return ComputeScore(pos, true);
167   }
168
169   // Proceed with the difference calculation if possible
170   void update_eval(const Position& pos) {
171     UpdateAccumulatorIfPossible(pos);
172   }
173
174 } // namespace Eval::NNUE