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