[yul-phaser] Replace mutation tests with hard-coded expectations with more robust ones that check the statistical distribution

- The old tests would easily break when adding/removing optimiser steps.
This commit is contained in:
Kamil Śliwak 2020-09-11 22:01:37 +02:00 committed by chriseth
parent d75f0ba437
commit 5637bdcaec

View File

@ -22,6 +22,7 @@
#include <tools/yulPhaser/SimulationRNG.h>
#include <libsolutil/CommonIO.h>
#include <libyul/optimiser/Suite.h>
#include <boost/test/unit_test.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -31,6 +32,7 @@
using namespace std;
using namespace solidity::util;
using namespace solidity::yul;
namespace solidity::phaser::test
{
@ -41,19 +43,32 @@ BOOST_AUTO_TEST_SUITE(GeneRandomisationTest)
BOOST_AUTO_TEST_CASE(geneRandomisation_should_iterate_over_genes_and_replace_them_with_random_ones_with_given_probability)
{
Chromosome chromosome("fcCUnDvejs");
function<Mutation> mutation01 = geneRandomisation(0.1);
function<Mutation> mutation05 = geneRandomisation(0.5);
function<Mutation> mutation10 = geneRandomisation(1.0);
size_t constexpr inputLength = 1000;
double constexpr tolerance = 0.05;
// Use genes that do not represent valid step abbreviations to be able to easily spot added steps.
assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0);
Chromosome input = Chromosome(string(inputLength, '.'));
SimulationRNG::reset(1);
BOOST_TEST(countDifferences(mutation01(chromosome), chromosome), 2);
BOOST_TEST(countDifferences(mutation05(chromosome), chromosome), 5);
BOOST_TEST(countDifferences(mutation10(chromosome), chromosome), 7);
SimulationRNG::reset(2);
BOOST_TEST(countDifferences(mutation01(chromosome), chromosome), 1);
BOOST_TEST(countDifferences(mutation05(chromosome), chromosome), 3);
BOOST_TEST(countDifferences(mutation10(chromosome), chromosome), 9);
for (size_t randomisationChancePercent = 20; randomisationChancePercent <= 100; randomisationChancePercent += 20)
{
double const randomisationChance = (randomisationChancePercent / 100.0);
Chromosome output = geneRandomisation(randomisationChance)(input);
string outputGenes = output.genes();
BOOST_REQUIRE(output.length() == input.length());
double const expectedValue = randomisationChance;
double const variance = randomisationChance * (1 - randomisationChance);
double const randomisedGeneCount = input.length() - static_cast<size_t>(count(outputGenes.begin(), outputGenes.end(), '.'));
double const squaredError =
(inputLength - randomisedGeneCount) * expectedValue * expectedValue +
randomisedGeneCount * (1 - expectedValue) * (1 - expectedValue);
BOOST_TEST(abs(randomisedGeneCount / inputLength - expectedValue) < tolerance);
BOOST_TEST(abs(squaredError / inputLength - variance) < tolerance);
}
}
BOOST_AUTO_TEST_CASE(geneRandomisation_should_return_identical_chromosome_if_probability_is_zero)
@ -66,17 +81,33 @@ BOOST_AUTO_TEST_CASE(geneRandomisation_should_return_identical_chromosome_if_pro
BOOST_AUTO_TEST_CASE(geneDeletion_should_iterate_over_genes_and_delete_them_with_given_probability)
{
Chromosome chromosome("fcCUnDvejs");
function<Mutation> mutation01 = geneDeletion(0.1);
function<Mutation> mutation05 = geneDeletion(0.5);
size_t constexpr inputLength = 1000;
double constexpr tolerance = 0.05;
// Use genes that do not represent valid step abbreviations to be able to easily spot added steps.
assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0);
Chromosome input = Chromosome(string(inputLength, '.'));
SimulationRNG::reset(1);
// fcCUnDvejs
BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace("fcCU Dvejs")));
BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace(" D ejs")));
SimulationRNG::reset(2);
BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace("fcUnDvejs")));
BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace(" Un s")));
for (size_t deletionChancePercent = 20; deletionChancePercent < 100; deletionChancePercent += 20)
{
double const deletionChance = (deletionChancePercent / 100.0);
Chromosome output = geneDeletion(deletionChance)(input);
string outputGenes = output.genes();
BOOST_REQUIRE(output.length() <= input.length());
BOOST_REQUIRE(static_cast<size_t>(count(outputGenes.begin(), outputGenes.end(), '.')) == output.length());
double const expectedValue = deletionChance;
double const variance = deletionChance * (1 - deletionChance);
double const deletedGeneCount = input.length() - output.length();
double const squaredError =
(inputLength - deletedGeneCount) * expectedValue * expectedValue +
deletedGeneCount * (1 - expectedValue) * (1 - expectedValue);
BOOST_TEST(abs(deletedGeneCount / inputLength - expectedValue) < tolerance);
BOOST_TEST(abs(squaredError / inputLength - variance) < tolerance);
}
}
BOOST_AUTO_TEST_CASE(geneDeletion_should_return_identical_chromosome_if_probability_is_zero)
@ -97,17 +128,37 @@ BOOST_AUTO_TEST_CASE(geneDeletion_should_delete_all_genes_if_probability_is_one)
BOOST_AUTO_TEST_CASE(geneAddition_should_iterate_over_gene_positions_and_insert_new_genes_with_given_probability)
{
Chromosome chromosome("fcCUnDvejs");
function<Mutation> mutation01 = geneAddition(0.1);
function<Mutation> mutation05 = geneAddition(0.5);
size_t constexpr inputLength = 1000;
double constexpr tolerance = 0.05;
size_t constexpr maxAdditions = inputLength + 1;
// Use genes that do not represent valid step abbreviations to be able to easily spot added steps.
assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0);
Chromosome input = Chromosome(string(inputLength, '.'));
SimulationRNG::reset(1);
// f c C U n D v e j s
BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace(" f c C UC n D v e jx s"))); // 20% more
BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace("s f cu C U nj D v eO j sf"))); // 50% more
SimulationRNG::reset(2);
BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace(" f cp C U n D v e j s"))); // 10% more
BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace("M f ce Ce U n D v e jo s"))); // 40% more
for (size_t additionChancePercent = 20; additionChancePercent < 100; additionChancePercent += 20)
{
double const additionChance = (additionChancePercent / 100.0);
Chromosome output = geneAddition(additionChance)(input);
BOOST_REQUIRE(output.length() >= input.length());
BOOST_REQUIRE(output.length() <= inputLength + maxAdditions);
string_view outputGenes = output.genes();
size_t preservedGeneCount = static_cast<size_t>(count(outputGenes.begin(), outputGenes.end(), '.'));
BOOST_REQUIRE(preservedGeneCount == input.length());
double const expectedValue = additionChance;
double const variance = additionChance * (1 - additionChance);
double const addedGeneCount = (output.length() - preservedGeneCount);
double const squaredError =
(maxAdditions - addedGeneCount) * expectedValue * expectedValue +
addedGeneCount * (1 - expectedValue) * (1 - expectedValue);
BOOST_TEST(abs(addedGeneCount / maxAdditions - expectedValue) < tolerance);
BOOST_TEST(abs(squaredError / maxAdditions - variance) < tolerance);
}
}
BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position)