diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp index 0cb0fe29f..12555845c 100644 --- a/test/yulPhaser/Population.cpp +++ b/test/yulPhaser/Population.cpp @@ -48,6 +48,14 @@ namespace solidity::phaser::test class PopulationFixture { protected: + static ChromosomePair twoStepSwap(Chromosome const& _chromosome1, Chromosome const& _chromosome2) + { + return ChromosomePair{ + Chromosome(vector{_chromosome1.optimisationSteps()[0], _chromosome2.optimisationSteps()[1]}), + Chromosome(vector{_chromosome2.optimisationSteps()[0], _chromosome1.optimisationSteps()[1]}), + }; + } + shared_ptr m_fitnessMetric = make_shared(); }; @@ -309,6 +317,53 @@ 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>{{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{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>{{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{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(m_fitnessMetric), population}) + ); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index b336daf73..acdae1eb7 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -117,6 +117,37 @@ Population Population::crossover(PairSelection const& _selection, function Population::symmetricCrossoverWithRemainder( + PairSelection const& _selection, + function _symmetricCrossover +) const +{ + vector indexSelected(m_individuals.size(), false); + + vector crossedIndividuals; + for (auto const& [i, j]: _selection.materialise(m_individuals.size())) + { + auto children = _symmetricCrossover( + m_individuals[i].chromosome, + m_individuals[j].chromosome + ); + crossedIndividuals.emplace_back(move(get<0>(children)), *m_fitnessMetric); + crossedIndividuals.emplace_back(move(get<1>(children)), *m_fitnessMetric); + indexSelected[i] = true; + indexSelected[j] = true; + } + + vector remainder; + for (size_t i = 0; i < indexSelected.size(); ++i) + if (!indexSelected[i]) + remainder.emplace_back(m_individuals[i]); + + return { + Population(m_fitnessMetric, crossedIndividuals), + Population(m_fitnessMetric, remainder), + }; +} + namespace solidity::phaser { diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index cf82a5aa8..2d79dfef2 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -100,6 +100,10 @@ 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; + std::tuple symmetricCrossoverWithRemainder( + PairSelection const& _selection, + std::function _symmetricCrossover + ) const; friend Population operator+(Population _a, Population _b);