/* 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 . */ // Code for calculating NNUE evaluation function #include #include #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 feature_transformer; // Evaluation function AlignedPtr network; // Evaluation function file name std::string fileName; namespace Detail { // Initialize the evaluation function parameters template void Initialize(AlignedPtr& pointer) { pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); std::memset(pointer.get(), 0, sizeof(T)); } template void Initialize(LargePagePtr& pointer) { static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); pointer.reset(reinterpret_cast(aligned_large_pages_alloc(sizeof(T)))); std::memset(pointer.get(), 0, sizeof(T)); } // Read evaluation function parameters template bool ReadParameters(std::istream& stream, T& reference) { std::uint32_t header; header = read_little_endian(stream); if (!stream || header != T::GetHashValue()) return false; return reference.ReadParameters(stream); } } // namespace Detail // Initialize the evaluation function parameters void Initialize() { Detail::Initialize(feature_transformer); Detail::Initialize(network); } // Read network header bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture) { std::uint32_t version, size; version = read_little_endian(stream); *hash_value = read_little_endian(stream); size = read_little_endian(stream); if (!stream || version != kVersion) return false; architecture->resize(size); stream.read(&(*architecture)[0], size); return !stream.fail(); } // Read network parameters bool ReadParameters(std::istream& stream) { std::uint32_t hash_value; std::string architecture; if (!ReadHeader(stream, &hash_value, &architecture)) return false; if (hash_value != kHashValue) return false; if (!Detail::ReadParameters(stream, *feature_transformer)) return false; if (!Detail::ReadParameters(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 = kCacheLineSize; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformed_features_unaligned[ FeatureTransformer::kBufferSize + alignment / sizeof(TransformedFeatureType)]; char buffer_unaligned[Network::kBufferSize + alignment]; auto* transformed_features = align_ptr_up(&transformed_features_unaligned[0]); auto* buffer = align_ptr_up(&buffer_unaligned[0]); #else alignas(alignment) TransformedFeatureType transformed_features[FeatureTransformer::kBufferSize]; alignas(alignment) char buffer[Network::kBufferSize]; #endif ASSERT_ALIGNED(transformed_features, alignment); ASSERT_ALIGNED(buffer, alignment); feature_transformer->Transform(pos, transformed_features); const auto output = network->Propagate(transformed_features, buffer); return static_cast(output[0] / FV_SCALE); } // Load eval, from a file stream or a memory stream bool load_eval(std::string name, std::istream& stream) { Initialize(); fileName = name; return ReadParameters(stream); } } // namespace Stockfish::Eval::NNUE