diff --git a/test/yulPhaser/SimulationRNG.cpp b/test/yulPhaser/SimulationRNG.cpp index 3c3098f25..f158fcbf6 100644 --- a/test/yulPhaser/SimulationRNG.cpp +++ b/test/yulPhaser/SimulationRNG.cpp @@ -31,6 +31,57 @@ namespace solidity::phaser::test BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(RandomTest) +BOOST_AUTO_TEST_CASE(bernoulliTrial_should_produce_samples_with_right_expected_value_and_variance) +{ + SimulationRNG::reset(1); + constexpr size_t numSamples = 10000; + constexpr double successProbability = 0.4; + constexpr double relativeTolerance = 0.05; + + // For bernoulli distribution with success probability p: EX = p, VarX = p(1 - p) + constexpr double expectedValue = successProbability; + constexpr double variance = successProbability * (1 - successProbability); + + vector samples; + for (uint32_t i = 0; i < numSamples; ++i) + samples.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); +} + +BOOST_AUTO_TEST_CASE(bernoulliTrial_can_be_reset) +{ + constexpr size_t numSamples = 10; + constexpr double successProbability = 0.4; + + SimulationRNG::reset(1); + vector samples1; + for (uint32_t i = 0; i < numSamples; ++i) + samples1.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + vector samples2; + for (uint32_t i = 0; i < numSamples; ++i) + samples2.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + SimulationRNG::reset(1); + vector samples3; + for (uint32_t i = 0; i < numSamples; ++i) + samples3.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + SimulationRNG::reset(2); + vector samples4; + for (uint32_t i = 0; i < numSamples; ++i) + samples4.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + BOOST_TEST(samples1 != samples2); + BOOST_TEST(samples1 == samples3); + BOOST_TEST(samples1 != samples4); + BOOST_TEST(samples2 != samples3); + BOOST_TEST(samples2 != samples4); + BOOST_TEST(samples3 != samples4); +} + BOOST_AUTO_TEST_CASE(uniformInt_returns_different_values_when_called_multiple_times) { SimulationRNG::reset(1); diff --git a/tools/yulPhaser/SimulationRNG.cpp b/tools/yulPhaser/SimulationRNG.cpp index 6432d5e5b..1103db5a8 100644 --- a/tools/yulPhaser/SimulationRNG.cpp +++ b/tools/yulPhaser/SimulationRNG.cpp @@ -17,6 +17,7 @@ #include +#include #include #include @@ -27,6 +28,13 @@ using namespace solidity::phaser; thread_local boost::random::mt19937 SimulationRNG::s_generator(SimulationRNG::generateSeed()); +bool SimulationRNG::bernoulliTrial(double _successProbability) +{ + boost::random::bernoulli_distribution<> distribution(_successProbability); + + return static_cast(distribution(s_generator)); +} + uint32_t SimulationRNG::uniformInt(uint32_t _min, uint32_t _max) { boost::random::uniform_int_distribution<> distribution(_min, _max); diff --git a/tools/yulPhaser/SimulationRNG.h b/tools/yulPhaser/SimulationRNG.h index e884b4f36..1e8a1da18 100644 --- a/tools/yulPhaser/SimulationRNG.h +++ b/tools/yulPhaser/SimulationRNG.h @@ -37,6 +37,7 @@ namespace solidity::phaser class SimulationRNG { public: + static bool bernoulliTrial(double _successProbability); static uint32_t uniformInt(uint32_t _min, uint32_t _max); static uint32_t binomialInt(uint32_t _numTrials, double _successProbability);