145 lines
4.6 KiB
C++
145 lines
4.6 KiB
C++
/*
|
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
|
|
|
Stockfish is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Stockfish is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// Code for calculating NNUE evaluation function
|
|
|
|
#include <iostream>
|
|
#include <set>
|
|
|
|
#include "../evaluate.h"
|
|
#include "../position.h"
|
|
#include "../misc.h"
|
|
#include "../uci.h"
|
|
#include "../types.h"
|
|
|
|
#include "evaluate_nnue.h"
|
|
|
|
namespace Stockfish::Eval::NNUE {
|
|
|
|
// Input feature converter
|
|
LargePagePtr<FeatureTransformer> featureTransformer;
|
|
|
|
// Evaluation function
|
|
AlignedPtr<Network> network;
|
|
|
|
// Evaluation function file name
|
|
std::string fileName;
|
|
|
|
namespace Detail {
|
|
|
|
// Initialize the evaluation function parameters
|
|
template <typename T>
|
|
void initialize(AlignedPtr<T>& pointer) {
|
|
|
|
pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
|
|
std::memset(pointer.get(), 0, sizeof(T));
|
|
}
|
|
|
|
template <typename T>
|
|
void initialize(LargePagePtr<T>& pointer) {
|
|
|
|
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))));
|
|
std::memset(pointer.get(), 0, sizeof(T));
|
|
}
|
|
|
|
// Read evaluation function parameters
|
|
template <typename T>
|
|
bool read_parameters(std::istream& stream, T& reference) {
|
|
|
|
std::uint32_t header;
|
|
header = read_little_endian<std::uint32_t>(stream);
|
|
if (!stream || header != T::get_hash_value()) return false;
|
|
return reference.read_parameters(stream);
|
|
}
|
|
|
|
} // namespace Detail
|
|
|
|
// Initialize the evaluation function parameters
|
|
void initialize() {
|
|
|
|
Detail::initialize(featureTransformer);
|
|
Detail::initialize(network);
|
|
}
|
|
|
|
// Read network header
|
|
bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* architecture)
|
|
{
|
|
std::uint32_t version, size;
|
|
|
|
version = read_little_endian<std::uint32_t>(stream);
|
|
*hashValue = read_little_endian<std::uint32_t>(stream);
|
|
size = read_little_endian<std::uint32_t>(stream);
|
|
if (!stream || version != Version) return false;
|
|
architecture->resize(size);
|
|
stream.read(&(*architecture)[0], size);
|
|
return !stream.fail();
|
|
}
|
|
|
|
// Read network parameters
|
|
bool read_parameters(std::istream& stream) {
|
|
|
|
std::uint32_t hashValue;
|
|
std::string architecture;
|
|
if (!read_header(stream, &hashValue, &architecture)) return false;
|
|
if (hashValue != HashValue) return false;
|
|
if (!Detail::read_parameters(stream, *featureTransformer)) return false;
|
|
if (!Detail::read_parameters(stream, *network)) return false;
|
|
return stream && stream.peek() == std::ios::traits_type::eof();
|
|
}
|
|
|
|
// Evaluation function. Perform differential calculation.
|
|
Value evaluate(const Position& pos) {
|
|
|
|
// We manually align the arrays on the stack because with gcc < 9.3
|
|
// overaligning stack variables with alignas() doesn't work correctly.
|
|
|
|
constexpr uint64_t alignment = CacheLineSize;
|
|
|
|
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
|
|
TransformedFeatureType transformedFeaturesUnaligned[
|
|
FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
|
|
char bufferUnaligned[Network::BufferSize + alignment];
|
|
|
|
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
|
|
auto* buffer = align_ptr_up<alignment>(&bufferUnaligned[0]);
|
|
#else
|
|
alignas(alignment)
|
|
TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
|
|
alignas(alignment) char buffer[Network::BufferSize];
|
|
#endif
|
|
|
|
ASSERT_ALIGNED(transformedFeatures, alignment);
|
|
ASSERT_ALIGNED(buffer, alignment);
|
|
|
|
featureTransformer->transform(pos, transformedFeatures);
|
|
const auto output = network->propagate(transformedFeatures, buffer);
|
|
|
|
return static_cast<Value>(output[0] / OutputScale);
|
|
}
|
|
|
|
// Load eval, from a file stream or a memory stream
|
|
bool load_eval(std::string name, std::istream& stream) {
|
|
|
|
initialize();
|
|
fileName = name;
|
|
return read_parameters(stream);
|
|
}
|
|
|
|
} // namespace Stockfish::Eval::NNUE
|