--
e54b9c7bbb0c58475676c268e2e19c69f4bce48a by Jorg Brown <jorg@google.com>:
Tweak ABSL_PREDICT_TRUE slightly, for better code on some platforms and/or
optimization levels.  "false || (x)" is more verbose than "!!(x)", but
ultimately more efficient.
For example, given this code:
void InitIfNecessary() {
  if (ABSL_PREDICT_TRUE(NeedsInit())) {
    SlowInitIfNecessary();
  }
}
Clang with default optimization level will produce:
Before this CL              After this CL
InitIfNecessary:            InitIfNecessary:
  push rbp                    push rbp
  mov  rbp, rsp               mov  rbp, rsp
  call NeedsInit              call NeedsInit
  xor  al, -1
  xor  al, -1
  test al, 1                  test al, 1
  jne  .LBB2_1                jne  .LBB3_1
  jmp  .LBB2_2                jmp  .LBB3_2
.LBB2_1:                    .LBB3_1:
  call SlowInitIfNecessary    call SlowInitIfNecessary
.LBB2_2:                    .LBB3_2:
  pop  rbp                    pop  rbp
  ret                         ret
PiperOrigin-RevId: 276401386
--
0a3c4dfd8342bf2b1b11a87f1c662c883f73cab7 by Abseil Team <absl-team@google.com>:
Fix comment nit: sem_open => sem_init.
The code calls sem_init, not sem_open, to initialize an unnamed semaphore.
(sem_open creates or opens a named semaphore.)
PiperOrigin-RevId: 276344072
--
b36a664e9459057509a90e83d3482e1d3a4c44c7 by Abseil Team <absl-team@google.com>:
Fix typo in flat_hash_map.h: exchaged -> exchanged
PiperOrigin-RevId: 276295792
--
7bbd8d18276eb110c8335743e35fceb662ddf3d6 by Samuel Benzaquen <sbenza@google.com>:
Add assertions to verify use of iterators.
PiperOrigin-RevId: 276283300
--
677398a8ffcb1f59182cffe57a4fe7ff147a0404 by Laramie Leavitt <lar@google.com>:
Migrate distribution_impl.h/cc to generate_real.h/cc.
Combine the methods RandU64To<Float,Double> into a single method:
GenerateRealFromBits().
Remove rejection sampling from absl::uniform_real_distribution.
PiperOrigin-RevId: 276158675
--
c60c9d11d24b0c546329d998e78e15a84b3153f5 by Abseil Team <absl-team@google.com>:
Internal change
PiperOrigin-RevId: 276126962
--
4c840cab6a8d86efa29b397cafaf7520eece68cc by Andy Soffer <asoffer@google.com>:
Update CMakeLists.txt to address https://github.com/abseil/abseil-cpp/issues/365.
This does not cover every platform, but it does at least address the
first-order issue of assuming gcc implies x86.
PiperOrigin-RevId: 276116253
--
98da366e6b5d51afe5d7ac6722126aca23d85ee6 by Abseil Team <absl-team@google.com>:
Internal change
PiperOrigin-RevId: 276097452
GitOrigin-RevId: e54b9c7bbb0c58475676c268e2e19c69f4bce48a
Change-Id: I02d84454bb71ab21ad3d39650acf6cc6e36f58d7
		
	
			
		
			
				
	
	
		
			614 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			614 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2017 The Abseil Authors.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //      https://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| #include "absl/random/beta_distribution.h"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <cstddef>
 | |
| #include <cstdint>
 | |
| #include <iterator>
 | |
| #include <random>
 | |
| #include <sstream>
 | |
| #include <string>
 | |
| #include <unordered_map>
 | |
| #include <vector>
 | |
| 
 | |
| #include "gmock/gmock.h"
 | |
| #include "gtest/gtest.h"
 | |
| #include "absl/base/internal/raw_logging.h"
 | |
| #include "absl/random/internal/chi_square.h"
 | |
| #include "absl/random/internal/distribution_test_util.h"
 | |
| #include "absl/random/internal/sequence_urbg.h"
 | |
| #include "absl/random/random.h"
 | |
| #include "absl/strings/str_cat.h"
 | |
| #include "absl/strings/str_format.h"
 | |
| #include "absl/strings/str_replace.h"
 | |
| #include "absl/strings/strip.h"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| template <typename IntType>
 | |
| class BetaDistributionInterfaceTest : public ::testing::Test {};
 | |
| 
 | |
| using RealTypes = ::testing::Types<float, double, long double>;
 | |
| TYPED_TEST_CASE(BetaDistributionInterfaceTest, RealTypes);
 | |
| 
 | |
| TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) {
 | |
|   // The threshold for whether std::exp(1/a) is finite.
 | |
|   const TypeParam kSmallA =
 | |
|       1.0f / std::log((std::numeric_limits<TypeParam>::max)());
 | |
|   // The threshold for whether a * std::log(a) is finite.
 | |
|   const TypeParam kLargeA =
 | |
|       std::exp(std::log((std::numeric_limits<TypeParam>::max)()) -
 | |
|                std::log(std::log((std::numeric_limits<TypeParam>::max)())));
 | |
|   const TypeParam kLargeAPPC = std::exp(
 | |
|       std::log((std::numeric_limits<TypeParam>::max)()) -
 | |
|       std::log(std::log((std::numeric_limits<TypeParam>::max)())) - 10.0f);
 | |
|   using param_type = typename absl::beta_distribution<TypeParam>::param_type;
 | |
| 
 | |
|   constexpr int kCount = 1000;
 | |
|   absl::InsecureBitGen gen;
 | |
|   const TypeParam kValues[] = {
 | |
|       TypeParam(1e-20), TypeParam(1e-12), TypeParam(1e-8), TypeParam(1e-4),
 | |
|       TypeParam(1e-3), TypeParam(0.1), TypeParam(0.25),
 | |
|       std::nextafter(TypeParam(0.5), TypeParam(0)),  // 0.5 - epsilon
 | |
|       std::nextafter(TypeParam(0.5), TypeParam(1)),  // 0.5 + epsilon
 | |
|       TypeParam(0.5), TypeParam(1.0),                //
 | |
|       std::nextafter(TypeParam(1), TypeParam(0)),    // 1 - epsilon
 | |
|       std::nextafter(TypeParam(1), TypeParam(2)),    // 1 + epsilon
 | |
|       TypeParam(12.5), TypeParam(1e2), TypeParam(1e8), TypeParam(1e12),
 | |
|       TypeParam(1e20),                        //
 | |
|       kSmallA,                                //
 | |
|       std::nextafter(kSmallA, TypeParam(0)),  //
 | |
|       std::nextafter(kSmallA, TypeParam(1)),  //
 | |
|       kLargeA,                                //
 | |
|       std::nextafter(kLargeA, TypeParam(0)),  //
 | |
|       std::nextafter(kLargeA, std::numeric_limits<TypeParam>::max()),
 | |
|       kLargeAPPC,  //
 | |
|       std::nextafter(kLargeAPPC, TypeParam(0)),
 | |
|       std::nextafter(kLargeAPPC, std::numeric_limits<TypeParam>::max()),
 | |
|       // Boundary cases.
 | |
|       std::numeric_limits<TypeParam>::max(),
 | |
|       std::numeric_limits<TypeParam>::epsilon(),
 | |
|       std::nextafter(std::numeric_limits<TypeParam>::min(),
 | |
|                      TypeParam(1)),                  // min + epsilon
 | |
|       std::numeric_limits<TypeParam>::min(),         // smallest normal
 | |
|       std::numeric_limits<TypeParam>::denorm_min(),  // smallest denorm
 | |
|       std::numeric_limits<TypeParam>::min() / 2,     // denorm
 | |
|       std::nextafter(std::numeric_limits<TypeParam>::min(),
 | |
|                      TypeParam(0)),  // denorm_max
 | |
|   };
 | |
|   for (TypeParam alpha : kValues) {
 | |
|     for (TypeParam beta : kValues) {
 | |
|       ABSL_INTERNAL_LOG(
 | |
|           INFO, absl::StrFormat("Smoke test for Beta(%a, %a)", alpha, beta));
 | |
| 
 | |
|       param_type param(alpha, beta);
 | |
|       absl::beta_distribution<TypeParam> before(alpha, beta);
 | |
|       EXPECT_EQ(before.alpha(), param.alpha());
 | |
|       EXPECT_EQ(before.beta(), param.beta());
 | |
| 
 | |
|       {
 | |
|         absl::beta_distribution<TypeParam> via_param(param);
 | |
|         EXPECT_EQ(via_param, before);
 | |
|         EXPECT_EQ(via_param.param(), before.param());
 | |
|       }
 | |
| 
 | |
|       // Smoke test.
 | |
|       for (int i = 0; i < kCount; ++i) {
 | |
|         auto sample = before(gen);
 | |
|         EXPECT_TRUE(std::isfinite(sample));
 | |
|         EXPECT_GE(sample, before.min());
 | |
|         EXPECT_LE(sample, before.max());
 | |
|       }
 | |
| 
 | |
|       // Validate stream serialization.
 | |
|       std::stringstream ss;
 | |
|       ss << before;
 | |
|       absl::beta_distribution<TypeParam> after(3.8f, 1.43f);
 | |
|       EXPECT_NE(before.alpha(), after.alpha());
 | |
|       EXPECT_NE(before.beta(), after.beta());
 | |
|       EXPECT_NE(before.param(), after.param());
 | |
|       EXPECT_NE(before, after);
 | |
| 
 | |
|       ss >> after;
 | |
| 
 | |
| #if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
 | |
|     defined(__ppc__) || defined(__PPC__)
 | |
|       if (std::is_same<TypeParam, long double>::value) {
 | |
|         // Roundtripping floating point values requires sufficient precision
 | |
|         // to reconstruct the exact value. It turns out that long double
 | |
|         // has some errors doing this on ppc.
 | |
|         if (alpha <= std::numeric_limits<double>::max() &&
 | |
|             alpha >= std::numeric_limits<double>::lowest()) {
 | |
|           EXPECT_EQ(static_cast<double>(before.alpha()),
 | |
|                     static_cast<double>(after.alpha()))
 | |
|               << ss.str();
 | |
|         }
 | |
|         if (beta <= std::numeric_limits<double>::max() &&
 | |
|             beta >= std::numeric_limits<double>::lowest()) {
 | |
|           EXPECT_EQ(static_cast<double>(before.beta()),
 | |
|                     static_cast<double>(after.beta()))
 | |
|               << ss.str();
 | |
|         }
 | |
|         continue;
 | |
|       }
 | |
| #endif
 | |
| 
 | |
|       EXPECT_EQ(before.alpha(), after.alpha());
 | |
|       EXPECT_EQ(before.beta(), after.beta());
 | |
|       EXPECT_EQ(before, after)           //
 | |
|           << ss.str() << " "             //
 | |
|           << (ss.good() ? "good " : "")  //
 | |
|           << (ss.bad() ? "bad " : "")    //
 | |
|           << (ss.eof() ? "eof " : "")    //
 | |
|           << (ss.fail() ? "fail " : "");
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) {
 | |
|   // Extreme cases when the params are abnormal.
 | |
|   absl::InsecureBitGen gen;
 | |
|   constexpr int kCount = 1000;
 | |
|   const TypeParam kSmallValues[] = {
 | |
|       std::numeric_limits<TypeParam>::min(),
 | |
|       std::numeric_limits<TypeParam>::denorm_min(),
 | |
|       std::nextafter(std::numeric_limits<TypeParam>::min(),
 | |
|                      TypeParam(0)),  // denorm_max
 | |
|       std::numeric_limits<TypeParam>::epsilon(),
 | |
|   };
 | |
|   const TypeParam kLargeValues[] = {
 | |
|       std::numeric_limits<TypeParam>::max() * static_cast<TypeParam>(0.9999),
 | |
|       std::numeric_limits<TypeParam>::max() - 1,
 | |
|       std::numeric_limits<TypeParam>::max(),
 | |
|   };
 | |
|   {
 | |
|     // Small alpha and beta.
 | |
|     // Useful WolframAlpha plots:
 | |
|     //   * plot InverseBetaRegularized[x, 0.0001, 0.0001] from 0.495 to 0.505
 | |
|     //   * Beta[1.0, 0.0000001, 0.0000001]
 | |
|     //   * Beta[0.9999, 0.0000001, 0.0000001]
 | |
|     for (TypeParam alpha : kSmallValues) {
 | |
|       for (TypeParam beta : kSmallValues) {
 | |
|         int zeros = 0;
 | |
|         int ones = 0;
 | |
|         absl::beta_distribution<TypeParam> d(alpha, beta);
 | |
|         for (int i = 0; i < kCount; ++i) {
 | |
|           TypeParam x = d(gen);
 | |
|           if (x == 0.0) {
 | |
|             zeros++;
 | |
|           } else if (x == 1.0) {
 | |
|             ones++;
 | |
|           }
 | |
|         }
 | |
|         EXPECT_EQ(ones + zeros, kCount);
 | |
|         if (alpha == beta) {
 | |
|           EXPECT_NE(ones, 0);
 | |
|           EXPECT_NE(zeros, 0);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   {
 | |
|     // Small alpha, large beta.
 | |
|     // Useful WolframAlpha plots:
 | |
|     //   * plot InverseBetaRegularized[x, 0.0001, 10000] from 0.995 to 1
 | |
|     //   * Beta[0, 0.0000001, 1000000]
 | |
|     //   * Beta[0.001, 0.0000001, 1000000]
 | |
|     //   * Beta[1, 0.0000001, 1000000]
 | |
|     for (TypeParam alpha : kSmallValues) {
 | |
|       for (TypeParam beta : kLargeValues) {
 | |
|         absl::beta_distribution<TypeParam> d(alpha, beta);
 | |
|         for (int i = 0; i < kCount; ++i) {
 | |
|           EXPECT_EQ(d(gen), 0.0);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   {
 | |
|     // Large alpha, small beta.
 | |
|     // Useful WolframAlpha plots:
 | |
|     //   * plot InverseBetaRegularized[x, 10000, 0.0001] from 0 to 0.001
 | |
|     //   * Beta[0.99, 1000000, 0.0000001]
 | |
|     //   * Beta[1, 1000000, 0.0000001]
 | |
|     for (TypeParam alpha : kLargeValues) {
 | |
|       for (TypeParam beta : kSmallValues) {
 | |
|         absl::beta_distribution<TypeParam> d(alpha, beta);
 | |
|         for (int i = 0; i < kCount; ++i) {
 | |
|           EXPECT_EQ(d(gen), 1.0);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   {
 | |
|     // Large alpha and beta.
 | |
|     absl::beta_distribution<TypeParam> d(std::numeric_limits<TypeParam>::max(),
 | |
|                                          std::numeric_limits<TypeParam>::max());
 | |
|     for (int i = 0; i < kCount; ++i) {
 | |
|       EXPECT_EQ(d(gen), 0.5);
 | |
|     }
 | |
|   }
 | |
|   {
 | |
|     // Large alpha and beta but unequal.
 | |
|     absl::beta_distribution<TypeParam> d(
 | |
|         std::numeric_limits<TypeParam>::max(),
 | |
|         std::numeric_limits<TypeParam>::max() * 0.9999);
 | |
|     for (int i = 0; i < kCount; ++i) {
 | |
|       TypeParam x = d(gen);
 | |
|       EXPECT_NE(x, 0.5f);
 | |
|       EXPECT_FLOAT_EQ(x, 0.500025f);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| class BetaDistributionModel {
 | |
|  public:
 | |
|   explicit BetaDistributionModel(::testing::tuple<double, double> p)
 | |
|       : alpha_(::testing::get<0>(p)), beta_(::testing::get<1>(p)) {}
 | |
| 
 | |
|   double Mean() const { return alpha_ / (alpha_ + beta_); }
 | |
| 
 | |
|   double Variance() const {
 | |
|     return alpha_ * beta_ / (alpha_ + beta_ + 1) / (alpha_ + beta_) /
 | |
|            (alpha_ + beta_);
 | |
|   }
 | |
| 
 | |
|   double Kurtosis() const {
 | |
|     return 3 + 6 *
 | |
|                    ((alpha_ - beta_) * (alpha_ - beta_) * (alpha_ + beta_ + 1) -
 | |
|                     alpha_ * beta_ * (2 + alpha_ + beta_)) /
 | |
|                    alpha_ / beta_ / (alpha_ + beta_ + 2) / (alpha_ + beta_ + 3);
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   const double alpha_;
 | |
|   const double beta_;
 | |
| };
 | |
| 
 | |
| class BetaDistributionTest
 | |
|     : public ::testing::TestWithParam<::testing::tuple<double, double>>,
 | |
|       public BetaDistributionModel {
 | |
|  public:
 | |
|   BetaDistributionTest() : BetaDistributionModel(GetParam()) {}
 | |
| 
 | |
|  protected:
 | |
|   template <class D>
 | |
|   bool SingleZTestOnMeanAndVariance(double p, size_t samples);
 | |
| 
 | |
|   template <class D>
 | |
|   bool SingleChiSquaredTest(double p, size_t samples, size_t buckets);
 | |
| 
 | |
|   absl::InsecureBitGen rng_;
 | |
| };
 | |
| 
 | |
| template <class D>
 | |
| bool BetaDistributionTest::SingleZTestOnMeanAndVariance(double p,
 | |
|                                                         size_t samples) {
 | |
|   D dis(alpha_, beta_);
 | |
| 
 | |
|   std::vector<double> data;
 | |
|   data.reserve(samples);
 | |
|   for (size_t i = 0; i < samples; i++) {
 | |
|     const double variate = dis(rng_);
 | |
|     EXPECT_FALSE(std::isnan(variate));
 | |
|     // Note that equality is allowed on both sides.
 | |
|     EXPECT_GE(variate, 0.0);
 | |
|     EXPECT_LE(variate, 1.0);
 | |
|     data.push_back(variate);
 | |
|   }
 | |
| 
 | |
|   // We validate that the sample mean and sample variance are indeed from a
 | |
|   // Beta distribution with the given shape parameters.
 | |
|   const auto m = absl::random_internal::ComputeDistributionMoments(data);
 | |
| 
 | |
|   // The variance of the sample mean is variance / n.
 | |
|   const double mean_stddev = std::sqrt(Variance() / static_cast<double>(m.n));
 | |
| 
 | |
|   // The variance of the sample variance is (approximately):
 | |
|   //   (kurtosis - 1) * variance^2 / n
 | |
|   const double variance_stddev = std::sqrt(
 | |
|       (Kurtosis() - 1) * Variance() * Variance() / static_cast<double>(m.n));
 | |
|   // z score for the sample variance.
 | |
|   const double z_variance = (m.variance - Variance()) / variance_stddev;
 | |
| 
 | |
|   const double max_err = absl::random_internal::MaxErrorTolerance(p);
 | |
|   const double z_mean = absl::random_internal::ZScore(Mean(), m);
 | |
|   const bool pass =
 | |
|       absl::random_internal::Near("z", z_mean, 0.0, max_err) &&
 | |
|       absl::random_internal::Near("z_variance", z_variance, 0.0, max_err);
 | |
|   if (!pass) {
 | |
|     ABSL_INTERNAL_LOG(
 | |
|         INFO,
 | |
|         absl::StrFormat(
 | |
|             "Beta(%f, %f), "
 | |
|             "mean: sample %f, expect %f, which is %f stddevs away, "
 | |
|             "variance: sample %f, expect %f, which is %f stddevs away.",
 | |
|             alpha_, beta_, m.mean, Mean(),
 | |
|             std::abs(m.mean - Mean()) / mean_stddev, m.variance, Variance(),
 | |
|             std::abs(m.variance - Variance()) / variance_stddev));
 | |
|   }
 | |
|   return pass;
 | |
| }
 | |
| 
 | |
| template <class D>
 | |
| bool BetaDistributionTest::SingleChiSquaredTest(double p, size_t samples,
 | |
|                                                 size_t buckets) {
 | |
|   constexpr double kErr = 1e-7;
 | |
|   std::vector<double> cutoffs, expected;
 | |
|   const double bucket_width = 1.0 / static_cast<double>(buckets);
 | |
|   int i = 1;
 | |
|   int unmerged_buckets = 0;
 | |
|   for (; i < buckets; ++i) {
 | |
|     const double p = bucket_width * static_cast<double>(i);
 | |
|     const double boundary =
 | |
|         absl::random_internal::BetaIncompleteInv(alpha_, beta_, p);
 | |
|     // The intention is to add `boundary` to the list of `cutoffs`. It becomes
 | |
|     // problematic, however, when the boundary values are not monotone, due to
 | |
|     // numerical issues when computing the inverse regularized incomplete
 | |
|     // Beta function. In these cases, we merge that bucket with its previous
 | |
|     // neighbor and merge their expected counts.
 | |
|     if ((cutoffs.empty() && boundary < kErr) ||
 | |
|         (!cutoffs.empty() && boundary <= cutoffs.back())) {
 | |
|       unmerged_buckets++;
 | |
|       continue;
 | |
|     }
 | |
|     if (boundary >= 1.0 - 1e-10) {
 | |
|       break;
 | |
|     }
 | |
|     cutoffs.push_back(boundary);
 | |
|     expected.push_back(static_cast<double>(1 + unmerged_buckets) *
 | |
|                        bucket_width * static_cast<double>(samples));
 | |
|     unmerged_buckets = 0;
 | |
|   }
 | |
|   cutoffs.push_back(std::numeric_limits<double>::infinity());
 | |
|   // Merge all remaining buckets.
 | |
|   expected.push_back(static_cast<double>(buckets - i + 1) * bucket_width *
 | |
|                      static_cast<double>(samples));
 | |
|   // Make sure that we don't merge all the buckets, making this test
 | |
|   // meaningless.
 | |
|   EXPECT_GE(cutoffs.size(), 3) << alpha_ << ", " << beta_;
 | |
| 
 | |
|   D dis(alpha_, beta_);
 | |
| 
 | |
|   std::vector<int32_t> counts(cutoffs.size(), 0);
 | |
|   for (int i = 0; i < samples; i++) {
 | |
|     const double x = dis(rng_);
 | |
|     auto it = std::upper_bound(cutoffs.begin(), cutoffs.end(), x);
 | |
|     counts[std::distance(cutoffs.begin(), it)]++;
 | |
|   }
 | |
| 
 | |
|   // Null-hypothesis is that the distribution is beta distributed with the
 | |
|   // provided alpha, beta params (not estimated from the data).
 | |
|   const int dof = cutoffs.size() - 1;
 | |
| 
 | |
|   const double chi_square = absl::random_internal::ChiSquare(
 | |
|       counts.begin(), counts.end(), expected.begin(), expected.end());
 | |
|   const bool pass =
 | |
|       (absl::random_internal::ChiSquarePValue(chi_square, dof) >= p);
 | |
|   if (!pass) {
 | |
|     for (int i = 0; i < cutoffs.size(); i++) {
 | |
|       ABSL_INTERNAL_LOG(
 | |
|           INFO, absl::StrFormat("cutoff[%d] = %f, actual count %d, expected %d",
 | |
|                                 i, cutoffs[i], counts[i],
 | |
|                                 static_cast<int>(expected[i])));
 | |
|     }
 | |
| 
 | |
|     ABSL_INTERNAL_LOG(
 | |
|         INFO, absl::StrFormat(
 | |
|                   "Beta(%f, %f) %s %f, p = %f", alpha_, beta_,
 | |
|                   absl::random_internal::kChiSquared, chi_square,
 | |
|                   absl::random_internal::ChiSquarePValue(chi_square, dof)));
 | |
|   }
 | |
|   return pass;
 | |
| }
 | |
| 
 | |
| TEST_P(BetaDistributionTest, TestSampleStatistics) {
 | |
|   static constexpr int kRuns = 20;
 | |
|   static constexpr double kPFail = 0.02;
 | |
|   const double p =
 | |
|       absl::random_internal::RequiredSuccessProbability(kPFail, kRuns);
 | |
|   static constexpr int kSampleCount = 10000;
 | |
|   static constexpr int kBucketCount = 100;
 | |
|   int failed = 0;
 | |
|   for (int i = 0; i < kRuns; ++i) {
 | |
|     if (!SingleZTestOnMeanAndVariance<absl::beta_distribution<double>>(
 | |
|             p, kSampleCount)) {
 | |
|       failed++;
 | |
|     }
 | |
|     if (!SingleChiSquaredTest<absl::beta_distribution<double>>(
 | |
|             0.005, kSampleCount, kBucketCount)) {
 | |
|       failed++;
 | |
|     }
 | |
|   }
 | |
|   // Set so that the test is not flaky at --runs_per_test=10000
 | |
|   EXPECT_LE(failed, 5);
 | |
| }
 | |
| 
 | |
| std::string ParamName(
 | |
|     const ::testing::TestParamInfo<::testing::tuple<double, double>>& info) {
 | |
|   std::string name = absl::StrCat("alpha_", ::testing::get<0>(info.param),
 | |
|                                   "__beta_", ::testing::get<1>(info.param));
 | |
|   return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
 | |
| }
 | |
| 
 | |
| INSTANTIATE_TEST_CASE_P(
 | |
|     TestSampleStatisticsCombinations, BetaDistributionTest,
 | |
|     ::testing::Combine(::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4),
 | |
|                        ::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4)),
 | |
|     ParamName);
 | |
| 
 | |
| INSTANTIATE_TEST_CASE_P(
 | |
|     TestSampleStatistics_SelectedPairs, BetaDistributionTest,
 | |
|     ::testing::Values(std::make_pair(0.5, 1000), std::make_pair(1000, 0.5),
 | |
|                       std::make_pair(900, 1000), std::make_pair(10000, 20000),
 | |
|                       std::make_pair(4e5, 2e7), std::make_pair(1e7, 1e5)),
 | |
|     ParamName);
 | |
| 
 | |
| // NOTE: absl::beta_distribution is not guaranteed to be stable.
 | |
| TEST(BetaDistributionTest, StabilityTest) {
 | |
|   // absl::beta_distribution stability relies on the stability of
 | |
|   // absl::random_interna::RandU64ToDouble, std::exp, std::log, std::pow,
 | |
|   // and std::sqrt.
 | |
|   //
 | |
|   // This test also depends on the stability of std::frexp.
 | |
|   using testing::ElementsAre;
 | |
|   absl::random_internal::sequence_urbg urbg({
 | |
|       0xffff00000000e6c8ull, 0xffff0000000006c8ull, 0x800003766295CFA9ull,
 | |
|       0x11C819684E734A41ull, 0x832603766295CFA9ull, 0x7fbe76c8b4395800ull,
 | |
|       0xB3472DCA7B14A94Aull, 0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull,
 | |
|       0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, 0x00035C904C70A239ull,
 | |
|       0x00009E0BCBAADE14ull, 0x0000000000622CA7ull, 0x4864f22c059bf29eull,
 | |
|       0x247856d8b862665cull, 0xe46e86e9a1337e10ull, 0xd8c8541f3519b133ull,
 | |
|       0xffe75b52c567b9e4ull, 0xfffff732e5709c5bull, 0xff1f7f0b983532acull,
 | |
|       0x1ec2e8986d2362caull, 0xC332DDEFBE6C5AA5ull, 0x6558218568AB9702ull,
 | |
|       0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, 0xECDD4775619F1510ull,
 | |
|       0x814c8e35fe9a961aull, 0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull,
 | |
|       0x1224e62c978bbc7full, 0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull,
 | |
|       0x1bbc23cfa8fac721ull, 0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull,
 | |
|       0x836d794457c08849ull, 0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull,
 | |
|       0xb12d74fdd718c8c5ull, 0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull,
 | |
|       0x5738341045ba0d85ull, 0xf3fd722dc65ad09eull, 0xfa14fd21ea2a5705ull,
 | |
|       0xffe6ea4d6edb0c73ull, 0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull,
 | |
|       0xEAAD8E716B93D5A0ull, 0xD08ED1D0AFC725E0ull, 0x8E3C5B2F8E7594B7ull,
 | |
|       0x8FF6E2FBF2122B64ull, 0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull,
 | |
|       0xD1CFF191B3A8C1ADull, 0x2F2F2218BE0E1777ull, 0xEA752DFE8B021FA1ull,
 | |
|   });
 | |
| 
 | |
|   // Convert the real-valued result into a unit64 where we compare
 | |
|   // 5 (float) or 10 (double) decimal digits plus the base-2 exponent.
 | |
|   auto float_to_u64 = [](float d) {
 | |
|     int exp = 0;
 | |
|     auto f = std::frexp(d, &exp);
 | |
|     return (static_cast<uint64_t>(1e5 * f) * 10000) + std::abs(exp);
 | |
|   };
 | |
|   auto double_to_u64 = [](double d) {
 | |
|     int exp = 0;
 | |
|     auto f = std::frexp(d, &exp);
 | |
|     return (static_cast<uint64_t>(1e10 * f) * 10000) + std::abs(exp);
 | |
|   };
 | |
| 
 | |
|   std::vector<uint64_t> output(20);
 | |
|   {
 | |
|     // Algorithm Joehnk (float)
 | |
|     absl::beta_distribution<float> dist(0.1f, 0.2f);
 | |
|     std::generate(std::begin(output), std::end(output),
 | |
|                   [&] { return float_to_u64(dist(urbg)); });
 | |
|     EXPECT_EQ(44, urbg.invocations());
 | |
|     EXPECT_THAT(output,  //
 | |
|                 testing::ElementsAre(
 | |
|                     998340000, 619030004, 500000001, 999990000, 996280000,
 | |
|                     500000001, 844740004, 847210001, 999970000, 872320000,
 | |
|                     585480007, 933280000, 869080042, 647670031, 528240004,
 | |
|                     969980004, 626050008, 915930002, 833440033, 878040015));
 | |
|   }
 | |
| 
 | |
|   urbg.reset();
 | |
|   {
 | |
|     // Algorithm Joehnk (double)
 | |
|     absl::beta_distribution<double> dist(0.1, 0.2);
 | |
|     std::generate(std::begin(output), std::end(output),
 | |
|                   [&] { return double_to_u64(dist(urbg)); });
 | |
|     EXPECT_EQ(44, urbg.invocations());
 | |
|     EXPECT_THAT(
 | |
|         output,  //
 | |
|         testing::ElementsAre(
 | |
|             99834713000000, 61903356870004, 50000000000001, 99999721170000,
 | |
|             99628374770000, 99999999990000, 84474397860004, 84721276240001,
 | |
|             99997407490000, 87232528120000, 58548364780007, 93328932910000,
 | |
|             86908237770042, 64767917930031, 52824581970004, 96998544140004,
 | |
|             62605946270008, 91593604380002, 83345031740033, 87804397230015));
 | |
|   }
 | |
| 
 | |
|   urbg.reset();
 | |
|   {
 | |
|     // Algorithm Cheng 1
 | |
|     absl::beta_distribution<double> dist(0.9, 2.0);
 | |
|     std::generate(std::begin(output), std::end(output),
 | |
|                   [&] { return double_to_u64(dist(urbg)); });
 | |
|     EXPECT_EQ(62, urbg.invocations());
 | |
|     EXPECT_THAT(
 | |
|         output,  //
 | |
|         testing::ElementsAre(
 | |
|             62069004780001, 64433204450001, 53607416560000, 89644295430008,
 | |
|             61434586310019, 55172615890002, 62187161490000, 56433684810003,
 | |
|             80454622050005, 86418558710003, 92920514700001, 64645184680001,
 | |
|             58549183380000, 84881283650005, 71078728590002, 69949694970000,
 | |
|             73157461710001, 68592191300001, 70747623900000, 78584696930005));
 | |
|   }
 | |
| 
 | |
|   urbg.reset();
 | |
|   {
 | |
|     // Algorithm Cheng 2
 | |
|     absl::beta_distribution<double> dist(1.5, 2.5);
 | |
|     std::generate(std::begin(output), std::end(output),
 | |
|                   [&] { return double_to_u64(dist(urbg)); });
 | |
|     EXPECT_EQ(54, urbg.invocations());
 | |
|     EXPECT_THAT(
 | |
|         output,  //
 | |
|         testing::ElementsAre(
 | |
|             75000029250001, 76751482860001, 53264575220000, 69193133650005,
 | |
|             78028324470013, 91573587560002, 59167523770000, 60658618560002,
 | |
|             80075870540000, 94141320460004, 63196592770003, 78883906300002,
 | |
|             96797992590001, 76907587800001, 56645167560000, 65408302280003,
 | |
|             53401156320001, 64731238570000, 83065573750001, 79788333820001));
 | |
|   }
 | |
| }
 | |
| 
 | |
| // This is an implementation-specific test. If any part of the implementation
 | |
| // changes, then it is likely that this test will change as well.  Also, if
 | |
| // dependencies of the distribution change, such as RandU64ToDouble, then this
 | |
| // is also likely to change.
 | |
| TEST(BetaDistributionTest, AlgorithmBounds) {
 | |
|   {
 | |
|     absl::random_internal::sequence_urbg urbg(
 | |
|         {0x7fbe76c8b4395800ull, 0x8000000000000000ull});
 | |
|     // u=0.499, v=0.5
 | |
|     absl::beta_distribution<double> dist(1e-4, 1e-4);
 | |
|     double a = dist(urbg);
 | |
|     EXPECT_EQ(a, 2.0202860861567108529e-09);
 | |
|     EXPECT_EQ(2, urbg.invocations());
 | |
|   }
 | |
| 
 | |
|   // Test that both the float & double algorithms appropriately reject the
 | |
|   // initial draw.
 | |
|   {
 | |
|     // 1/alpha = 1/beta = 2.
 | |
|     absl::beta_distribution<float> dist(0.5, 0.5);
 | |
| 
 | |
|     // first two outputs are close to 1.0 - epsilon,
 | |
|     // thus:  (u ^ 2 + v ^ 2) > 1.0
 | |
|     absl::random_internal::sequence_urbg urbg(
 | |
|         {0xffff00000006e6c8ull, 0xffff00000007c7c8ull, 0x800003766295CFA9ull,
 | |
|          0x11C819684E734A41ull});
 | |
|     {
 | |
|       double y = absl::beta_distribution<double>(0.5, 0.5)(urbg);
 | |
|       EXPECT_EQ(4, urbg.invocations());
 | |
|       EXPECT_EQ(y, 0.9810668952633862) << y;
 | |
|     }
 | |
| 
 | |
|     // ...and:  log(u) * a ~= log(v) * b ~= -0.02
 | |
|     // thus z ~= -0.02 + log(1 + e(~0))
 | |
|     //        ~= -0.02 + 0.69
 | |
|     // thus z > 0
 | |
|     urbg.reset();
 | |
|     {
 | |
|       float x = absl::beta_distribution<float>(0.5, 0.5)(urbg);
 | |
|       EXPECT_EQ(4, urbg.invocations());
 | |
|       EXPECT_NEAR(0.98106688261032104, x, 0.0000005) << x << "f";
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace
 |