Add a HalfKA architecture (a product of K - king, and A - any piece) along with all required infrastructure. HalfKA doesn't discriminate kings compared to HalfKP. Keep old architecture as the default one.

This commit is contained in:
Tomasz Sobczyk
2020-10-28 15:24:29 +01:00
committed by nodchip
parent 317fda2516
commit ec9e49e875
10 changed files with 573 additions and 0 deletions

50
src/nnue/features/a.cpp Normal file
View File

@@ -0,0 +1,50 @@
#include "a.h"
#include "index_list.h"
// Definition of input feature A of NNUE evaluation function
namespace Eval::NNUE::Features {
// Orient a square according to perspective (flip rank for black)
inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * SQ_A8));
}
// Find the index of the feature quantity from the king position and PieceSquare
inline IndexType A::make_index(
Color perspective, Square s, Piece pc) {
return IndexType(orient(perspective, s) + kpp_board_index[pc][perspective]);
}
// Get a list of indices with a value of 1 among the features
void A::append_active_indices(
const Position& pos,
Color perspective,
IndexList* active) {
Bitboard bb = pos.pieces();
while (bb) {
Square s = pop_lsb(&bb);
active->push_back(make_index(perspective, s, pos.piece_on(s)));
}
}
// Get a list of indices whose values have changed from the previous one in the feature quantity
void A::append_changed_indices(
const Position& pos,
Color perspective,
IndexList* removed,
IndexList* added) {
const auto& dp = pos.state()->dirtyPiece;
for (int i = 0; i < dp.dirty_num; ++i) {
Piece pc = dp.piece[i];
if (dp.from[i] != SQ_NONE)
removed->push_back(make_index(perspective, dp.from[i], pc));
if (dp.to[i] != SQ_NONE)
added->push_back(make_index(perspective, dp.to[i], pc));
}
}
} // namespace Eval::NNUE::Features

54
src/nnue/features/a.h Normal file
View File

@@ -0,0 +1,54 @@
#ifndef _NNUE_FEATURES_A_H_
#define _NNUE_FEATURES_A_H_
#include "features_common.h"
#include "evaluate.h"
// Definition of input feature A of NNUE evaluation function
// A is a union of P features and K features, so technically the
// same effect can be achieved by including both P and K features
// but it would result in slower index appending because
// P would conditionally exclude K features and vice versa,
// where A doesn't have any conditionals.
namespace Eval::NNUE::Features {
// Feature P: PieceSquare of pieces other than balls
class A {
public:
// feature quantity name
static constexpr const char* kName = "A";
// Hash value embedded in the evaluation function file
static constexpr std::uint32_t kHashValue = 0x7A4C414Cu;
// number of feature dimensions
static constexpr IndexType kDimensions = PS_END2;
// The maximum value of the number of indexes whose value is 1 at the same time among the feature values
static constexpr IndexType kMaxActiveDimensions = 32;
// Timing of full calculation instead of difference calculation
static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kNone;
// Get a list of indices with a value of 1 among the features
static void append_active_indices(
const Position& pos,
Color perspective,
IndexList* active);
// Get a list of indices whose values have changed from the previous one in the feature quantity
static void append_changed_indices(
const Position& pos,
Color perspective,
IndexList* removed,
IndexList* added);
private:
// Index of a feature for a given piece on some square
static IndexType make_index(Color perspective, Square s, Piece pc);
};
} // namespace Eval::NNUE::Features
#endif // #ifndef _NNUE_FEATURES_UNION_P_K_H_

View File

@@ -0,0 +1,89 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 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/>.
*/
//Definition of input features HalfKA of NNUE evaluation function
#include "half_ka.h"
#include "index_list.h"
namespace Eval::NNUE::Features {
// Orient a square according to perspective (flip rank for black)
inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * SQ_A8));
}
// Find the index of the feature quantity from the king position and PieceSquare
template <Side AssociatedKing>
inline IndexType HalfKA<AssociatedKing>::make_index(
Color perspective,
Square s,
Piece pc,
Square ksq) {
return IndexType(orient(perspective, s) + kpp_board_index[pc][perspective] + PS_END2 * ksq);
}
// Get a list of indices for active features
template <Side AssociatedKing>
void HalfKA<AssociatedKing>::append_active_indices(
const Position& pos,
Color perspective,
IndexList* active) {
Square ksq = orient(
perspective,
pos.square<KING>(
AssociatedKing == Side::kFriend ? perspective : ~perspective));
Bitboard bb = pos.pieces();
while (bb) {
Square s = pop_lsb(&bb);
active->push_back(make_index(perspective, s, pos.piece_on(s), ksq));
}
}
// Get a list of indices for recently changed features
template <Side AssociatedKing>
void HalfKA<AssociatedKing>::append_changed_indices(
const Position& pos,
Color perspective,
IndexList* removed,
IndexList* added) {
Square ksq = orient(
perspective,
pos.square<KING>(
AssociatedKing == Side::kFriend ? perspective : ~perspective));
const auto& dp = pos.state()->dirtyPiece;
for (int i = 0; i < dp.dirty_num; ++i) {
Piece pc = dp.piece[i];
if (dp.from[i] != SQ_NONE)
removed->push_back(make_index(perspective, dp.from[i], pc, ksq));
if (dp.to[i] != SQ_NONE)
added->push_back(make_index(perspective, dp.to[i], pc, ksq));
}
}
template class HalfKA<Side::kFriend>;
template class HalfKA<Side::kEnemy>;
} // namespace Eval::NNUE::Features

View File

@@ -0,0 +1,75 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 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/>.
*/
#ifndef NNUE_FEATURES_HALF_KA_H_INCLUDED
#define NNUE_FEATURES_HALF_KA_H_INCLUDED
#include "features_common.h"
#include "evaluate.h"
//Definition of input features HalfKPK of NNUE evaluation function
namespace Eval::NNUE::Features {
// Feature HalfKPK: Combination of the position of own king
// and the position of pieces other than kings
template <Side AssociatedKing>
class HalfKA {
public:
// Feature name
static constexpr const char* kName = (AssociatedKing == Side::kFriend) ?
"HalfKA(Friend)" : "HalfKA(Enemy)";
// Hash value embedded in the evaluation file
static constexpr std::uint32_t kHashValue =
0x5F134CB9u ^ (AssociatedKing == Side::kFriend);
// Number of feature dimensions
static constexpr IndexType kDimensions =
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_END2);
// Maximum number of simultaneously active features
static constexpr IndexType kMaxActiveDimensions = 32;
// Trigger for full calculation instead of difference calculation
static constexpr TriggerEvent kRefreshTrigger =
(AssociatedKing == Side::kFriend) ?
TriggerEvent::kFriendKingMoved : TriggerEvent::kEnemyKingMoved;
// Get a list of indices for active features
static void append_active_indices(
const Position& pos,
Color perspective,
IndexList* active);
// Get a list of indices for recently changed features
static void append_changed_indices(
const Position& pos,
Color perspective,
IndexList* removed,
IndexList* added);
private:
// Index of a feature for a given king position and another piece on some square
static IndexType make_index(Color perspective, Square s, Piece pc, Square sq_k);
};
} // namespace Eval::NNUE::Features
#endif // #ifndef NNUE_FEATURES_HALF_KA_H_INCLUDED

View File

@@ -0,0 +1,86 @@
#include "half_relative_ka.h"
#include "index_list.h"
//Definition of input features HalfRelativeKA of NNUE evaluation function
namespace Eval::NNUE::Features {
// Orient a square according to perspective (flip rank for black)
inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * SQ_A8));
}
// Find the index of the feature quantity from the ball position and PieceSquare
template <Side AssociatedKing>
inline IndexType HalfRelativeKA<AssociatedKing>::make_index(
Color perspective,
Square s,
Piece pc,
Square sq_k) {
const IndexType p = IndexType(orient(perspective, s) + kpp_board_index[pc][perspective]);
return make_index(sq_k, p);
}
// Find the index of the feature quantity from the ball position and PieceSquare
template <Side AssociatedKing>
inline IndexType HalfRelativeKA<AssociatedKing>::make_index(
Square sq_k,
IndexType p) {
constexpr IndexType W = kBoardWidth;
constexpr IndexType H = kBoardHeight;
const IndexType piece_index = (p - PS_W_PAWN) / SQUARE_NB;
const Square sq_p = static_cast<Square>((p - PS_W_PAWN) % SQUARE_NB);
const IndexType relative_file = file_of(sq_p) - file_of(sq_k) + (W / 2);
const IndexType relative_rank = rank_of(sq_p) - rank_of(sq_k) + (H / 2);
return H * W * piece_index + H * relative_file + relative_rank;
}
// Get a list of indices with a value of 1 among the features
template <Side AssociatedKing>
void HalfRelativeKA<AssociatedKing>::append_active_indices(
const Position& pos,
Color perspective,
IndexList* active) {
Square ksq = orient(
perspective,
pos.square<KING>(
AssociatedKing == Side::kFriend ? perspective : ~perspective));
Bitboard bb = pos.pieces();
while (bb) {
Square s = pop_lsb(&bb);
active->push_back(make_index(perspective, s, pos.piece_on(s), ksq));
}
}
// Get a list of indices whose values have changed from the previous one in the feature quantity
template <Side AssociatedKing>
void HalfRelativeKA<AssociatedKing>::append_changed_indices(
const Position& pos,
Color perspective,
IndexList* removed,
IndexList* added) {
Square ksq = orient(
perspective,
pos.square<KING>(
AssociatedKing == Side::kFriend ? perspective : ~perspective));
const auto& dp = pos.state()->dirtyPiece;
for (int i = 0; i < dp.dirty_num; ++i) {
Piece pc = dp.piece[i];
if (dp.from[i] != SQ_NONE)
removed->push_back(make_index(perspective, dp.from[i], pc, ksq));
if (dp.to[i] != SQ_NONE)
added->push_back(make_index(perspective, dp.to[i], pc, ksq));
}
}
template class HalfRelativeKA<Side::kFriend>;
template class HalfRelativeKA<Side::kEnemy>;
} // namespace Eval::NNUE::Features

View File

@@ -0,0 +1,68 @@
#ifndef _NNUE_FEATURES_HALF_RELATIVE_KA_H_
#define _NNUE_FEATURES_HALF_RELATIVE_KA_H_
#include "features_common.h"
#include "evaluate.h"
// Definition of input features HalfRelativeKA of NNUE evaluation function
// K - King
// A - Any piece
// KA - product of K and A
namespace Eval::NNUE::Features {
// Feature HalfRelativeKA: Relative position of each piece other than the ball based on own ball or enemy ball
template <Side AssociatedKing>
class HalfRelativeKA {
public:
// feature quantity name
static constexpr const char* kName = (AssociatedKing == Side::kFriend) ?
"HalfRelativeKA(Friend)" : "HalfRelativeKA(Enemy)";
// Hash value embedded in the evaluation function file
static constexpr std::uint32_t kHashValue =
0xA123051Fu ^ (AssociatedKing == Side::kFriend);
static constexpr IndexType kNumPieceKinds = 6 * 2;
// width of the virtual board with the ball in the center
static constexpr IndexType kBoardWidth = FILE_NB * 2 - 1;
// height of a virtual board with balls in the center
static constexpr IndexType kBoardHeight = RANK_NB * 2 - 1;
// number of feature dimensions
static constexpr IndexType kDimensions =
kNumPieceKinds * kBoardHeight * kBoardWidth;
// The maximum value of the number of indexes whose value is 1 at the same time among the feature values
static constexpr IndexType kMaxActiveDimensions = 32;
// Timing of full calculation instead of difference calculation
static constexpr TriggerEvent kRefreshTrigger =
(AssociatedKing == Side::kFriend) ?
TriggerEvent::kFriendKingMoved : TriggerEvent::kEnemyKingMoved;
// Get a list of indices with a value of 1 among the features
static void append_active_indices(
const Position& pos,
Color perspective,
IndexList* active);
// Get a list of indices whose values have changed from the previous one in the feature quantity
static void append_changed_indices(
const Position& pos,
Color perspective,
IndexList* removed,
IndexList* added);
// Find the index of the feature quantity from the ball position and PieceSquare
static IndexType make_index(Square s, IndexType p);
// Find the index of the feature quantity from the ball position and PieceSquare
static IndexType make_index(Color perspective, Square s, Piece pc, Square sq_k);
};
} // namespace Eval::NNUE::Features
#endif // #ifndef _NNUE_FEATURES_HALF_RELATIVE_KA_H_