diff --git a/src/eval/evaluate_mir_inv_tools.cpp b/src/eval/evaluate_mir_inv_tools.cpp deleted file mode 100644 index 3667b9f5..00000000 --- a/src/eval/evaluate_mir_inv_tools.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#if defined(EVAL_NNUE) || defined(EVAL_LEARN) - -#include "evaluate_mir_inv_tools.h" - -namespace Eval -{ - - // --- tables - - // Value when a certain PieceSquare is seen from the other side - // BONA_PIECE_INIT is -1, so it must be a signed type. - // Even if KPPT is expanded, PieceSquare will not exceed 2^15 for the time being, so int16_t is good. - int16_t inv_piece_[PieceSquare::PS_END]; - - // Returns the one at the position where a PieceSquare on the board is mirrored. - int16_t mir_piece_[PieceSquare::PS_END]; - - - // --- methods - -// Returns the value when a certain PieceSquare is seen from the other side - PieceSquare inv_piece(PieceSquare p) { return (PieceSquare)inv_piece_[p]; } - - // Returns the one at the position where a PieceSquare on the board is mirrored. - PieceSquare mir_piece(PieceSquare p) { return (PieceSquare)mir_piece_[p]; } - - std::function mir_piece_init_function; - - void init_mir_inv_tables() - { - // Initialize the mirror and inverse tables. - - // Initialization is limited to once. - static bool first = true; - if (!first) return; - first = false; - - // exchange f and e - int t[] = { - PieceSquare::PS_W_PAWN , PieceSquare::PS_B_PAWN , - PieceSquare::PS_W_KNIGHT , PieceSquare::PS_B_KNIGHT , - PieceSquare::PS_W_BISHOP , PieceSquare::PS_B_BISHOP , - PieceSquare::PS_W_ROOK , PieceSquare::PS_B_ROOK , - PieceSquare::PS_W_QUEEN , PieceSquare::PS_B_QUEEN , - }; - - // Insert uninitialized value. - for (PieceSquare p = PieceSquare::PS_NONE; p < PieceSquare::PS_END; ++p) - { - inv_piece_[p] = PieceSquare::PS_NOT_INIT; - - // mirror does not work for hand pieces. Just return the original value. - mir_piece_[p] = (p < PieceSquare::PS_W_PAWN) ? p : PieceSquare::PS_NOT_INIT; - } - - for (PieceSquare p = PieceSquare::PS_NONE; p < PieceSquare::PS_END; ++p) - { - for (int i = 0; i < 32 /* t.size() */; i += 2) - { - if (t[i] <= p && p < t[i + 1]) - { - Square sq = (Square)(p - t[i]); - - // found!! - PieceSquare q = (p < PieceSquare::PS_W_PAWN) ? PieceSquare(sq + t[i + 1]) : (PieceSquare)(rotate180(sq) + t[i + 1]); - inv_piece_[p] = q; - inv_piece_[q] = p; - - /* - It's a bit tricky, but regarding p - p >= PieceSquare::PS_W_PAWN - When. - - For this p, let n be an integer (i in the above code can only be an even number), - a) When t[2n + 0] <= p inv->mir->inv must be the original location. - assert(p == inv_piece(mir_piece(inv_piece(mir_piece(p))))); - - // inv->mir->inv->mir must be the original location. - assert(p == mir_piece(inv_piece(mir_piece(inv_piece(p))))); - } - -#if 0 - // Pre-verification that it is okay to mirror the evaluation function - // When writing a value, there is an assertion, so if you can't mirror it, - // Should get caught in the assert. - - // Apery's WCSC26 evaluation function, kpp p1==0 or p1==20 (0th step on the back) - // There is dust in it, and if you don't avoid it, it will get caught in the assert. - - std::unordered_set s; - vector a = { - f_hand_pawn - 1,e_hand_pawn - 1, - f_hand_lance - 1, e_hand_lance - 1, - f_hand_knight - 1, e_hand_knight - 1, - f_hand_silver - 1, e_hand_silver - 1, - f_hand_gold - 1, e_hand_gold - 1, - f_hand_bishop - 1, e_hand_bishop - 1, - f_hand_rook - 1, e_hand_rook - 1, - }; - for (auto b : a) - s.insert((PieceSquare)b); - - // Excludes walks, incense, and katsura on the board that do not appear further (Apery also contains garbage here) - for (Rank r = RANK_1; r <= RANK_2; ++r) - for (File f = FILE_1; f <= FILE_9; ++f) - { - if (r == RANK_1) - { - // first step - PieceSquare b1 = PieceSquare(PieceSquare::PS_W_PAWN + (f | r)); - s.insert(b1); - s.insert(inv_piece[b1]); - - // 1st stage incense - PieceSquare b2 = PieceSquare(f_lance + (f | r)); - s.insert(b2); - s.insert(inv_piece[b2]); - } - - // Katsura on the 1st and 2nd steps - PieceSquare b = PieceSquare(PieceSquare::PS_W_KNIGHT + (f | r)); - s.insert(b); - s.insert(inv_piece[b]); - } - - cout << "\nchecking kpp_write().."; - for (auto sq : SQ) - { - cout << sq << ' '; - for (PieceSquare p1 = PieceSquare::PS_NONE; p1 < PieceSquare::PS_END; ++p1) - for (PieceSquare p2 = PieceSquare::PS_NONE; p2 < PieceSquare::PS_END; ++p2) - if (!s.count(p1) && !s.count(p2)) - kpp_write(sq, p1, p2, kpp[sq][p1][p2]); - } - cout << "\nchecking kkp_write().."; - - for (auto sq1 : SQ) - { - cout << sq1 << ' '; - for (auto sq2 : SQ) - for (PieceSquare p1 = PieceSquare::PS_NONE; p1 < PieceSquare::PS_END; ++p1) - if (!s.count(p1)) - kkp_write(sq1, sq2, p1, kkp[sq1][sq2][p1]); - } - cout << "..done!" << endl; -#endif - } - -} - -#endif // defined(EVAL_NNUE) || defined(EVAL_LEARN) diff --git a/src/eval/evaluate_mir_inv_tools.h b/src/eval/evaluate_mir_inv_tools.h deleted file mode 100644 index 1f193b17..00000000 --- a/src/eval/evaluate_mir_inv_tools.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef _EVALUATE_MIR_INV_TOOLS_ -#define _EVALUATE_MIR_INV_TOOLS_ - -#if defined(EVAL_NNUE) || defined(EVAL_LEARN) - -// PieceSquare's mirror (horizontal flip) and inverse (180° on the board) tools to get pieces. - -#include "../types.h" -#include "../evaluate.h" -#include - -namespace Eval -{ - // ------------------------------------------------- - // tables - // ------------------------------------------------- - - // --- Provide Mirror and Inverse to PieceSquare. - - // These arrays are initialized by calling init() or init_mir_inv_tables();. - // If you want to use only this table from the evaluation function, - // Call init_mir_inv_tables(). - // These arrays are referenced from the KK/KKP/KPP classes below. - - // Returns the value when a certain PieceSquare is seen from the other side - extern PieceSquare inv_piece(PieceSquare p); - - // Returns the one at the position where a PieceSquare on the board is mirrored. - extern PieceSquare mir_piece(PieceSquare p); - - - // callback called when initializing mir_piece/inv_piece - // Used when extending fe_end on the user side. - // Inv_piece_ and inv_piece_ are exposed because they are necessary for this initialization. - // At the timing when mir_piece_init_function is called, until fe_old_end - // It is guaranteed that these tables have been initialized. - extern std::function mir_piece_init_function; - extern int16_t mir_piece_[PieceSquare::PS_END]; - extern int16_t inv_piece_[PieceSquare::PS_END]; - - // The table above will be initialized when you call this function explicitly or call init(). - extern void init_mir_inv_tables(); -} - -#endif // defined(EVAL_NNUE) || defined(EVAL_LEARN) - -#endif diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 5cbf821d..8edc9bb8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1028,102 +1028,3 @@ std::string Eval::trace(const Position& pos) { return ss.str(); } - -// Check whether the pieceListFw[] held internally is a correct PieceSquare. -// Note: For debugging. slow. -bool EvalList::is_valid(const Position& pos) -{ - std::set piece_numbers; - for (Square sq = SQ_A1; sq != SQUARE_NB; ++sq) { - auto piece_number = piece_id_list[sq]; - if (piece_number == PieceId::PIECE_ID_NONE) { - continue; - } - assert(!piece_numbers.count(piece_number)); - piece_numbers.insert(piece_number); - } - - for (int i = 0; i < PieceId::PIECE_ID_KING; ++i) - { - PieceSquare fw = pieceListFw[i]; - // Go to the Position class to see if this fw really exists. - - if (fw == PieceSquare::PS_NONE) { - continue; - } - - // Out of range - if (!(0 <= fw && fw < PieceSquare::PS_END)) - return false; - - // Since it is a piece on the board, I will check if this piece really exists. - for (Piece pc = NO_PIECE; pc < PIECE_NB; ++pc) - { - auto pt = type_of(pc); - if (pt == NO_PIECE_TYPE || pt == 7) // non-existing piece - continue; - - // PieceSquare start number of piece pc - auto s = PieceSquare(kpp_board_index[pc].from[Color::WHITE]); - if (s <= fw && fw < s + SQUARE_NB) - { - // Since it was found, check if this piece is at sq. - Square sq = (Square)(fw - s); - Piece pc2 = pos.piece_on(sq); - - if (pc2 != pc) - return false; - - goto Found; - } - } - // It was a piece that did not exist for some reason.. - return false; - Found:; - } - - // Validate piece_id_list - for (auto sq = SQUARE_ZERO; sq < SQUARE_NB; ++sq) { - Piece expected_piece = pos.piece_on(sq); - PieceId piece_number = piece_id_list[sq]; - if (piece_number == PieceId::PIECE_ID_NONE) { - assert(expected_piece == NO_PIECE); - if (expected_piece != NO_PIECE) { - return false; - } - continue; - } - - PieceSquare bona_piece_white = pieceListFw[piece_number]; - Piece actual_piece; - for (actual_piece = NO_PIECE; actual_piece < PIECE_NB; ++actual_piece) { - if (kpp_board_index[actual_piece].from[Color::WHITE] == PieceSquare::PS_NONE) { - continue; - } - - if (kpp_board_index[actual_piece].from[Color::WHITE] <= bona_piece_white - && bona_piece_white < kpp_board_index[actual_piece].from[Color::WHITE] + SQUARE_NB) { - break; - } - } - - assert(actual_piece != PIECE_NB); - if (actual_piece == PIECE_NB) { - return false; - } - - assert(actual_piece == expected_piece); - if (actual_piece != expected_piece) { - return false; - } - - Square actual_square = static_cast( - bona_piece_white - kpp_board_index[actual_piece].from[Color::WHITE]); - assert(sq == actual_square); - if (sq != actual_square) { - return false; - } - } - - return true; -} diff --git a/src/extra/sfen_packer.cpp b/src/extra/sfen_packer.cpp index 68576c82..ac789ce8 100644 --- a/src/extra/sfen_packer.cpp +++ b/src/extra/sfen_packer.cpp @@ -276,13 +276,6 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre // Active color sideToMove = (Color)stream.read_one_bit(); - // clear evalList. It is cleared when memset is cleared to zero above... - evalList.clear(); - - // In updating the PieceList, we have to set which piece is where, - // A counter of how much each piece has been used - PieceId next_piece_number = PieceId::PIECE_ID_ZERO; - pieceList[W_KING][0] = SQUARE_NB; pieceList[B_KING][0] = SQUARE_NB; @@ -327,14 +320,6 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre put_piece(Piece(pc), sq); - // update evalList - PieceId piece_no = - (pc == B_KING) ?PieceId::PIECE_ID_BKING :// Move ball - (pc == W_KING) ?PieceId::PIECE_ID_WKING :// Backing ball - next_piece_number++; // otherwise - - evalList.put_piece(piece_no, sq, pc); // Place the pc piece in the sq box - //cout << sq << ' ' << board[sq] << ' ' << stream.get_cursor() << endl; if (stream.get_cursor()> 256) @@ -402,9 +387,6 @@ set_state(st); //std::cout << *this << std::endl; assert(pos_is_ok()); -#if defined(EVAL_NNUE) - assert(evalList.is_valid(*this)); -#endif // defined(EVAL_NNUE) return 0; } diff --git a/src/learn/learning_tools.cpp b/src/learn/learning_tools.cpp index c97b4910..de6da9c5 100644 --- a/src/learn/learning_tools.cpp +++ b/src/learn/learning_tools.cpp @@ -20,237 +20,6 @@ namespace EvalLearningTools double Weight::eta3; uint64_t Weight::eta1_epoch; uint64_t Weight::eta2_epoch; - - std::vector min_index_flag; - - // --- initialization for each individual table - - void init_min_index_flag() - { - // Initialization of mir_piece and inv_piece must be completed. - assert(Eval::mir_piece(PieceSquare::PS_W_PAWN) == PieceSquare::PS_B_PAWN); - - // Initialize the flag array for dimension reduction - // Not involved in KPPP. - - KK g_kk; - g_kk.set(SQUARE_NB, PieceSquare::PS_END, 0); - KKP g_kkp; - g_kkp.set(SQUARE_NB, PieceSquare::PS_END, g_kk.max_index()); - KPP g_kpp; - g_kpp.set(SQUARE_NB, PieceSquare::PS_END, g_kkp.max_index()); - - uint64_t size = g_kpp.max_index(); - min_index_flag.resize(size); - -#pragma omp parallel - { -#if defined(_OPENMP) - // To prevent the logical 64 cores from being used when there are two CPUs under Windows - // explicitly assign to CPU here - int thread_index = omp_get_thread_num(); // get your thread number - WinProcGroup::bindThisThread(thread_index); -#endif - -#pragma omp for schedule(dynamic,20000) - - for (int64_t index_ = 0; index_ < (int64_t)size; ++index_) - { - // It seems that the loop variable must be a sign type due to OpenMP restrictions, but - // It's really difficult to use. - uint64_t index = (uint64_t)index_; - - if (g_kk.is_ok(index)) - { - // Make sure that the original index will be restored by conversion from index and reverse conversion. - // It is a process that is executed only once at startup, so write it in assert. - assert(g_kk.fromIndex(index).toIndex() == index); - - KK a[KK_LOWER_COUNT]; - g_kk.fromIndex(index).toLowerDimensions(a); - - // Make sure that the first element of dimension reduction is the same as the original index. - assert(a[0].toIndex() == index); - - uint64_t min_index = UINT64_MAX; - for (auto& e : a) - min_index = std::min(min_index, e.toIndex()); - min_index_flag[index] = (min_index == index); - } - else if (g_kkp.is_ok(index)) - { - assert(g_kkp.fromIndex(index).toIndex() == index); - - KKP x = g_kkp.fromIndex(index); - KKP a[KKP_LOWER_COUNT]; - x.toLowerDimensions(a); - - assert(a[0].toIndex() == index); - - uint64_t min_index = UINT64_MAX; - for (auto& e : a) - min_index = std::min(min_index, e.toIndex()); - min_index_flag[index] = (min_index == index); - } - else if (g_kpp.is_ok(index)) - { - assert(g_kpp.fromIndex(index).toIndex() == index); - - KPP x = g_kpp.fromIndex(index); - KPP a[KPP_LOWER_COUNT]; - x.toLowerDimensions(a); - - assert(a[0].toIndex() == index); - - uint64_t min_index = UINT64_MAX; - for (auto& e : a) - min_index = std::min(min_index, e.toIndex()); - min_index_flag[index] = (min_index == index); - } - else - { - assert(false); - } - } - } - } - - void learning_tools_unit_test_kpp() - { - - // test KPP triangulation for bugs - // All combinations of k-p0-p1 are properly handled by KPP, and the dimension reduction at that time is - // Determine if it is correct. - - KK g_kk; - g_kk.set(SQUARE_NB, PieceSquare::PS_END, 0); - KKP g_kkp; - g_kkp.set(SQUARE_NB, PieceSquare::PS_END, g_kk.max_index()); - KPP g_kpp; - g_kpp.set(SQUARE_NB, PieceSquare::PS_END, g_kkp.max_index()); - - std::vector f; - f.resize(g_kpp.max_index() - g_kpp.min_index()); - - for(auto k = SQUARE_ZERO ; k < SQUARE_NB ; ++k) - for(auto p0 = PieceSquare::PS_NONE; p0 < PieceSquare::PS_END ; ++p0) - for (auto p1 = PieceSquare::PS_NONE; p1 < PieceSquare::PS_END; ++p1) - { - KPP kpp_org = g_kpp.fromKPP(k,p0,p1); - KPP kpp0; - KPP kpp1 = g_kpp.fromKPP(flip_file(k), mir_piece(p0), mir_piece(p1)); - KPP kpp_array[2]; - - auto index = kpp_org.toIndex(); - assert(g_kpp.is_ok(index)); - - kpp0 = g_kpp.fromIndex(index); - - //if (kpp0 != kpp_org) - // std::cout << "index = " << index << "," << kpp_org << "," << kpp0 << std::endl; - - kpp0.toLowerDimensions(kpp_array); - - assert(kpp_array[0] == kpp0); - assert(kpp0 == kpp_org); - assert(kpp_array[1] == kpp1); - - auto index2 = kpp1.toIndex(); - f[index - g_kpp.min_index()] = f[index2-g_kpp.min_index()] = true; - } - - // Check if there is no missing index. - for(size_t index = 0 ; index < f.size(); index++) - if (!f[index]) - { - std::cout << index << g_kpp.fromIndex(index + g_kpp.min_index()) << std::endl; - } - } - - void learning_tools_unit_test_kppp() - { - // Test for missing KPPP calculations - - KPPP g_kppp; - g_kppp.set(15, PieceSquare::PS_END,0); - uint64_t min_index = g_kppp.min_index(); - uint64_t max_index = g_kppp.max_index(); - - // Confirm last element. - //KPPP x = KPPP::fromIndex(max_index-1); - //std::cout << x << std::endl; - - for (uint64_t index = min_index; index < max_index; ++index) - { - KPPP x = g_kppp.fromIndex(index); - //std::cout << x << std::endl; - -#if 0 - if ((index % 10000000) == 0) - std::cout << "index = " << index << std::endl; - - // index = 9360000000 - // done. - - if (x.toIndex() != index) - { - std::cout << "assertion failed , index = " << index << std::endl; - } -#endif - - assert(x.toIndex() == index); - -// ASSERT((&kppp_ksq_pcpcpc(x.king(), x.piece0(), x.piece1(), x.piece2()) - &kppp[0][0]) == (index - min_index)); - } - - } - - void learning_tools_unit_test_kkpp() - { - KKPP g_kkpp; - g_kkpp.set(SQUARE_NB, 10000, 0); - uint64_t n = 0; - for (int k = 0; k -#include "../eval/evaluate_mir_inv_tools.h" - #if defined(SGD_UPDATE) || defined(USE_KPPP_MIRROR_WRITE) #include "../misc.h" // PRNG , my_insertion_sort #endif @@ -17,27 +15,6 @@ namespace EvalLearningTools { - // ------------------------------------------------- - // Initialization - // ------------------------------------------------- - - // Initialize the tables in this EvalLearningTools namespace. - // Be sure to call once before learning starts. - // In this function, we also call init_mir_inv_tables(). - // (It is not necessary to call init_mir_inv_tables() when calling this function.) - void init(); - - // ------------------------------------------------- - // flags - // ------------------------------------------------- - - // When the dimension is lowered, it may become the smallest index among them - // A flag array that is true for the known index. - // This array is also initialized by init(). - // KPPP is not involved. - // Therefore, the valid index range of this array is from KK::min_index() to KPP::max_index(). - extern std::vector min_index_flag; - // ------------------------------------------------- // Array for learning that stores gradients etc. // ------------------------------------------------- @@ -217,817 +194,6 @@ namespace EvalLearningTools std::array get_grad() const { return std::array{w[0].get_grad(), w[1].get_grad()}; } }; - - // ------------------------------------------------ - - // A helper that calculates the index when the Weight array is serialized. - // ------------------------------------------------ - - - // Base class for KK,KKP,KPP,KKPP - // How to use these classes - // - // 1. Initialize with set() first. Example) KK g_kk; g_kk.set(SQUARE_NB,fe_end,0); - // 2. Next create an instance with fromIndex(), fromKK(), etc. - // 3. Access using properties such as king(), piece0(), piece1(). - // - // It may be difficult to understand just by this explanation, but if you look at init_grad(), add_grad(), update_weights() etc. in the learning part - // I think you can understand it including the necessity. - // - // Note: this derived class may indirectly reference the above inv_piece/mir_piece for dimension reduction, so - // Initialize by calling EvalLearningTools::init() or init_mir_inv_tables() first. - // - // Remarks) /*final*/ is written for the function name that should not be overridden on the derived class side. - // The function that should be overridden on the derived class side is a pure virtual function with "= 0". - // Only virtual functions are added to the derived class that may or may not be overridden. - // - struct SerializerBase - { - - // Minimum value and maximum value of serial number +1 when serializing KK, KKP, KPP arrays. - /*final*/ uint64_t min_index() const { return min_index_; } - /*final*/ uint64_t max_index() const { return min_index() + max_raw_index_; } - - // max_index() - min_index() the value of. - // Calculate the value from max_king_sq_,fe_end_ etc. on the derived class side and return it. - virtual uint64_t size() const = 0; - - // Determine if the given index is more than min_index() and less than max_index(). - /*final*/ bool is_ok(uint64_t index) { return min_index() <= index && index < max_index(); } - - // Make sure to call this set(). Otherwise, construct an instance using fromKK()/fromIndex() etc. on the derived class side. - virtual void set(int max_king_sq, uint64_t fe_end, uint64_t min_index) - { - max_king_sq_ = max_king_sq; - fe_end_ = fe_end; - min_index_ = min_index; - max_raw_index_ = size(); - } - - // Get the index when serialized, based on the value of the current member. - /*final*/ uint64_t toIndex() const { - return min_index() + toRawIndex(); - } - - // Returns the index when serializing. (The value of min_index() is before addition) - virtual uint64_t toRawIndex() const = 0; - - protected: - // The value of min_index() returned by this class - uint64_t min_index_; - - // The value of max_index() returned by this class = min_index() + max_raw_index_ - // This variable is calculated by size() of the derived class. - uint64_t max_raw_index_; - - // The number of balls to support (normally SQUARE_NB) - int max_king_sq_; - - // Maximum PieceSquare value supported - uint64_t fe_end_; - - }; - - struct KK : public SerializerBase - { - protected: - KK(Square king0, Square king1,bool inverse) : king0_(king0), king1_(king1) , inverse_sign(inverse) {} - public: - KK() {} - - virtual uint64_t size() const { return max_king_sq_ * max_king_sq_; } - - // builder that creates KK object from index (serial number) - KK fromIndex(uint64_t index) const { assert(index >= min_index()); return fromRawIndex(index - min_index()); } - - // builder that creates KK object from raw_index (number starting from 0, not serial number) - KK fromRawIndex(uint64_t raw_index) const - { - int king1 = (int)(raw_index % SQUARE_NB); - raw_index /= SQUARE_NB; - int king0 = (int)(raw_index /* % SQUARE_NB */); - assert(king0 < SQUARE_NB); - return fromKK((Square)king0, (Square)king1 , false); - } - KK fromKK(Square king0, Square king1 , bool inverse) const - { - // The variable name kk is used in the Eval::kk array etc., so it needs to be different. (The same applies to KKP, KPP classes, etc.) - KK my_kk(king0, king1, inverse); - my_kk.set(max_king_sq_, fe_end_, min_index()); - return my_kk; - } - KK fromKK(Square king0, Square king1) const { return fromKK(king0, king1, false); } - - // When you construct this object using fromIndex(), you can get information with the following accessors. - Square king0() const { return king0_; } - Square king1() const { return king1_; } - -// number of dimension reductions -#if defined(USE_KK_INVERSE_WRITE) - #define KK_LOWER_COUNT 4 -#elif defined(USE_KK_MIRROR_WRITE) - #define KK_LOWER_COUNT 2 -#else - #define KK_LOWER_COUNT 1 -#endif - -#if defined(USE_KK_INVERSE_WRITE) && !defined(USE_KK_MIRROR_WRITE) - // USE_KK_INVERSE_WRITE If you use it, please also define USE_KK_MIRROR_WRITE. - static_assert(false, "define also USE_KK_MIRROR_WRITE!"); -#endif - - // Get the index of the low-dimensional array. - // When USE_KK_INVERSE_WRITE is enabled, the inverse of them will be in [2] and [3]. - // Note that the sign of grad must be reversed for this dimension reduction. - // You can use is_inverse() because it can be determined. - void toLowerDimensions(/*out*/KK kk_[KK_LOWER_COUNT]) const { - kk_[0] = fromKK(king0_, king1_,false); -#if defined(USE_KK_MIRROR_WRITE) - kk_[1] = fromKK(flip_file(king0_),flip_file(king1_),false); -#if defined(USE_KK_INVERSE_WRITE) - kk_[2] = fromKK(rotate180(king1_), rotate180(king0_),true); - kk_[3] = fromKK(rotate180(flip_file(king1_)) , rotate180(flip_file(king0_)),true); -#endif -#endif - } - - // Get the index when counting the value of min_index() of this class as 0. - virtual uint64_t toRawIndex() const { - return (uint64_t)king0_ * (uint64_t)max_king_sq_ + (uint64_t)king1_; - } - - // Returns whether or not the dimension lowered with toLowerDimensions is inverse. - bool is_inverse() const { - return inverse_sign; - } - - // When is_inverse() == true, reverse the sign that is not grad's turn and return it. - template - std::array apply_inverse_sign(const std::array& rhs) - { - return !is_inverse() ? rhs : std::array{-rhs[0], rhs[1]}; - } - - // comparison operator - bool operator==(const KK& rhs) { return king0() == rhs.king0() && king1() == rhs.king1(); } - bool operator!=(const KK& rhs) { return !(*this == rhs); } - - private: - Square king0_, king1_ ; - bool inverse_sign; - }; - - // Output for debugging. - static std::ostream& operator<<(std::ostream& os, KK rhs) - { - os << "KK(" << rhs.king0() << "," << rhs.king1() << ")"; - return os; - } - - // Same as KK. For KKP. - struct KKP : public SerializerBase - { - protected: - KKP(Square king0, Square king1, PieceSquare p) : king0_(king0), king1_(king1), piece_(p), inverse_sign(false) {} - KKP(Square king0, Square king1, PieceSquare p, bool inverse) : king0_(king0), king1_(king1), piece_(p),inverse_sign(inverse) {} - public: - KKP() {} - - virtual uint64_t size() const { return (uint64_t)max_king_sq_*(uint64_t)max_king_sq_*(uint64_t)fe_end_; } - - // builder that creates KKP object from index (serial number) - KKP fromIndex(uint64_t index) const { assert(index >= min_index()); return fromRawIndex(index - min_index()); } - - // A builder that creates a KKP object from raw_index (a number that starts from 0, not a serial number) - KKP fromRawIndex(uint64_t raw_index) const - { - int piece = (int)(raw_index % PieceSquare::PS_END); - raw_index /= PieceSquare::PS_END; - int king1 = (int)(raw_index % SQUARE_NB); - raw_index /= SQUARE_NB; - int king0 = (int)(raw_index /* % SQUARE_NB */); - assert(king0 < SQUARE_NB); - return fromKKP((Square)king0, (Square)king1, (PieceSquare)piece,false); - } - - KKP fromKKP(Square king0, Square king1, PieceSquare p, bool inverse) const - { - KKP my_kkp(king0, king1, p, inverse); - my_kkp.set(max_king_sq_,fe_end_,min_index()); - return my_kkp; - } - KKP fromKKP(Square king0, Square king1, PieceSquare p) const { return fromKKP(king0, king1, p, false); } - - // When you construct this object using fromIndex(), you can get information with the following accessors. - Square king0() const { return king0_; } - Square king1() const { return king1_; } - PieceSquare piece() const { return piece_; } - - // Number of KKP dimension reductions -#if defined(USE_KKP_INVERSE_WRITE) - #define KKP_LOWER_COUNT 4 -#elif defined(USE_KKP_MIRROR_WRITE) - #define KKP_LOWER_COUNT 2 -#else - #define KKP_LOWER_COUNT 1 -#endif - -#if defined(USE_KKP_INVERSE_WRITE) && !defined(USE_KKP_MIRROR_WRITE) - // USE_KKP_INVERSE_WRITE If you use it, please also define USE_KKP_MIRROR_WRITE. - static_assert(false, "define also USE_KKP_MIRROR_WRITE!"); -#endif - - // Get the index of the low-dimensional array. The mirrored one is returned to kkp_[1]. - // When USE_KKP_INVERSE_WRITE is enabled, the inverse of them will be in [2] and [3]. - // Note that the sign of grad must be reversed for this dimension reduction. - // You can use is_inverse() because it can be determined. - void toLowerDimensions(/*out*/ KKP kkp_[KKP_LOWER_COUNT]) const { - kkp_[0] = fromKKP(king0_, king1_, piece_,false); -#if defined(USE_KKP_MIRROR_WRITE) - kkp_[1] = fromKKP(flip_file(king0_), flip_file(king1_), Eval::mir_piece(piece_),false); -#if defined(USE_KKP_INVERSE_WRITE) - kkp_[2] = fromKKP( rotate180(king1_), rotate180(king0_), Eval::inv_piece(piece_),true); - kkp_[3] = fromKKP( rotate180(flip_file(king1_)), rotate180(flip_file(king0_)) , Eval::inv_piece(Eval::mir_piece(piece_)),true); -#endif -#endif - } - - // Get the index when counting the value of min_index() of this class as 0. - virtual uint64_t toRawIndex() const { - return ((uint64_t)king0_ * (uint64_t)max_king_sq_ + (uint64_t)king1_) * (uint64_t)fe_end_ + (uint64_t)piece_; - } - - // Returns whether or not the dimension lowered with toLowerDimensions is inverse. - bool is_inverse() const { - return inverse_sign; - } - - // When is_inverse() == true, reverse the sign that is not grad's turn and return it. - template - std::array apply_inverse_sign(const std::array& rhs) - { - return !is_inverse() ? rhs : std::array{-rhs[0], rhs[1]}; - } - - // comparison operator - bool operator==(const KKP& rhs) { return king0() == rhs.king0() && king1() == rhs.king1() && piece() == rhs.piece(); } - bool operator!=(const KKP& rhs) { return !(*this == rhs); } - - private: - Square king0_, king1_; - PieceSquare piece_; - bool inverse_sign; - }; - - // Output for debugging. - static std::ostream& operator<<(std::ostream& os, KKP rhs) - { - os << "KKP(" << rhs.king0() << "," << rhs.king1() << "," << rhs.piece() << ")"; - return os; - } - - - // Same as KK and KKP. For KPP - struct KPP : public SerializerBase - { - protected: - KPP(Square king, PieceSquare p0, PieceSquare p1) : king_(king), piece0_(p0), piece1_(p1) {} - - public: - KPP() {} - - // The minimum and maximum KPP values ​​of serial numbers when serializing KK, KKP, KPP arrays. -#if !defined(USE_TRIANGLE_WEIGHT_ARRAY) - virtual uint64_t size() const { return (uint64_t)max_king_sq_*(uint64_t)fe_end_*(uint64_t)fe_end_; } -#else - // Triangularize the square array part of [fe_end][fe_end] of kpp[SQUARE_NB][fe_end][fe_end]. - // If kpp[SQUARE_NB][triangle_fe_end], the first row of this triangular array has one element, the second row has two elements, and so on. - // hence triangle_fe_end = 1 + 2 + .. + fe_end = fe_end * (fe_end + 1) / 2 - virtual uint64_t size() const { return (uint64_t)max_king_sq_*(uint64_t)triangle_fe_end; } -#endif - - virtual void set(int max_king_sq, uint64_t fe_end, uint64_t min_index) - { - // This value is used in size(), and size() is used in SerializerBase::set(), so calculate first. - triangle_fe_end = (uint64_t)fe_end*((uint64_t)fe_end + 1) / 2; - - SerializerBase::set(max_king_sq, fe_end, min_index); - } - - // builder that creates KPP object from index (serial number) - KPP fromIndex(uint64_t index) const { assert(index >= min_index()); return fromRawIndex(index - min_index()); } - - // A builder that creates KPP objects from raw_index (a number that starts from 0, not a serial number) - KPP fromRawIndex(uint64_t raw_index) const - { - const uint64_t triangle_fe_end = (uint64_t)fe_end_*((uint64_t)fe_end_ + 1) / 2; - -#if !defined(USE_TRIANGLE_WEIGHT_ARRAY) - int piece1 = (int)(raw_index % fe_end_); - raw_index /= fe_end_; - int piece0 = (int)(raw_index % fe_end_); - raw_index /= fe_end_; -#else - uint64_t index2 = raw_index % triangle_fe_end; - - // Write the expression to find piece0, piece1 from index2 here. - // This is the inverse function of index2 = i * (i+1) / 2 + j. - // If j = 0, i^2 + i-2 * index2 == 0 - // From the solution formula of the quadratic equation i = (sqrt(8*index2+1)-1) / 2. - // After i is converted into an integer, j can be calculated as j = index2-i * (i + 1) / 2. - - // PieceSquare assumes 32bit (may not fit in 16bit), so this multiplication must be 64bit. - int piece1 = int(sqrt(8 * index2 + 1) - 1) / 2; - int piece0 = int(index2 - (uint64_t)piece1*((uint64_t)piece1 + 1) / 2); - - assert(piece1 < (int)fe_end_); - assert(piece0 < (int)fe_end_); - assert(piece0 > piece1); - - raw_index /= triangle_fe_end; -#endif - int king = (int)(raw_index /* % SQUARE_NB */); - assert(king < max_king_sq_); - return fromKPP((Square)king, (PieceSquare)piece0, (PieceSquare)piece1); - } - - KPP fromKPP(Square king, PieceSquare p0, PieceSquare p1) const - { - KPP my_kpp(king, p0, p1); - my_kpp.set(max_king_sq_,fe_end_,min_index()); - return my_kpp; - } - - // When you construct this object using fromIndex(), you can get information with the following accessors. - Square king() const { return king_; } - PieceSquare piece0() const { return piece0_; } - PieceSquare piece1() const { return piece1_; } - - -// number of dimension reductions -#if defined(USE_KPP_MIRROR_WRITE) - #if !defined(USE_TRIANGLE_WEIGHT_ARRAY) - #define KPP_LOWER_COUNT 4 - #else - #define KPP_LOWER_COUNT 2 - #endif -#else - #if !defined(USE_TRIANGLE_WEIGHT_ARRAY) - #define KPP_LOWER_COUNT 2 - #else - #define KPP_LOWER_COUNT 1 - #endif -#endif - - // Get the index of the low-dimensional array. The ones with p1 and p2 swapped, the ones mirrored, etc. are returned. - void toLowerDimensions(/*out*/ KPP kpp_[KPP_LOWER_COUNT]) const { - -#if defined(USE_TRIANGLE_WEIGHT_ARRAY) - // Note that if you use a triangular array, the swapped piece0 and piece1 will not be returned. - kpp_[0] = fromKPP(king_, piece0_, piece1_); -#if defined(USE_KPP_MIRROR_WRITE) - kpp_[1] = fromKPP(flip_file(king_), Eval::mir_piece(piece0_), Eval::mir_piece(piece1_)); -#endif - -#else - // When not using triangular array - kpp_[0] = fromKPP(king_, piece0_, piece1_); - kpp_[1] = fromKPP(king_, piece1_, piece0_); -#if defined(USE_KPP_MIRROR_WRITE) - kpp_[2] = fromKPP(flip_file(king_), mir_piece(piece0_), mir_piece(piece1_)); - kpp_[3] = fromKPP(flip_file(king_), mir_piece(piece1_), mir_piece(piece0_)); -#endif -#endif - } - - // Get the index when counting the value of min_index() of this class as 0. - virtual uint64_t toRawIndex() const { - -#if !defined(USE_TRIANGLE_WEIGHT_ARRAY) - - return ((uint64_t)king_ * (uint64_t)fe_end_ + (uint64_t)piece0_) * (uint64_t)fe_end_ + (uint64_t)piece1_; - -#else - // Macro similar to that used in Bonanza 6.0 - auto PcPcOnSq = [&](Square k, PieceSquare i, PieceSquare j) - { - - // (i,j) in this triangular array is the element in the i-th row and the j-th column. - // 1st row + 2 + ... + i = i * (i+1) / 2 because the i-th row and 0th column is the total of the elements up to that point - // The i-th row and the j-th column is j plus this. i*(i+1)/2+j - - // PieceSquare type is assumed to be 32 bits, so if you do not pay attention to multiplication, it will overflow. - return (uint64_t)k * triangle_fe_end + (uint64_t)(uint64_t(i)*(uint64_t(i)+1) / 2 + uint64_t(j)); - }; - - auto k = king_; - auto i = piece0_; - auto j = piece1_; - - return (i >= j) ? PcPcOnSq(k, i, j) : PcPcOnSq(k, j, i); -#endif - } - - // Returns whether or not the dimension lowered with toLowerDimensions is inverse. - // Prepared to match KK, KKP and interface. This method always returns false for this KPP class. - bool is_inverse() const { - return false; - } - - // comparison operator - bool operator==(const KPP& rhs) { - return king() == rhs.king() && - ((piece0() == rhs.piece0() && piece1() == rhs.piece1()) -#if defined(USE_TRIANGLE_WEIGHT_ARRAY) - // When using a triangular array, allow swapping of piece0 and piece1. - || (piece0() == rhs.piece1() && piece1() == rhs.piece0()) -#endif - ); } - bool operator!=(const KPP& rhs) { return !(*this == rhs); } - - - private: - Square king_; - PieceSquare piece0_, piece1_; - - uint64_t triangle_fe_end; // = (uint64_t)fe_end_*((uint64_t)fe_end_ + 1) / 2; - }; - - // Output for debugging. - static std::ostream& operator<<(std::ostream& os, KPP rhs) - { - os << "KPP(" << rhs.king() << "," << rhs.piece0() << "," << rhs.piece1() << ")"; - return os; - } - - // 4 pieces related to KPPP. However, if there is a turn and you do not consider mirrors etc., memory of 2 TB or more is required for learning. - // Even if you use a triangular array, you need 50GB x 12 bytes = 600GB for learning. - // It takes about half as much as storing only the mirrored one. - // Here, the triangular array is always used and the mirrored one is stored. - // - // Also, king() of this class is not limited to Square of the actual king, but a value from 0 to (king_sq-1) is simply returned. - // This needs to be converted to an appropriate ball position on the user side when performing compression using a mirror. - // - // Later, regarding the pieces0,1,2 returned by this class, - // piece0() >piece1() >piece2() - // It is, and it is necessary to keep this constraint when passing piece0,1,2 in the constructor. - struct KPPP : public SerializerBase - { - protected: - KPPP(int king, PieceSquare p0, PieceSquare p1, PieceSquare p2) : - king_(king), piece0_(p0), piece1_(p1), piece2_(p2) - { - assert(piece0_ > piece1_ && piece1_ > piece2_); - /* sort_piece(); */ - } - - public: - KPPP() {} - - virtual uint64_t size() const { return (uint64_t)max_king_sq_*triangle_fe_end; } - - // Set fe_end and king_sq. - // fe_end: fe_end assumed by this KPPP class - // king_sq: Number of balls to handle in KPPP. - // 3 layers x 3 mirrors = 3 layers x 5 lines = 15 - // 2 steps x 2 mirrors without mirror = 18 - // Set this first using set() on the side that uses this KPPP class. - virtual void set(int max_king_sq, uint64_t fe_end,uint64_t min_index) { - // This value is used in size(), and size() is used in SerializerBase::set(), so calculate first. - triangle_fe_end = fe_end * (fe_end - 1) * (fe_end - 2) / 6; - - SerializerBase::set(max_king_sq, fe_end, min_index); - } - - // number of dimension reductions - // For the time being, the dimension reduction of the mirror is not supported. I wonder if I'll do it here... -/* -#if defined(USE_KPPP_MIRROR_WRITE) -#define KPPP_LOWER_COUNT 2 -#else -#define KPPP_LOWER_COUNT 1 -#endif -*/ -#define KPPP_LOWER_COUNT 1 - - // Get the index of the low-dimensional array. - // Note that the one with p0,p1,p2 swapped will not be returned. - // Also, the mirrored one is returned only when USE_KPPP_MIRROR_WRITE is enabled. - void toLowerDimensions(/*out*/ KPPP kppp_[KPPP_LOWER_COUNT]) const - { - kppp_[0] = fromKPPP(king_, piece0_, piece1_,piece2_); -#if KPPP_LOWER_COUNT > 1 - // If mir_piece is done, it will be in a state not sorted. Need code to sort. - PieceSquare p_list[3] = { mir_piece(piece2_), mir_piece(piece1_), mir_piece(piece0_) }; - my_insertion_sort(p_list, 0, 3); - kppp_[1] = fromKPPP((int)flip_file((Square)king_), p_list[2] , p_list[1], p_list[0]); -#endif - } - - // builder that creates KPPP object from index (serial number) - KPPP fromIndex(uint64_t index) const { assert(index >= min_index()); return fromRawIndex(index - min_index()); } - - // A builder that creates KPPP objects from raw_index (a number that starts from 0, not a serial number) - KPPP fromRawIndex(uint64_t raw_index) const - { - uint64_t index2 = raw_index % triangle_fe_end; - - // Write the expression to find piece0, piece1, piece2 from index2 here. - // This is the inverse function of index2 = i(i-1)(i-2)/6-1 + j(j+1)/2 + k. - // For j = k = 0, the real root is i = ... from the solution formula of the cubic equation. (The following formula) - // However, if index2 is 0 or 1, there are multiple real solutions. You have to consider this. It is necessary to take measures against insufficient calculation accuracy. - // After i is calculated, i can be converted into an integer, then put in the first expression and then j can be calculated in the same way as in KPP. - - // This process is a relatively difficult numerical calculation. Various ideas are needed. - - int piece0; - if (index2 <= 1) - { - // There are multiple real solutions only when index2 == 0,1. - piece0 = (int)index2 + 2; - - } else { - - //double t = pow(sqrt((243 *index2 * index2-1) * 3) + 27 * index2, 1.0 / 3); - // → In this case, the content of sqrt() will overflow if index2 becomes large. - - // Since the contents of sqrt() overflow, do not multiply 3.0 in sqrt, but multiply sqrt(3.0) outside sqrt. - // Since the contents of sqrt() will overflow, use an approximate expression when index2 is large. - - double t; - - if (index2 < 100000000) - t = pow(sqrt((243.0 *index2 * index2 - 1)) * sqrt(3.0) + 27 * index2, 1.0 / 3); - else - // If index2 is very large, we can think of the contents of sqrt as approximately √243 * index2. - t = pow( index2 * sqrt(243 * 3.0) + 27 * index2, 1.0 / 3); - - // Add deltas to avoid a slight calculation error when rounding. - // If it is too large, it may increase by 1 so adjustment is necessary. - - const double delta = 0.000000001; - - piece0 = int(t / pow(3.0, 2.0 / 3) + 1.0 / (pow(3.0, 1.0 / 3) * t) + delta) + 1; - // Uuu. Is it really like this? ('Ω`) - } - - //Since piece2 is obtained, substitute piece2 for i of i(i-1)(i-2)/6 (=a) in the above formula. Also substitute k = 0. - // j(j+1)/2 = index2-a - // This is from the solution formula of the quadratic equation.. - - uint64_t a = (uint64_t)piece0*((uint64_t)piece0 - 1)*((uint64_t)piece0 - 2) / 6; - int piece1 = int((1 + sqrt(8.0 * (index2 - a ) + 1)) / 2); - uint64_t b = (uint64_t)piece1 * (piece1 - 1) / 2; - int piece2 = int(index2 - a - b); - -#if 0 - if (!((piece0 > piece1 && piece1 > piece2))) - { - std::cout << index << " , " << index2 << "," << a << "," << sqrt(8.0 * (index2 - a) + 1); - } -#endif - - assert(piece0 > piece1 && piece1 > piece2); - - assert(piece2 < (int)fe_end_); - assert(piece1 < (int)fe_end_); - assert(piece0 < (int)fe_end_); - - raw_index /= triangle_fe_end; - - int king = (int)(raw_index /* % SQUARE_NB */); - assert(king < max_king_sq_); - - // Propagate king_sq and fe_end. - return fromKPPP((Square)king, (PieceSquare)piece0, (PieceSquare)piece1 , (PieceSquare)piece2); - } - - // Specify k,p0,p1,p2 to build KPPP instance. - // The king_sq and fe_end passed by set() which is internally retained are inherited. - KPPP fromKPPP(int king, PieceSquare p0, PieceSquare p1, PieceSquare p2) const - { - KPPP kppp(king, p0, p1, p2); - kppp.set(max_king_sq_, fe_end_,min_index()); - return kppp; - } - - // Get the index when counting the value of min_index() of this class as 0. - virtual uint64_t toRawIndex() const { - - // Macro similar to the one used in Bonanza 6.0 - // Precondition) i> j> k. - // NG in case of i==j,j==k. - auto PcPcPcOnSq = [this](int king, PieceSquare i, PieceSquare j , PieceSquare k) - { - // (i,j,k) in this triangular array is the element in the i-th row and the j-th column. - // 0th row 0th column 0th is the sum of the elements up to that point, so 0 + 0 + 1 + 3 + 6 + ... + (i)*(i-1)/2 = i*( i-1)*(i-2)/6 - // i-th row, j-th column, 0-th is j with j added. + j*(j-1) / 2 - // i-th row, j-th column and k-th row is k plus it. + k - assert(i > j && j > k); - - // PieceSquare type is assumed to be 32 bits, so if you do not pay attention to multiplication, it will overflow. - return (uint64_t)king * triangle_fe_end + (uint64_t)( - uint64_t(i)*(uint64_t(i) - 1) * (uint64_t(i) - 2) / 6 - + uint64_t(j)*(uint64_t(j) - 1) / 2 - + uint64_t(k) - ); - }; - - return PcPcPcOnSq(king_, piece0_, piece1_, piece2_); - } - - // When you construct this object using fromIndex(), you can get information with the following accessors. - int king() const { return king_; } - PieceSquare piece0() const { return piece0_; } - PieceSquare piece1() const { return piece1_; } - PieceSquare piece2() const { return piece2_; } - // Returns whether or not the dimension lowered with toLowerDimensions is inverse. - // Prepared to match KK, KKP and interface. This method always returns false for this KPPP class. - bool is_inverse() const { - return false; - } - - // Returns the number of elements in a triangular array. It is assumed that the kppp array is the following two-dimensional array. - // kppp[king_sq][triangle_fe_end]; - uint64_t get_triangle_fe_end() const { return triangle_fe_end; } - - // comparison operator - bool operator==(const KPPP& rhs) { - // piece0> piece1> piece2 is assumed, so there is no possibility of replacement. - return king() == rhs.king() && piece0() == rhs.piece0() && piece1() == rhs.piece1() && piece2() == rhs.piece2(); - } - bool operator!=(const KPPP& rhs) { return !(*this == rhs); } - - private: - - int king_; - PieceSquare piece0_, piece1_,piece2_; - - // The part of the square array of [fe_end][fe_end][fe_end] of kppp[king_sq][fe_end][fe_end][fe_end] is made into a triangular array. - // If kppp[king_sq][triangle_fe_end], the number of elements from the 0th row of this triangular array is 0,0,1,3,..., The nth row is n(n-1)/2. - // therefore, - // triangle_fe_end = Σn(n-1)/2 , n=0..fe_end-1 - // = fe_end * (fe_end - 1) * (fe_end - 2) / 6 - uint64_t triangle_fe_end; // ((uint64_t)PieceSquare::PS_END)*((uint64_t)PieceSquare::PS_END - 1)*((uint64_t)PieceSquare::PS_END - 2) / 6; - }; - - // Output for debugging. - static std::ostream& operator<<(std::ostream& os, KPPP rhs) - { - os << "KPPP(" << rhs.king() << "," << rhs.piece0() << "," << rhs.piece1() << "," << rhs.piece2() << ")"; - return os; - } - - // For learning about 4 pieces by KKPP. - // - // Same design as KPPP class. In KPPP class, treat as one with less p. - // The positions of the two balls are encoded as values ​​from 0 to king_sq-1. - // - // Later, regarding the pieces0 and 1 returned by this class, - // piece0() >piece1() - // It is, and it is necessary to keep this constraint even when passing piece0,1 in the constructor. - // - // Due to this constraint, PieceSquareZero cannot be assigned to piece0 and piece1 at the same time and passed. - // If you want to support learning of dropped frames, you need to devise with evaluate(). - struct KKPP: SerializerBase - { - protected: - KKPP(int king, PieceSquare p0, PieceSquare p1) : - king_(king), piece0_(p0), piece1_(p1) - { - assert(piece0_ > piece1_); - /* sort_piece(); */ - } - - public: - KKPP() {} - - virtual uint64_t size() const { return (uint64_t)max_king_sq_*triangle_fe_end; } - - // Set fe_end and king_sq. - // fe_end: fe_end assumed by this KPPP class - // king_sq: Number of balls to handle in KPPP. - // 9 steps x mirrors 9 steps x 5 squared squares (balls before and after) = 45*45 = 2025. - // Set this first using set() on the side that uses this KKPP class. - void set(int max_king_sq, uint64_t fe_end , uint64_t min_index) { - // This value is used in size(), and size() is used in SerializerBase::set(), so calculate first. - triangle_fe_end = fe_end * (fe_end - 1) / 2; - - SerializerBase::set(max_king_sq, fe_end, min_index); - } - - // number of dimension reductions - // For the time being, the dimension reduction of the mirror is not supported. I wonder if I'll do it here... (Because the memory for learning is a waste) -#define KKPP_LOWER_COUNT 1 - - // Get the index of the low-dimensional array. - //Note that the one with p0,p1,p2 swapped will not be returned. - // Also, the mirrored one is returned only when USE_KPPP_MIRROR_WRITE is enabled. - void toLowerDimensions(/*out*/ KKPP kkpp_[KPPP_LOWER_COUNT]) const - { - kkpp_[0] = fromKKPP(king_, piece0_, piece1_); - - // When mirroring, mir_piece will not be sorted. Need code to sort. - // We also need to define a mirror for king_. - } - - // builder that creates KKPP object from index (serial number) - KKPP fromIndex(uint64_t index) const { assert(index >= min_index()); return fromRawIndex(index - min_index()); } - - // builder that creates KKPP object from raw_index (number starting from 0, not serial number) - KKPP fromRawIndex(uint64_t raw_index) const - { - uint64_t index2 = raw_index % triangle_fe_end; - - // Write the expression to find piece0, piece1, piece2 from index2 here. - // This is the inverse function of index2 = i(i-1)/2 + j. - // Use the formula of the solution of the quadratic equation with j=0. - // When index2=0, it is a double root, but the smaller one does not satisfy i>j and is ignored. - - int piece0 = (int(sqrt(8 * index2 + 1)) + 1)/2; - int piece1 = int(index2 - piece0 * (piece0 - 1) /2 ); - - assert(piece0 > piece1); - - assert(piece1 < (int)fe_end_); - assert(piece0 < (int)fe_end_); - - raw_index /= triangle_fe_end; - - int king = (int)(raw_index /* % SQUARE_NB */); - assert(king < max_king_sq_); - - // Propagate king_sq and fe_end. - return fromKKPP(king, (PieceSquare)piece0, (PieceSquare)piece1); - } - - // Specify k,p0,p1 to build KKPP instance. - // The king_sq and fe_end passed by set() which is internally retained are inherited. - KKPP fromKKPP(int king, PieceSquare p0, PieceSquare p1) const - { - KKPP kkpp(king, p0, p1); - kkpp.set(max_king_sq_, fe_end_,min_index()); - return kkpp; - } - - // Get the index when counting the value of min_index() of this class as 0. - virtual uint64_t toRawIndex() const { - - // Macro similar to the one used in Bonanza 6.0 - // Precondition) i> j. - // NG in case of i==j,j==k. - auto PcPcOnSq = [this](int king, PieceSquare i, PieceSquare j) - { - assert(i > j); - - // PieceSquare type is assumed to be 32 bits, so if you do not pay attention to multiplication, it will overflow. - return (uint64_t)king * triangle_fe_end + (uint64_t)( - + uint64_t(i)*(uint64_t(i) - 1) / 2 - + uint64_t(j) - ); - }; - - return PcPcOnSq(king_, piece0_, piece1_); - } - - // When you construct this object using fromIndex(), fromKKPP(), you can get information with the following accessors. - int king() const { return king_; } - PieceSquare piece0() const { return piece0_; } - PieceSquare piece1() const { return piece1_; } - - // Returns whether or not the dimension lowered with toLowerDimensions is inverse. - // Prepared to match KK, KKP and interface. In this KKPP class, this method always returns false. - bool is_inverse() const { - return false; - } - - //Returns the number of elements in a triangular array. It is assumed that the kkpp array is the following two-dimensional array. - // kkpp[king_sq][triangle_fe_end]; - uint64_t get_triangle_fe_end() const { return triangle_fe_end; } - - // comparison operator - bool operator==(const KKPP& rhs) { - // Since piece0> piece1 is assumed, there is no possibility of replacement. - return king() == rhs.king() && piece0() == rhs.piece0() && piece1() == rhs.piece1(); - } - bool operator!=(const KKPP& rhs) { return !(*this == rhs); } - - private: - - int king_; - PieceSquare piece0_, piece1_; - - // Triangularize the square array part of [fe_end][fe_end] of kppp[king_sq][fe_end][fe_end]. - uint64_t triangle_fe_end = 0; - - }; - - // Output for debugging. - static std::ostream& operator<<(std::ostream& os, KKPP rhs) - { - os << "KKPP(" << rhs.king() << "," << rhs.piece0() << "," << rhs.piece1() << ")"; - return os; - } - - } #endif // defined (EVAL_LEARN) diff --git a/src/nnue/features/half_relative_kp.cpp b/src/nnue/features/half_relative_kp.cpp index 7f15ff39..015ecb73 100644 --- a/src/nnue/features/half_relative_kp.cpp +++ b/src/nnue/features/half_relative_kp.cpp @@ -11,49 +11,41 @@ namespace NNUE { namespace Features { +// Orient a square according to perspective (rotates by 180 for black) +inline Square orient(Color perspective, Square s) { + return Square(int(s) ^ (bool(perspective) * 63)); +} + // Find the index of the feature quantity from the ball position and PieceSquare template inline IndexType HalfRelativeKP::MakeIndex( - Square sq_k, PieceSquare p) { + Color perspective, Square s, Piece pc, Square sq_k) { + const IndexType p = IndexType(orient(perspective, s) + kpp_board_index[pc][perspective]); + return MakeIndex(sq_k, p); +} + +// Find the index of the feature quantity from the ball position and PieceSquare +template +inline IndexType HalfRelativeKP::MakeIndex( + Square sq_k, IndexType p) { constexpr IndexType W = kBoardWidth; constexpr IndexType H = kBoardHeight; - const IndexType piece_index = (p - PieceSquare::PS_W_PAWN) / SQUARE_NB; - const Square sq_p = static_cast((p - PieceSquare::PS_W_PAWN) % SQUARE_NB); + const IndexType piece_index = (p - PS_W_PAWN) / SQUARE_NB; + const Square sq_p = static_cast((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 the piece information -template -inline void HalfRelativeKP::GetPieces( - const Position& pos, Color perspective, - PieceSquare** pieces, Square* sq_target_k) { - *pieces = (perspective == BLACK) ? - pos.eval_list()->piece_list_fb() : - pos.eval_list()->piece_list_fw(); - const PieceId target = (AssociatedKing == Side::kFriend) ? - static_cast(PieceId::PIECE_ID_KING + perspective) : - static_cast(PieceId::PIECE_ID_KING + ~perspective); - *sq_target_k = static_cast(((*pieces)[target] - PieceSquare::PS_W_KING) % SQUARE_NB); -} - // Get a list of indices with a value of 1 among the features template void HalfRelativeKP::AppendActiveIndices( const Position& pos, Color perspective, IndexList* active) { - // do nothing if array size is small to avoid compiler warning - if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return; - - PieceSquare* pieces; - Square sq_target_k; - GetPieces(pos, perspective, &pieces, &sq_target_k); - for (PieceId i = PieceId::PIECE_ID_ZERO; i < PieceId::PIECE_ID_KING; ++i) { - if (pieces[i] >= PieceSquare::PS_W_PAWN) { - if (pieces[i] != PieceSquare::PS_NONE) { - active->push_back(MakeIndex(sq_target_k, pieces[i])); - } - } + Square ksq = orient(perspective, pos.square(perspective)); + Bitboard bb = pos.pieces() & ~pos.pieces(KING); + while (bb) { + Square s = pop_lsb(&bb); + active->push_back(MakeIndex(perspective, s, pos.piece_on(s), ksq)); } } @@ -62,26 +54,15 @@ template void HalfRelativeKP::AppendChangedIndices( const Position& pos, Color perspective, IndexList* removed, IndexList* added) { - PieceSquare* pieces; - Square sq_target_k; - GetPieces(pos, perspective, &pieces, &sq_target_k); + Square ksq = orient(perspective, pos.square(perspective)); const auto& dp = pos.state()->dirtyPiece; for (int i = 0; i < dp.dirty_num; ++i) { - if (dp.pieceId[i] >= PieceId::PIECE_ID_KING) continue; - const auto old_p = static_cast( - dp.old_piece[i].from[perspective]); - if (old_p >= PieceSquare::PS_W_PAWN) { - if (old_p != PieceSquare::PS_NONE) { - removed->push_back(MakeIndex(sq_target_k, old_p)); - } - } - const auto new_p = static_cast( - dp.new_piece[i].from[perspective]); - if (new_p >= PieceSquare::PS_W_PAWN) { - if (new_p != PieceSquare::PS_NONE) { - added->push_back(MakeIndex(sq_target_k, new_p)); - } - } + Piece pc = dp.piece[i]; + if (type_of(pc) == KING) continue; + if (dp.from[i] != SQ_NONE) + removed->push_back(MakeIndex(perspective, dp.from[i], pc, ksq)); + if (dp.to[i] != SQ_NONE) + added->push_back(MakeIndex(perspective, dp.to[i], pc, ksq)); } } diff --git a/src/nnue/features/half_relative_kp.h b/src/nnue/features/half_relative_kp.h index 9561ab91..2d4182e4 100644 --- a/src/nnue/features/half_relative_kp.h +++ b/src/nnue/features/half_relative_kp.h @@ -25,7 +25,7 @@ class HalfRelativeKP { static constexpr std::uint32_t kHashValue = 0xF9180919u ^ (AssociatedKing == Side::kFriend); // Piece type excluding balls - static constexpr IndexType kNumPieceKinds = (PieceSquare::PS_END - PieceSquare::PS_W_PAWN) / SQUARE_NB; + static constexpr IndexType kNumPieceKinds = 5 * 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 @@ -34,7 +34,7 @@ class HalfRelativeKP { 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 = PieceId::PIECE_ID_KING; + static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count // Timing of full calculation instead of difference calculation static constexpr TriggerEvent kRefreshTrigger = (AssociatedKing == Side::kFriend) ? @@ -49,12 +49,9 @@ class HalfRelativeKP { IndexList* removed, IndexList* added); // Find the index of the feature quantity from the ball position and PieceSquare - static IndexType MakeIndex(Square sq_k, PieceSquare p); - - private: - // Get the piece information - static void GetPieces(const Position& pos, Color perspective, - PieceSquare** pieces, Square* sq_target_k); + static IndexType MakeIndex(Square s, IndexType p); + // Find the index of the feature quantity from the ball position and PieceSquare + static IndexType MakeIndex(Color perspective, Square s, Piece pc, Square sq_k); }; } // namespace Features diff --git a/src/nnue/features/k.cpp b/src/nnue/features/k.cpp index 001e4b98..314b1338 100644 --- a/src/nnue/features/k.cpp +++ b/src/nnue/features/k.cpp @@ -11,19 +11,21 @@ namespace NNUE { namespace Features { +// Orient a square according to perspective (rotates by 180 for black) +inline Square orient(Color perspective, Square s) { + return Square(int(s) ^ (bool(perspective) * 63)); +} + +// Index of a feature for a given king position. +IndexType K::MakeIndex(Color perspective, Square s, Color king_color) { + return IndexType(orient(perspective, s) + bool(perspective ^ king_color) * 64); +} + // Get a list of indices with a value of 1 among the features void K::AppendActiveIndices( const Position& pos, Color perspective, IndexList* active) { - // do nothing if array size is small to avoid compiler warning - if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return; - - const PieceSquare* pieces = (perspective == BLACK) ? - pos.eval_list()->piece_list_fb() : - pos.eval_list()->piece_list_fw(); - assert(pieces[PieceId::PIECE_ID_BKING] != PieceSquare::PS_NONE); - assert(pieces[PieceId::PIECE_ID_WKING] != PieceSquare::PS_NONE); - for (PieceId i = PieceId::PIECE_ID_KING; i < PieceId::PIECE_ID_NONE; ++i) { - active->push_back(pieces[i] - PieceSquare::PS_END); + for (auto color : Colors) { + active->push_back(MakeIndex(perspective, pos.square(color), color)); } } @@ -32,12 +34,19 @@ void K::AppendChangedIndices( const Position& pos, Color perspective, IndexList* removed, IndexList* added) { const auto& dp = pos.state()->dirtyPiece; - if (dp.pieceId[0] >= PieceId::PIECE_ID_KING) { - removed->push_back( - dp.old_piece[0].from[perspective] - PieceSquare::PS_END); - added->push_back( - dp.new_piece[0].from[perspective] - PieceSquare::PS_END); + Color king_color; + if (dp.piece[0] == Piece::W_KING) { + king_color = WHITE; } + else if (dp.piece[0] == Piece::B_KING) { + king_color = BLACK; + } + else { + return; + } + + removed->push_back(MakeIndex(perspective, dp.from[0], king_color)); + added->push_back(MakeIndex(perspective, dp.to[0], king_color)); } } // namespace Features diff --git a/src/nnue/features/k.h b/src/nnue/features/k.h index 28431010..0c394f4e 100644 --- a/src/nnue/features/k.h +++ b/src/nnue/features/k.h @@ -35,6 +35,10 @@ class K { // Get a list of indices whose values ​​have changed from the previous one in the feature quantity static void AppendChangedIndices(const Position& pos, Color perspective, IndexList* removed, IndexList* added); + +private: + // Index of a feature for a given king position. + static IndexType MakeIndex(Color perspective, Square s, Color king_color); }; } // namespace Features diff --git a/src/nnue/features/p.cpp b/src/nnue/features/p.cpp index 8b24f544..b4a6faf9 100644 --- a/src/nnue/features/p.cpp +++ b/src/nnue/features/p.cpp @@ -11,19 +11,24 @@ namespace NNUE { namespace Features { +// Orient a square according to perspective (rotates by 180 for black) +inline Square orient(Color perspective, Square s) { + return Square(int(s) ^ (bool(perspective) * 63)); +} + +// Find the index of the feature quantity from the king position and PieceSquare +inline IndexType P::MakeIndex( + 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 P::AppendActiveIndices( const Position& pos, Color perspective, IndexList* active) { - // do nothing if array size is small to avoid compiler warning - if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return; - - const PieceSquare* pieces = (perspective == BLACK) ? - pos.eval_list()->piece_list_fb() : - pos.eval_list()->piece_list_fw(); - for (PieceId i = PieceId::PIECE_ID_ZERO; i < PieceId::PIECE_ID_KING; ++i) { - if (pieces[i] != PieceSquare::PS_NONE) { - active->push_back(pieces[i]); - } + Bitboard bb = pos.pieces() & ~pos.pieces(KING); + while (bb) { + Square s = pop_lsb(&bb); + active->push_back(MakeIndex(perspective, s, pos.piece_on(s))); } } @@ -33,13 +38,12 @@ void P::AppendChangedIndices( IndexList* removed, IndexList* added) { const auto& dp = pos.state()->dirtyPiece; for (int i = 0; i < dp.dirty_num; ++i) { - if (dp.pieceId[i] >= PieceId::PIECE_ID_KING) continue; - if (dp.old_piece[i].from[perspective] != PieceSquare::PS_NONE) { - removed->push_back(dp.old_piece[i].from[perspective]); - } - if (dp.new_piece[i].from[perspective] != PieceSquare::PS_NONE) { - added->push_back(dp.new_piece[i].from[perspective]); - } + Piece pc = dp.piece[i]; + if (type_of(pc) == KING) continue; + if (dp.from[i] != SQ_NONE) + removed->push_back(MakeIndex(perspective, dp.from[i], pc)); + if (dp.to[i] != SQ_NONE) + added->push_back(MakeIndex(perspective, dp.to[i], pc)); } } diff --git a/src/nnue/features/p.h b/src/nnue/features/p.h index 2a83c4ad..b3d4191e 100644 --- a/src/nnue/features/p.h +++ b/src/nnue/features/p.h @@ -22,9 +22,9 @@ class P { // Hash value embedded in the evaluation function file static constexpr std::uint32_t kHashValue = 0x764CFB4Bu; // number of feature dimensions - static constexpr IndexType kDimensions = PieceSquare::PS_END; + static constexpr IndexType kDimensions = PS_END; // The maximum value of the number of indexes whose value is 1 at the same time among the feature values - static constexpr IndexType kMaxActiveDimensions = PieceId::PIECE_ID_KING; + static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count // Timing of full calculation instead of difference calculation static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kNone; @@ -35,6 +35,10 @@ class P { // Get a list of indices whose values ​​have changed from the previous one in the feature quantity static void AppendChangedIndices(const Position& pos, Color perspective, IndexList* removed, IndexList* added); + + private: + // Index of a feature for a given piece on some square + static IndexType MakeIndex(Color perspective, Square s, Piece pc); }; } // namespace Features diff --git a/src/nnue/trainer/features/factorizer_half_kp.h b/src/nnue/trainer/features/factorizer_half_kp.h index 48a99797..955894e8 100644 --- a/src/nnue/trainer/features/factorizer_half_kp.h +++ b/src/nnue/trainer/features/factorizer_half_kp.h @@ -62,8 +62,8 @@ class Factorizer> { IndexType index_offset = AppendBaseFeature( kProperties[kFeaturesHalfKP], base_index, training_features); - const auto sq_k = static_cast(base_index / PieceSquare::PS_END); - const auto p = static_cast(base_index % PieceSquare::PS_END); + const auto sq_k = static_cast(base_index / PS_END); + const auto p = static_cast(base_index % PS_END); // kFeaturesHalfK { const auto& properties = kProperties[kFeaturesHalfK]; @@ -76,7 +76,7 @@ class Factorizer> { index_offset += InheritFeaturesIfRequired

( index_offset, kProperties[kFeaturesP], p, training_features); // kFeaturesHalfRelativeKP - if (p >= PieceSquare::PS_W_PAWN) { + if (p >= PS_W_PAWN) { index_offset += InheritFeaturesIfRequired>( index_offset, kProperties[kFeaturesHalfRelativeKP], HalfRelativeKP::MakeIndex(sq_k, p), diff --git a/src/types.h b/src/types.h index d34781e5..bcc4f77f 100644 --- a/src/types.h +++ b/src/types.h @@ -444,6 +444,11 @@ constexpr Square to_sq(Move m) { return Square(m & 0x3F); } +// Return relative square when turning the board 180 degrees +constexpr Square rotate180(Square sq) { + return (Square)(sq ^ 0x3F); +} + constexpr int from_to(Move m) { return m & 0xFFF; }