From fb214f7ef39bed3ab29b6b9e13011e017fcc0f41 Mon Sep 17 00:00:00 2001 From: tttak Date: Sat, 6 Jul 2024 18:08:42 +0900 Subject: [PATCH] HalfKP-KSDG_512x2-8-96.20240706 --- source/Makefile | 5 +- source/config.h | 2 +- ...p-kingsafety_distinguishgolds_512x2-8-96.h | 36 +++ .../features/king_safety_distinguishgolds.cpp | 257 ++++++++++++++++++ .../features/king_safety_distinguishgolds.h | 88 ++++++ source/eval/nnue/nnue_architecture.h | 6 +- source/extra/long_effect.h | 3 + 7 files changed, 393 insertions(+), 4 deletions(-) create mode 100644 source/eval/nnue/architectures/halfkp-kingsafety_distinguishgolds_512x2-8-96.h create mode 100644 source/eval/nnue/features/king_safety_distinguishgolds.cpp create mode 100644 source/eval/nnue/features/king_safety_distinguishgolds.h diff --git a/source/Makefile b/source/Makefile index 9f5267383..0c5abd40b 100644 --- a/source/Makefile +++ b/source/Makefile @@ -30,9 +30,9 @@ # ビルドを確認したDocker image → nvcr.io/nvidia/tensorrt:22.12-py3 -YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE +#YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE #YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_KP256 -#YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_HALFKPE9 +YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_HALFKPE9 #YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_HALFKP_512X2_16_32 #YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_HALFKP_1024X2_8_32 #YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_HALFKP_1024X2_8_64 @@ -501,6 +501,7 @@ ifneq (,$(findstring YANEURAOU_ENGINE_NNUE,$(YANEURAOU_EDITION))) eval/nnue/features/half_relative_kp.cpp \ eval/nnue/features/half_kpe9.cpp \ eval/nnue/features/pe9.cpp \ + eval/nnue/features/king_safety_distinguishgolds.cpp \ engine/yaneuraou-engine/yaneuraou-search.cpp ifneq (,$(findstring em++,$(COMPILER))) diff --git a/source/config.h b/source/config.h index 798fbddbc..de889dc89 100644 --- a/source/config.h +++ b/source/config.h @@ -178,7 +178,7 @@ // 評価関数で金と小駒の成りを区別する // 駒の特徴量はBonaPiece。これはBonanzaに倣っている。 // このオプションを有効化すると、金と小駒の成りを区別する。(Bonanzaとは異なる特徴量になる) -// #define DISTINGUISH_GOLDS +#define DISTINGUISH_GOLDS // エンジンオプションをコンパイル時に指定したい時に用いる。 // ";"で区切って複数指定できる。 diff --git a/source/eval/nnue/architectures/halfkp-kingsafety_distinguishgolds_512x2-8-96.h b/source/eval/nnue/architectures/halfkp-kingsafety_distinguishgolds_512x2-8-96.h new file mode 100644 index 000000000..c151bcac1 --- /dev/null +++ b/source/eval/nnue/architectures/halfkp-kingsafety_distinguishgolds_512x2-8-96.h @@ -0,0 +1,36 @@ +// NNUE評価関数で用いる入力特徴量とネットワーク構造の定義 + +#include "../features/feature_set.h" +#include "../features/half_kp.h" +#include "../features/king_safety_distinguishgolds.h" + +#include "../layers/input_slice.h" +#include "../layers/affine_transform.h" +#include "../layers/clipped_relu.h" + +namespace Eval { + +namespace NNUE { + +// 評価関数で用いる入力特徴量 +using RawFeatures = Features::FeatureSet< + Features::HalfKP, Features::KingSafety_DistinguishGolds>; + +// 変換後の入力特徴量の次元数 +constexpr IndexType kTransformedFeatureDimensions = 512; + +namespace Layers { + +// ネットワーク構造の定義 +using InputLayer = InputSlice; +using HiddenLayer1 = ClippedReLU>; +using HiddenLayer2 = ClippedReLU>; +using OutputLayer = AffineTransform; + +} // namespace Layers + +using Network = Layers::OutputLayer; + +} // namespace NNUE + +} // namespace Eval diff --git a/source/eval/nnue/features/king_safety_distinguishgolds.cpp b/source/eval/nnue/features/king_safety_distinguishgolds.cpp new file mode 100644 index 000000000..8cc9f48fd --- /dev/null +++ b/source/eval/nnue/features/king_safety_distinguishgolds.cpp @@ -0,0 +1,257 @@ +// NNUE評価関数の入力特徴量KingSafety_DistinguishGoldsの定義 + +#include "../../../config.h" + +#if defined(EVAL_NNUE) && defined(LONG_EFFECT_LIBRARY) && defined(USE_BOARD_EFFECT_PREV) && defined(DISTINGUISH_GOLDS) + +#include "king_safety_distinguishgolds.h" +#include "index_list.h" + +namespace Eval { + +namespace NNUE { + +namespace Features { + +// 盤上の駒のBonaPieceからPieceへの変換配列 +Piece sqBonaPieceToPiece[] = {B_PAWN, W_PAWN, B_LANCE, W_LANCE, B_KNIGHT, W_KNIGHT, B_SILVER, W_SILVER, B_GOLD, W_GOLD + , B_BISHOP, W_BISHOP, B_HORSE, W_HORSE, B_ROOK, W_ROOK, B_DRAGON, W_DRAGON + , B_PRO_PAWN, W_PRO_PAWN, B_PRO_LANCE, W_PRO_LANCE, B_PRO_KNIGHT, W_PRO_KNIGHT, B_PRO_SILVER, W_PRO_SILVER + , B_KING, W_KING }; + +// BonaPieceからSquareとPieceを取得する +// ・持ち駒の場合は「SQ_NB、NO_PIECE」を返す。本来は持ち駒のPieceを正確に算出することもできるが、この評価関数では不要なので。 +template +inline void KingSafety_DistinguishGolds::GetSquarePieceFromBonaPiece(BonaPiece bp, Square &sq, Piece &pc) { + // 持ち駒の場合 + if (bp < fe_hand_end) { + sq = SQ_NB; + pc = NO_PIECE; + } + // 盤上の駒の場合 + else { + sq = static_cast((bp - fe_hand_end) % SQ_NB); + pc = sqBonaPieceToPiece[(bp - fe_hand_end) / SQ_NB]; + } +} + +// Squareのdirty判定 +template +inline bool KingSafety_DistinguishGolds::IsDirty(Square dirty_sq24[], int dirty_sq24_count, Square sq) { + for (int i = 0; i < dirty_sq24_count; ++i) { + if (dirty_sq24[i] == sq) { + return true; + } + } + return false; +} + +// Effect24::Directの算出 +template +inline Effect24::Direct KingSafety_DistinguishGolds::CalcDirect(Square sq_king, Square sq) { + int file_diff = file_of(sq) - file_of(sq_king); + int rank_diff = rank_of(sq) - rank_of(sq_king); + int calc = file_diff * 5 + rank_diff + 12; + + return Effect24::Direct(calc < 12 ? calc : calc - 1); +} + +// 利き数の取得 +template +inline int KingSafety_DistinguishGolds::GetEffectCount(const Position& pos, Square sq, Color perspective, bool prev_effect) { + if (sq == SQ_NB) { + return 0; + } + else { + if (prev_effect) { + return std::min(int(pos.board_effect_prev[perspective].effect(sq)), 3); + } + else { + return std::min(int(pos.board_effect[perspective].effect(sq)), 3); + } + } +} + +// Pieceの先後反転 +template +inline Piece KingSafety_DistinguishGolds::Inv(Piece pc) { + if (pc == NO_PIECE) { + return NO_PIECE; + } + else if (pc == PIECE_WALL) { + return PIECE_WALL; + } + else { + return make_piece(~color_of(pc), type_of(pc)); + } +} + +// Effect24::Directの先後反転 +template +inline Effect24::Direct KingSafety_DistinguishGolds::Inv(Effect24::Direct dir) { + return Effect24::DIRECT_NB - static_cast(1) - dir; +} + +// 特徴量のインデックスを求める +template +inline IndexType KingSafety_DistinguishGolds::MakeIndex(Color perspective, Effect24::Direct dir, Piece pc, int effect1, int effect2) { + if (perspective == WHITE) { + pc = Inv(pc); + dir = Inv(dir); + } + + return ((static_cast(dir) + * static_cast(PIECE_WALL_NB) + static_cast(pc)) + * 4 + effect1) + * 4 + effect2; +} + +// 特徴量のうち、値が1であるインデックスのリストを取得する +template +void KingSafety_DistinguishGolds::AppendActiveIndices( + const Position& pos, Color perspective, IndexList* active) { + // コンパイラの警告を回避するため、配列サイズが小さい場合は何もしない + if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return; + + // perspective側の玉のマス(先手目線) + SquareWithWall sqww_king = to_sqww(pos.king_square(perspective)); + + // 24近傍をループ + for (Effect24::Direct dir : Effect24::Direct()) { + SquareWithWall sqww = sqww_king + DirectToDeltaWW(dir); + + // 盤内の場合 + if (is_ok(sqww)) { + Square sq = sqww_to_sq(sqww); + active->push_back(MakeIndex(perspective, dir, pos.piece_on(sq) + , GetEffectCount(pos, sq, perspective, false) + , GetEffectCount(pos, sq, ~perspective, false) + )); + } + + // 盤外の場合 + else { + active->push_back(MakeIndex(perspective, dir, PIECE_WALL, 0, 0)); + } + } +} + +// 特徴量のうち、一手前から値が変化したインデックスのリストを取得する +template +void KingSafety_DistinguishGolds::AppendChangedIndices( + const Position& pos, Color perspective, + IndexList* removed, IndexList* added) { + + // perspective側の玉のマス(先手目線) + Square sq_king = pos.king_square(perspective); + SquareWithWall sqww_king = to_sqww(sq_king); + + // pieces(先手目線) + BonaPiece* pieces = pos.eval_list()->piece_list_fb(); + const auto& dp = pos.state()->dirtyPiece; + + // 玉の24近傍でdirtyなマス + Square dirty_sq24[4] = {SQ_NB, SQ_NB, SQ_NB, SQ_NB}; + int dirty_sq24_count = 0; + + for (int i = 0; i < dp.dirty_num; ++i) { + // old_piece(先手目線) + const auto old_piece = static_cast(dp.changed_piece[i].old_piece.from[BLACK]); + Square old_sq; + Piece old_pc; + GetSquarePieceFromBonaPiece(old_piece, old_sq, old_pc); + + // old_pieceのマスが玉の24近傍の場合 + if (old_sq != SQ_NB && dist(sq_king, old_sq) <= 2) { + dirty_sq24[dirty_sq24_count] = old_sq; + ++dirty_sq24_count; + + Effect24::Direct dir = CalcDirect(sq_king, old_sq); + + removed->push_back(MakeIndex(perspective, dir, old_pc + , GetEffectCount(pos, old_sq, perspective, true) + , GetEffectCount(pos, old_sq, ~perspective, true) + )); + + // iが0以外の場合(iが1の場合)は取られた駒なので除外 + if (i == 0) { + added->push_back(MakeIndex(perspective, dir, NO_PIECE + , GetEffectCount(pos, old_sq, perspective, false) + , GetEffectCount(pos, old_sq, ~perspective, false) + )); + } + } + + // new_piece(先手目線) + const auto new_piece = static_cast(dp.changed_piece[i].new_piece.from[BLACK]); + Square new_sq; + Piece new_pc; + GetSquarePieceFromBonaPiece(new_piece, new_sq, new_pc); + + // new_pieceのマスが玉の24近傍の場合 + if (new_sq != SQ_NB && dist(sq_king, new_sq) <= 2) { + dirty_sq24[dirty_sq24_count] = new_sq; + ++dirty_sq24_count; + + Effect24::Direct dir = CalcDirect(sq_king, new_sq); + + // 「dp.dirty_num == 2 && i == 0)」の場合は除外 + if ( (dp.dirty_num == 1 && i == 0) + || (dp.dirty_num == 2 && i == 1)) { + removed->push_back(MakeIndex(perspective, dir, NO_PIECE + , GetEffectCount(pos, new_sq, perspective, true) + , GetEffectCount(pos, new_sq, ~perspective, true) + )); + } + + added->push_back(MakeIndex(perspective, dir, new_pc + , GetEffectCount(pos, new_sq, perspective, false) + , GetEffectCount(pos, new_sq, ~perspective, false) + )); + } + } + + // 24近傍をループ + for (Effect24::Direct dir : Effect24::Direct()) { + SquareWithWall sqww = sqww_king + DirectToDeltaWW(dir); + + // 盤内の場合 + if (is_ok(sqww)) { + Square sq = sqww_to_sq(sqww); + + // dirtyな場合は既に処理済み + if (IsDirty(dirty_sq24, dirty_sq24_count, sq)) { + continue; + } + + int effectCount_prev_1 = GetEffectCount(pos, sq, perspective, true); + int effectCount_prev_2 = GetEffectCount(pos, sq, ~perspective, true); + int effectCount_now_1 = GetEffectCount(pos, sq, perspective, false); + int effectCount_now_2 = GetEffectCount(pos, sq, ~perspective, false); + + // 利き数に変化があった場合 + if ( effectCount_prev_1 != effectCount_now_1 + || effectCount_prev_2 != effectCount_now_2) { + Piece pc = pos.piece_on(sq); + removed->push_back(MakeIndex(perspective, dir, pc, effectCount_prev_1, effectCount_prev_2)); + added->push_back(MakeIndex(perspective, dir, pc, effectCount_now_1, effectCount_now_2)); + } + } + + // 盤外の場合 + else { + // 何もしない + } + } +} + +template class KingSafety_DistinguishGolds; +template class KingSafety_DistinguishGolds; + +} // namespace Features + +} // namespace NNUE + +} // namespace Eval + +#endif // defined(EVAL_NNUE) diff --git a/source/eval/nnue/features/king_safety_distinguishgolds.h b/source/eval/nnue/features/king_safety_distinguishgolds.h new file mode 100644 index 000000000..ba5be16bf --- /dev/null +++ b/source/eval/nnue/features/king_safety_distinguishgolds.h @@ -0,0 +1,88 @@ +// NNUE評価関数の入力特徴量KingSafety_DistinguishGoldsの定義 + +#ifndef _NNUE_FEATURES_KING_SAFETY_DISTINGUISH_GOLDS_H_ +#define _NNUE_FEATURES_KING_SAFETY_DISTINGUISH_GOLDS_H_ + +#include "../../../config.h" + +#if defined(EVAL_NNUE) && defined(LONG_EFFECT_LIBRARY) + +#include "../../../evaluate.h" +#include "features_common.h" + +#include "../../../extra/long_effect.h" + +namespace Eval { + +namespace NNUE { + +namespace Features { + +// 特徴量KingSafety_DistinguishGolds:玉の安全度(玉の24近傍の駒と利き数)(金と小駒の成り駒(と金、成香、成桂、成銀)を区別する) +template +class KingSafety_DistinguishGolds { + public: + // 特徴量名 + static constexpr const char* kName = + (AssociatedKing == Side::kFriend) ? "KingSafety_DistinguishGolds(Friend)" : "KingSafety_DistinguishGolds(Enemy)"; + // 評価関数ファイルに埋め込むハッシュ値 + static constexpr std::uint32_t kHashValue = + 0x596B2375u ^ (AssociatedKing == Side::kFriend); + + // 壁のPiece値を定義 + static constexpr Piece PIECE_WALL = PIECE_NB; + static constexpr Piece PIECE_WALL_NB = static_cast(PIECE_WALL + 1); + + // 特徴量の次元数 + // ・玉の24近傍 + // ・駒はNO_PIECE、PIECE_WALLを含む + // ・利き数は先手と後手を同時に評価する + // ・利き数は0~3に制限する + static constexpr IndexType kDimensions = static_cast(Effect24::DIRECT_NB) * static_cast(PIECE_WALL_NB) * 4 * 4; + // 特徴量のうち、同時に値が1となるインデックスの数の最大値 + static constexpr IndexType kMaxActiveDimensions = Effect24::DIRECT_NB; + + // 差分計算の代わりに全計算を行うタイミング + static constexpr TriggerEvent kRefreshTrigger = + (AssociatedKing == Side::kFriend) ? + TriggerEvent::kFriendKingMoved : TriggerEvent::kEnemyKingMoved; + + // 特徴量のうち、値が1であるインデックスのリストを取得する + static void AppendActiveIndices(const Position& pos, Color perspective, + IndexList* active); + + // 特徴量のうち、一手前から値が変化したインデックスのリストを取得する + static void AppendChangedIndices(const Position& pos, Color perspective, + IndexList* removed, IndexList* added); + + // 特徴量のインデックスを求める + static IndexType MakeIndex(Color perspective, Effect24::Direct dir, Piece pc, int effect1, int effect2); + + // Pieceの先後反転 + static Piece Inv(Piece pc); + + // Effect24::Directの先後反転 + static Effect24::Direct Inv(Effect24::Direct dir); + + // BonaPieceからSquareとPieceを取得する + static void GetSquarePieceFromBonaPiece(BonaPiece bp, Square &sq, Piece &pc); + + // Squareのdirty判定 + static bool IsDirty(Square dirty_sq24[], int dirty_sq24_count, Square sq); + + // Effect24::Directの算出 + static Effect24::Direct CalcDirect(Square sq_king, Square sq); + + // 利き数の取得 + static int GetEffectCount(const Position& pos, Square sq, Color perspective, bool prev_effect); +}; + +} // namespace Features + +} // namespace NNUE + +} // namespace Eval + +#endif // defined(EVAL_NNUE) + +#endif diff --git a/source/eval/nnue/nnue_architecture.h b/source/eval/nnue/nnue_architecture.h index c0a148cab..bdcc16229 100644 --- a/source/eval/nnue/nnue_architecture.h +++ b/source/eval/nnue/nnue_architecture.h @@ -30,7 +30,11 @@ #elif defined(EVAL_NNUE_HALFKPE9) // halfkpe9型 -#include "architectures/halfkpe9_256x2-32-32.h" +//#include "architectures/halfkpe9_256x2-32-32.h" + +// HalfKP-KSDG型 +//#include "architectures/halfkp-kingsafety_distinguishgolds_256x2-32-32.h" +#include "architectures/halfkp-kingsafety_distinguishgolds_512x2-8-96.h" #elif defined(YANEURAOU_ENGINE_NNUE_HALFKP_512X2_16_32) diff --git a/source/extra/long_effect.h b/source/extra/long_effect.h index b8f90101e..44816a7bf 100644 --- a/source/extra/long_effect.h +++ b/source/extra/long_effect.h @@ -173,6 +173,9 @@ namespace Effect24 // 24近傍で8近傍に利く長い利きの方向。24近傍 = 4筋*9段+5升=41升 ≦ 48升*WordBoard = 96byte = ymm(32) * 3 extern ymm ymm_direct_to_around8[3]; + ENABLE_FULL_OPERATORS_ON(Direct) + ENABLE_RANGE_OPERATORS_ON(Direct, DIRECT_ZERO, DIRECT_NB) + // --- init // このnamespaceで用いるテーブルの初期化