diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index eb4b8241c..da6995e38 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -158,6 +158,7 @@ set(yul_phaser_sources ../tools/yulPhaser/Chromosome.cpp ../tools/yulPhaser/FitnessMetrics.cpp ../tools/yulPhaser/GeneticAlgorithms.cpp + ../tools/yulPhaser/Mutations.cpp ../tools/yulPhaser/PairSelections.cpp ../tools/yulPhaser/Population.cpp ../tools/yulPhaser/Program.cpp diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp index 98532c9dc..3e228f627 100644 --- a/test/yulPhaser/Population.cpp +++ b/test/yulPhaser/Population.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include +#include #include #include #include @@ -226,6 +228,70 @@ BOOST_FIXTURE_TEST_CASE(select_should_return_empty_population_if_selection_is_em BOOST_TEST(population.select(selection).individuals().empty()); } +BOOST_FIXTURE_TEST_CASE(mutate_should_return_population_containing_individuals_indicated_by_selection_with_mutation_applied, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); + RangeSelection selection(0.25, 0.75); + assert(selection.materialise(population.individuals().size()) == (vector{1, 2})); + + Population expectedPopulation(m_fitnessMetric, {Chromosome("fc"), Chromosome("fg")}); + + BOOST_TEST(population.mutate(selection, geneSubstitution(0, BlockFlattener::name)) == expectedPopulation); +} + +BOOST_FIXTURE_TEST_CASE(mutate_should_include_duplicates_if_selection_contains_duplicates, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")}); + RangeSelection selection(0.0, 1.0); + assert(selection.materialise(population.individuals().size()) == (vector{0, 1})); + + BOOST_TEST( + population.mutate(selection, geneSubstitution(0, BlockFlattener::name)) == + Population(m_fitnessMetric, {Chromosome("fa"), Chromosome("fa")}) + ); +} + +BOOST_FIXTURE_TEST_CASE(mutate_should_return_empty_population_if_selection_is_empty, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc")}); + RangeSelection selection(0.0, 0.0); + assert(selection.materialise(population.individuals().size()).empty()); + + BOOST_TEST(population.mutate(selection, geneSubstitution(0, BlockFlattener::name)).individuals().empty()); +} + +BOOST_FIXTURE_TEST_CASE(crossover_should_return_population_containing_individuals_indicated_by_selection_with_crossover_applied, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); + PairMosaicSelection selection({{0, 1}, {2, 1}}, 0.5); + assert(selection.materialise(population.individuals().size()) == (vector>{{0, 1}, {2, 1}})); + + Population expectedPopulation(m_fitnessMetric, {Chromosome("ac"), Chromosome("ca"), Chromosome("cg"), Chromosome("gc")}); + + BOOST_TEST(population.crossover(selection, fixedPointCrossover(0.5)) == expectedPopulation); +} + +BOOST_FIXTURE_TEST_CASE(crossover_should_include_duplicates_if_selection_contains_duplicates, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")}); + PairMosaicSelection selection({{0, 0}, {1, 1}}, 1.0); + assert(selection.materialise(population.individuals().size()) == (vector>{{0, 0}, {1, 1}})); + + BOOST_TEST( + population.crossover(selection, fixedPointCrossover(0.5)) == + Population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa"), Chromosome("aa"), Chromosome("aa")}) + ); +} + +BOOST_FIXTURE_TEST_CASE(crossover_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.crossover(selection, fixedPointCrossover(0.5)).individuals().empty()); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index b39f5dad5..61702634f 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -17,6 +17,7 @@ #include +#include #include #include @@ -93,6 +94,31 @@ Population Population::select(Selection const& _selection) const return Population(m_fitnessMetric, selectedIndividuals); } +Population Population::mutate(Selection const& _selection, function _mutation) const +{ + vector mutatedIndividuals; + for (size_t i: _selection.materialise(m_individuals.size())) + mutatedIndividuals.emplace_back(_mutation(m_individuals[i].chromosome), *m_fitnessMetric); + + return Population(m_fitnessMetric, mutatedIndividuals); +} + +Population Population::crossover(PairSelection const& _selection, function _crossover) const +{ + vector crossedIndividuals; + for (auto const& [i, j]: _selection.materialise(m_individuals.size())) + { + auto [childChromosome1, childChromosome2] = _crossover( + m_individuals[i].chromosome, + m_individuals[j].chromosome + ); + crossedIndividuals.emplace_back(move(childChromosome1), *m_fitnessMetric); + crossedIndividuals.emplace_back(move(childChromosome2), *m_fitnessMetric); + } + + return Population(m_fitnessMetric, crossedIndividuals); +} + Population operator+(Population _a, Population _b) { // This operator is meant to be used only with populations sharing the same metric (and, to make diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index f4f42346d..29c82efed 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -39,6 +40,7 @@ solidity::phaser::Population operator+(solidity::phaser::Population _a, solidity namespace solidity::phaser { +class PairSelection; class Selection; /** @@ -104,6 +106,8 @@ public: ); Population select(Selection const& _selection) const; + Population mutate(Selection const& _selection, std::function _mutation) const; + Population crossover(PairSelection const& _selection, std::function _crossover) const; friend Population (::operator+)(Population _a, Population _b); std::shared_ptr fitnessMetric() const { return m_fitnessMetric; }