bool useNNUE;
string eval_file_loaded = "None";
- /// init_NNUE() tries to load a nnue network at startup time, or when the engine
+ /// NNUE::init() tries to load a nnue network at startup time, or when the engine
/// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
/// The name of the nnue network is always retrieved from the EvalFile option.
/// We search the given network in three locations: internally (the default
/// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
/// variable to have the engine search in a special directory in their distro.
- void init_NNUE() {
+ void NNUE::init() {
useNNUE = Options["Use NNUE"];
if (!useNNUE)
}
}
- /// verify_NNUE() verifies that the last net used was loaded successfully
- void verify_NNUE() {
+ /// NNUE::verify() verifies that the last net used was loaded successfully
+ void NNUE::verify() {
string eval_file = string(Options["EvalFile"]);
Value Eval::evaluate(const Position& pos) {
- bool useClassical = abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count());
- bool classical = !Eval::useNNUE
- || useClassical
- || (abs(eg_value(pos.psq_score())) > PawnValueMg / 8 && !(pos.this_thread()->nodes & 0xF));
- Value v = classical ? Evaluation<NO_TRACE>(pos).value()
- : NNUE::evaluate(pos) * 5 / 4 + Tempo;
-
- if ( useClassical
- && Eval::useNNUE
- && abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count()))
- v = NNUE::evaluate(pos) * 5 / 4 + Tempo;
+ Value v;
+
+ if (!Eval::useNNUE)
+ v = Evaluation<NO_TRACE>(pos).value();
+ else
+ {
+ // Scale and shift NNUE for compatibility with search and classical evaluation
+ auto adjusted_NNUE = [&](){
+ int mat = pos.non_pawn_material();
+ return NNUE::evaluate(pos) * (1024 + mat / 32) / 1024 + Tempo;
+ };
+
+ // If there is PSQ imbalance use classical eval, with small probability if it is small
+ 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));
+
+ v = classical ? Evaluation<NO_TRACE>(pos).value() : adjusted_NNUE();
+
+ // If the classical eval is small and imbalance large, use NNUE nevertheless.
+ // For the case of opposite colored bishops, switch to NNUE eval with
+ // small probability if the classical eval is less than the threshold.
+ if ( largePsq
+ && (abs(v) * 16 < NNUEThreshold2 * r50
+ || ( pos.opposite_bishops()
+ && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50
+ && !(pos.this_thread()->nodes & 0xB))))
+ v = adjusted_NNUE();
+ }
// Damp down the evaluation linearly when shuffling
v = v * (100 - pos.rule50_count()) / 100;