Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ endif
SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \
material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
nnue/evaluate_nnue.cpp nnue/features/half_kp.cpp
nnue/evaluate_nnue.cpp nnue/features/half_ka_v2.cpp

OBJS = $(notdir $(SRCS:.cpp=.o))

Expand Down
12 changes: 5 additions & 7 deletions src/evaluate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1112,10 +1112,9 @@ Value Eval::evaluate(const Position& pos) {
// Scale and shift NNUE for compatibility with search and classical evaluation
auto adjusted_NNUE = [&]()
{
int material = pos.non_pawn_material() + 4 * PawnValueMg * pos.count<PAWN>();
int scale = 580
+ material / 32
- 4 * pos.rule50_count();
int param1 = 903, param2 = 28, param3 = 28;

int scale = param1 + param2 * pos.count<PAWN>() + param3 * pos.non_pawn_material() / 1024;

Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE;

Expand All @@ -1130,7 +1129,7 @@ Value Eval::evaluate(const Position& pos) {
Value psq = Value(abs(eg_value(pos.psq_score())));
int r50 = 16 + pos.rule50_count();
bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50;
bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB));
bool classical = largePsq;

// Use classical evaluation for really low piece endgames.
// One critical case is the draw for bishop + A/H file pawn vs naked king.
Expand All @@ -1147,8 +1146,7 @@ Value Eval::evaluate(const Position& pos) {
&& !lowPieceEndgame
&& ( abs(v) * 16 < NNUEThreshold2 * r50
|| ( pos.opposite_bishops()
&& abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50
&& !(pos.this_thread()->nodes & 0xB))))
&& abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50)))
v = adjusted_NNUE();
}

Expand Down
2 changes: 1 addition & 1 deletion src/evaluate.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace Eval {
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue
// for the build process (profile-build and fishtest) to work. Do not change the
// name of the macro, as it is used in the Makefile.
#define EvalFileDefaultName "nn-62ef826d1a6d.nnue"
#define EvalFileDefaultName "nn-8a08400ed089.nnue"

namespace NNUE {

Expand Down
24 changes: 16 additions & 8 deletions src/nnue/evaluate_nnue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace Stockfish::Eval::NNUE {
LargePagePtr<FeatureTransformer> featureTransformer;

// Evaluation function
AlignedPtr<Network> network;
AlignedPtr<Network> network[LayerStacks];

// Evaluation function file name
std::string fileName;
Expand Down Expand Up @@ -83,7 +83,8 @@ namespace Stockfish::Eval::NNUE {
void initialize() {

Detail::initialize(featureTransformer);
Detail::initialize(network);
for (std::size_t i = 0; i < LayerStacks; ++i)
Detail::initialize(network[i]);
}

// Read network header
Expand All @@ -92,7 +93,7 @@ namespace Stockfish::Eval::NNUE {
std::uint32_t version, size;

version = read_little_endian<std::uint32_t>(stream);
*hashValue = read_little_endian<std::uint32_t>(stream);
*hashValue = read_little_endian<std::uint32_t>(stream);
size = read_little_endian<std::uint32_t>(stream);
if (!stream || version != Version) return false;
desc->resize(size);
Expand All @@ -117,7 +118,8 @@ namespace Stockfish::Eval::NNUE {
if (!read_header(stream, &hashValue, &netDescription)) return false;
if (hashValue != HashValue) return false;
if (!Detail::read_parameters(stream, *featureTransformer)) return false;
if (!Detail::read_parameters(stream, *network)) return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
if (!Detail::read_parameters(stream, *(network[i]))) return false;
return stream && stream.peek() == std::ios::traits_type::eof();
}

Expand All @@ -126,7 +128,8 @@ namespace Stockfish::Eval::NNUE {

if (!write_header(stream, HashValue, netDescription)) return false;
if (!Detail::write_parameters(stream, *featureTransformer)) return false;
if (!Detail::write_parameters(stream, *network)) return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
if (!Detail::write_parameters(stream, *(network[i]))) return false;
return (bool)stream;
}

Expand Down Expand Up @@ -154,10 +157,15 @@ namespace Stockfish::Eval::NNUE {
ASSERT_ALIGNED(transformedFeatures, alignment);
ASSERT_ALIGNED(buffer, alignment);

featureTransformer->transform(pos, transformedFeatures);
const auto output = network->propagate(transformedFeatures, buffer);
const std::size_t bucket = (popcount(pos.pieces()) - 1) / 4;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: pos.count<ALL_PIECES>() is probably faster on most machines than popcount(pos.pieces()).


return static_cast<Value>(output[0] / OutputScale);
const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket);
if (lazy) {
return static_cast<Value>(psqt / OutputScale);
} else {
const auto output = network[bucket]->propagate(transformedFeatures, buffer);
return static_cast<Value>((output[0] + psqt) / OutputScale);
}
}

// Load eval, from a file stream or a memory stream
Expand Down
25 changes: 12 additions & 13 deletions src/nnue/features/half_kp.cpp → src/nnue/features/half_ka_v2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,32 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

//Definition of input features HalfKP of NNUE evaluation function
//Definition of input features HalfKAv2 of NNUE evaluation function

#include "half_kp.h"
#include "half_ka_v2.h"

#include "../../position.h"

namespace Stockfish::Eval::NNUE::Features {

// Orient a square according to perspective (rotates by 180 for black)
inline Square HalfKP::orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * 63));
inline Square HalfKAv2::orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * 56));
}

// Index of a feature for a given king position and another piece on some square
inline IndexType HalfKP::make_index(Color perspective, Square s, Piece pc, Square ksq) {
inline IndexType HalfKAv2::make_index(Color perspective, Square s, Piece pc, Square ksq) {
return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq);
}

// Get a list of indices for active features
void HalfKP::append_active_indices(
void HalfKAv2::append_active_indices(
const Position& pos,
Color perspective,
ValueListInserter<IndexType> active
) {
Square ksq = orient(perspective, pos.square<KING>(perspective));
Bitboard bb = pos.pieces() & ~pos.pieces(KING);
Bitboard bb = pos.pieces();
while (bb)
{
Square s = pop_lsb(bb);
Expand All @@ -52,7 +52,7 @@ namespace Stockfish::Eval::NNUE::Features {

// append_changed_indices() : get a list of indices for recently changed features

void HalfKP::append_changed_indices(
void HalfKAv2::append_changed_indices(
Square ksq,
StateInfo* st,
Color perspective,
Expand All @@ -63,23 +63,22 @@ namespace Stockfish::Eval::NNUE::Features {
Square oriented_ksq = orient(perspective, ksq);
for (int i = 0; i < dp.dirty_num; ++i) {
Piece pc = dp.piece[i];
if (type_of(pc) == KING) continue;
if (dp.from[i] != SQ_NONE)
removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq));
if (dp.to[i] != SQ_NONE)
added.push_back(make_index(perspective, dp.to[i], pc, oriented_ksq));
}
}

int HalfKP::update_cost(StateInfo* st) {
int HalfKAv2::update_cost(StateInfo* st) {
return st->dirtyPiece.dirty_num;
}

int HalfKP::refresh_cost(const Position& pos) {
return pos.count<ALL_PIECES>() - 2;
int HalfKAv2::refresh_cost(const Position& pos) {
return pos.count<ALL_PIECES>();
}

bool HalfKP::requires_refresh(StateInfo* st, Color perspective) {
bool HalfKAv2::requires_refresh(StateInfo* st, Color perspective) {
return st->dirtyPiece.piece[0] == make_piece(perspective, KING);
}

Expand Down
51 changes: 26 additions & 25 deletions src/nnue/features/half_kp.h → src/nnue/features/half_ka_v2.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

//Definition of input features HalfKP of NNUE evaluation function

#ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
#define NNUE_FEATURES_HALF_KP_H_INCLUDED
#ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED
#define NNUE_FEATURES_HALF_KA_V2_H_INCLUDED

#include "../nnue_common.h"

Expand All @@ -32,33 +32,34 @@ namespace Stockfish {

namespace Stockfish::Eval::NNUE::Features {

// Feature HalfKP: Combination of the position of own king
// and the position of pieces other than kings
class HalfKP {
// Feature HalfKAv2: Combination of the position of own king
// and the position of pieces
class HalfKAv2 {

// unique number for each piece type on each square
enum {
PS_NONE = 0,
PS_W_PAWN = 1,
PS_B_PAWN = 1 * SQUARE_NB + 1,
PS_W_KNIGHT = 2 * SQUARE_NB + 1,
PS_B_KNIGHT = 3 * SQUARE_NB + 1,
PS_W_BISHOP = 4 * SQUARE_NB + 1,
PS_B_BISHOP = 5 * SQUARE_NB + 1,
PS_W_ROOK = 6 * SQUARE_NB + 1,
PS_B_ROOK = 7 * SQUARE_NB + 1,
PS_W_QUEEN = 8 * SQUARE_NB + 1,
PS_B_QUEEN = 9 * SQUARE_NB + 1,
PS_NB = 10 * SQUARE_NB + 1
PS_W_PAWN = 0,
PS_B_PAWN = 1 * SQUARE_NB,
PS_W_KNIGHT = 2 * SQUARE_NB,
PS_B_KNIGHT = 3 * SQUARE_NB,
PS_W_BISHOP = 4 * SQUARE_NB,
PS_B_BISHOP = 5 * SQUARE_NB,
PS_W_ROOK = 6 * SQUARE_NB,
PS_B_ROOK = 7 * SQUARE_NB,
PS_W_QUEEN = 8 * SQUARE_NB,
PS_B_QUEEN = 9 * SQUARE_NB,
PS_KING = 10 * SQUARE_NB,
PS_NB = 11 * SQUARE_NB
};

static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = {
// convention: W - us, B - them
// viewed from other side, W and B are reversed
{ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE,
PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE },
{ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE,
PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE }
{ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE,
PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE },
{ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE,
PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE }
};

// Orient a square according to perspective (rotates by 180 for black)
Expand All @@ -69,17 +70,17 @@ namespace Stockfish::Eval::NNUE::Features {

public:
// Feature name
static constexpr const char* Name = "HalfKP(Friend)";
static constexpr const char* Name = "HalfKAv2(Friend)";

// Hash value embedded in the evaluation file
static constexpr std::uint32_t HashValue = 0x5D69D5B8u;
static constexpr std::uint32_t HashValue = 0x5f234cb8u;

// Number of feature dimensions
static constexpr IndexType Dimensions =
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_NB);

// Maximum number of simultaneously active features. 30 because kins are not included.
static constexpr IndexType MaxActiveDimensions = 30;
// Maximum number of simultaneously active features.
static constexpr IndexType MaxActiveDimensions = 32;

// Get a list of indices for active features
static void append_active_indices(
Expand Down Expand Up @@ -107,4 +108,4 @@ namespace Stockfish::Eval::NNUE::Features {

} // namespace Stockfish::Eval::NNUE::Features

#endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED
Loading