Post-merge fixes.

This commit is contained in:
Tomasz Sobczyk
2021-04-18 20:33:17 +02:00
parent 08e255960d
commit 19f712cdbb
33 changed files with 372 additions and 768 deletions
+119
View File
@@ -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 };
+18
View File
@@ -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
View File
@@ -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
View File
@@ -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
-12
View File
@@ -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
View File
@@ -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
+2 -7
View File
@@ -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
+1
View File
@@ -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>;
-44
View File
@@ -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];
+6 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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);
+2 -2
View File
@@ -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
View File
@@ -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);
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -13,7 +13,7 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
namespace Tools { namespace Stockfish::Tools {
struct OpeningBook { struct OpeningBook {
+1 -1
View File
@@ -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]; };
+1 -5
View File
@@ -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);
+6 -4
View File
@@ -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);
+1 -1
View File
@@ -17,7 +17,7 @@
#include <thread> #include <thread>
#include <functional> #include <functional>
namespace Tools{ namespace Stockfish::Tools{
enum struct SfenReaderMode enum struct SfenReaderMode
{ {
+1 -1
View File
@@ -10,7 +10,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
namespace Tools { namespace Stockfish::Tools {
enum struct SfenOutputType enum struct SfenOutputType
{ {
+7 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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&);
+1 -1
View File
@@ -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
View File
@@ -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;
} }