Tests: Derive check_histogram's tolerance from the sampling distribution libeigen/eigen!2981 Closes #3130 Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
diff --git a/test/rand.cpp b/test/rand.cpp index 99df7b9..4ed1a88 100644 --- a/test/rand.cpp +++ b/test/rand.cpp
@@ -131,6 +131,24 @@ RangeType bin_width_; }; +// A bin of probability p receives Binomial(count, p) samples, so the normalized hist(i) has +// standard deviation sqrt((1 - p) / (count * p)) about 1. Bonferroni over the bins: k solving +// erfc(k / sqrt(2)) = kFalseFailureRate / bins caps at kFalseFailureRate the probability that +// any bin strays past k sigma. +inline double histogram_k_sigma(int bins) { + constexpr double kFalseFailureRate = 1e-9; + const double target = kFalseFailureRate / double(bins); + double lo = 0.0, hi = 40.0; + for (int iter = 0; iter < 100; ++iter) { + const double mid = 0.5 * (lo + hi); + if (std::erfc(mid / std::sqrt(2.0)) > target) + lo = mid; + else + hi = mid; + } + return hi; +} + template <typename Scalar> void check_histogram(Scalar x, Scalar y, int bins) { constexpr int repeats = 10000; @@ -143,12 +161,16 @@ int bin = hist_helper.bin(r); hist(bin)++; } - // Normalize bins by probability. + // Normalize bins by probability, and bound each by its own sampling standard deviation. hist /= count; + const double k = histogram_k_sigma(bins); + Eigen::VectorXd tol(bins); for (int i = 0; i < bins; ++i) { - hist(i) = hist(i) / hist_helper.uniform_bin_probability(i); + const double p = hist_helper.uniform_bin_probability(i); + hist(i) = hist(i) / p; + tol(i) = k * std::sqrt((1.0 - p) / (count * p)); } - VERIFY(((hist.array() - 1.0).abs() < 0.05).all()); + VERIFY(((hist.array() - 1.0).abs() < tol.array()).all()); } template <typename Scalar> @@ -163,12 +185,16 @@ int bin = hist_helper.bin(r); hist(bin)++; } - // Normalize bins by probability. + // Normalize bins by probability, and bound each by its own sampling standard deviation. hist /= count; + const double k = histogram_k_sigma(bins); + Eigen::VectorXd tol(bins); for (int i = 0; i < bins; ++i) { - hist(i) = hist(i) / hist_helper.uniform_bin_probability(i); + const double p = hist_helper.uniform_bin_probability(i); + hist(i) = hist(i) / p; + tol(i) = k * std::sqrt((1.0 - p) / (count * p)); } - VERIFY(((hist.array() - 1.0).abs() < 0.05).all()); + VERIFY(((hist.array() - 1.0).abs() < tol.array()).all()); } template <>