Merge pull request #8515 from imapp-pl/yul-phaser-classic-genetic-algorithm

[yul-phaser] Classic genetic algorithm
This commit is contained in:
chriseth
2020-04-15 12:01:51 +02:00
committed by GitHub
20 changed files with 881 additions and 32 deletions
+204
View File
@@ -31,6 +31,7 @@
using namespace std;
using namespace boost::unit_test::framework;
using namespace boost::test_tools;
using namespace solidity::util;
namespace solidity::phaser::test
{
@@ -41,6 +42,18 @@ protected:
shared_ptr<FitnessMetric> m_fitnessMetric = make_shared<ChromosomeLengthMetric>();
};
class ClassicGeneticAlgorithmFixture: public GeneticAlgorithmFixture
{
protected:
ClassicGeneticAlgorithm::Options m_options = {
/* elitePoolSize = */ 0.0,
/* crossoverChance = */ 0.0,
/* mutationChance = */ 0.0,
/* deletionChance = */ 0.0,
/* additionChance = */ 0.0,
};
};
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(GeneticAlgorithmsTest)
BOOST_AUTO_TEST_SUITE(RandomAlgorithmTest)
@@ -186,6 +199,197 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove
}));
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(ClassicGeneticAlgorithmTest)
BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_proportional_to_fitness, ClassicGeneticAlgorithmFixture)
{
constexpr double relativeTolerance = 0.1;
constexpr size_t populationSize = 1000;
assert(populationSize % 4 == 0 && "Choose a number divisible by 4 for this test");
auto population =
Population::makeRandom(m_fitnessMetric, populationSize / 4, 0, 0) +
Population::makeRandom(m_fitnessMetric, populationSize / 4, 1, 1) +
Population::makeRandom(m_fitnessMetric, populationSize / 4, 2, 2) +
Population::makeRandom(m_fitnessMetric, populationSize / 4, 3, 3);
map<size_t, double> expectedProbabilities = {
{0, 4.0 / (4 + 3 + 2 + 1)},
{1, 3.0 / (4 + 3 + 2 + 1)},
{2, 2.0 / (4 + 3 + 2 + 1)},
{3, 1.0 / (4 + 3 + 2 + 1)},
};
double const expectedValue = (
0.0 * expectedProbabilities[0] +
1.0 * expectedProbabilities[1] +
2.0 * expectedProbabilities[2] +
3.0 * expectedProbabilities[3]
);
double const variance = (
(0.0 - expectedValue) * (0.0 - expectedValue) * expectedProbabilities[0] +
(1.0 - expectedValue) * (1.0 - expectedValue) * expectedProbabilities[1] +
(2.0 - expectedValue) * (2.0 - expectedValue) * expectedProbabilities[2] +
(3.0 - expectedValue) * (3.0 - expectedValue) * expectedProbabilities[3]
);
ClassicGeneticAlgorithm algorithm(m_options);
Population newPopulation = algorithm.runNextRound(population);
BOOST_TEST(newPopulation.individuals().size() == population.individuals().size());
vector<size_t> newFitness = chromosomeLengths(newPopulation);
BOOST_TEST(abs(mean(newFitness) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(newFitness, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_only_individuals_existing_in_the_original_population, ClassicGeneticAlgorithmFixture)
{
constexpr size_t populationSize = 1000;
auto population = Population::makeRandom(m_fitnessMetric, populationSize, 1, 10);
set<string> originalSteps;
for (auto const& individual: population.individuals())
originalSteps.insert(toString(individual.chromosome));
ClassicGeneticAlgorithm algorithm(m_options);
Population newPopulation = algorithm.runNextRound(population);
for (auto const& individual: newPopulation.individuals())
BOOST_TEST(originalSteps.count(toString(individual.chromosome)) == 1);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_crossover, ClassicGeneticAlgorithmFixture)
{
auto population = Population(m_fitnessMetric, {
Chromosome("aa"), Chromosome("aa"), Chromosome("aa"),
Chromosome("ff"), Chromosome("ff"), Chromosome("ff"),
Chromosome("gg"), Chromosome("gg"), Chromosome("gg"),
});
set<string> originalSteps{"aa", "ff", "gg"};
set<string> crossedSteps{"af", "fa", "fg", "gf", "ga", "ag"};
m_options.crossoverChance = 0.8;
ClassicGeneticAlgorithm algorithm(m_options);
SimulationRNG::reset(1);
Population newPopulation = algorithm.runNextRound(population);
size_t totalCrossed = 0;
size_t totalUnchanged = 0;
for (auto const& individual: newPopulation.individuals())
{
totalCrossed += crossedSteps.count(toString(individual.chromosome));
totalUnchanged += originalSteps.count(toString(individual.chromosome));
}
BOOST_TEST(totalCrossed + totalUnchanged == newPopulation.individuals().size());
BOOST_TEST(totalCrossed >= 2);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_mutation, ClassicGeneticAlgorithmFixture)
{
m_options.mutationChance = 0.6;
ClassicGeneticAlgorithm algorithm(m_options);
constexpr size_t populationSize = 1000;
constexpr double relativeTolerance = 0.05;
double const expectedValue = m_options.mutationChance;
double const variance = m_options.mutationChance * (1 - m_options.mutationChance);
Chromosome chromosome("aaaaaaaaaa");
vector<Chromosome> chromosomes(populationSize, chromosome);
Population population(m_fitnessMetric, chromosomes);
SimulationRNG::reset(1);
Population newPopulation = algorithm.runNextRound(population);
vector<size_t> bernoulliTrials;
for (auto const& individual: newPopulation.individuals())
{
string steps = toString(individual.chromosome);
for (char step: steps)
bernoulliTrials.push_back(static_cast<size_t>(step != 'a'));
}
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithmFixture)
{
m_options.deletionChance = 0.6;
ClassicGeneticAlgorithm algorithm(m_options);
constexpr size_t populationSize = 1000;
constexpr double relativeTolerance = 0.05;
double const expectedValue = m_options.deletionChance;
double const variance = m_options.deletionChance * (1 - m_options.deletionChance);
Chromosome chromosome("aaaaaaaaaa");
vector<Chromosome> chromosomes(populationSize, chromosome);
Population population(m_fitnessMetric, chromosomes);
SimulationRNG::reset(1);
Population newPopulation = algorithm.runNextRound(population);
vector<size_t> bernoulliTrials;
for (auto const& individual: newPopulation.individuals())
{
string steps = toString(individual.chromosome);
for (size_t i = 0; i < chromosome.length(); ++i)
bernoulliTrials.push_back(static_cast<size_t>(i >= steps.size()));
}
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_addition, ClassicGeneticAlgorithmFixture)
{
m_options.additionChance = 0.6;
ClassicGeneticAlgorithm algorithm(m_options);
constexpr size_t populationSize = 1000;
constexpr double relativeTolerance = 0.05;
double const expectedValue = m_options.additionChance;
double const variance = m_options.additionChance * (1 - m_options.additionChance);
Chromosome chromosome("aaaaaaaaaa");
vector<Chromosome> chromosomes(populationSize, chromosome);
Population population(m_fitnessMetric, chromosomes);
SimulationRNG::reset(1);
Population newPopulation = algorithm.runNextRound(population);
vector<size_t> bernoulliTrials;
for (auto const& individual: newPopulation.individuals())
{
string steps = toString(individual.chromosome);
for (size_t i = 0; i < chromosome.length() + 1; ++i)
{
BOOST_REQUIRE(chromosome.length() <= steps.size() && steps.size() <= 2 * chromosome.length() + 1);
bernoulliTrials.push_back(static_cast<size_t>(i < steps.size() - chromosome.length()));
}
}
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite, ClassicGeneticAlgorithmFixture)
{
auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 6, 5, 5);
assert((chromosomeLengths(population) == vector<size_t>{3, 3, 3, 3, 5, 5, 5, 5, 5, 5}));
m_options.elitePoolSize = 0.5;
m_options.deletionChance = 1.0;
ClassicGeneticAlgorithm algorithm(m_options);
Population newPopulation = algorithm.runNextRound(population);
BOOST_TEST((chromosomeLengths(newPopulation) == vector<size_t>{0, 0, 0, 0, 0, 3, 3, 3, 3, 5}));
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
+47
View File
@@ -212,6 +212,39 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_second_mutation_i
BOOST_TEST(mutation(chromosome) == Chromosome("f"));
}
BOOST_AUTO_TEST_CASE(mutationSequence_should_apply_all_mutations)
{
Chromosome chromosome("aaaaa");
function<Mutation> mutation = mutationSequence({
geneSubstitution(3, Chromosome("g").optimisationSteps()[0]),
geneSubstitution(2, Chromosome("f").optimisationSteps()[0]),
geneSubstitution(1, Chromosome("c").optimisationSteps()[0]),
});
BOOST_TEST(mutation(chromosome) == Chromosome("acfga"));
}
BOOST_AUTO_TEST_CASE(mutationSequence_apply_mutations_in_the_order_they_are_given)
{
Chromosome chromosome("aa");
function<Mutation> mutation = mutationSequence({
geneSubstitution(0, Chromosome("g").optimisationSteps()[0]),
geneSubstitution(1, Chromosome("c").optimisationSteps()[0]),
geneSubstitution(0, Chromosome("f").optimisationSteps()[0]),
geneSubstitution(1, Chromosome("o").optimisationSteps()[0]),
});
BOOST_TEST(mutation(chromosome) == Chromosome("fo"));
}
BOOST_AUTO_TEST_CASE(mutationSequence_should_return_unmodified_chromosome_if_given_no_mutations)
{
Chromosome chromosome("aa");
function<Mutation> mutation = mutationSequence({});
BOOST_TEST(mutation(chromosome) == chromosome);
}
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_swap_chromosome_parts_at_random_point)
{
function<Crossover> crossover = randomPointCrossover();
@@ -225,6 +258,20 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_swap_chromosome_parts_at_random
BOOST_TEST(result2 == Chromosome("cccaaaaaaa"));
}
BOOST_AUTO_TEST_CASE(symmetricRandomPointCrossover_should_swap_chromosome_parts_at_random_point)
{
function<SymmetricCrossover> crossover = symmetricRandomPointCrossover();
SimulationRNG::reset(1);
tuple<Chromosome, Chromosome> result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc"));
tuple<Chromosome, Chromosome> expectedPair1 = {Chromosome("aaaccc"), Chromosome("cccaaaaaaa")};
BOOST_TEST(result1 == expectedPair1);
tuple<Chromosome, Chromosome> result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa"));
tuple<Chromosome, Chromosome> expectedPair2 = {Chromosome("ccccccaaaa"), Chromosome("aaaaaa")};
BOOST_TEST(result2 == expectedPair2);
}
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_only_consider_points_available_on_both_chromosomes)
{
SimulationRNG::reset(1);
+72
View File
@@ -119,6 +119,78 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_collection_has_one_el
BOOST_TEST(RandomPairSelection(2.0).materialise(1).empty());
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(PairsFromRandomSubsetTest)
BOOST_AUTO_TEST_CASE(materialise_should_return_random_values_with_equal_probabilities)
{
constexpr int collectionSize = 1000;
constexpr double selectionChance = 0.7;
constexpr double relativeTolerance = 0.001;
constexpr double expectedValue = selectionChance;
constexpr double variance = selectionChance * (1 - selectionChance);
SimulationRNG::reset(1);
vector<tuple<size_t, size_t>> pairs = PairsFromRandomSubset(selectionChance).materialise(collectionSize);
vector<double> bernoulliTrials(collectionSize, 0);
for (auto& pair: pairs)
{
BOOST_REQUIRE(get<1>(pair) < collectionSize);
BOOST_REQUIRE(get<1>(pair) < collectionSize);
bernoulliTrials[get<0>(pair)] = 1.0;
bernoulliTrials[get<1>(pair)] = 1.0;
}
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_collection_indices)
{
const size_t collectionSize = 200;
constexpr double selectionChance = 0.5;
vector<tuple<size_t, size_t>> pairs = PairsFromRandomSubset(selectionChance).materialise(collectionSize);
BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<0>(pair) <= collectionSize; }));
BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<1>(pair) <= collectionSize; }));
}
BOOST_AUTO_TEST_CASE(materialise_should_use_unique_indices)
{
constexpr size_t collectionSize = 200;
constexpr double selectionChance = 0.5;
vector<tuple<size_t, size_t>> pairs = PairsFromRandomSubset(selectionChance).materialise(collectionSize);
set<size_t> indices;
for (auto& pair: pairs)
{
indices.insert(get<0>(pair));
indices.insert(get<1>(pair));
}
BOOST_TEST(indices.size() == 2 * pairs.size());
}
BOOST_AUTO_TEST_CASE(materialise_should_return_no_indices_if_collection_is_empty)
{
BOOST_TEST(PairsFromRandomSubset(0.0).materialise(0).empty());
BOOST_TEST(PairsFromRandomSubset(0.5).materialise(0).empty());
BOOST_TEST(PairsFromRandomSubset(1.0).materialise(0).empty());
}
BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_selection_chance_is_zero)
{
BOOST_TEST(PairsFromRandomSubset(0.0).materialise(0).empty());
BOOST_TEST(PairsFromRandomSubset(0.0).materialise(100).empty());
}
BOOST_AUTO_TEST_CASE(materialise_should_return_all_pairs_if_selection_chance_is_one)
{
BOOST_TEST(PairsFromRandomSubset(1.0).materialise(0).empty());
BOOST_TEST(PairsFromRandomSubset(1.0).materialise(100).size() == 50);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(PairMosaicSelectionTest)
+17
View File
@@ -52,6 +52,11 @@ protected:
/* gewepDeletionVsAdditionChance = */ 0.3,
/* gewepGenesToRandomise = */ 0.4,
/* gewepGenesToAddOrDelete = */ 0.2,
/* classicElitePoolSize = */ 0.0,
/* classicCrossoverChance = */ 0.75,
/* classicMutationChance = */ 0.2,
/* classicDeletionChance = */ 0.2,
/* classicAdditionChance = */ 0.2,
};
};
@@ -122,6 +127,18 @@ BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_opt
BOOST_TEST(gewepAlgorithm->options().deletionVsAdditionChance == m_options.gewepDeletionVsAdditionChance);
BOOST_TEST(gewepAlgorithm->options().percentGenesToRandomise == m_options.gewepGenesToRandomise.value());
BOOST_TEST(gewepAlgorithm->options().percentGenesToAddOrDelete == m_options.gewepGenesToAddOrDelete.value());
m_options.algorithm = Algorithm::Classic;
unique_ptr<GeneticAlgorithm> algorithm3 = GeneticAlgorithmFactory::build(m_options, 100);
BOOST_REQUIRE(algorithm3 != nullptr);
auto classicAlgorithm = dynamic_cast<ClassicGeneticAlgorithm*>(algorithm3.get());
BOOST_REQUIRE(classicAlgorithm != nullptr);
BOOST_TEST(classicAlgorithm->options().elitePoolSize == m_options.classicElitePoolSize);
BOOST_TEST(classicAlgorithm->options().crossoverChance == m_options.classicCrossoverChance);
BOOST_TEST(classicAlgorithm->options().mutationChance == m_options.classicMutationChance);
BOOST_TEST(classicAlgorithm->options().deletionChance == m_options.classicDeletionChance);
BOOST_TEST(classicAlgorithm->options().additionChance == m_options.classicAdditionChance);
}
BOOST_FIXTURE_TEST_CASE(build_should_set_random_algorithm_elite_pool_size_based_on_population_size_if_not_specified, GeneticAlgorithmFactoryFixture)
+80
View File
@@ -48,6 +48,14 @@ namespace solidity::phaser::test
class PopulationFixture
{
protected:
static ChromosomePair twoStepSwap(Chromosome const& _chromosome1, Chromosome const& _chromosome2)
{
return ChromosomePair{
Chromosome(vector<string>{_chromosome1.optimisationSteps()[0], _chromosome2.optimisationSteps()[1]}),
Chromosome(vector<string>{_chromosome2.optimisationSteps()[0], _chromosome1.optimisationSteps()[1]}),
};
}
shared_ptr<FitnessMetric> m_fitnessMetric = make_shared<ChromosomeLengthMetric>();
};
@@ -104,6 +112,23 @@ BOOST_FIXTURE_TEST_CASE(constructor_should_copy_chromosomes_compute_fitness_and_
BOOST_TEST(individuals[2].chromosome == chromosomes[1]);
}
BOOST_FIXTURE_TEST_CASE(constructor_should_accept_individuals_without_recalculating_fitness, PopulationFixture)
{
vector<Individual> customIndividuals = {
Individual(Chromosome("aaaccc"), 20),
Individual(Chromosome("aaa"), 10),
Individual(Chromosome("aaaf"), 30),
};
assert(customIndividuals[0].fitness != m_fitnessMetric->evaluate(customIndividuals[0].chromosome));
assert(customIndividuals[1].fitness != m_fitnessMetric->evaluate(customIndividuals[1].chromosome));
assert(customIndividuals[2].fitness != m_fitnessMetric->evaluate(customIndividuals[2].chromosome));
Population population(m_fitnessMetric, customIndividuals);
vector<Individual> expectedIndividuals{customIndividuals[1], customIndividuals[0], customIndividuals[2]};
BOOST_TEST(population.individuals() == expectedIndividuals);
}
BOOST_FIXTURE_TEST_CASE(makeRandom_should_get_chromosome_lengths_from_specified_generator, PopulationFixture)
{
size_t chromosomeCount = 30;
@@ -292,6 +317,61 @@ BOOST_FIXTURE_TEST_CASE(crossover_should_return_empty_population_if_selection_is
BOOST_TEST(population.crossover(selection, fixedPointCrossover(0.5)).individuals().empty());
}
BOOST_FIXTURE_TEST_CASE(symmetricCrossoverWithRemainder_should_return_crossed_population_and_remainder, PopulationFixture)
{
Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")});
PairMosaicSelection selection({{2, 1}}, 0.25);
assert(selection.materialise(population.individuals().size()) == (vector<tuple<size_t, size_t>>{{2, 1}}));
Population expectedCrossedPopulation(m_fitnessMetric, {Chromosome("gc"), Chromosome("cg")});
Population expectedRemainder(m_fitnessMetric, {Chromosome("aa"), Chromosome("hh")});
BOOST_TEST(
population.symmetricCrossoverWithRemainder(selection, twoStepSwap) ==
(tuple<Population, Population>{expectedCrossedPopulation, expectedRemainder})
);
}
BOOST_FIXTURE_TEST_CASE(symmetricCrossoverWithRemainder_should_allow_crossing_the_same_individual_multiple_times, PopulationFixture)
{
Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")});
PairMosaicSelection selection({{0, 0}, {2, 1}}, 1.0);
assert(selection.materialise(population.individuals().size()) == (vector<tuple<size_t, size_t>>{{0, 0}, {2, 1}, {0, 0}, {2, 1}}));
Population expectedCrossedPopulation(m_fitnessMetric, {
Chromosome("aa"), Chromosome("aa"),
Chromosome("aa"), Chromosome("aa"),
Chromosome("gc"), Chromosome("cg"),
Chromosome("gc"), Chromosome("cg"),
});
Population expectedRemainder(m_fitnessMetric, {Chromosome("hh")});
BOOST_TEST(
population.symmetricCrossoverWithRemainder(selection, twoStepSwap) ==
(tuple<Population, Population>{expectedCrossedPopulation, expectedRemainder})
);
}
BOOST_FIXTURE_TEST_CASE(symmetricCrossoverWithRemainder_should_return_empty_population_if_selection_is_empty, PopulationFixture)
{
Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc")});
PairMosaicSelection selection({}, 0.0);
assert(selection.materialise(population.individuals().size()).empty());
BOOST_TEST(
population.symmetricCrossoverWithRemainder(selection, twoStepSwap) ==
(tuple<Population, Population>{Population(m_fitnessMetric), population})
);
}
BOOST_FIXTURE_TEST_CASE(combine_should_add_two_populations_from_a_pair, PopulationFixture)
{
Population population1(m_fitnessMetric, {Chromosome("aa"), Chromosome("hh")});
Population population2(m_fitnessMetric, {Chromosome("gg"), Chromosome("cc")});
BOOST_TEST(Population::combine({population1, population2}) == population1 + population2);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
+56
View File
@@ -25,9 +25,11 @@
#include <boost/test/unit_test.hpp>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;
using namespace solidity::util;
namespace solidity::phaser::test
{
@@ -199,6 +201,60 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_no_indices_if_collection_is_empty
BOOST_TEST(RandomSelection(2.0).materialise(0).empty());
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(RandomSubsetTest)
BOOST_AUTO_TEST_CASE(materialise_should_return_random_values_with_equal_probabilities)
{
constexpr int collectionSize = 1000;
constexpr double selectionChance = 0.7;
constexpr double relativeTolerance = 0.001;
constexpr double expectedValue = selectionChance;
constexpr double variance = selectionChance * (1 - selectionChance);
SimulationRNG::reset(1);
auto indices = convertContainer<set<size_t>>(RandomSubset(selectionChance).materialise(collectionSize));
vector<double> bernoulliTrials(collectionSize);
for (size_t i = 0; i < collectionSize; ++i)
bernoulliTrials[i] = indices.count(i);
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_collection_indices)
{
const size_t collectionSize = 200;
vector<size_t> indices = RandomSubset(0.5).materialise(collectionSize);
BOOST_TEST(all_of(indices.begin(), indices.end(), [&](auto const& index){ return index <= collectionSize; }));
}
BOOST_AUTO_TEST_CASE(materialise_should_return_indices_in_the_same_order_they_are_in_the_container)
{
const size_t collectionSize = 200;
vector<size_t> indices = RandomSubset(0.5).materialise(collectionSize);
for (size_t i = 1; i < indices.size(); ++i)
BOOST_TEST(indices[i - 1] < indices[i]);
}
BOOST_AUTO_TEST_CASE(materialise_should_return_no_indices_if_collection_is_empty)
{
BOOST_TEST(RandomSubset(0.5).materialise(0).empty());
}
BOOST_AUTO_TEST_CASE(materialise_should_return_no_indices_if_selection_chance_is_zero)
{
BOOST_TEST(RandomSubset(0.0).materialise(10).empty());
}
BOOST_AUTO_TEST_CASE(materialise_should_return_all_indices_if_selection_chance_is_one)
{
BOOST_TEST(RandomSubset(1.0).materialise(10).size() == 10);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
+27
View File
@@ -33,12 +33,39 @@
#include <tools/yulPhaser/Mutations.h>
#include <tools/yulPhaser/Population.h>
#include <boost/test/tools/detail/print_helper.hpp>
#include <cassert>
#include <functional>
#include <map>
#include <string>
#include <tuple>
#include <vector>
// OPERATORS FOR BOOST::TEST
/// Output operator for arbitrary two-element tuples.
/// Necessary to make BOOST_TEST() work with such tuples.
template<typename T1, typename T2>
std::ostream& operator<<(std::ostream& _output, std::tuple<T1, T2> const& _tuple)
{
_output << "(" << std::get<0>(_tuple) << ", " << std::get<1>(_tuple) << ")";
return _output;
}
namespace boost::test_tools::tt_detail
{
// Boost won't find find the << operator unless we put it in the std namespace which is illegal.
// The recommended solution is to overload print_log_value<> struct and make it use our global operator.
template<typename T1,typename T2>
struct print_log_value<std::tuple<T1, T2>>
{
void operator()(std::ostream& _output, std::tuple<T1, T2> const& _tuple) { ::operator<<(_output, _tuple); }
};
}
namespace solidity::phaser::test
{