summaryrefslogtreecommitdiff
path: root/absl/random/beta_distribution_test.cc
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2019-08-08 10:56:58 -0700
committerGravatar CJ Johnson <johnsoncj@google.com>2019-08-08 14:19:45 -0400
commitaa844899c937bde5d2b24f276b59997e5b668bde (patch)
treecd18e64150abc74b85bbbf6abf990f66fa47cacd /absl/random/beta_distribution_test.cc
parentfcb104594b0bb4b8ac306cb2f55ecdad40974683 (diff)
Creation of LTS branch "lts_2019_08_08"20190808
- 9ee91d3e430fb33a4590486573792eb0fa146c2d Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 8efba58a3b656e9b41fb0471ae6453425a61c520 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - b49b8d16b67ec6912899684b732e6367f258cfdb Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 67222ffc4c83d918ce8395aa61769eeb77df4c4d Export of internal Abseil changes by Abseil Team <absl-team@google.com> - c5c4db4f5191fe5e76cbf68dcc71fb28702f7d2b Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 14550beb3b7b97195e483fb74b5efb906395c31e Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 52e88ee56b72cf32bc66534d942c7398ce481331 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 36d37ab992038f52276ca66b9da80c1cf0f57dc2 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - ad1485c8986246b2ae9105e512738d0e97aec887 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - f3840bc5e33ce4932e35986cf3718450c6f02af2 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 278b26058c036833a4f7f3047d3f4d9296527f87 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - c6c3c1b498e4ee939b24be59cae29d59c3863be8 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 44efe96dfca674a17b45ca53fc77fb69f1e29bf4 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 3c98fcc0461bd2a4b9c149d4748a7373a225cf4b Merge pull request #340 from jtsylve/macos_cxx17_fix by Matt Calabrese <38107210+mattcalabrese-google@users.noreply.github.com> - 74d91756c11bc22f9b0108b94da9326f7f9e376f Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - e6b050212c859fbaf67abac76105da10ec348274 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - c964fcffac27bd4a9ff67fe393410dd1146ef8b8 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 72e09a54d993b192db32be14c65adf7e9bd08c31 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - d65e19dfcd8697076f68598c0131c6930cdcd74d Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 5162fc83d2f3b79a9753ed59594c43966afdd37a Merge pull request #336 from shields/patch-2 by Shaindel Schwartz <31392632+shaindelschwartz@users.noreply.github.com> - 0389f7bf58fa41f35b3ad60be61d32f31e4f8ed6 Merge pull request #335 from shields/patch-1 by Shaindel Schwartz <31392632+shaindelschwartz@users.noreply.github.com> - e9324d926a9189e222741fce6e676f0944661a72 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 43ef2148c0936ebf7cb4be6b19927a9d9d145b8f Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - a13d3df2b3ba68aeead92e2d078fba0510d55024 Merge pull request #323 from gosnik/master by Gennadiy Rozental <rogeeff@google.com> - 310a11865c97c5cdcc42a4ee2c2e3578423afe69 Merge pull request #324 from RasPat1/patch-1 by Gennadiy Rozental <rogeeff@google.com> - 8f11724067248acc330b4d1f12f0c76d03f2cfb1 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - b1dd425423380126f6441ce4fbb6f8f6c75b793a Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 361cb8a9db2f2130442389fd80593255be26d681 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 0238ab0a831f179518c1a814f9584e99da2d75a3 Merge pull request #321 from christoph-cullmann/c4245_fix... by Xiaoyi Zhang <zhangxy988@gmail.com> - 61c9bf3e3e1c28a4aa6d7f1be4b37fd473bb5529 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - bc9101f9982391019521161a36179b52555ed212 Merge pull request #320 from christoph-cullmann/master by Xiaoyi Zhang <zhangxy988@gmail.com> - 2f76a9bf50046e396138cc8eeb3cdc17b7a5ac24 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 4adaf5490921f13028b55018c9f550277de5aebb Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 27c30ec671cb7b5ba84c4e79feff7fd0b0ac6338 Avoid undefined behavior when nullptr is passed to memcpy... by Roman Gershman <romange@gmail.com> - ce65f5ac3cbf897bb5e3de1a51d80fd00866abaa Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - a18fc7461e7409c2ad64e28537261db1e02e76fa Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 8a394b19c149cab50534b04c5e21d42bc2217a7d Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - daf381e8535a1f1f1b8a75966a74e7cca63dee89 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - fa00c321073c7ea40a4fc3dfc8a06309eae3d025 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 436ba6c4a0ea3a06eca6e055f9c8d296bf3bae12 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 0cbdc774b97f7e80ab60dbe2ed4eaca3b2e33fc8 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 27c2f6e2f3b5929fbd322b0f0ca392eb02efd9f8 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - aa468ad75539619b47979911297efbb629c52e44 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - cd86d0d20ab167c33b23d3875db68d1d4bad3a3b Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 33841c5c963aa9c3f096ef8e6c1e71624b941940 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - ca3f87560a0eef716195cadf66dc6b938a579ec6 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - d902eb869bcfacc1bad14933ed9af4bed006d481 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - a02f62f456f2c4a7ecf2be3104fe0c6e16fbad9a Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 0b545b460141b882b244a1efcef7621d59278160 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - dbae8764fbd429bf7d7745e24bcf73962177a7c0 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 044da8a29c923506af0f0b46bc46f43c1e1300b5 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 6cc6ac44e065b9e8975fadfd6ccb99cbcf89aac4 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 666fc1266bccfd8e6eaaa084e7b42580bb8eb199 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 93dfcf74cb5fccae3da07897d8613ae6cab958a0 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 2c8421e1c6cef0da9e8a20b01c15256ec9ec116d Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 5b65c4af5107176555b23a638e5947686410ac1f Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - eab2078b53c9e3d9d240135c09d27e3393acb50a Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 253eb7416421661873afbaa33828a850db978541 [CMake] Set correct flags for clang-cl (#278) by Loo Rong Jie <loorongjie@gmail.com> - e75672f6afc7e8f23ee7b532e86d1b3b9be3984e Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - bf29470384a101b307873b26d358433138c857fc Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 6fd827124facd8336981e73218997f9e73029b4f Merge pull request #280 from chiumichael/master by Derek Mauro <761129+derekmauro@users.noreply.github.com> - 7c7754fb3ed9ffb57d35fe8658f3ba4d73a31e72 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 256be563447a315f2a7993ec669460ba475fa86a Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 88a152ae747c3c42dc9167d46c590929b048d436 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - c1cecb25a94c075725e9d2640f6b978a8f61957b Implement Span::first and Span::last from C++20 (#274) by Girts <girtsf@users.noreply.github.com> - 38b704384cd2f17590b3922b97744be0b43622c9 Changed HTTP URLs to HTTPS where possible (#270) by nik7273 <nik8470@gmail.com> - febc5ee6a92d0eb7dac1fceaa6c648cf6521b4dc Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 9fdf5e5b805412cb2a2e624d3e9a11588120465f Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 419f3184f8ebcdb23105295eadd2a569f3351eb9 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - b312c3cb53a0aad75a85ac2bf57c4a614fbd48d4 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 308ce31528a7edfa39f5f6d36142278a0ae1bf45 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 93d155bc4414f6c121bb1f19dba9fdb27c8943bc Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 426eaa4aa44e4580418bee46c1bd13911151bfb1 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 2901ec32a919311384d6ad4194e2d927c06831f7 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - d78310fe5a82f2e0e6e16509ef8079c8d7e4674e Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - a4cb1c8ba61531a63f9d309eea01ac1d43d8371d Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 540e2537b92cd4abfae6ceddfe24304345461f32 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 89ea0c5ff34aaa5855cfc7aa41f323b8a0ef0ede Merge pull request #255 from uilianries/hotfix/conan by ahedberg <ahedberg@google.com> - 5e0dcf72c64fae912184d2e0de87195fe8f0a425 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 0dffca4e36791c7beeda04da10460b534283948a Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 6b4201f9ef650637510a21b8d6cbcc3bee4a606f Fix GCC8 warnings by Boris Staletic <boris.staletic@gmail.com> - 0b1e6d417b414aad9282e32e8c49c719edeb63c1 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - efccc502606bed768e50a6cd5806d8eb13e4e935 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 5e6a78131f7bd5940218462c07d88cdefdd75dbe Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 5eea0f713c14ac17788b83e496f11903f8e2bbb0 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 66f9becbb98ecc083f4db349b4b1e0ca9de93b15 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 018b4db1d73ec8238e6dc4b17fd9e1fd7468d0ed Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 9449ae94397f2fd683851348e25ed8c93f75b3b9 Merge pull request #243 from ThomsonTan/FixIntrinsic by Alex Strelnikov <strel@google.com> - b16aeb6756bdab08cdf12d40baab5b51f7d15b16 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 7ffbe09f3d85504bd018783bbe1e2c12992fe47c Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 01b471d9f3ebef27f5aaca14b66509099fa8cd6c Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 7bd8f36c741c7cbe311611d7981bf38ba04c6fef Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 968a34ffdaadd7db062a9621dfbdf8b2d16e05af Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 3e2e9b5557e76d098de4b8a2a659125b98ca519b Merge pull request #231 from uilianries/feature/conan by Mark Barolak <mbxx@users.noreply.github.com> - 111ca7060a6ff50115ca85b59f6b5d8c8c5e9105 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 389ec3f906f018661a5308458d623d01f96d7b23 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 8fbcdb90952c57828c4a9c2f6d79fcd7cae9088f Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 455dc17ba1af9635f0b60155bc565bc572a1e722 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - f197d7c72a54064cfde5a2058f1513a4a0ee36fb Export of internal Abseil changes. by Abseil Team <absl-team@google.com> - 284378a71b32dfb3af4e3661f585e671d1b603a3 Export of internal Abseil changes. by Abseil Team <absl-team@google.com> GitOrigin-RevId: 9ee91d3e430fb33a4590486573792eb0fa146c2d Change-Id: Ia06e548bc106cc9d136f6c65714be6645317aced
Diffstat (limited to 'absl/random/beta_distribution_test.cc')
-rw-r--r--absl/random/beta_distribution_test.cc614
1 files changed, 614 insertions, 0 deletions
diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc
new file mode 100644
index 00000000..966ad08b
--- /dev/null
+++ b/absl/random/beta_distribution_test.cc
@@ -0,0 +1,614 @@
+// 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(%f, %f)", 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