1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/random/beta_distribution.h"
16
17 #include <algorithm>
18 #include <cfloat>
19 #include <cstddef>
20 #include <cstdint>
21 #include <iterator>
22 #include <random>
23 #include <sstream>
24 #include <string>
25 #include <type_traits>
26 #include <unordered_map>
27 #include <vector>
28
29 #include "gmock/gmock.h"
30 #include "gtest/gtest.h"
31 #include "absl/base/internal/raw_logging.h"
32 #include "absl/numeric/internal/representation.h"
33 #include "absl/random/internal/chi_square.h"
34 #include "absl/random/internal/distribution_test_util.h"
35 #include "absl/random/internal/pcg_engine.h"
36 #include "absl/random/internal/sequence_urbg.h"
37 #include "absl/random/random.h"
38 #include "absl/strings/str_cat.h"
39 #include "absl/strings/str_format.h"
40 #include "absl/strings/str_replace.h"
41 #include "absl/strings/strip.h"
42
43 namespace {
44
45 template <typename IntType>
46 class BetaDistributionInterfaceTest : public ::testing::Test {};
47
48 // double-double arithmetic is not supported well by either GCC or Clang; see
49 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048,
50 // https://bugs.llvm.org/show_bug.cgi?id=49131, and
51 // https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests
52 // with double doubles until compiler support is better.
53 using RealTypes =
54 std::conditional<absl::numeric_internal::IsDoubleDouble(),
55 ::testing::Types<float, double>,
56 ::testing::Types<float, double, long double>>::type;
57 TYPED_TEST_CASE(BetaDistributionInterfaceTest, RealTypes);
58
TYPED_TEST(BetaDistributionInterfaceTest,SerializeTest)59 TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) {
60 // The threshold for whether std::exp(1/a) is finite.
61 const TypeParam kSmallA =
62 1.0f / std::log((std::numeric_limits<TypeParam>::max)());
63 // The threshold for whether a * std::log(a) is finite.
64 const TypeParam kLargeA =
65 std::exp(std::log((std::numeric_limits<TypeParam>::max)()) -
66 std::log(std::log((std::numeric_limits<TypeParam>::max)())));
67 using param_type = typename absl::beta_distribution<TypeParam>::param_type;
68
69 constexpr int kCount = 1000;
70 absl::InsecureBitGen gen;
71 const TypeParam kValues[] = {
72 TypeParam(1e-20), TypeParam(1e-12), TypeParam(1e-8), TypeParam(1e-4),
73 TypeParam(1e-3), TypeParam(0.1), TypeParam(0.25),
74 std::nextafter(TypeParam(0.5), TypeParam(0)), // 0.5 - epsilon
75 std::nextafter(TypeParam(0.5), TypeParam(1)), // 0.5 + epsilon
76 TypeParam(0.5), TypeParam(1.0), //
77 std::nextafter(TypeParam(1), TypeParam(0)), // 1 - epsilon
78 std::nextafter(TypeParam(1), TypeParam(2)), // 1 + epsilon
79 TypeParam(12.5), TypeParam(1e2), TypeParam(1e8), TypeParam(1e12),
80 TypeParam(1e20), //
81 kSmallA, //
82 std::nextafter(kSmallA, TypeParam(0)), //
83 std::nextafter(kSmallA, TypeParam(1)), //
84 kLargeA, //
85 std::nextafter(kLargeA, TypeParam(0)), //
86 std::nextafter(kLargeA, std::numeric_limits<TypeParam>::max()),
87 // Boundary cases.
88 std::numeric_limits<TypeParam>::max(),
89 std::numeric_limits<TypeParam>::epsilon(),
90 std::nextafter(std::numeric_limits<TypeParam>::min(),
91 TypeParam(1)), // min + epsilon
92 std::numeric_limits<TypeParam>::min(), // smallest normal
93 std::numeric_limits<TypeParam>::denorm_min(), // smallest denorm
94 std::numeric_limits<TypeParam>::min() / 2, // denorm
95 std::nextafter(std::numeric_limits<TypeParam>::min(),
96 TypeParam(0)), // denorm_max
97 };
98 for (TypeParam alpha : kValues) {
99 for (TypeParam beta : kValues) {
100 ABSL_INTERNAL_LOG(
101 INFO, absl::StrFormat("Smoke test for Beta(%a, %a)", alpha, beta));
102
103 param_type param(alpha, beta);
104 absl::beta_distribution<TypeParam> before(alpha, beta);
105 EXPECT_EQ(before.alpha(), param.alpha());
106 EXPECT_EQ(before.beta(), param.beta());
107
108 {
109 absl::beta_distribution<TypeParam> via_param(param);
110 EXPECT_EQ(via_param, before);
111 EXPECT_EQ(via_param.param(), before.param());
112 }
113
114 // Smoke test.
115 for (int i = 0; i < kCount; ++i) {
116 auto sample = before(gen);
117 EXPECT_TRUE(std::isfinite(sample));
118 EXPECT_GE(sample, before.min());
119 EXPECT_LE(sample, before.max());
120 }
121
122 // Validate stream serialization.
123 std::stringstream ss;
124 ss << before;
125 absl::beta_distribution<TypeParam> after(3.8f, 1.43f);
126 EXPECT_NE(before.alpha(), after.alpha());
127 EXPECT_NE(before.beta(), after.beta());
128 EXPECT_NE(before.param(), after.param());
129 EXPECT_NE(before, after);
130
131 ss >> after;
132
133 EXPECT_EQ(before.alpha(), after.alpha());
134 EXPECT_EQ(before.beta(), after.beta());
135 EXPECT_EQ(before, after) //
136 << ss.str() << " " //
137 << (ss.good() ? "good " : "") //
138 << (ss.bad() ? "bad " : "") //
139 << (ss.eof() ? "eof " : "") //
140 << (ss.fail() ? "fail " : "");
141 }
142 }
143 }
144
TYPED_TEST(BetaDistributionInterfaceTest,DegenerateCases)145 TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) {
146 // We use a fixed bit generator for distribution accuracy tests. This allows
147 // these tests to be deterministic, while still testing the qualify of the
148 // implementation.
149 absl::random_internal::pcg64_2018_engine rng(0x2B7E151628AED2A6);
150
151 // Extreme cases when the params are abnormal.
152 constexpr int kCount = 1000;
153 const TypeParam kSmallValues[] = {
154 std::numeric_limits<TypeParam>::min(),
155 std::numeric_limits<TypeParam>::denorm_min(),
156 std::nextafter(std::numeric_limits<TypeParam>::min(),
157 TypeParam(0)), // denorm_max
158 std::numeric_limits<TypeParam>::epsilon(),
159 };
160 const TypeParam kLargeValues[] = {
161 std::numeric_limits<TypeParam>::max() * static_cast<TypeParam>(0.9999),
162 std::numeric_limits<TypeParam>::max() - 1,
163 std::numeric_limits<TypeParam>::max(),
164 };
165 {
166 // Small alpha and beta.
167 // Useful WolframAlpha plots:
168 // * plot InverseBetaRegularized[x, 0.0001, 0.0001] from 0.495 to 0.505
169 // * Beta[1.0, 0.0000001, 0.0000001]
170 // * Beta[0.9999, 0.0000001, 0.0000001]
171 for (TypeParam alpha : kSmallValues) {
172 for (TypeParam beta : kSmallValues) {
173 int zeros = 0;
174 int ones = 0;
175 absl::beta_distribution<TypeParam> d(alpha, beta);
176 for (int i = 0; i < kCount; ++i) {
177 TypeParam x = d(rng);
178 if (x == 0.0) {
179 zeros++;
180 } else if (x == 1.0) {
181 ones++;
182 }
183 }
184 EXPECT_EQ(ones + zeros, kCount);
185 if (alpha == beta) {
186 EXPECT_NE(ones, 0);
187 EXPECT_NE(zeros, 0);
188 }
189 }
190 }
191 }
192 {
193 // Small alpha, large beta.
194 // Useful WolframAlpha plots:
195 // * plot InverseBetaRegularized[x, 0.0001, 10000] from 0.995 to 1
196 // * Beta[0, 0.0000001, 1000000]
197 // * Beta[0.001, 0.0000001, 1000000]
198 // * Beta[1, 0.0000001, 1000000]
199 for (TypeParam alpha : kSmallValues) {
200 for (TypeParam beta : kLargeValues) {
201 absl::beta_distribution<TypeParam> d(alpha, beta);
202 for (int i = 0; i < kCount; ++i) {
203 EXPECT_EQ(d(rng), 0.0);
204 }
205 }
206 }
207 }
208 {
209 // Large alpha, small beta.
210 // Useful WolframAlpha plots:
211 // * plot InverseBetaRegularized[x, 10000, 0.0001] from 0 to 0.001
212 // * Beta[0.99, 1000000, 0.0000001]
213 // * Beta[1, 1000000, 0.0000001]
214 for (TypeParam alpha : kLargeValues) {
215 for (TypeParam beta : kSmallValues) {
216 absl::beta_distribution<TypeParam> d(alpha, beta);
217 for (int i = 0; i < kCount; ++i) {
218 EXPECT_EQ(d(rng), 1.0);
219 }
220 }
221 }
222 }
223 {
224 // Large alpha and beta.
225 absl::beta_distribution<TypeParam> d(std::numeric_limits<TypeParam>::max(),
226 std::numeric_limits<TypeParam>::max());
227 for (int i = 0; i < kCount; ++i) {
228 EXPECT_EQ(d(rng), 0.5);
229 }
230 }
231 {
232 // Large alpha and beta but unequal.
233 absl::beta_distribution<TypeParam> d(
234 std::numeric_limits<TypeParam>::max(),
235 std::numeric_limits<TypeParam>::max() * 0.9999);
236 for (int i = 0; i < kCount; ++i) {
237 TypeParam x = d(rng);
238 EXPECT_NE(x, 0.5f);
239 EXPECT_FLOAT_EQ(x, 0.500025f);
240 }
241 }
242 }
243
244 class BetaDistributionModel {
245 public:
BetaDistributionModel(::testing::tuple<double,double> p)246 explicit BetaDistributionModel(::testing::tuple<double, double> p)
247 : alpha_(::testing::get<0>(p)), beta_(::testing::get<1>(p)) {}
248
Mean() const249 double Mean() const { return alpha_ / (alpha_ + beta_); }
250
Variance() const251 double Variance() const {
252 return alpha_ * beta_ / (alpha_ + beta_ + 1) / (alpha_ + beta_) /
253 (alpha_ + beta_);
254 }
255
Kurtosis() const256 double Kurtosis() const {
257 return 3 + 6 *
258 ((alpha_ - beta_) * (alpha_ - beta_) * (alpha_ + beta_ + 1) -
259 alpha_ * beta_ * (2 + alpha_ + beta_)) /
260 alpha_ / beta_ / (alpha_ + beta_ + 2) / (alpha_ + beta_ + 3);
261 }
262
263 protected:
264 const double alpha_;
265 const double beta_;
266 };
267
268 class BetaDistributionTest
269 : public ::testing::TestWithParam<::testing::tuple<double, double>>,
270 public BetaDistributionModel {
271 public:
BetaDistributionTest()272 BetaDistributionTest() : BetaDistributionModel(GetParam()) {}
273
274 protected:
275 template <class D>
276 bool SingleZTestOnMeanAndVariance(double p, size_t samples);
277
278 template <class D>
279 bool SingleChiSquaredTest(double p, size_t samples, size_t buckets);
280
281 absl::InsecureBitGen rng_;
282 };
283
284 template <class D>
SingleZTestOnMeanAndVariance(double p,size_t samples)285 bool BetaDistributionTest::SingleZTestOnMeanAndVariance(double p,
286 size_t samples) {
287 D dis(alpha_, beta_);
288
289 std::vector<double> data;
290 data.reserve(samples);
291 for (size_t i = 0; i < samples; i++) {
292 const double variate = dis(rng_);
293 EXPECT_FALSE(std::isnan(variate));
294 // Note that equality is allowed on both sides.
295 EXPECT_GE(variate, 0.0);
296 EXPECT_LE(variate, 1.0);
297 data.push_back(variate);
298 }
299
300 // We validate that the sample mean and sample variance are indeed from a
301 // Beta distribution with the given shape parameters.
302 const auto m = absl::random_internal::ComputeDistributionMoments(data);
303
304 // The variance of the sample mean is variance / n.
305 const double mean_stddev = std::sqrt(Variance() / static_cast<double>(m.n));
306
307 // The variance of the sample variance is (approximately):
308 // (kurtosis - 1) * variance^2 / n
309 const double variance_stddev = std::sqrt(
310 (Kurtosis() - 1) * Variance() * Variance() / static_cast<double>(m.n));
311 // z score for the sample variance.
312 const double z_variance = (m.variance - Variance()) / variance_stddev;
313
314 const double max_err = absl::random_internal::MaxErrorTolerance(p);
315 const double z_mean = absl::random_internal::ZScore(Mean(), m);
316 const bool pass =
317 absl::random_internal::Near("z", z_mean, 0.0, max_err) &&
318 absl::random_internal::Near("z_variance", z_variance, 0.0, max_err);
319 if (!pass) {
320 ABSL_INTERNAL_LOG(
321 INFO,
322 absl::StrFormat(
323 "Beta(%f, %f), "
324 "mean: sample %f, expect %f, which is %f stddevs away, "
325 "variance: sample %f, expect %f, which is %f stddevs away.",
326 alpha_, beta_, m.mean, Mean(),
327 std::abs(m.mean - Mean()) / mean_stddev, m.variance, Variance(),
328 std::abs(m.variance - Variance()) / variance_stddev));
329 }
330 return pass;
331 }
332
333 template <class D>
SingleChiSquaredTest(double p,size_t samples,size_t buckets)334 bool BetaDistributionTest::SingleChiSquaredTest(double p, size_t samples,
335 size_t buckets) {
336 constexpr double kErr = 1e-7;
337 std::vector<double> cutoffs, expected;
338 const double bucket_width = 1.0 / static_cast<double>(buckets);
339 int i = 1;
340 int unmerged_buckets = 0;
341 for (; i < buckets; ++i) {
342 const double p = bucket_width * static_cast<double>(i);
343 const double boundary =
344 absl::random_internal::BetaIncompleteInv(alpha_, beta_, p);
345 // The intention is to add `boundary` to the list of `cutoffs`. It becomes
346 // problematic, however, when the boundary values are not monotone, due to
347 // numerical issues when computing the inverse regularized incomplete
348 // Beta function. In these cases, we merge that bucket with its previous
349 // neighbor and merge their expected counts.
350 if ((cutoffs.empty() && boundary < kErr) ||
351 (!cutoffs.empty() && boundary <= cutoffs.back())) {
352 unmerged_buckets++;
353 continue;
354 }
355 if (boundary >= 1.0 - 1e-10) {
356 break;
357 }
358 cutoffs.push_back(boundary);
359 expected.push_back(static_cast<double>(1 + unmerged_buckets) *
360 bucket_width * static_cast<double>(samples));
361 unmerged_buckets = 0;
362 }
363 cutoffs.push_back(std::numeric_limits<double>::infinity());
364 // Merge all remaining buckets.
365 expected.push_back(static_cast<double>(buckets - i + 1) * bucket_width *
366 static_cast<double>(samples));
367 // Make sure that we don't merge all the buckets, making this test
368 // meaningless.
369 EXPECT_GE(cutoffs.size(), 3) << alpha_ << ", " << beta_;
370
371 D dis(alpha_, beta_);
372
373 std::vector<int32_t> counts(cutoffs.size(), 0);
374 for (int i = 0; i < samples; i++) {
375 const double x = dis(rng_);
376 auto it = std::upper_bound(cutoffs.begin(), cutoffs.end(), x);
377 counts[std::distance(cutoffs.begin(), it)]++;
378 }
379
380 // Null-hypothesis is that the distribution is beta distributed with the
381 // provided alpha, beta params (not estimated from the data).
382 const int dof = cutoffs.size() - 1;
383
384 const double chi_square = absl::random_internal::ChiSquare(
385 counts.begin(), counts.end(), expected.begin(), expected.end());
386 const bool pass =
387 (absl::random_internal::ChiSquarePValue(chi_square, dof) >= p);
388 if (!pass) {
389 for (int i = 0; i < cutoffs.size(); i++) {
390 ABSL_INTERNAL_LOG(
391 INFO, absl::StrFormat("cutoff[%d] = %f, actual count %d, expected %d",
392 i, cutoffs[i], counts[i],
393 static_cast<int>(expected[i])));
394 }
395
396 ABSL_INTERNAL_LOG(
397 INFO, absl::StrFormat(
398 "Beta(%f, %f) %s %f, p = %f", alpha_, beta_,
399 absl::random_internal::kChiSquared, chi_square,
400 absl::random_internal::ChiSquarePValue(chi_square, dof)));
401 }
402 return pass;
403 }
404
TEST_P(BetaDistributionTest,TestSampleStatistics)405 TEST_P(BetaDistributionTest, TestSampleStatistics) {
406 static constexpr int kRuns = 20;
407 static constexpr double kPFail = 0.02;
408 const double p =
409 absl::random_internal::RequiredSuccessProbability(kPFail, kRuns);
410 static constexpr int kSampleCount = 10000;
411 static constexpr int kBucketCount = 100;
412 int failed = 0;
413 for (int i = 0; i < kRuns; ++i) {
414 if (!SingleZTestOnMeanAndVariance<absl::beta_distribution<double>>(
415 p, kSampleCount)) {
416 failed++;
417 }
418 if (!SingleChiSquaredTest<absl::beta_distribution<double>>(
419 0.005, kSampleCount, kBucketCount)) {
420 failed++;
421 }
422 }
423 // Set so that the test is not flaky at --runs_per_test=10000
424 EXPECT_LE(failed, 5);
425 }
426
ParamName(const::testing::TestParamInfo<::testing::tuple<double,double>> & info)427 std::string ParamName(
428 const ::testing::TestParamInfo<::testing::tuple<double, double>>& info) {
429 std::string name = absl::StrCat("alpha_", ::testing::get<0>(info.param),
430 "__beta_", ::testing::get<1>(info.param));
431 return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
432 }
433
434 INSTANTIATE_TEST_CASE_P(
435 TestSampleStatisticsCombinations, BetaDistributionTest,
436 ::testing::Combine(::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4),
437 ::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4)),
438 ParamName);
439
440 INSTANTIATE_TEST_CASE_P(
441 TestSampleStatistics_SelectedPairs, BetaDistributionTest,
442 ::testing::Values(std::make_pair(0.5, 1000), std::make_pair(1000, 0.5),
443 std::make_pair(900, 1000), std::make_pair(10000, 20000),
444 std::make_pair(4e5, 2e7), std::make_pair(1e7, 1e5)),
445 ParamName);
446
447 // NOTE: absl::beta_distribution is not guaranteed to be stable.
TEST(BetaDistributionTest,StabilityTest)448 TEST(BetaDistributionTest, StabilityTest) {
449 // absl::beta_distribution stability relies on the stability of
450 // absl::random_interna::RandU64ToDouble, std::exp, std::log, std::pow,
451 // and std::sqrt.
452 //
453 // This test also depends on the stability of std::frexp.
454 using testing::ElementsAre;
455 absl::random_internal::sequence_urbg urbg({
456 0xffff00000000e6c8ull, 0xffff0000000006c8ull, 0x800003766295CFA9ull,
457 0x11C819684E734A41ull, 0x832603766295CFA9ull, 0x7fbe76c8b4395800ull,
458 0xB3472DCA7B14A94Aull, 0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull,
459 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, 0x00035C904C70A239ull,
460 0x00009E0BCBAADE14ull, 0x0000000000622CA7ull, 0x4864f22c059bf29eull,
461 0x247856d8b862665cull, 0xe46e86e9a1337e10ull, 0xd8c8541f3519b133ull,
462 0xffe75b52c567b9e4ull, 0xfffff732e5709c5bull, 0xff1f7f0b983532acull,
463 0x1ec2e8986d2362caull, 0xC332DDEFBE6C5AA5ull, 0x6558218568AB9702ull,
464 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, 0xECDD4775619F1510ull,
465 0x814c8e35fe9a961aull, 0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull,
466 0x1224e62c978bbc7full, 0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull,
467 0x1bbc23cfa8fac721ull, 0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull,
468 0x836d794457c08849ull, 0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull,
469 0xb12d74fdd718c8c5ull, 0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull,
470 0x5738341045ba0d85ull, 0xf3fd722dc65ad09eull, 0xfa14fd21ea2a5705ull,
471 0xffe6ea4d6edb0c73ull, 0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull,
472 0xEAAD8E716B93D5A0ull, 0xD08ED1D0AFC725E0ull, 0x8E3C5B2F8E7594B7ull,
473 0x8FF6E2FBF2122B64ull, 0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull,
474 0xD1CFF191B3A8C1ADull, 0x2F2F2218BE0E1777ull, 0xEA752DFE8B021FA1ull,
475 });
476
477 // Convert the real-valued result into a unit64 where we compare
478 // 5 (float) or 10 (double) decimal digits plus the base-2 exponent.
479 auto float_to_u64 = [](float d) {
480 int exp = 0;
481 auto f = std::frexp(d, &exp);
482 return (static_cast<uint64_t>(1e5 * f) * 10000) + std::abs(exp);
483 };
484 auto double_to_u64 = [](double d) {
485 int exp = 0;
486 auto f = std::frexp(d, &exp);
487 return (static_cast<uint64_t>(1e10 * f) * 10000) + std::abs(exp);
488 };
489
490 std::vector<uint64_t> output(20);
491 {
492 // Algorithm Joehnk (float)
493 absl::beta_distribution<float> dist(0.1f, 0.2f);
494 std::generate(std::begin(output), std::end(output),
495 [&] { return float_to_u64(dist(urbg)); });
496 EXPECT_EQ(44, urbg.invocations());
497 EXPECT_THAT(output, //
498 testing::ElementsAre(
499 998340000, 619030004, 500000001, 999990000, 996280000,
500 500000001, 844740004, 847210001, 999970000, 872320000,
501 585480007, 933280000, 869080042, 647670031, 528240004,
502 969980004, 626050008, 915930002, 833440033, 878040015));
503 }
504
505 urbg.reset();
506 {
507 // Algorithm Joehnk (double)
508 absl::beta_distribution<double> dist(0.1, 0.2);
509 std::generate(std::begin(output), std::end(output),
510 [&] { return double_to_u64(dist(urbg)); });
511 EXPECT_EQ(44, urbg.invocations());
512 EXPECT_THAT(
513 output, //
514 testing::ElementsAre(
515 99834713000000, 61903356870004, 50000000000001, 99999721170000,
516 99628374770000, 99999999990000, 84474397860004, 84721276240001,
517 99997407490000, 87232528120000, 58548364780007, 93328932910000,
518 86908237770042, 64767917930031, 52824581970004, 96998544140004,
519 62605946270008, 91593604380002, 83345031740033, 87804397230015));
520 }
521
522 urbg.reset();
523 {
524 // Algorithm Cheng 1
525 absl::beta_distribution<double> dist(0.9, 2.0);
526 std::generate(std::begin(output), std::end(output),
527 [&] { return double_to_u64(dist(urbg)); });
528 EXPECT_EQ(62, urbg.invocations());
529 EXPECT_THAT(
530 output, //
531 testing::ElementsAre(
532 62069004780001, 64433204450001, 53607416560000, 89644295430008,
533 61434586310019, 55172615890002, 62187161490000, 56433684810003,
534 80454622050005, 86418558710003, 92920514700001, 64645184680001,
535 58549183380000, 84881283650005, 71078728590002, 69949694970000,
536 73157461710001, 68592191300001, 70747623900000, 78584696930005));
537 }
538
539 urbg.reset();
540 {
541 // Algorithm Cheng 2
542 absl::beta_distribution<double> dist(1.5, 2.5);
543 std::generate(std::begin(output), std::end(output),
544 [&] { return double_to_u64(dist(urbg)); });
545 EXPECT_EQ(54, urbg.invocations());
546 EXPECT_THAT(
547 output, //
548 testing::ElementsAre(
549 75000029250001, 76751482860001, 53264575220000, 69193133650005,
550 78028324470013, 91573587560002, 59167523770000, 60658618560002,
551 80075870540000, 94141320460004, 63196592770003, 78883906300002,
552 96797992590001, 76907587800001, 56645167560000, 65408302280003,
553 53401156320001, 64731238570000, 83065573750001, 79788333820001));
554 }
555 }
556
557 // This is an implementation-specific test. If any part of the implementation
558 // changes, then it is likely that this test will change as well. Also, if
559 // dependencies of the distribution change, such as RandU64ToDouble, then this
560 // is also likely to change.
TEST(BetaDistributionTest,AlgorithmBounds)561 TEST(BetaDistributionTest, AlgorithmBounds) {
562 #if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0
563 // We're using an x87-compatible FPU, and intermediate operations are
564 // performed with 80-bit floats. This produces slightly different results from
565 // what we expect below.
566 GTEST_SKIP()
567 << "Skipping the test because we detected x87 floating-point semantics";
568 #endif
569
570 {
571 absl::random_internal::sequence_urbg urbg(
572 {0x7fbe76c8b4395800ull, 0x8000000000000000ull});
573 // u=0.499, v=0.5
574 absl::beta_distribution<double> dist(1e-4, 1e-4);
575 double a = dist(urbg);
576 EXPECT_EQ(a, 2.0202860861567108529e-09);
577 EXPECT_EQ(2, urbg.invocations());
578 }
579
580 // Test that both the float & double algorithms appropriately reject the
581 // initial draw.
582 {
583 // 1/alpha = 1/beta = 2.
584 absl::beta_distribution<float> dist(0.5, 0.5);
585
586 // first two outputs are close to 1.0 - epsilon,
587 // thus: (u ^ 2 + v ^ 2) > 1.0
588 absl::random_internal::sequence_urbg urbg(
589 {0xffff00000006e6c8ull, 0xffff00000007c7c8ull, 0x800003766295CFA9ull,
590 0x11C819684E734A41ull});
591 {
592 double y = absl::beta_distribution<double>(0.5, 0.5)(urbg);
593 EXPECT_EQ(4, urbg.invocations());
594 EXPECT_EQ(y, 0.9810668952633862) << y;
595 }
596
597 // ...and: log(u) * a ~= log(v) * b ~= -0.02
598 // thus z ~= -0.02 + log(1 + e(~0))
599 // ~= -0.02 + 0.69
600 // thus z > 0
601 urbg.reset();
602 {
603 float x = absl::beta_distribution<float>(0.5, 0.5)(urbg);
604 EXPECT_EQ(4, urbg.invocations());
605 EXPECT_NEAR(0.98106688261032104, x, 0.0000005) << x << "f";
606 }
607 }
608 }
609
610 } // namespace
611