diff --git a/app/src/matchmaking/sprt/sprt.cpp b/app/src/matchmaking/sprt/sprt.cpp index 9931835d4..f154ce9dd 100644 --- a/app/src/matchmaking/sprt/sprt.cpp +++ b/app/src/matchmaking/sprt/sprt.cpp @@ -74,6 +74,11 @@ static double regularize(int value) { return value; } +static double regularize(double value) { + if (value <= 1e-32) return 1e-32; + return value; +} + double SPRT::getLLR(const Stats& stats, bool penta) const noexcept { if (penta) return getLLR(stats.penta_WW, stats.penta_WD, stats.penta_WL, stats.penta_DD, stats.penta_LD, stats.penta_LL); @@ -176,8 +181,8 @@ static double itp(F f, double a, double b, double f_a, double f_b, double k_1, d double x_f = (f_b * a - f_a * b) / (f_b - f_a); - double sigma = (x_half - x_f) / std::abs(x_half - x_f); - double x_t = delta <= std::abs(x_half - x_f) ? x_f + sigma * delta : x_half; + int sigma = (x_half - x_f) >= 0 ? 1 : -1; + double x_t = delta <= std::abs(x_half - x_f) ? x_f + sigma * delta : x_half; double x_itp = std::abs(x_t - x_half) <= r ? x_t : x_half - sigma * r; @@ -204,7 +209,7 @@ double SPRT::getLLR_logistic(double total, std::array scores, std::ar // given an empirical distribution. See proposition 1.1 of [1] for details. // // [1]: Michel Van den Bergh, Comments on Normalized Elo, - // https://www.cantate.be/Fishtest/normalized_elo_practical.pdf + // https://www.cantate.be/Fishtest/comparing_approximations.pdf const auto mle = [&](double s) -> std::array { const double theta_epsilon = 1e-3; @@ -236,7 +241,9 @@ double SPRT::getLLR_logistic(double total, std::array scores, std::ar std::array p0 = mle(s0); std::array p1 = mle(s1); std::array lpr; - for (size_t i = 0; i < N; i++) lpr[i] = std::log(p1[i]) - std::log(p0[i]); + for (size_t i = 0; i < N; i++) + lpr[i] = std::log(regularize(p1[i])) - std::log(regularize(p0[i])); + return total * mean(lpr, probs); } @@ -249,6 +256,7 @@ double SPRT::getLLR_normalized(double total, std::array scores, std:: // [1]: Michel Van den Bergh, Comments on Normalized Elo, // https://www.cantate.be/Fishtest/normalized_elo_practical.pdf const auto mle = [&](double mu_ref, double t_star) -> std::array { + const double var_epsilon = 1e-15; const double theta_epsilon = 1e-7; const double mle_epsilon = 1e-4; @@ -260,10 +268,14 @@ double SPRT::getLLR_normalized(double total, std::array scores, std:: for (int iterations = 0; iterations < 10; iterations++) { // Calculate phi. auto [mu, var] = mean_and_variance(scores, p); + + // Variance too small? + if (var < var_epsilon) break; + + double sigma = std::sqrt(var); std::array phi; for (size_t i = 0; i < N; i++) { double a_i = scores[i]; - double sigma = std::sqrt(var); phi[i] = a_i - mu_ref - 0.5 * t_star * sigma * (1.0 + ((a_i - mu) / sigma) * ((a_i - mu) / sigma)); } @@ -305,7 +317,9 @@ double SPRT::getLLR_normalized(double total, std::array scores, std:: std::array p0 = mle(0.5, t0); std::array p1 = mle(0.5, t1); std::array lpr; - for (size_t i = 0; i < N; i++) lpr[i] = std::log(p1[i]) - std::log(p0[i]); + for (size_t i = 0; i < N; i++) + lpr[i] = std::log(regularize(p1[i])) - std::log(regularize(p0[i])); + return total * mean(lpr, probs); }