mirror of
https://github.com/HChaZZY/Stockfish.git
synced 2025-12-25 03:26:24 +08:00
Fixed compilation errors.
This commit is contained in:
@@ -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<void()> 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 <t[2n + 1], the first piece
|
||||
b) When t[2n + 1] <= p <t[2n + 2], the back piece
|
||||
Is.
|
||||
|
||||
Therefore, if p in the range of a) is set to q = rotate180(p-t[2n+0]) + t[2n+1], it becomes the back piece in the box rotated 180 degrees.
|
||||
So inv_piece[] is initialized by swapping p and q.
|
||||
*/
|
||||
|
||||
// There is no mirror for hand pieces.
|
||||
if (p < PieceSquare::PS_W_PAWN)
|
||||
continue;
|
||||
|
||||
PieceSquare r1 = (PieceSquare)(flip_file(sq) + t[i]);
|
||||
mir_piece_[p] = r1;
|
||||
mir_piece_[r1] = p;
|
||||
|
||||
PieceSquare p2 = (PieceSquare)(sq + t[i + 1]);
|
||||
PieceSquare r2 = (PieceSquare)(flip_file(sq) + t[i + 1]);
|
||||
mir_piece_[p2] = r2;
|
||||
mir_piece_[r2] = p2;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mir_piece_init_function)
|
||||
mir_piece_init_function();
|
||||
|
||||
for (PieceSquare p = PieceSquare::PS_NONE; p < PieceSquare::PS_END; ++p)
|
||||
{
|
||||
// It remains uninitialized. The initialization code in the table above is incorrect.
|
||||
assert(mir_piece_[p] != PieceSquare::PS_NOT_INIT && mir_piece_[p] < PieceSquare::PS_END);
|
||||
assert(inv_piece_[p] != PieceSquare::PS_NOT_INIT && inv_piece_[p] < PieceSquare::PS_END);
|
||||
|
||||
// mir and inv return to their original coordinates after being applied twice.
|
||||
assert(mir_piece_[mir_piece_[p]] == p);
|
||||
assert(inv_piece_[inv_piece_[p]] == p);
|
||||
|
||||
// mir->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<PieceSquare> s;
|
||||
vector<int> 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)
|
||||
@@ -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 <functional>
|
||||
|
||||
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<void()> 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
|
||||
@@ -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<PieceId> 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<Square>(
|
||||
bona_piece_white - kpp_board_index[actual_piece].from[Color::WHITE]);
|
||||
assert(sq == actual_square);
|
||||
if (sq != actual_square) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -20,237 +20,6 @@ namespace EvalLearningTools
|
||||
double Weight::eta3;
|
||||
uint64_t Weight::eta1_epoch;
|
||||
uint64_t Weight::eta2_epoch;
|
||||
|
||||
std::vector<bool> 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<bool> 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<SQUARE_NB; ++k)
|
||||
for (int i = 0; i<10000; ++i) // As a test, assuming a large fe_end, try turning at 10000.
|
||||
for (int j = 0; j < i; ++j)
|
||||
{
|
||||
auto kkpp = g_kkpp.fromKKPP(k, (PieceSquare)i, (PieceSquare)j);
|
||||
auto r = kkpp.toRawIndex();
|
||||
assert(n++ == r);
|
||||
auto kkpp2 = g_kkpp.fromIndex(r + g_kkpp.min_index());
|
||||
assert(kkpp2.king() == k && kkpp2.piece0() == i && kkpp2.piece1() == j);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize this entire EvalLearningTools
|
||||
void init()
|
||||
{
|
||||
// Initialization is required only once after startup, so a flag for that.
|
||||
static bool first = true;
|
||||
|
||||
if (first)
|
||||
{
|
||||
std::cout << "EvalLearningTools init..";
|
||||
|
||||
// Make mir_piece() and inv_piece() available.
|
||||
// After this, the min_index_flag is initialized, but
|
||||
// It depends on this, so you need to do this first.
|
||||
init_mir_inv_tables();
|
||||
|
||||
//learning_tools_unit_test_kpp();
|
||||
//learning_tools_unit_test_kppp();
|
||||
//learning_tools_unit_test_kkpp();
|
||||
|
||||
// It may be the last time to execute UnitTest, but since init_min_index_flag() takes a long time,
|
||||
// I want to do this at the time of debugging.
|
||||
|
||||
init_min_index_flag();
|
||||
|
||||
std::cout << "done." << std::endl;
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#if defined (EVAL_LEARN)
|
||||
#include <array>
|
||||
|
||||
#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<bool> min_index_flag;
|
||||
|
||||
// -------------------------------------------------
|
||||
// Array for learning that stores gradients etc.
|
||||
// -------------------------------------------------
|
||||
@@ -217,817 +194,6 @@ namespace EvalLearningTools
|
||||
|
||||
std::array<LearnFloatType, 2> get_grad() const { return std::array<LearnFloatType, 2>{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 <typename T>
|
||||
std::array<T, 2> apply_inverse_sign(const std::array<T, 2>& rhs)
|
||||
{
|
||||
return !is_inverse() ? rhs : std::array<T, 2>{-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 <typename T>
|
||||
std::array<T, 2> apply_inverse_sign(const std::array<T, 2>& rhs)
|
||||
{
|
||||
return !is_inverse() ? rhs : std::array<T, 2>{-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)
|
||||
|
||||
@@ -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 <Side AssociatedKing>
|
||||
inline IndexType HalfRelativeKP<AssociatedKing>::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 <Side AssociatedKing>
|
||||
inline IndexType HalfRelativeKP<AssociatedKing>::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<Square>((p - PieceSquare::PS_W_PAWN) % SQUARE_NB);
|
||||
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 the piece information
|
||||
template <Side AssociatedKing>
|
||||
inline void HalfRelativeKP<AssociatedKing>::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>(PieceId::PIECE_ID_KING + perspective) :
|
||||
static_cast<PieceId>(PieceId::PIECE_ID_KING + ~perspective);
|
||||
*sq_target_k = static_cast<Square>(((*pieces)[target] - PieceSquare::PS_W_KING) % SQUARE_NB);
|
||||
}
|
||||
|
||||
// Get a list of indices with a value of 1 among the features
|
||||
template <Side AssociatedKing>
|
||||
void HalfRelativeKP<AssociatedKing>::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<KING>(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 <Side AssociatedKing>
|
||||
void HalfRelativeKP<AssociatedKing>::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<KING>(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<PieceSquare>(
|
||||
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<PieceSquare>(
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<KING>(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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -62,8 +62,8 @@ class Factorizer<HalfKP<AssociatedKing>> {
|
||||
IndexType index_offset = AppendBaseFeature<FeatureType>(
|
||||
kProperties[kFeaturesHalfKP], base_index, training_features);
|
||||
|
||||
const auto sq_k = static_cast<Square>(base_index / PieceSquare::PS_END);
|
||||
const auto p = static_cast<PieceSquare>(base_index % PieceSquare::PS_END);
|
||||
const auto sq_k = static_cast<Square>(base_index / PS_END);
|
||||
const auto p = static_cast<IndexType>(base_index % PS_END);
|
||||
// kFeaturesHalfK
|
||||
{
|
||||
const auto& properties = kProperties[kFeaturesHalfK];
|
||||
@@ -76,7 +76,7 @@ class Factorizer<HalfKP<AssociatedKing>> {
|
||||
index_offset += InheritFeaturesIfRequired<P>(
|
||||
index_offset, kProperties[kFeaturesP], p, training_features);
|
||||
// kFeaturesHalfRelativeKP
|
||||
if (p >= PieceSquare::PS_W_PAWN) {
|
||||
if (p >= PS_W_PAWN) {
|
||||
index_offset += InheritFeaturesIfRequired<HalfRelativeKP<AssociatedKing>>(
|
||||
index_offset, kProperties[kFeaturesHalfRelativeKP],
|
||||
HalfRelativeKP<AssociatedKing>::MakeIndex(sq_k, p),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user