mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 16:47:37 +00:00
Post-merge fixes.
This commit is contained in:
@@ -38,8 +38,127 @@
|
|||||||
#include "uci.h"
|
#include "uci.h"
|
||||||
#include "incbin/incbin.h"
|
#include "incbin/incbin.h"
|
||||||
|
|
||||||
|
// Macro to embed the default efficiently updatable neural network (NNUE) file
|
||||||
|
// data in the engine binary (using incbin.h, by Dale Weiler).
|
||||||
|
// This macro invocation will declare the following three variables
|
||||||
|
// const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data
|
||||||
|
// const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end
|
||||||
|
// const unsigned int gEmbeddedNNUESize; // the size of the embedded file
|
||||||
|
// Note that this does not work in Microsoft Visual Studio.
|
||||||
|
#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF)
|
||||||
|
INCBIN(EmbeddedNNUE, EvalFileDefaultName);
|
||||||
|
#else
|
||||||
|
const unsigned char gEmbeddedNNUEData[1] = {0x0};
|
||||||
|
const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1];
|
||||||
|
const unsigned int gEmbeddedNNUESize = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
namespace Stockfish {
|
||||||
|
|
||||||
|
namespace Eval {
|
||||||
|
|
||||||
|
namespace NNUE {
|
||||||
|
string eval_file_loaded = "None";
|
||||||
|
UseNNUEMode useNNUE;
|
||||||
|
|
||||||
|
/// NNUE::init() tries to load a NNUE network at startup time, or when the engine
|
||||||
|
/// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
|
||||||
|
/// The name of the NNUE network is always retrieved from the EvalFile option.
|
||||||
|
/// We search the given network in three locations: internally (the default
|
||||||
|
/// network may be embedded in the binary), in the active working directory and
|
||||||
|
/// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
|
||||||
|
/// variable to have the engine search in a special directory in their distro.
|
||||||
|
|
||||||
|
static UseNNUEMode nnue_mode_from_option(const UCI::Option& mode)
|
||||||
|
{
|
||||||
|
if (mode == "false")
|
||||||
|
return UseNNUEMode::False;
|
||||||
|
else if (mode == "true")
|
||||||
|
return UseNNUEMode::True;
|
||||||
|
else if (mode == "pure")
|
||||||
|
return UseNNUEMode::Pure;
|
||||||
|
|
||||||
|
return UseNNUEMode::False;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
|
||||||
|
useNNUE = nnue_mode_from_option(Options["Use NNUE"]);
|
||||||
|
if (useNNUE == UseNNUEMode::False)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string eval_file = string(Options["EvalFile"]);
|
||||||
|
|
||||||
|
#if defined(DEFAULT_NNUE_DIRECTORY)
|
||||||
|
#define stringify2(x) #x
|
||||||
|
#define stringify(x) stringify2(x)
|
||||||
|
vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) };
|
||||||
|
#else
|
||||||
|
vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (string directory : dirs)
|
||||||
|
if (eval_file_loaded != eval_file)
|
||||||
|
{
|
||||||
|
if (directory != "<internal>")
|
||||||
|
{
|
||||||
|
ifstream stream(directory + eval_file, ios::binary);
|
||||||
|
if (load_eval(eval_file, stream))
|
||||||
|
eval_file_loaded = eval_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (directory == "<internal>" && eval_file == EvalFileDefaultName)
|
||||||
|
{
|
||||||
|
// C++ way to prepare a buffer for a memory stream
|
||||||
|
class MemoryBuffer : public basic_streambuf<char> {
|
||||||
|
public: MemoryBuffer(char* p, size_t n) { setg(p, p, p + n); setp(p, p + n); }
|
||||||
|
};
|
||||||
|
|
||||||
|
MemoryBuffer buffer(const_cast<char*>(reinterpret_cast<const char*>(gEmbeddedNNUEData)),
|
||||||
|
size_t(gEmbeddedNNUESize));
|
||||||
|
|
||||||
|
istream stream(&buffer);
|
||||||
|
if (load_eval(eval_file, stream))
|
||||||
|
eval_file_loaded = eval_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NNUE::verify() verifies that the last net used was loaded successfully
|
||||||
|
void verify() {
|
||||||
|
|
||||||
|
string eval_file = string(Options["EvalFile"]);
|
||||||
|
|
||||||
|
if (useNNUE != UseNNUEMode::False && eval_file_loaded != eval_file)
|
||||||
|
{
|
||||||
|
UCI::OptionsMap defaults;
|
||||||
|
UCI::init(defaults);
|
||||||
|
|
||||||
|
string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available.";
|
||||||
|
string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully.";
|
||||||
|
string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file.";
|
||||||
|
string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + string(defaults["EvalFile"]);
|
||||||
|
string msg5 = "The engine will be terminated now.";
|
||||||
|
|
||||||
|
sync_cout << "info string ERROR: " << msg1 << sync_endl;
|
||||||
|
sync_cout << "info string ERROR: " << msg2 << sync_endl;
|
||||||
|
sync_cout << "info string ERROR: " << msg3 << sync_endl;
|
||||||
|
sync_cout << "info string ERROR: " << msg4 << sync_endl;
|
||||||
|
sync_cout << "info string ERROR: " << msg5 << sync_endl;
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useNNUE != UseNNUEMode::False)
|
||||||
|
sync_cout << "info string NNUE evaluation using " << eval_file << " enabled" << sync_endl;
|
||||||
|
else
|
||||||
|
sync_cout << "info string classical evaluation enabled" << sync_endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace Trace {
|
namespace Trace {
|
||||||
|
|
||||||
enum Tracing { NO_TRACE, TRACE };
|
enum Tracing { NO_TRACE, TRACE };
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ namespace Stockfish {
|
|||||||
class Position;
|
class Position;
|
||||||
|
|
||||||
namespace Eval {
|
namespace Eval {
|
||||||
|
|
||||||
std::string trace(const Position& pos);
|
std::string trace(const Position& pos);
|
||||||
Value evaluate(const Position& pos);
|
Value evaluate(const Position& pos);
|
||||||
|
|
||||||
@@ -36,6 +37,23 @@ namespace Eval {
|
|||||||
// name of the macro, as it is used in the Makefile.
|
// name of the macro, as it is used in the Makefile.
|
||||||
#define EvalFileDefaultName "nn-62ef826d1a6d.nnue"
|
#define EvalFileDefaultName "nn-62ef826d1a6d.nnue"
|
||||||
|
|
||||||
|
namespace NNUE {
|
||||||
|
enum struct UseNNUEMode
|
||||||
|
{
|
||||||
|
False,
|
||||||
|
True,
|
||||||
|
Pure
|
||||||
|
};
|
||||||
|
|
||||||
|
extern UseNNUEMode useNNUE;
|
||||||
|
extern std::string eval_file_loaded;
|
||||||
|
|
||||||
|
Value evaluate(const Position& pos);
|
||||||
|
bool load_eval(std::string name, std::istream& stream);
|
||||||
|
void init();
|
||||||
|
void verify();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Eval
|
} // namespace Eval
|
||||||
|
|
||||||
} // namespace Stockfish
|
} // namespace Stockfish
|
||||||
|
|||||||
+2
-2
@@ -61,10 +61,10 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
SynchronizedRegionLogger sync_region_cout(std::cout);
|
|
||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
|
SynchronizedRegionLogger sync_region_cout(std::cout);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// Version number. If Version is left empty, then compile date in the format
|
/// Version number. If Version is left empty, then compile date in the format
|
||||||
|
|||||||
+8
-142
@@ -1,33 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
Stockfish is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Code for calculating NNUE evaluation function
|
// Code for calculating NNUE evaluation function
|
||||||
|
|
||||||
#include "evaluate_nnue.h"
|
|
||||||
|
|
||||||
#include "position.h"
|
|
||||||
#include "misc.h"
|
|
||||||
#include "uci.h"
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "../evaluate.h"
|
#include "../evaluate.h"
|
||||||
@@ -49,36 +37,18 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
// Evaluation function file name
|
// Evaluation function file name
|
||||||
std::string fileName;
|
std::string fileName;
|
||||||
|
|
||||||
// Saved evaluation function file name
|
|
||||||
std::string savedfileName = "nn.bin";
|
|
||||||
|
|
||||||
// Get a string that represents the structure of the evaluation function
|
|
||||||
std::string get_architecture_string() {
|
|
||||||
return "Features=" + FeatureTransformer::get_structure_string() +
|
|
||||||
",Network=" + Network::get_structure_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_layers_info() {
|
|
||||||
return
|
|
||||||
FeatureTransformer::get_layers_info()
|
|
||||||
+ '\n' + Network::get_layers_info();
|
|
||||||
}
|
|
||||||
|
|
||||||
UseNNUEMode useNNUE;
|
|
||||||
std::string eval_file_loaded = "None";
|
|
||||||
|
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
|
|
||||||
// Initialize the evaluation function parameters
|
// Initialize the evaluation function parameters
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void initialize(AlignedPtr<T>& pointer) {
|
void Initialize(AlignedPtr<T>& pointer) {
|
||||||
|
|
||||||
pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
|
pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
|
||||||
std::memset(pointer.get(), 0, sizeof(T));
|
std::memset(pointer.get(), 0, sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void initialize(LargePagePtr<T>& pointer) {
|
void Initialize(LargePagePtr<T>& pointer) {
|
||||||
|
|
||||||
static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
|
static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
|
||||||
pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
|
pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
|
||||||
@@ -95,35 +65,17 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
return reference.ReadParameters(stream);
|
return reference.ReadParameters(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// write evaluation function parameters
|
|
||||||
template <typename T>
|
|
||||||
bool WriteParameters(std::ostream& stream, const AlignedPtr<T>& pointer) {
|
|
||||||
constexpr std::uint32_t header = T::GetHashValue();
|
|
||||||
|
|
||||||
stream.write(reinterpret_cast<const char*>(&header), sizeof(header));
|
|
||||||
|
|
||||||
return pointer->WriteParameters(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool WriteParameters(std::ostream& stream, const LargePagePtr<T>& pointer) {
|
|
||||||
constexpr std::uint32_t header = T::GetHashValue();
|
|
||||||
|
|
||||||
stream.write(reinterpret_cast<const char*>(&header), sizeof(header));
|
|
||||||
|
|
||||||
return pointer->WriteParameters(stream);
|
|
||||||
}
|
|
||||||
} // namespace Detail
|
} // namespace Detail
|
||||||
|
|
||||||
// Initialize the evaluation function parameters
|
// Initialize the evaluation function parameters
|
||||||
void initialize() {
|
void Initialize() {
|
||||||
|
|
||||||
Detail::initialize(feature_transformer);
|
Detail::Initialize(feature_transformer);
|
||||||
Detail::initialize(network);
|
Detail::Initialize(network);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read network header
|
// Read network header
|
||||||
bool read_header(std::istream& stream, std::uint32_t* hash_value, std::string* architecture)
|
bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture)
|
||||||
{
|
{
|
||||||
std::uint32_t version, size;
|
std::uint32_t version, size;
|
||||||
|
|
||||||
@@ -136,48 +88,18 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
return !stream.fail();
|
return !stream.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the header
|
|
||||||
bool write_header(std::ostream& stream,
|
|
||||||
std::uint32_t hash_value, const std::string& architecture) {
|
|
||||||
|
|
||||||
stream.write(reinterpret_cast<const char*>(&kVersion), sizeof(kVersion));
|
|
||||||
stream.write(reinterpret_cast<const char*>(&hash_value), sizeof(hash_value));
|
|
||||||
|
|
||||||
const std::uint32_t size = static_cast<std::uint32_t>(architecture.size());
|
|
||||||
|
|
||||||
stream.write(reinterpret_cast<const char*>(&size), sizeof(size));
|
|
||||||
stream.write(architecture.data(), size);
|
|
||||||
|
|
||||||
return !stream.fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read network parameters
|
// Read network parameters
|
||||||
bool ReadParameters(std::istream& stream) {
|
bool ReadParameters(std::istream& stream) {
|
||||||
|
|
||||||
std::uint32_t hash_value;
|
std::uint32_t hash_value;
|
||||||
std::string architecture;
|
std::string architecture;
|
||||||
if (!read_header(stream, &hash_value, &architecture)) return false;
|
if (!ReadHeader(stream, &hash_value, &architecture)) return false;
|
||||||
if (hash_value != kHashValue) return false;
|
if (hash_value != kHashValue) return false;
|
||||||
if (!Detail::ReadParameters(stream, *feature_transformer)) return false;
|
if (!Detail::ReadParameters(stream, *feature_transformer)) return false;
|
||||||
if (!Detail::ReadParameters(stream, *network)) return false;
|
if (!Detail::ReadParameters(stream, *network)) return false;
|
||||||
return stream && stream.peek() == std::ios::traits_type::eof();
|
return stream && stream.peek() == std::ios::traits_type::eof();
|
||||||
}
|
}
|
||||||
|
|
||||||
// write evaluation function parameters
|
|
||||||
bool WriteParameters(std::ostream& stream) {
|
|
||||||
|
|
||||||
if (!write_header(stream, kHashValue, get_architecture_string()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!Detail::WriteParameters(stream, feature_transformer))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!Detail::WriteParameters(stream, network))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return !stream.fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluation function. Perform differential calculation.
|
// Evaluation function. Perform differential calculation.
|
||||||
Value evaluate(const Position& pos) {
|
Value evaluate(const Position& pos) {
|
||||||
|
|
||||||
@@ -211,65 +133,9 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
// Load eval, from a file stream or a memory stream
|
// Load eval, from a file stream or a memory stream
|
||||||
bool load_eval(std::string name, std::istream& stream) {
|
bool load_eval(std::string name, std::istream& stream) {
|
||||||
|
|
||||||
initialize();
|
Initialize();
|
||||||
fileName = name;
|
fileName = name;
|
||||||
return ReadParameters(stream);
|
return ReadParameters(stream);
|
||||||
}
|
|
||||||
|
|
||||||
static UseNNUEMode nnue_mode_from_option(const UCI::Option& mode)
|
|
||||||
{
|
|
||||||
if (mode == "false")
|
|
||||||
return UseNNUEMode::False;
|
|
||||||
else if (mode == "true")
|
|
||||||
return UseNNUEMode::True;
|
|
||||||
else if (mode == "pure")
|
|
||||||
return UseNNUEMode::Pure;
|
|
||||||
|
|
||||||
return UseNNUEMode::False;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init() {
|
|
||||||
|
|
||||||
useNNUE = nnue_mode_from_option(Options["Use NNUE"]);
|
|
||||||
|
|
||||||
if (Options["SkipLoadingEval"])
|
|
||||||
{
|
|
||||||
eval_file_loaded.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useNNUE == UseNNUEMode::False)
|
|
||||||
{
|
|
||||||
// Keep the eval file loaded. Useful for mixed bench.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string eval_file = std::string(Options["EvalFile"]);
|
|
||||||
|
|
||||||
#if defined(DEFAULT_NNUE_DIRECTORY)
|
|
||||||
#define stringify2(x) #x
|
|
||||||
#define stringify(x) stringify2(x)
|
|
||||||
std::vector<std::string> dirs = { "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) };
|
|
||||||
#else
|
|
||||||
std::vector<std::string> dirs = { "" , CommandLine::binaryDirectory };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (std::string directory : dirs)
|
|
||||||
{
|
|
||||||
if (eval_file_loaded != eval_file)
|
|
||||||
{
|
|
||||||
std::ifstream stream(directory + eval_file, std::ios::binary);
|
|
||||||
if (load_eval(eval_file, stream))
|
|
||||||
{
|
|
||||||
sync_cout << "info string Loaded eval file " << directory + eval_file << sync_endl;
|
|
||||||
eval_file_loaded = eval_file;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sync_cout << "info string ERROR: failed to load eval file " << directory + eval_file << sync_endl;
|
|
||||||
eval_file_loaded.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Stockfish::Eval::NNUE
|
} // namespace Stockfish::Eval::NNUE
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
Stockfish is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -23,19 +20,10 @@
|
|||||||
|
|
||||||
#include "nnue_feature_transformer.h"
|
#include "nnue_feature_transformer.h"
|
||||||
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace Stockfish::Eval::NNUE {
|
namespace Stockfish::Eval::NNUE {
|
||||||
|
|
||||||
enum struct UseNNUEMode
|
|
||||||
{
|
|
||||||
False,
|
|
||||||
True,
|
|
||||||
Pure
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hash value of evaluation function structure
|
// Hash value of evaluation function structure
|
||||||
constexpr std::uint32_t kHashValue =
|
constexpr std::uint32_t kHashValue =
|
||||||
FeatureTransformer::GetHashValue() ^ Network::GetHashValue();
|
FeatureTransformer::GetHashValue() ^ Network::GetHashValue();
|
||||||
|
|||||||
+31
-267
@@ -1,17 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
Stockfish is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -26,276 +23,43 @@
|
|||||||
|
|
||||||
namespace Stockfish::Eval::NNUE::Features {
|
namespace Stockfish::Eval::NNUE::Features {
|
||||||
|
|
||||||
// Class template that represents a list of values
|
// Class template that represents a list of values
|
||||||
template <typename T, T... Values>
|
template <typename T, T... Values>
|
||||||
struct CompileTimeList;
|
struct CompileTimeList;
|
||||||
|
|
||||||
template <typename T, T First, T... Remaining>
|
template <typename T, T First, T... Remaining>
|
||||||
struct CompileTimeList<T, First, Remaining...> {
|
struct CompileTimeList<T, First, Remaining...> {
|
||||||
static constexpr bool contains(T value) {
|
static constexpr bool Contains(T value) {
|
||||||
return value == First || CompileTimeList<T, Remaining...>::contains(value);
|
return value == First || CompileTimeList<T, Remaining...>::Contains(value);
|
||||||
}
|
}
|
||||||
|
static constexpr std::array<T, sizeof...(Remaining) + 1>
|
||||||
|
kValues = {{First, Remaining...}};
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr std::array<T, sizeof...(Remaining) + 1>
|
// Base class of feature set
|
||||||
kValues = {{First, Remaining...}};
|
template <typename Derived>
|
||||||
};
|
class FeatureSetBase {
|
||||||
|
|
||||||
template <typename T, T First, T... Remaining>
|
};
|
||||||
constexpr std::array<T, sizeof...(Remaining) + 1>
|
|
||||||
CompileTimeList<T, First, Remaining...>::kValues;
|
|
||||||
|
|
||||||
template <typename T>
|
// Class template that represents the feature set
|
||||||
struct CompileTimeList<T> {
|
template <typename FeatureType>
|
||||||
static constexpr bool contains(T /*value*/) {
|
class FeatureSet<FeatureType> : public FeatureSetBase<FeatureSet<FeatureType>> {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static constexpr std::array<T, 0> kValues = { {} };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Class template that adds to the beginning of the list
|
public:
|
||||||
template <typename T, typename ListType, T Value>
|
// Hash value embedded in the evaluation file
|
||||||
struct AppendToList;
|
static constexpr std::uint32_t kHashValue = FeatureType::kHashValue;
|
||||||
|
// Number of feature dimensions
|
||||||
|
static constexpr IndexType kDimensions = FeatureType::kDimensions;
|
||||||
|
// Maximum number of simultaneously active features
|
||||||
|
static constexpr IndexType kMaxActiveDimensions =
|
||||||
|
FeatureType::kMaxActiveDimensions;
|
||||||
|
// Trigger for full calculation instead of difference calculation
|
||||||
|
using SortedTriggerSet =
|
||||||
|
CompileTimeList<TriggerEvent, FeatureType::kRefreshTrigger>;
|
||||||
|
static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;
|
||||||
|
|
||||||
template <typename T, T... Values, T AnotherValue>
|
};
|
||||||
struct AppendToList<T, CompileTimeList<T, Values...>, AnotherValue> {
|
|
||||||
using Result = CompileTimeList<T, AnotherValue, Values...>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Class template for adding to a sorted, unique list
|
|
||||||
template <typename T, typename ListType, T Value>
|
|
||||||
struct InsertToSet;
|
|
||||||
|
|
||||||
template <typename T, T First, T... Remaining, T AnotherValue>
|
|
||||||
struct InsertToSet<T, CompileTimeList<T, First, Remaining...>, AnotherValue> {
|
|
||||||
using Result =
|
|
||||||
std::conditional_t<
|
|
||||||
CompileTimeList<T, First, Remaining...>::contains(AnotherValue),
|
|
||||||
CompileTimeList<T, First, Remaining...>,
|
|
||||||
std::conditional_t<
|
|
||||||
(AnotherValue < First),
|
|
||||||
CompileTimeList<T, AnotherValue, First, Remaining...>,
|
|
||||||
typename AppendToList<T, typename InsertToSet<
|
|
||||||
T, CompileTimeList<T, Remaining...>, AnotherValue>::Result,
|
|
||||||
First
|
|
||||||
>::Result
|
|
||||||
>
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, T Value>
|
|
||||||
struct InsertToSet<T, CompileTimeList<T>, Value> {
|
|
||||||
using Result = CompileTimeList<T, Value>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Base class of feature set
|
|
||||||
template <typename Derived>
|
|
||||||
class FeatureSetBase {
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Get a list of indices for active features
|
|
||||||
template <typename IndexListType>
|
|
||||||
static void append_active_indices(
|
|
||||||
const Position& pos, TriggerEvent trigger, IndexListType active[2]) {
|
|
||||||
|
|
||||||
for (Color perspective : { WHITE, BLACK }) {
|
|
||||||
Derived::collect_active_indices(
|
|
||||||
pos, trigger, perspective, &active[perspective]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a list of indices for recently changed features
|
|
||||||
template <typename PositionType, typename IndexListType>
|
|
||||||
static void append_changed_indices(
|
|
||||||
const PositionType& pos,
|
|
||||||
TriggerEvent trigger,
|
|
||||||
IndexListType removed[2],
|
|
||||||
IndexListType added[2],
|
|
||||||
bool reset[2]) {
|
|
||||||
|
|
||||||
const auto& dp = pos.state()->dirtyPiece;
|
|
||||||
|
|
||||||
for (Color perspective : { WHITE, BLACK }) {
|
|
||||||
switch (trigger) {
|
|
||||||
case TriggerEvent::kNone:
|
|
||||||
break;
|
|
||||||
case TriggerEvent::kFriendKingMoved:
|
|
||||||
if (dp.dirty_num == 0) continue;
|
|
||||||
reset[perspective] = dp.piece[0] == make_piece(perspective, KING);
|
|
||||||
break;
|
|
||||||
case TriggerEvent::kEnemyKingMoved:
|
|
||||||
if (dp.dirty_num == 0) continue;
|
|
||||||
reset[perspective] = dp.piece[0] == make_piece(~perspective, KING);
|
|
||||||
break;
|
|
||||||
case TriggerEvent::kAnyKingMoved:
|
|
||||||
if (dp.dirty_num == 0) continue;
|
|
||||||
reset[perspective] = type_of(dp.piece[0]) == KING;
|
|
||||||
break;
|
|
||||||
case TriggerEvent::kAnyPieceMoved:
|
|
||||||
reset[perspective] = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reset[perspective]) {
|
|
||||||
Derived::collect_active_indices(
|
|
||||||
pos, trigger, perspective, &added[perspective]);
|
|
||||||
} else {
|
|
||||||
Derived::collect_changed_indices(
|
|
||||||
pos, trigger, perspective,
|
|
||||||
&removed[perspective], &added[perspective]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Class template that represents the feature set
|
|
||||||
// do internal processing in reverse order of template arguments in order to linearize the amount of calculation at runtime
|
|
||||||
template <typename FirstFeatureType, typename... RemainingFeatureTypes>
|
|
||||||
class FeatureSet<FirstFeatureType, RemainingFeatureTypes...> :
|
|
||||||
public FeatureSetBase<
|
|
||||||
FeatureSet<FirstFeatureType, RemainingFeatureTypes...>
|
|
||||||
> {
|
|
||||||
|
|
||||||
private:
|
|
||||||
using Head = FirstFeatureType;
|
|
||||||
using Tail = FeatureSet<RemainingFeatureTypes...>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Hash value embedded in the evaluation function file
|
|
||||||
static constexpr std::uint32_t kHashValue =
|
|
||||||
Head::kHashValue ^ (Tail::kHashValue << 1) ^ (Tail::kHashValue >> 31);
|
|
||||||
|
|
||||||
// number of feature dimensions
|
|
||||||
static constexpr IndexType kDimensions =
|
|
||||||
Head::kDimensions + Tail::kDimensions;
|
|
||||||
|
|
||||||
// The maximum value of the number of indexes whose value is 1 at the same time among the feature values
|
|
||||||
static constexpr IndexType kMaxActiveDimensions =
|
|
||||||
Head::kMaxActiveDimensions + Tail::kMaxActiveDimensions;
|
|
||||||
|
|
||||||
// List of timings to perform all calculations instead of difference calculation
|
|
||||||
using SortedTriggerSet = typename InsertToSet<TriggerEvent,
|
|
||||||
typename Tail::SortedTriggerSet, Head::kRefreshTrigger>::Result;
|
|
||||||
|
|
||||||
static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;
|
|
||||||
|
|
||||||
// Get the feature quantity name
|
|
||||||
static std::string get_name() {
|
|
||||||
return std::string(Head::kName) + "+" + Tail::get_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Get a list of indices with a value of 1 among the features
|
|
||||||
template <typename IndexListType>
|
|
||||||
static void collect_active_indices(
|
|
||||||
const Position& pos,
|
|
||||||
const TriggerEvent trigger,
|
|
||||||
const Color perspective,
|
|
||||||
IndexListType* const active) {
|
|
||||||
|
|
||||||
Tail::collect_active_indices(pos, trigger, perspective, active);
|
|
||||||
if (Head::kRefreshTrigger == trigger) {
|
|
||||||
const auto start = active->size();
|
|
||||||
Head::append_active_indices(pos, perspective, active);
|
|
||||||
|
|
||||||
for (auto i = start; i < active->size(); ++i) {
|
|
||||||
(*active)[i] += Tail::kDimensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a list of indices whose values have changed from the previous one in the feature quantity
|
|
||||||
template <typename IndexListType>
|
|
||||||
static void collect_changed_indices(
|
|
||||||
const Position& pos,
|
|
||||||
const TriggerEvent trigger,
|
|
||||||
const Color perspective,
|
|
||||||
IndexListType* const removed,
|
|
||||||
IndexListType* const added) {
|
|
||||||
|
|
||||||
Tail::collect_changed_indices(pos, trigger, perspective, removed, added);
|
|
||||||
if (Head::kRefreshTrigger == trigger) {
|
|
||||||
const auto start_removed = removed->size();
|
|
||||||
const auto start_added = added->size();
|
|
||||||
Head::append_changed_indices(pos, perspective, removed, added);
|
|
||||||
|
|
||||||
for (auto i = start_removed; i < removed->size(); ++i) {
|
|
||||||
(*removed)[i] += Tail::kDimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i = start_added; i < added->size(); ++i) {
|
|
||||||
(*added)[i] += Tail::kDimensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the base class and the class template that recursively uses itself a friend
|
|
||||||
friend class FeatureSetBase<FeatureSet>;
|
|
||||||
|
|
||||||
template <typename... FeatureTypes>
|
|
||||||
friend class FeatureSet;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Class template that represents the feature set
|
|
||||||
template <typename FeatureType>
|
|
||||||
class FeatureSet<FeatureType> : public FeatureSetBase<FeatureSet<FeatureType>> {
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Hash value embedded in the evaluation file
|
|
||||||
static constexpr std::uint32_t kHashValue = FeatureType::kHashValue;
|
|
||||||
|
|
||||||
// Number of feature dimensions
|
|
||||||
static constexpr IndexType kDimensions = FeatureType::kDimensions;
|
|
||||||
|
|
||||||
// Maximum number of simultaneously active features
|
|
||||||
static constexpr IndexType kMaxActiveDimensions =
|
|
||||||
FeatureType::kMaxActiveDimensions;
|
|
||||||
|
|
||||||
// Trigger for full calculation instead of difference calculation
|
|
||||||
using SortedTriggerSet =
|
|
||||||
CompileTimeList<TriggerEvent, FeatureType::kRefreshTrigger>;
|
|
||||||
|
|
||||||
static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;
|
|
||||||
|
|
||||||
// Get the feature quantity name
|
|
||||||
static std::string get_name() {
|
|
||||||
return FeatureType::kName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Get a list of indices for active features
|
|
||||||
static void collect_active_indices(
|
|
||||||
const Position& pos,
|
|
||||||
const TriggerEvent trigger,
|
|
||||||
const Color perspective,
|
|
||||||
IndexList* const active) {
|
|
||||||
|
|
||||||
if (FeatureType::kRefreshTrigger == trigger) {
|
|
||||||
FeatureType::append_active_indices(pos, perspective, active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a list of indices for recently changed features
|
|
||||||
static void collect_changed_indices(
|
|
||||||
const Position& pos,
|
|
||||||
const TriggerEvent trigger,
|
|
||||||
const Color perspective,
|
|
||||||
IndexList* const removed,
|
|
||||||
IndexList* const added) {
|
|
||||||
|
|
||||||
if (FeatureType::kRefreshTrigger == trigger) {
|
|
||||||
FeatureType::append_changed_indices(pos, perspective, removed, added);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the base class and the class template that recursively uses itself a friend
|
|
||||||
friend class FeatureSetBase<FeatureSet>;
|
|
||||||
|
|
||||||
template <typename... FeatureTypes>
|
|
||||||
friend class FeatureSet;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Stockfish::Eval::NNUE::Features
|
} // namespace Stockfish::Eval::NNUE::Features
|
||||||
|
|
||||||
|
|||||||
@@ -33,16 +33,11 @@ namespace Stockfish::Eval::NNUE::Features {
|
|||||||
|
|
||||||
// Trigger to perform full calculations instead of difference only
|
// Trigger to perform full calculations instead of difference only
|
||||||
enum class TriggerEvent {
|
enum class TriggerEvent {
|
||||||
kNone, // Calculate the difference whenever possible
|
kFriendKingMoved // calculate full evaluation when own king moves
|
||||||
kFriendKingMoved, // calculate full evaluation when own king moves
|
|
||||||
kEnemyKingMoved, // calculate full evaluation when opponent king moves
|
|
||||||
kAnyKingMoved, // calculate full evaluation when any king moves
|
|
||||||
kAnyPieceMoved, // always calculate full evaluation
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Side {
|
enum class Side {
|
||||||
kFriend, // side to move
|
kFriend // side to move
|
||||||
kEnemy, // opponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Stockfish::Eval::NNUE::Features
|
} // namespace Stockfish::Eval::NNUE::Features
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ namespace Stockfish::Eval::NNUE::Features {
|
|||||||
if (dp.to[i] != SQ_NONE)
|
if (dp.to[i] != SQ_NONE)
|
||||||
added->push_back(make_index(perspective, dp.to[i], pc, ksq));
|
added->push_back(make_index(perspective, dp.to[i], pc, ksq));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template class HalfKP<Side::kFriend>;
|
template class HalfKP<Side::kFriend>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
Stockfish is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -55,8 +52,6 @@ namespace Stockfish::Eval::NNUE::Layers {
|
|||||||
static constexpr std::size_t kBufferSize =
|
static constexpr std::size_t kBufferSize =
|
||||||
PreviousLayer::kBufferSize + kSelfBufferSize;
|
PreviousLayer::kBufferSize + kSelfBufferSize;
|
||||||
|
|
||||||
static constexpr int kLayerIndex = PreviousLayer::kLayerIndex + 1;
|
|
||||||
|
|
||||||
// Hash value embedded in the evaluation file
|
// Hash value embedded in the evaluation file
|
||||||
static constexpr std::uint32_t GetHashValue() {
|
static constexpr std::uint32_t GetHashValue() {
|
||||||
std::uint32_t hash_value = 0xCC03DAE4u;
|
std::uint32_t hash_value = 0xCC03DAE4u;
|
||||||
@@ -66,27 +61,6 @@ namespace Stockfish::Eval::NNUE::Layers {
|
|||||||
return hash_value;
|
return hash_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string get_name() {
|
|
||||||
return "AffineTransform[" +
|
|
||||||
std::to_string(kOutputDimensions) + "<-" +
|
|
||||||
std::to_string(kInputDimensions) + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
// A string that represents the structure from the input layer to this layer
|
|
||||||
static std::string get_structure_string() {
|
|
||||||
return get_name() + "(" +
|
|
||||||
PreviousLayer::get_structure_string() + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string get_layers_info() {
|
|
||||||
std::string info = PreviousLayer::get_layers_info();
|
|
||||||
info += "\n - ";
|
|
||||||
info += std::to_string(kLayerIndex);
|
|
||||||
info += " - ";
|
|
||||||
info += get_name();
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read network parameters
|
// Read network parameters
|
||||||
bool ReadParameters(std::istream& stream) {
|
bool ReadParameters(std::istream& stream) {
|
||||||
if (!previous_layer_.ReadParameters(stream)) return false;
|
if (!previous_layer_.ReadParameters(stream)) return false;
|
||||||
@@ -148,21 +122,6 @@ namespace Stockfish::Eval::NNUE::Layers {
|
|||||||
return !stream.fail();
|
return !stream.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
// write parameters
|
|
||||||
bool WriteParameters(std::ostream& stream) const {
|
|
||||||
if (!previous_layer_.WriteParameters(stream))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stream.write(reinterpret_cast<const char*>(biases_),
|
|
||||||
kOutputDimensions * sizeof(BiasType));
|
|
||||||
|
|
||||||
stream.write(reinterpret_cast<const char*>(weights_),
|
|
||||||
kOutputDimensions * kPaddedInputDimensions *
|
|
||||||
sizeof(WeightType));
|
|
||||||
|
|
||||||
return !stream.fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forward propagation
|
// Forward propagation
|
||||||
const OutputType* Propagate(
|
const OutputType* Propagate(
|
||||||
const TransformedFeatureType* transformed_features, char* buffer) const {
|
const TransformedFeatureType* transformed_features, char* buffer) const {
|
||||||
@@ -474,9 +433,6 @@ namespace Stockfish::Eval::NNUE::Layers {
|
|||||||
using BiasType = OutputType;
|
using BiasType = OutputType;
|
||||||
using WeightType = std::int8_t;
|
using WeightType = std::int8_t;
|
||||||
|
|
||||||
// Make the learning class a friend
|
|
||||||
friend class Trainer<AffineTransform>;
|
|
||||||
|
|
||||||
PreviousLayer previous_layer_;
|
PreviousLayer previous_layer_;
|
||||||
|
|
||||||
alignas(kCacheLineSize) BiasType biases_[kOutputDimensions];
|
alignas(kCacheLineSize) BiasType biases_[kOutputDimensions];
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
Stockfish is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -25,10 +22,14 @@
|
|||||||
|
|
||||||
namespace Stockfish::Eval::NNUE {
|
namespace Stockfish::Eval::NNUE {
|
||||||
|
|
||||||
|
// The accumulator of a StateInfo without parent is set to the INIT state
|
||||||
|
enum AccumulatorState { EMPTY, COMPUTED, INIT };
|
||||||
|
|
||||||
// Class that holds the result of affine transformation of input features
|
// Class that holds the result of affine transformation of input features
|
||||||
struct alignas(kCacheLineSize) Accumulator {
|
struct alignas(kCacheLineSize) Accumulator {
|
||||||
std::int16_t accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions];
|
std::int16_t
|
||||||
bool computed_accumulation;
|
accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions];
|
||||||
|
AccumulatorState state[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Stockfish::Eval::NNUE
|
} // namespace Stockfish::Eval::NNUE
|
||||||
|
|||||||
+135
-236
@@ -1,17 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
Stockfish is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -25,8 +22,7 @@
|
|||||||
#include "nnue_architecture.h"
|
#include "nnue_architecture.h"
|
||||||
#include "features/index_list.h"
|
#include "features/index_list.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring> // std::memset()
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Stockfish::Eval::NNUE {
|
namespace Stockfish::Eval::NNUE {
|
||||||
|
|
||||||
@@ -41,7 +37,6 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
#define vec_store(a,b) _mm512_store_si512(a,b)
|
#define vec_store(a,b) _mm512_store_si512(a,b)
|
||||||
#define vec_add_16(a,b) _mm512_add_epi16(a,b)
|
#define vec_add_16(a,b) _mm512_add_epi16(a,b)
|
||||||
#define vec_sub_16(a,b) _mm512_sub_epi16(a,b)
|
#define vec_sub_16(a,b) _mm512_sub_epi16(a,b)
|
||||||
#define vec_zero _mm512_setzero_si512()
|
|
||||||
static constexpr IndexType kNumRegs = 8; // only 8 are needed
|
static constexpr IndexType kNumRegs = 8; // only 8 are needed
|
||||||
|
|
||||||
#elif USE_AVX2
|
#elif USE_AVX2
|
||||||
@@ -50,7 +45,6 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
#define vec_store(a,b) _mm256_store_si256(a,b)
|
#define vec_store(a,b) _mm256_store_si256(a,b)
|
||||||
#define vec_add_16(a,b) _mm256_add_epi16(a,b)
|
#define vec_add_16(a,b) _mm256_add_epi16(a,b)
|
||||||
#define vec_sub_16(a,b) _mm256_sub_epi16(a,b)
|
#define vec_sub_16(a,b) _mm256_sub_epi16(a,b)
|
||||||
#define vec_zero _mm256_setzero_si256()
|
|
||||||
static constexpr IndexType kNumRegs = 16;
|
static constexpr IndexType kNumRegs = 16;
|
||||||
|
|
||||||
#elif USE_SSE2
|
#elif USE_SSE2
|
||||||
@@ -59,7 +53,6 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
#define vec_store(a,b) *(a)=(b)
|
#define vec_store(a,b) *(a)=(b)
|
||||||
#define vec_add_16(a,b) _mm_add_epi16(a,b)
|
#define vec_add_16(a,b) _mm_add_epi16(a,b)
|
||||||
#define vec_sub_16(a,b) _mm_sub_epi16(a,b)
|
#define vec_sub_16(a,b) _mm_sub_epi16(a,b)
|
||||||
#define vec_zero _mm_setzero_si128()
|
|
||||||
static constexpr IndexType kNumRegs = Is64Bit ? 16 : 8;
|
static constexpr IndexType kNumRegs = Is64Bit ? 16 : 8;
|
||||||
|
|
||||||
#elif USE_MMX
|
#elif USE_MMX
|
||||||
@@ -68,7 +61,6 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
#define vec_store(a,b) *(a)=(b)
|
#define vec_store(a,b) *(a)=(b)
|
||||||
#define vec_add_16(a,b) _mm_add_pi16(a,b)
|
#define vec_add_16(a,b) _mm_add_pi16(a,b)
|
||||||
#define vec_sub_16(a,b) _mm_sub_pi16(a,b)
|
#define vec_sub_16(a,b) _mm_sub_pi16(a,b)
|
||||||
#define vec_zero _mm_setzero_si64()
|
|
||||||
static constexpr IndexType kNumRegs = 8;
|
static constexpr IndexType kNumRegs = 8;
|
||||||
|
|
||||||
#elif USE_NEON
|
#elif USE_NEON
|
||||||
@@ -77,7 +69,6 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
#define vec_store(a,b) *(a)=(b)
|
#define vec_store(a,b) *(a)=(b)
|
||||||
#define vec_add_16(a,b) vaddq_s16(a,b)
|
#define vec_add_16(a,b) vaddq_s16(a,b)
|
||||||
#define vec_sub_16(a,b) vsubq_s16(a,b)
|
#define vec_sub_16(a,b) vsubq_s16(a,b)
|
||||||
#define vec_zero {0}
|
|
||||||
static constexpr IndexType kNumRegs = 16;
|
static constexpr IndexType kNumRegs = 16;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -109,33 +100,12 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
static constexpr std::size_t kBufferSize =
|
static constexpr std::size_t kBufferSize =
|
||||||
kOutputDimensions * sizeof(OutputType);
|
kOutputDimensions * sizeof(OutputType);
|
||||||
|
|
||||||
static constexpr int kLayerIndex = 0;
|
|
||||||
|
|
||||||
// Hash value embedded in the evaluation file
|
// Hash value embedded in the evaluation file
|
||||||
static constexpr std::uint32_t GetHashValue() {
|
static constexpr std::uint32_t GetHashValue() {
|
||||||
|
|
||||||
return RawFeatures::kHashValue ^ kOutputDimensions;
|
return RawFeatures::kHashValue ^ kOutputDimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string get_name() {
|
|
||||||
return RawFeatures::get_name() + "[" +
|
|
||||||
std::to_string(kInputDimensions) + "->" +
|
|
||||||
std::to_string(kHalfDimensions) + "x2]";
|
|
||||||
}
|
|
||||||
|
|
||||||
// a string representing the structure
|
|
||||||
static std::string get_structure_string() {
|
|
||||||
return get_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string get_layers_info() {
|
|
||||||
std::string info = " - ";
|
|
||||||
info += std::to_string(kLayerIndex);
|
|
||||||
info += " - ";
|
|
||||||
info += get_name();
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read network parameters
|
// Read network parameters
|
||||||
bool ReadParameters(std::istream& stream) {
|
bool ReadParameters(std::istream& stream) {
|
||||||
|
|
||||||
@@ -146,38 +116,11 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
return !stream.fail();
|
return !stream.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
// write parameters
|
|
||||||
bool WriteParameters(std::ostream& stream) const {
|
|
||||||
stream.write(reinterpret_cast<const char*>(biases_),
|
|
||||||
kHalfDimensions * sizeof(BiasType));
|
|
||||||
|
|
||||||
stream.write(reinterpret_cast<const char*>(weights_),
|
|
||||||
kHalfDimensions * kInputDimensions * sizeof(WeightType));
|
|
||||||
|
|
||||||
return !stream.fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proceed with the difference calculation if possible
|
|
||||||
bool update_accumulator_if_possible(const Position& pos) const {
|
|
||||||
|
|
||||||
const auto now = pos.state();
|
|
||||||
if (now->accumulator.computed_accumulation)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const auto prev = now->previous;
|
|
||||||
if (prev && prev->accumulator.computed_accumulation) {
|
|
||||||
update_accumulator(pos);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert input features
|
// Convert input features
|
||||||
void Transform(const Position& pos, OutputType* output) const {
|
void Transform(const Position& pos, OutputType* output) const {
|
||||||
|
|
||||||
if (!update_accumulator_if_possible(pos))
|
UpdateAccumulator(pos, WHITE);
|
||||||
refresh_accumulator(pos);
|
UpdateAccumulator(pos, BLACK);
|
||||||
|
|
||||||
const auto& accumulation = pos.state()->accumulator.accumulation;
|
const auto& accumulation = pos.state()->accumulator.accumulation;
|
||||||
|
|
||||||
@@ -221,13 +164,6 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
&reinterpret_cast<const __m512i*>(accumulation[perspectives[p]][0])[j * 2 + 0]);
|
&reinterpret_cast<const __m512i*>(accumulation[perspectives[p]][0])[j * 2 + 0]);
|
||||||
__m512i sum1 = _mm512_load_si512(
|
__m512i sum1 = _mm512_load_si512(
|
||||||
&reinterpret_cast<const __m512i*>(accumulation[perspectives[p]][0])[j * 2 + 1]);
|
&reinterpret_cast<const __m512i*>(accumulation[perspectives[p]][0])[j * 2 + 1]);
|
||||||
for (IndexType i = 1; i < kRefreshTriggers.size(); ++i) {
|
|
||||||
sum0 = _mm512_add_epi16(sum0, reinterpret_cast<const __m512i*>(
|
|
||||||
accumulation[perspectives[p]][i])[j * 2 + 0]);
|
|
||||||
sum1 = _mm512_add_epi16(sum1, reinterpret_cast<const __m512i*>(
|
|
||||||
accumulation[perspectives[p]][i])[j * 2 + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
_mm512_store_si512(&out[j], _mm512_permutexvar_epi64(kControl,
|
_mm512_store_si512(&out[j], _mm512_permutexvar_epi64(kControl,
|
||||||
_mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), kZero)));
|
_mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), kZero)));
|
||||||
}
|
}
|
||||||
@@ -239,13 +175,6 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 0]);
|
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 0]);
|
||||||
__m256i sum1 = _mm256_load_si256(
|
__m256i sum1 = _mm256_load_si256(
|
||||||
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 1]);
|
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 1]);
|
||||||
for (IndexType i = 1; i < kRefreshTriggers.size(); ++i) {
|
|
||||||
sum0 = _mm256_add_epi16(sum0, reinterpret_cast<const __m256i*>(
|
|
||||||
accumulation[perspectives[p]][i])[j * 2 + 0]);
|
|
||||||
sum1 = _mm256_add_epi16(sum1, reinterpret_cast<const __m256i*>(
|
|
||||||
accumulation[perspectives[p]][i])[j * 2 + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
_mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8(
|
_mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8(
|
||||||
_mm256_packs_epi16(sum0, sum1), kZero), kControl));
|
_mm256_packs_epi16(sum0, sum1), kZero), kControl));
|
||||||
}
|
}
|
||||||
@@ -257,13 +186,6 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
accumulation[perspectives[p]][0])[j * 2 + 0]);
|
accumulation[perspectives[p]][0])[j * 2 + 0]);
|
||||||
__m128i sum1 = _mm_load_si128(&reinterpret_cast<const __m128i*>(
|
__m128i sum1 = _mm_load_si128(&reinterpret_cast<const __m128i*>(
|
||||||
accumulation[perspectives[p]][0])[j * 2 + 1]);
|
accumulation[perspectives[p]][0])[j * 2 + 1]);
|
||||||
for (IndexType i = 1; i < kRefreshTriggers.size(); ++i) {
|
|
||||||
sum0 = _mm_add_epi16(sum0, reinterpret_cast<const __m128i*>(
|
|
||||||
accumulation[perspectives[p]][i])[j * 2 + 0]);
|
|
||||||
sum1 = _mm_add_epi16(sum1, reinterpret_cast<const __m128i*>(
|
|
||||||
accumulation[perspectives[p]][i])[j * 2 + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const __m128i packedbytes = _mm_packs_epi16(sum0, sum1);
|
const __m128i packedbytes = _mm_packs_epi16(sum0, sum1);
|
||||||
|
|
||||||
_mm_store_si128(&out[j],
|
_mm_store_si128(&out[j],
|
||||||
@@ -284,13 +206,6 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
accumulation[perspectives[p]][0])[j * 2 + 0]);
|
accumulation[perspectives[p]][0])[j * 2 + 0]);
|
||||||
__m64 sum1 = *(&reinterpret_cast<const __m64*>(
|
__m64 sum1 = *(&reinterpret_cast<const __m64*>(
|
||||||
accumulation[perspectives[p]][0])[j * 2 + 1]);
|
accumulation[perspectives[p]][0])[j * 2 + 1]);
|
||||||
for (IndexType i = 1; i < kRefreshTriggers.size(); ++i) {
|
|
||||||
sum0 = _mm_add_pi16(sum0, reinterpret_cast<const __m64*>(
|
|
||||||
accumulation[perspectives[p]][i])[j * 2 + 0]);
|
|
||||||
sum1 = _mm_add_pi16(sum1, reinterpret_cast<const __m64*>(
|
|
||||||
accumulation[perspectives[p]][i])[j * 2 + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const __m64 packedbytes = _mm_packs_pi16(sum0, sum1);
|
const __m64 packedbytes = _mm_packs_pi16(sum0, sum1);
|
||||||
out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s);
|
out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s);
|
||||||
}
|
}
|
||||||
@@ -300,22 +215,12 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
int16x8_t sum = reinterpret_cast<const int16x8_t*>(
|
int16x8_t sum = reinterpret_cast<const int16x8_t*>(
|
||||||
accumulation[perspectives[p]][0])[j];
|
accumulation[perspectives[p]][0])[j];
|
||||||
|
|
||||||
for (IndexType i = 1; i < kRefreshTriggers.size(); ++i) {
|
|
||||||
sum = vaddq_s16(sum, reinterpret_cast<const int16x8_t*>(
|
|
||||||
accumulation[perspectives[p]][i])[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
out[j] = vmax_s8(vqmovn_s16(sum), kZero);
|
out[j] = vmax_s8(vqmovn_s16(sum), kZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
for (IndexType j = 0; j < kHalfDimensions; ++j) {
|
for (IndexType j = 0; j < kHalfDimensions; ++j) {
|
||||||
BiasType sum = accumulation[static_cast<int>(perspectives[p])][0][j];
|
BiasType sum = accumulation[static_cast<int>(perspectives[p])][0][j];
|
||||||
for (IndexType i = 1; i < kRefreshTriggers.size(); ++i) {
|
|
||||||
sum += accumulation[static_cast<int>(perspectives[p])][i][j];
|
|
||||||
}
|
|
||||||
|
|
||||||
output[offset + j] = static_cast<OutputType>(
|
output[offset + j] = static_cast<OutputType>(
|
||||||
std::max<int>(0, std::min<int>(127, sum)));
|
std::max<int>(0, std::min<int>(127, sum)));
|
||||||
}
|
}
|
||||||
@@ -328,183 +233,177 @@ namespace Stockfish::Eval::NNUE {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Calculate cumulative value without using difference calculation
|
void UpdateAccumulator(const Position& pos, const Color c) const {
|
||||||
void refresh_accumulator(const Position& pos) const {
|
|
||||||
|
|
||||||
#ifdef VECTOR
|
#ifdef VECTOR
|
||||||
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
|
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
|
||||||
// is defined in the VECTOR code below, once in each branch
|
// is defined in the VECTOR code below, once in each branch
|
||||||
vec_t acc[kNumRegs];
|
vec_t acc[kNumRegs];
|
||||||
#endif
|
#endif
|
||||||
auto& accumulator = pos.state()->accumulator;
|
|
||||||
for (IndexType i = 0; i < kRefreshTriggers.size(); ++i) {
|
|
||||||
Features::IndexList active_indices[2];
|
|
||||||
RawFeatures::append_active_indices(pos, kRefreshTriggers[i],
|
|
||||||
active_indices);
|
|
||||||
for (Color perspective : { WHITE, BLACK }) {
|
|
||||||
#ifdef VECTOR
|
|
||||||
for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) {
|
|
||||||
auto accTile = reinterpret_cast<vec_t*>(
|
|
||||||
&accumulator.accumulation[perspective][i][j * kTileHeight]);
|
|
||||||
|
|
||||||
if (i == 0) {
|
// Look for a usable accumulator of an earlier position. We keep track
|
||||||
auto biasesTile = reinterpret_cast<const vec_t*>(
|
// of the estimated gain in terms of features to be added/subtracted.
|
||||||
&biases_[j * kTileHeight]);
|
StateInfo *st = pos.state(), *next = nullptr;
|
||||||
for (IndexType k = 0; k < kNumRegs; ++k)
|
int gain = pos.count<ALL_PIECES>() - 2;
|
||||||
acc[k] = biasesTile[k];
|
while (st->accumulator.state[c] == EMPTY)
|
||||||
} else {
|
{
|
||||||
for (IndexType k = 0; k < kNumRegs; ++k)
|
auto& dp = st->dirtyPiece;
|
||||||
acc[k] = vec_zero;
|
// The first condition tests whether an incremental update is
|
||||||
}
|
// possible at all: if this side's king has moved, it is not possible.
|
||||||
|
static_assert(std::is_same_v<RawFeatures::SortedTriggerSet,
|
||||||
|
Features::CompileTimeList<Features::TriggerEvent, Features::TriggerEvent::kFriendKingMoved>>,
|
||||||
|
"Current code assumes that only kFriendlyKingMoved refresh trigger is being used.");
|
||||||
|
if ( dp.piece[0] == make_piece(c, KING)
|
||||||
|
|| (gain -= dp.dirty_num + 1) < 0)
|
||||||
|
break;
|
||||||
|
next = st;
|
||||||
|
st = st->previous;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto index : active_indices[perspective]) {
|
if (st->accumulator.state[c] == COMPUTED)
|
||||||
const IndexType offset = kHalfDimensions * index + j * kTileHeight;
|
{
|
||||||
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
|
if (next == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
for (IndexType k = 0; k < kNumRegs; ++k)
|
// Update incrementally in two steps. First, we update the "next"
|
||||||
acc[k] = vec_add_16(acc[k], column[k]);
|
// accumulator. Then, we update the current accumulator (pos.state()).
|
||||||
}
|
|
||||||
|
|
||||||
for (IndexType k = 0; k < kNumRegs; k++)
|
// Gather all features to be updated. This code assumes HalfKP features
|
||||||
vec_store(&accTile[k], acc[k]);
|
// only and doesn't support refresh triggers.
|
||||||
}
|
static_assert(std::is_same_v<Features::FeatureSet<Features::HalfKP<Features::Side::kFriend>>,
|
||||||
#else
|
RawFeatures>);
|
||||||
if (i == 0) {
|
Features::IndexList removed[2], added[2];
|
||||||
std::memcpy(accumulator.accumulation[perspective][i], biases_,
|
Features::HalfKP<Features::Side::kFriend>::AppendChangedIndices(pos,
|
||||||
kHalfDimensions * sizeof(BiasType));
|
next->dirtyPiece, c, &removed[0], &added[0]);
|
||||||
} else {
|
for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
|
||||||
std::memset(accumulator.accumulation[perspective][i], 0,
|
Features::HalfKP<Features::Side::kFriend>::AppendChangedIndices(pos,
|
||||||
kHalfDimensions * sizeof(BiasType));
|
st2->dirtyPiece, c, &removed[1], &added[1]);
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto index : active_indices[perspective]) {
|
// Mark the accumulators as computed.
|
||||||
const IndexType offset = kHalfDimensions * index;
|
next->accumulator.state[c] = COMPUTED;
|
||||||
|
pos.state()->accumulator.state[c] = COMPUTED;
|
||||||
for (IndexType j = 0; j < kHalfDimensions; ++j)
|
|
||||||
accumulator.accumulation[perspective][i][j] += weights_[offset + j];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(USE_MMX)
|
|
||||||
_mm_empty();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
accumulator.computed_accumulation = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate cumulative value using difference calculation
|
|
||||||
void update_accumulator(const Position& pos) const {
|
|
||||||
|
|
||||||
|
// Now update the accumulators listed in info[], where the last element is a sentinel.
|
||||||
|
StateInfo *info[3] =
|
||||||
|
{ next, next == pos.state() ? nullptr : pos.state(), nullptr };
|
||||||
#ifdef VECTOR
|
#ifdef VECTOR
|
||||||
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
|
for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j)
|
||||||
// is defined in the VECTOR code below, once in each branch
|
{
|
||||||
vec_t acc[kNumRegs];
|
// Load accumulator
|
||||||
#endif
|
|
||||||
const auto& prev_accumulator = pos.state()->previous->accumulator;
|
|
||||||
auto& accumulator = pos.state()->accumulator;
|
|
||||||
for (IndexType i = 0; i < kRefreshTriggers.size(); ++i) {
|
|
||||||
Features::IndexList removed_indices[2], added_indices[2];
|
|
||||||
bool reset[2] = { false, false };
|
|
||||||
RawFeatures::append_changed_indices(pos, kRefreshTriggers[i],
|
|
||||||
removed_indices, added_indices, reset);
|
|
||||||
|
|
||||||
#ifdef VECTOR
|
|
||||||
for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) {
|
|
||||||
for (Color perspective : { WHITE, BLACK }) {
|
|
||||||
auto accTile = reinterpret_cast<vec_t*>(
|
auto accTile = reinterpret_cast<vec_t*>(
|
||||||
&accumulator.accumulation[perspective][i][j * kTileHeight]);
|
&st->accumulator.accumulation[c][0][j * kTileHeight]);
|
||||||
|
for (IndexType k = 0; k < kNumRegs; ++k)
|
||||||
if (reset[perspective]) {
|
acc[k] = vec_load(&accTile[k]);
|
||||||
if (i == 0) {
|
|
||||||
auto biasesTile = reinterpret_cast<const vec_t*>(
|
|
||||||
&biases_[j * kTileHeight]);
|
|
||||||
for (IndexType k = 0; k < kNumRegs; ++k)
|
|
||||||
acc[k] = biasesTile[k];
|
|
||||||
} else {
|
|
||||||
for (IndexType k = 0; k < kNumRegs; ++k)
|
|
||||||
acc[k] = vec_zero;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto prevAccTile = reinterpret_cast<const vec_t*>(
|
|
||||||
&prev_accumulator.accumulation[perspective][i][j * kTileHeight]);
|
|
||||||
|
|
||||||
for (IndexType k = 0; k < kNumRegs; ++k)
|
|
||||||
acc[k] = vec_load(&prevAccTile[k]);
|
|
||||||
|
|
||||||
|
for (IndexType i = 0; info[i]; ++i)
|
||||||
|
{
|
||||||
// Difference calculation for the deactivated features
|
// Difference calculation for the deactivated features
|
||||||
for (const auto index : removed_indices[perspective]) {
|
for (const auto index : removed[i])
|
||||||
|
{
|
||||||
const IndexType offset = kHalfDimensions * index + j * kTileHeight;
|
const IndexType offset = kHalfDimensions * index + j * kTileHeight;
|
||||||
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
|
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
|
||||||
|
|
||||||
for (IndexType k = 0; k < kNumRegs; ++k)
|
for (IndexType k = 0; k < kNumRegs; ++k)
|
||||||
acc[k] = vec_sub_16(acc[k], column[k]);
|
acc[k] = vec_sub_16(acc[k], column[k]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{ // Difference calculation for the activated features
|
// Difference calculation for the activated features
|
||||||
for (const auto index : added_indices[perspective]) {
|
for (const auto index : added[i])
|
||||||
|
{
|
||||||
const IndexType offset = kHalfDimensions * index + j * kTileHeight;
|
const IndexType offset = kHalfDimensions * index + j * kTileHeight;
|
||||||
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
|
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
|
||||||
|
|
||||||
for (IndexType k = 0; k < kNumRegs; ++k)
|
for (IndexType k = 0; k < kNumRegs; ++k)
|
||||||
acc[k] = vec_add_16(acc[k], column[k]);
|
acc[k] = vec_add_16(acc[k], column[k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store accumulator
|
||||||
|
accTile = reinterpret_cast<vec_t*>(
|
||||||
|
&info[i]->accumulator.accumulation[c][0][j * kTileHeight]);
|
||||||
|
for (IndexType k = 0; k < kNumRegs; ++k)
|
||||||
|
vec_store(&accTile[k], acc[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
for (IndexType i = 0; info[i]; ++i)
|
||||||
|
{
|
||||||
|
std::memcpy(info[i]->accumulator.accumulation[c][0],
|
||||||
|
st->accumulator.accumulation[c][0],
|
||||||
|
kHalfDimensions * sizeof(BiasType));
|
||||||
|
st = info[i];
|
||||||
|
|
||||||
|
// Difference calculation for the deactivated features
|
||||||
|
for (const auto index : removed[i])
|
||||||
|
{
|
||||||
|
const IndexType offset = kHalfDimensions * index;
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < kHalfDimensions; ++j)
|
||||||
|
st->accumulator.accumulation[c][0][j] -= weights_[offset + j];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Difference calculation for the activated features
|
||||||
|
for (const auto index : added[i])
|
||||||
|
{
|
||||||
|
const IndexType offset = kHalfDimensions * index;
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < kHalfDimensions; ++j)
|
||||||
|
st->accumulator.accumulation[c][0][j] += weights_[offset + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Refresh the accumulator
|
||||||
|
auto& accumulator = pos.state()->accumulator;
|
||||||
|
accumulator.state[c] = COMPUTED;
|
||||||
|
Features::IndexList active;
|
||||||
|
Features::HalfKP<Features::Side::kFriend>::AppendActiveIndices(pos, c, &active);
|
||||||
|
|
||||||
|
#ifdef VECTOR
|
||||||
|
for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j)
|
||||||
|
{
|
||||||
|
auto biasesTile = reinterpret_cast<const vec_t*>(
|
||||||
|
&biases_[j * kTileHeight]);
|
||||||
for (IndexType k = 0; k < kNumRegs; ++k)
|
for (IndexType k = 0; k < kNumRegs; ++k)
|
||||||
|
acc[k] = biasesTile[k];
|
||||||
|
|
||||||
|
for (const auto index : active)
|
||||||
|
{
|
||||||
|
const IndexType offset = kHalfDimensions * index + j * kTileHeight;
|
||||||
|
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
|
||||||
|
|
||||||
|
for (unsigned k = 0; k < kNumRegs; ++k)
|
||||||
|
acc[k] = vec_add_16(acc[k], column[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto accTile = reinterpret_cast<vec_t*>(
|
||||||
|
&accumulator.accumulation[c][0][j * kTileHeight]);
|
||||||
|
for (unsigned k = 0; k < kNumRegs; k++)
|
||||||
vec_store(&accTile[k], acc[k]);
|
vec_store(&accTile[k], acc[k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
std::memcpy(accumulator.accumulation[c][0], biases_,
|
||||||
|
kHalfDimensions * sizeof(BiasType));
|
||||||
|
|
||||||
|
for (const auto index : active)
|
||||||
|
{
|
||||||
|
const IndexType offset = kHalfDimensions * index;
|
||||||
|
|
||||||
|
for (IndexType j = 0; j < kHalfDimensions; ++j)
|
||||||
|
accumulator.accumulation[c][0][j] += weights_[offset + j];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#if defined(USE_MMX)
|
|
||||||
|
#if defined(USE_MMX)
|
||||||
_mm_empty();
|
_mm_empty();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
|
||||||
for (Color perspective : { WHITE, BLACK }) {
|
|
||||||
|
|
||||||
if (reset[perspective]) {
|
|
||||||
if (i == 0) {
|
|
||||||
std::memcpy(accumulator.accumulation[perspective][i], biases_,
|
|
||||||
kHalfDimensions * sizeof(BiasType));
|
|
||||||
} else {
|
|
||||||
std::memset(accumulator.accumulation[perspective][i], 0,
|
|
||||||
kHalfDimensions * sizeof(BiasType));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::memcpy(accumulator.accumulation[perspective][i],
|
|
||||||
prev_accumulator.accumulation[perspective][i],
|
|
||||||
kHalfDimensions * sizeof(BiasType));
|
|
||||||
// Difference calculation for the deactivated features
|
|
||||||
for (const auto index : removed_indices[perspective]) {
|
|
||||||
const IndexType offset = kHalfDimensions * index;
|
|
||||||
|
|
||||||
for (IndexType j = 0; j < kHalfDimensions; ++j)
|
|
||||||
accumulator.accumulation[perspective][i][j] -= weights_[offset + j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{ // Difference calculation for the activated features
|
|
||||||
for (const auto index : added_indices[perspective]) {
|
|
||||||
const IndexType offset = kHalfDimensions * index;
|
|
||||||
|
|
||||||
for (IndexType j = 0; j < kHalfDimensions; ++j)
|
|
||||||
accumulator.accumulation[perspective][i][j] += weights_[offset + j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
accumulator.computed_accumulation = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using BiasType = std::int16_t;
|
using BiasType = std::int16_t;
|
||||||
using WeightType = std::int16_t;
|
using WeightType = std::int16_t;
|
||||||
|
|
||||||
// Make the learning class a friend
|
|
||||||
friend class Trainer<FeatureTransformer>;
|
|
||||||
|
|
||||||
alignas(kCacheLineSize) BiasType biases_[kHalfDimensions];
|
alignas(kCacheLineSize) BiasType biases_[kHalfDimensions];
|
||||||
alignas(kCacheLineSize)
|
alignas(kCacheLineSize)
|
||||||
WeightType weights_[kHalfDimensions * kInputDimensions];
|
WeightType weights_[kHalfDimensions * kInputDimensions];
|
||||||
|
|||||||
+4
-2
@@ -707,7 +707,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||||||
++st->pliesFromNull;
|
++st->pliesFromNull;
|
||||||
|
|
||||||
// Used by NNUE
|
// Used by NNUE
|
||||||
st->accumulator.computed_accumulation = false;
|
st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
|
||||||
|
st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
|
||||||
auto& dp = st->dirtyPiece;
|
auto& dp = st->dirtyPiece;
|
||||||
dp.dirty_num = 1;
|
dp.dirty_num = 1;
|
||||||
|
|
||||||
@@ -1006,7 +1007,8 @@ void Position::do_null_move(StateInfo& newSt) {
|
|||||||
st = &newSt;
|
st = &newSt;
|
||||||
|
|
||||||
// Used by NNUE
|
// Used by NNUE
|
||||||
st->accumulator.computed_accumulation = false;
|
st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
|
||||||
|
st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
|
||||||
auto& dp = st->dirtyPiece;
|
auto& dp = st->dirtyPiece;
|
||||||
dp.dirty_num = 0;
|
dp.dirty_num = 0;
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -197,7 +197,7 @@ public:
|
|||||||
//static std::string sfen_from_rawdata(Piece board[81], Hand hands[2], Color turn, int gamePly);
|
//static std::string sfen_from_rawdata(Piece board[81], Hand hands[2], Color turn, int gamePly);
|
||||||
|
|
||||||
// Returns the position of the ball on the c side.
|
// Returns the position of the ball on the c side.
|
||||||
Square king_square(Color c) const { return pieceList[make_piece(c, KING)][0]; }
|
Square king_square(Color c) const { return lsb(pieces(c, KING)); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Initialization helpers (used while setting up a position)
|
// Initialization helpers (used while setting up a position)
|
||||||
@@ -445,6 +445,8 @@ inline StateInfo* Position::state() const {
|
|||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* const StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||||
|
|
||||||
} // namespace Stockfish
|
} // namespace Stockfish
|
||||||
|
|
||||||
#endif // #ifndef POSITION_H_INCLUDED
|
#endif // #ifndef POSITION_H_INCLUDED
|
||||||
|
|||||||
+3
-3
@@ -222,7 +222,7 @@ void MainThread::search() {
|
|||||||
Time.init(Limits, us, rootPos.game_ply());
|
Time.init(Limits, us, rootPos.game_ply());
|
||||||
TT.new_search();
|
TT.new_search();
|
||||||
|
|
||||||
Eval::NNUE::verify_eval_file_loaded();
|
Eval::NNUE::verify();
|
||||||
|
|
||||||
if (rootMoves.empty())
|
if (rootMoves.empty())
|
||||||
{
|
{
|
||||||
@@ -2143,7 +2143,7 @@ namespace Search
|
|||||||
return { mated_in(/*ss->ply*/ 0 + 1), {} };
|
return { mated_in(/*ss->ply*/ 0 + 1), {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bestValue = ::qsearch<PV>(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, 0);
|
auto bestValue = Stockfish::qsearch<PV>(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, 0);
|
||||||
|
|
||||||
// Returns the PV obtained.
|
// Returns the PV obtained.
|
||||||
std::vector<Move> pvs;
|
std::vector<Move> pvs;
|
||||||
@@ -2249,7 +2249,7 @@ namespace Search
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Depth adjustedDepth = std::max(1, rootDepth);
|
Depth adjustedDepth = std::max(1, rootDepth);
|
||||||
bestValue = ::search<PV>(pos, ss, alpha, beta, adjustedDepth, false);
|
bestValue = Stockfish::search<PV>(pos, ss, alpha, beta, adjustedDepth, false);
|
||||||
|
|
||||||
stable_sort(rootMoves.begin() + pvIdx, rootMoves.end());
|
stable_sort(rootMoves.begin() + pvIdx, rootMoves.end());
|
||||||
//my_stable_sort(pos.this_thread()->thread_id(),&rootMoves[0] + pvIdx, rootMoves.size() - pvIdx);
|
//my_stable_sort(pos.this_thread()->thread_id(),&rootMoves[0] + pvIdx, rootMoves.size() - pvIdx);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace Tools
|
namespace Stockfish::Tools
|
||||||
{
|
{
|
||||||
bool fen_is_ok(Position& pos, std::string input_fen) {
|
bool fen_is_ok(Position& pos, std::string input_fen) {
|
||||||
std::string pos_fen = pos.fen();
|
std::string pos_fen = pos.fen();
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
void convert(std::istringstream& is);
|
void convert(std::istringstream& is);
|
||||||
|
|
||||||
void convert_bin_from_pgn_extract(std::istringstream& is);
|
void convert_bin_from_pgn_extract(std::istringstream& is);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace Tools
|
namespace Stockfish::Tools
|
||||||
{
|
{
|
||||||
// Class to generate sfen with multiple threads
|
// Class to generate sfen with multiple threads
|
||||||
struct Gensfen
|
struct Gensfen
|
||||||
@@ -962,7 +962,7 @@ namespace Tools
|
|||||||
<< " - draw by insuff. mat. = " << params.detect_draw_by_insufficient_mating_material << endl;
|
<< " - draw by insuff. mat. = " << params.detect_draw_by_insufficient_mating_material << endl;
|
||||||
|
|
||||||
// Show if the training data generator uses NNUE.
|
// Show if the training data generator uses NNUE.
|
||||||
Eval::NNUE::verify_eval_file_loaded();
|
Eval::NNUE::verify();
|
||||||
|
|
||||||
Threads.main()->ponder = false;
|
Threads.main()->ponder = false;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
// Automatic generation of teacher position
|
// Automatic generation of teacher position
|
||||||
void gensfen(std::istringstream& is);
|
void gensfen(std::istringstream& is);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace Tools
|
namespace Stockfish::Tools
|
||||||
{
|
{
|
||||||
// Class to generate sfen with multiple threads
|
// Class to generate sfen with multiple threads
|
||||||
struct GensfenNonPv
|
struct GensfenNonPv
|
||||||
@@ -476,7 +476,7 @@ namespace Tools
|
|||||||
<< " - count = " << count << endl;
|
<< " - count = " << count << endl;
|
||||||
|
|
||||||
// Show if the training data generator uses NNUE.
|
// Show if the training data generator uses NNUE.
|
||||||
Eval::NNUE::verify_eval_file_loaded();
|
Eval::NNUE::verify();
|
||||||
|
|
||||||
Threads.main()->ponder = false;
|
Threads.main()->ponder = false;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
// Automatic generation of teacher position
|
// Automatic generation of teacher position
|
||||||
void gensfen_nonpv(std::istringstream& is);
|
void gensfen_nonpv(std::istringstream& is);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
EpdOpeningBook::EpdOpeningBook(const std::string& file, PRNG& prng) :
|
EpdOpeningBook::EpdOpeningBook(const std::string& file, PRNG& prng) :
|
||||||
OpeningBook(file)
|
OpeningBook(file)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
struct OpeningBook {
|
struct OpeningBook {
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
// packed sfen
|
// packed sfen
|
||||||
struct PackedSfen { std::uint8_t data[32]; };
|
struct PackedSfen { std::uint8_t data[32]; };
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
// Class that handles bitstream
|
// Class that handles bitstream
|
||||||
// useful when doing aspect encoding
|
// useful when doing aspect encoding
|
||||||
@@ -260,15 +260,11 @@ namespace Tools {
|
|||||||
|
|
||||||
pos.clear();
|
pos.clear();
|
||||||
std::memset(si, 0, sizeof(StateInfo));
|
std::memset(si, 0, sizeof(StateInfo));
|
||||||
std::fill_n(&pos.pieceList[0][0], sizeof(pos.pieceList) / sizeof(Square), SQ_NONE);
|
|
||||||
pos.st = si;
|
pos.st = si;
|
||||||
|
|
||||||
// Active color
|
// Active color
|
||||||
pos.sideToMove = (Color)stream.read_one_bit();
|
pos.sideToMove = (Color)stream.read_one_bit();
|
||||||
|
|
||||||
pos.pieceList[W_KING][0] = SQUARE_NB;
|
|
||||||
pos.pieceList[B_KING][0] = SQUARE_NB;
|
|
||||||
|
|
||||||
// First the position of the ball
|
// First the position of the ball
|
||||||
for (auto c : Colors)
|
for (auto c : Colors)
|
||||||
pos.board[stream.read_n_bit(6)] = make_piece(c, KING);
|
pos.board[stream.read_n_bit(6)] = make_piece(c, KING);
|
||||||
|
|||||||
@@ -7,11 +7,13 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
class Position;
|
namespace Stockfish {
|
||||||
struct StateInfo;
|
class Position;
|
||||||
class Thread;
|
struct StateInfo;
|
||||||
|
class Thread;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
int set_from_packed_sfen(Position& pos, const PackedSfen& sfen, StateInfo* si, Thread* th);
|
int set_from_packed_sfen(Position& pos, const PackedSfen& sfen, StateInfo* si, Thread* th);
|
||||||
PackedSfen sfen_pack(Position& pos);
|
PackedSfen sfen_pack(Position& pos);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace Tools{
|
namespace Stockfish::Tools{
|
||||||
|
|
||||||
enum struct SfenReaderMode
|
enum struct SfenReaderMode
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
enum struct SfenOutputType
|
enum struct SfenOutputType
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,9 +17,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
using namespace std;
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
namespace Tools {
|
|
||||||
|
|
||||||
// Helper class for exporting Sfen
|
// Helper class for exporting Sfen
|
||||||
struct SfenWriter
|
struct SfenWriter
|
||||||
@@ -28,13 +26,13 @@ namespace Tools {
|
|||||||
static constexpr size_t SFEN_WRITE_SIZE = 5000;
|
static constexpr size_t SFEN_WRITE_SIZE = 5000;
|
||||||
|
|
||||||
// File name to write and number of threads to create
|
// File name to write and number of threads to create
|
||||||
SfenWriter(string filename_, int thread_num, uint64_t save_count, SfenOutputType sfen_output_type)
|
SfenWriter(std::string filename_, int thread_num, uint64_t save_count, SfenOutputType sfen_output_type)
|
||||||
{
|
{
|
||||||
sfen_buffers_pool.reserve((size_t)thread_num * 10);
|
sfen_buffers_pool.reserve((size_t)thread_num * 10);
|
||||||
sfen_buffers.resize(thread_num);
|
sfen_buffers.resize(thread_num);
|
||||||
|
|
||||||
auto out = sync_region_cout.new_region();
|
auto out = sync_region_cout.new_region();
|
||||||
out << "INFO (sfen_writer): Creating new data file at " << filename_ << endl;
|
out << "INFO (sfen_writer): Creating new data file at " << filename_ << std::endl;
|
||||||
|
|
||||||
sfen_format = sfen_output_type;
|
sfen_format = sfen_output_type;
|
||||||
output_file_stream = create_new_sfen_output(filename_, sfen_format);
|
output_file_stream = create_new_sfen_output(filename_, sfen_format);
|
||||||
@@ -121,7 +119,7 @@ namespace Tools {
|
|||||||
{
|
{
|
||||||
while (!finished || sfen_buffers_pool.size())
|
while (!finished || sfen_buffers_pool.size())
|
||||||
{
|
{
|
||||||
vector<std::unique_ptr<PSVector>> buffers;
|
std::vector<std::unique_ptr<PSVector>> buffers;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mutex);
|
std::unique_lock<std::mutex> lk(mutex);
|
||||||
|
|
||||||
@@ -157,11 +155,11 @@ namespace Tools {
|
|||||||
// Rename the file and open it again.
|
// Rename the file and open it again.
|
||||||
// Add ios::app in consideration of overwriting.
|
// Add ios::app in consideration of overwriting.
|
||||||
// (Depending on the operation, it may not be necessary.)
|
// (Depending on the operation, it may not be necessary.)
|
||||||
string new_filename = filename + "_" + std::to_string(n);
|
std::string new_filename = filename + "_" + std::to_string(n);
|
||||||
output_file_stream = create_new_sfen_output(new_filename, sfen_format);
|
output_file_stream = create_new_sfen_output(new_filename, sfen_format);
|
||||||
|
|
||||||
auto out = sync_region_cout.new_region();
|
auto out = sync_region_cout.new_region();
|
||||||
out << "INFO (sfen_writer): Creating new data file at " << new_filename << endl;
|
out << "INFO (sfen_writer): Creating new data file at " << new_filename << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,7 +180,7 @@ namespace Tools {
|
|||||||
std::thread file_worker_thread;
|
std::thread file_worker_thread;
|
||||||
|
|
||||||
// Flag that all threads have finished
|
// Flag that all threads have finished
|
||||||
atomic<bool> finished;
|
std::atomic<bool> finished;
|
||||||
|
|
||||||
SfenOutputType sfen_format;
|
SfenOutputType sfen_format;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -25,7 +25,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace Tools::Stats
|
namespace Stockfish::Tools::Stats
|
||||||
{
|
{
|
||||||
struct StatisticGathererBase
|
struct StatisticGathererBase
|
||||||
{
|
{
|
||||||
@@ -398,7 +398,7 @@ namespace Tools::Stats
|
|||||||
m_castling += 1;
|
m_castling += 1;
|
||||||
else if (type_of(move) == PROMOTION)
|
else if (type_of(move) == PROMOTION)
|
||||||
m_promotion += 1;
|
m_promotion += 1;
|
||||||
else if (type_of(move) == ENPASSANT)
|
else if (type_of(move) == EN_PASSANT)
|
||||||
m_enpassant += 1;
|
m_enpassant += 1;
|
||||||
else if (type_of(move) == NORMAL)
|
else if (type_of(move) == NORMAL)
|
||||||
m_normal += 1;
|
m_normal += 1;
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace Tools::Stats {
|
namespace Stockfish::Tools::Stats {
|
||||||
|
|
||||||
void gather_statistics(std::istringstream& is);
|
void gather_statistics(std::istringstream& is);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace Tools
|
namespace Stockfish::Tools
|
||||||
{
|
{
|
||||||
using CommandFunc = void(*)(std::istringstream&);
|
using CommandFunc = void(*)(std::istringstream&);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace Tools {
|
namespace Stockfish::Tools {
|
||||||
|
|
||||||
void transform(std::istringstream& is);
|
void transform(std::istringstream& is);
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -45,9 +45,6 @@ namespace Stockfish {
|
|||||||
|
|
||||||
extern vector<string> setup_bench(const Position&, istream&);
|
extern vector<string> setup_bench(const Position&, istream&);
|
||||||
|
|
||||||
// FEN string of the initial position, normal chess
|
|
||||||
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// position() is called when engine receives the "position" UCI command.
|
// position() is called when engine receives the "position" UCI command.
|
||||||
@@ -93,7 +90,7 @@ namespace {
|
|||||||
Position p;
|
Position p;
|
||||||
p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main());
|
p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main());
|
||||||
|
|
||||||
Eval::NNUE::verify_eval_file_loaded();
|
Eval::NNUE::verify();
|
||||||
|
|
||||||
sync_cout << "\n" << Eval::trace(p) << sync_endl;
|
sync_cout << "\n" << Eval::trace(p) << sync_endl;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user