Fixed compilation errors.

This commit is contained in:
nodchip
2020-08-29 00:56:05 +09:00
parent 906c18eb46
commit f7bc4e6e45
14 changed files with 96 additions and 1511 deletions

View File

@@ -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)