From b8244f6a431ff39439e2cf2996a66d4cac9c1059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 21 Feb 2020 16:10:07 +0100 Subject: [PATCH] [yul-phaser] Extract the code that controls execution of algorithm rounds from GeneticAlgorithm into AlgorithmRunner --- test/CMakeLists.txt | 2 + test/yulPhaser/AlgorithmRunner.cpp | 93 +++++++++++++++++++++ test/yulPhaser/GeneticAlgorithms.cpp | 112 ++++++++------------------ tools/CMakeLists.txt | 2 + tools/yulPhaser/AlgorithmRunner.cpp | 32 ++++++++ tools/yulPhaser/AlgorithmRunner.h | 58 +++++++++++++ tools/yulPhaser/GeneticAlgorithms.cpp | 31 +++---- tools/yulPhaser/GeneticAlgorithms.h | 46 ++--------- tools/yulPhaser/Phaser.cpp | 40 ++++----- tools/yulPhaser/Phaser.h | 2 +- 10 files changed, 255 insertions(+), 163 deletions(-) create mode 100644 test/yulPhaser/AlgorithmRunner.cpp create mode 100644 tools/yulPhaser/AlgorithmRunner.cpp create mode 100644 tools/yulPhaser/AlgorithmRunner.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index da6995e38..d78711634 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -144,6 +144,7 @@ set(yul_phaser_sources yulPhaser/CommonTest.cpp yulPhaser/Chromosome.cpp yulPhaser/FitnessMetrics.cpp + yulPhaser/AlgorithmRunner.cpp yulPhaser/GeneticAlgorithms.cpp yulPhaser/Mutations.cpp yulPhaser/PairSelections.cpp @@ -155,6 +156,7 @@ set(yul_phaser_sources # FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries(). # My current workaround is just to include its source files here but this introduces # unnecessary duplication. Create a library or find a way to reuse the list in both places. + ../tools/yulPhaser/AlgorithmRunner.cpp ../tools/yulPhaser/Chromosome.cpp ../tools/yulPhaser/FitnessMetrics.cpp ../tools/yulPhaser/GeneticAlgorithms.cpp diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp new file mode 100644 index 000000000..16a4dd893 --- /dev/null +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -0,0 +1,93 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace boost::unit_test::framework; +using namespace boost::test_tools; +using namespace solidity::util; + +namespace solidity::phaser::test +{ + +class DummyAlgorithm: public GeneticAlgorithm +{ +public: + using GeneticAlgorithm::GeneticAlgorithm; + Population runNextRound(Population _population) override + { + ++m_currentRound; + return _population; + } + + size_t m_currentRound = 0; +}; + +class AlgorithmRunnerFixture +{ +protected: + shared_ptr m_fitnessMetric = make_shared(); + output_test_stream m_output; +}; + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest) + +BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRunnerFixture) +{ + AlgorithmRunner runner(Population(m_fitnessMetric), m_output); + DummyAlgorithm algorithm; + + BOOST_TEST(algorithm.m_currentRound == 0); + runner.run(algorithm, 10); + BOOST_TEST(algorithm.m_currentRound == 10); + runner.run(algorithm, 3); + BOOST_TEST(algorithm.m_currentRound == 13); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, AlgorithmRunnerFixture) +{ + // run() is allowed to print more but should at least print the first one + + AlgorithmRunner runner( + // NOTE: Chromosomes chosen so that they're not substrings of each other and are not + // words likely to appear in the output in normal circumstances. + Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}), + m_output + ); + + DummyAlgorithm algorithm; + + BOOST_TEST(m_output.is_empty()); + runner.run(algorithm, 1); + BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(runner.population().individuals()[0].chromosome)) == 1); + runner.run(algorithm, 3); + BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(runner.population().individuals()[0].chromosome)) == 4); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/GeneticAlgorithms.cpp b/test/yulPhaser/GeneticAlgorithms.cpp index 0d32e9c1d..13a311669 100644 --- a/test/yulPhaser/GeneticAlgorithms.cpp +++ b/test/yulPhaser/GeneticAlgorithms.cpp @@ -20,14 +20,10 @@ #include #include #include -#include - -#include #include #include -#include #include #include @@ -35,102 +31,58 @@ using namespace std; using namespace boost::unit_test::framework; using namespace boost::test_tools; -using namespace solidity::langutil; -using namespace solidity::util; namespace solidity::phaser::test { -class DummyAlgorithm: public GeneticAlgorithm -{ -public: - using GeneticAlgorithm::GeneticAlgorithm; - void runNextRound() override { ++m_currentRound; } - - size_t m_currentRound = 0; -}; - class GeneticAlgorithmFixture { protected: shared_ptr m_fitnessMetric = make_shared(); - output_test_stream m_output; }; BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(GeneticAlgorithmsTest) -BOOST_AUTO_TEST_SUITE(GeneticAlgorithmTest) - -BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, GeneticAlgorithmFixture) -{ - DummyAlgorithm algorithm(Population(m_fitnessMetric), m_output); - - BOOST_TEST(algorithm.m_currentRound == 0); - algorithm.run(10); - BOOST_TEST(algorithm.m_currentRound == 10); - algorithm.run(3); - BOOST_TEST(algorithm.m_currentRound == 13); -} - -BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, GeneticAlgorithmFixture) -{ - // run() is allowed to print more but should at least print the first one - - DummyAlgorithm algorithm( - // NOTE: Chromosomes chosen so that they're not substrings of each other and are not - // words likely to appear in the output in normal circumstances. - Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}), - m_output - ); - - BOOST_TEST(m_output.is_empty()); - algorithm.run(1); - BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(algorithm.population().individuals()[0].chromosome)) == 1); - algorithm.run(3); - BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(algorithm.population().individuals()[0].chromosome)) == 4); -} - -BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(RandomAlgorithmTest) BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_randomise_rest_of_population, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {0.5, 1, 1}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({0.5, 1, 1}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{1, 1, 1, 1, 3, 3, 3, 3})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{1, 1, 1, 1, 3, 3, 3, 3})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {0.5, 7, 7}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({0.5, 7, 7}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 7, 7, 7, 7})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 7, 7, 7, 7})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_replace_all_chromosomes_if_zero_size_elite, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {0.0, 1, 1}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({0.0, 1, 1}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{1, 1, 1, 1, 1, 1, 1, 1})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{1, 1, 1, 1, 1, 1, 1, 1})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_any_chromosomes_if_whole_population_is_the_elite, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {1.0, 1, 1}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({1.0, 1, 1}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 5, 5, 5, 5})); } BOOST_AUTO_TEST_SUITE_END() @@ -139,6 +91,7 @@ BOOST_AUTO_TEST_SUITE(GenerationalElitistWithExclusivePoolsTest) BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_regenerate_rest_of_population, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 6, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); GenerationalElitistWithExclusivePools::Options options = { /* mutationPoolSize = */ 0.2, @@ -148,17 +101,17 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_regenerate_rest_o /* percentGenesToRandomise = */ 0.0, /* percentGenesToAddOrDelete = */ 1.0, }; - GenerationalElitistWithExclusivePools algorithm(population, m_output, options); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); + GenerationalElitistWithExclusivePools algorithm(options); - algorithm.runNextRound(); + Population newPopulation = algorithm.runNextRound(population); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{0, 0, 3, 3, 3, 3, 3, 3, 3, 3})); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{0, 0, 3, 3, 3, 3, 3, 3, 3, 3})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 6, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); + assert(chromosomeLengths(population) == (vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); GenerationalElitistWithExclusivePools::Options options = { /* mutationPoolSize = */ 0.2, @@ -168,12 +121,11 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individ /* percentGenesToRandomise = */ 0.0, /* percentGenesToAddOrDelete = */ 1.0, }; - GenerationalElitistWithExclusivePools algorithm(population, m_output, options); - assert(chromosomeLengths(algorithm.population()) == (vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); + GenerationalElitistWithExclusivePools algorithm(options); - algorithm.runNextRound(); + Population newPopulation = algorithm.runNextRound(population); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 3, 3, 3, 3, 7, 7})); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 3, 3, 3, 3, 7, 7})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossover_pool_by_mutating_the_elite, GeneticAlgorithmFixture) @@ -188,13 +140,13 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove /* percentGenesToRandomise = */ 1.0, /* percentGenesToAddOrDelete = */ 1.0, }; - GenerationalElitistWithExclusivePools algorithm(population, m_output, options); + GenerationalElitistWithExclusivePools algorithm(options); SimulationRNG::reset(1); - algorithm.runNextRound(); + Population newPopulation = algorithm.runNextRound(population); BOOST_TEST(( - chromosomeLengths(algorithm.population()) == + chromosomeLengths(newPopulation) == vector{0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 11, 11} )); } @@ -205,6 +157,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove Population(m_fitnessMetric, {Chromosome("aa"), Chromosome("ff")}) + Population::makeRandom(m_fitnessMetric, 8, 6, 6) ); + assert((chromosomeLengths(population) == vector{2, 2, 6, 6, 6, 6, 6, 6, 6, 6})); GenerationalElitistWithExclusivePools::Options options = { /* mutationPoolSize = */ 0.0, @@ -214,14 +167,13 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove /* percentGenesToRandomise = */ 0.0, /* percentGenesToAddOrDelete = */ 0.0, }; - GenerationalElitistWithExclusivePools algorithm(population, m_output, options); - assert((chromosomeLengths(algorithm.population()) == vector{2, 2, 6, 6, 6, 6, 6, 6, 6, 6})); + GenerationalElitistWithExclusivePools algorithm(options); SimulationRNG::reset(1); - algorithm.runNextRound(); + Population newPopulation = algorithm.runNextRound(population); - vector const& newIndividuals = algorithm.population().individuals(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{2, 2, 2, 2, 2, 2, 2, 2, 2, 2})); + vector const& newIndividuals = newPopulation.individuals(); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{2, 2, 2, 2, 2, 2, 2, 2, 2, 2})); for (auto& individual: newIndividuals) BOOST_TEST(( individual.chromosome == Chromosome("aa") || diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index f281439bf..58867e5b9 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -15,6 +15,8 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") add_executable(yul-phaser yulPhaser/main.cpp + yulPhaser/AlgorithmRunner.h + yulPhaser/AlgorithmRunner.cpp yulPhaser/Phaser.h yulPhaser/Phaser.cpp yulPhaser/GeneticAlgorithms.h diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp new file mode 100644 index 000000000..881f5b46e --- /dev/null +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -0,0 +1,32 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +using namespace std; +using namespace solidity::phaser; + +void AlgorithmRunner::run(GeneticAlgorithm& _algorithm, optional _numRounds) +{ + for (size_t round = 0; !_numRounds.has_value() || round < _numRounds.value(); ++round) + { + m_population = _algorithm.runNextRound(m_population); + + m_outputStream << "---------- ROUND " << round << " ----------" << endl; + m_outputStream << m_population; + } +} diff --git a/tools/yulPhaser/AlgorithmRunner.h b/tools/yulPhaser/AlgorithmRunner.h new file mode 100644 index 000000000..ff436d73b --- /dev/null +++ b/tools/yulPhaser/AlgorithmRunner.h @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Contains the implementation of a class that manages the execution of a genetic algorithm. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace solidity::phaser +{ + +/** + * Manages a population and executes a genetic algorithm on it. It's independent of the + * implementation details of a specific algorithm which is pluggable via @a GeneticAlgorithm class. + * + * The class is also responsible for providing text feedback on the execution of the algorithm + * to the associated output stream. + */ +class AlgorithmRunner +{ +public: + AlgorithmRunner( + Population _initialPopulation, + std::ostream& _outputStream + ): + m_population(std::move(_initialPopulation)), + m_outputStream(_outputStream) {} + + void run(GeneticAlgorithm& _algorithm, std::optional _numRounds = std::nullopt); + + Population const& population() const { return m_population; } + +private: + Population m_population; + std::ostream& m_outputStream; +}; + +} diff --git a/tools/yulPhaser/GeneticAlgorithms.cpp b/tools/yulPhaser/GeneticAlgorithms.cpp index fa37ac5e3..432f3fe38 100644 --- a/tools/yulPhaser/GeneticAlgorithms.cpp +++ b/tools/yulPhaser/GeneticAlgorithms.cpp @@ -23,42 +23,31 @@ using namespace std; using namespace solidity::phaser; -void GeneticAlgorithm::run(optional _numRounds) -{ - for (size_t round = 0; !_numRounds.has_value() || round < _numRounds.value(); ++round) - { - runNextRound(); - - m_outputStream << "---------- ROUND " << round << " ----------" << endl; - m_outputStream << m_population; - } -} - -void RandomAlgorithm::runNextRound() +Population RandomAlgorithm::runNextRound(Population _population) { RangeSelection elite(0.0, m_options.elitePoolSize); - Population elitePopulation = m_population.select(elite); - size_t replacementCount = m_population.individuals().size() - elitePopulation.individuals().size(); + Population elitePopulation = _population.select(elite); + size_t replacementCount = _population.individuals().size() - elitePopulation.individuals().size(); - m_population = + return move(elitePopulation) + Population::makeRandom( - m_population.fitnessMetric(), + _population.fitnessMetric(), replacementCount, m_options.minChromosomeLength, m_options.maxChromosomeLength ); } -void GenerationalElitistWithExclusivePools::runNextRound() +Population GenerationalElitistWithExclusivePools::runNextRound(Population _population) { double elitePoolSize = 1.0 - (m_options.mutationPoolSize + m_options.crossoverPoolSize); RangeSelection elite(0.0, elitePoolSize); - m_population = - m_population.select(elite) + - m_population.select(elite).mutate( + return + _population.select(elite) + + _population.select(elite).mutate( RandomSelection(m_options.mutationPoolSize / elitePoolSize), alternativeMutations( m_options.randomisationChance, @@ -70,7 +59,7 @@ void GenerationalElitistWithExclusivePools::runNextRound() ) ) ) + - m_population.select(elite).crossover( + _population.select(elite).crossover( RandomPairSelection(m_options.crossoverPoolSize / elitePoolSize), randomPointCrossover() ); diff --git a/tools/yulPhaser/GeneticAlgorithms.h b/tools/yulPhaser/GeneticAlgorithms.h index bab475c4e..b9ccb4302 100644 --- a/tools/yulPhaser/GeneticAlgorithms.h +++ b/tools/yulPhaser/GeneticAlgorithms.h @@ -22,45 +22,25 @@ #include -#include -#include - namespace solidity::phaser { /** * Abstract base class for genetic algorithms. - * - * The main feature is the @a run() method that executes the algorithm, updating the internal - * population during each round and printing the results to the stream provided to the constructor. - * - * Derived classes can provide specific methods for updating the population by implementing - * the @a runNextRound() method. + * The main feature is the @a runNextRound() method that executes one round of the algorithm, + * on the supplied population. */ class GeneticAlgorithm { public: - GeneticAlgorithm(Population _initialPopulation, std::ostream& _outputStream): - m_population(std::move(_initialPopulation)), - m_outputStream(_outputStream) {} - + GeneticAlgorithm() {} GeneticAlgorithm(GeneticAlgorithm const&) = delete; GeneticAlgorithm& operator=(GeneticAlgorithm const&) = delete; virtual ~GeneticAlgorithm() = default; - Population const& population() const { return m_population; } - - void run(std::optional _numRounds = std::nullopt); - /// The method that actually implements the algorithm. Should use @a m_population as input and /// replace it with the updated state after the round. - virtual void runNextRound() = 0; - -protected: - Population m_population; - -private: - std::ostream& m_outputStream; + virtual Population runNextRound(Population _population) = 0; }; /** @@ -95,18 +75,13 @@ public: } }; - explicit RandomAlgorithm( - Population _initialPopulation, - std::ostream& _outputStream, - Options const& _options - ): - GeneticAlgorithm(_initialPopulation, _outputStream), + explicit RandomAlgorithm(Options const& _options): m_options(_options) { assert(_options.isValid()); } - void runNextRound() override; + Population runNextRound(Population _population) override; private: Options m_options; @@ -148,18 +123,13 @@ public: } }; - GenerationalElitistWithExclusivePools( - Population _initialPopulation, - std::ostream& _outputStream, - Options const& _options - ): - GeneticAlgorithm(_initialPopulation, _outputStream), + GenerationalElitistWithExclusivePools(Options const& _options): m_options(_options) { assert(_options.isValid()); } - void runNextRound() override; + Population runNextRound(Population _population) override; private: Options m_options; diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index a1a955d57..0a3e7f66e 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -181,37 +182,30 @@ void Phaser::runAlgorithm(string const& _sourcePath, Algorithm _algorithm) maxChromosomeLength ); + AlgorithmRunner algorithmRunner(population, cout); switch (_algorithm) { case Algorithm::Random: { - RandomAlgorithm( - population, - cout, - { - /* elitePoolSize = */ 1.0 / populationSize, - /* minChromosomeLength = */ minChromosomeLength, - /* maxChromosomeLength = */ maxChromosomeLength, - } - ).run(); - + RandomAlgorithm algorithm({ + /* elitePoolSize = */ 1.0 / populationSize, + /* minChromosomeLength = */ minChromosomeLength, + /* maxChromosomeLength = */ maxChromosomeLength, + }); + algorithmRunner.run(algorithm); break; } case Algorithm::GEWEP: { - GenerationalElitistWithExclusivePools( - population, - cout, - { - /* mutationPoolSize = */ 0.25, - /* crossoverPoolSize = */ 0.25, - /* randomisationChance = */ 0.9, - /* deletionVsAdditionChance = */ 0.5, - /* percentGenesToRandomise = */ 1.0 / maxChromosomeLength, - /* percentGenesToAddOrDelete = */ 1.0 / maxChromosomeLength, - } - ).run(); - + GenerationalElitistWithExclusivePools algorithm({ + /* mutationPoolSize = */ 0.25, + /* crossoverPoolSize = */ 0.25, + /* randomisationChance = */ 0.9, + /* deletionVsAdditionChance = */ 0.5, + /* percentGenesToRandomise = */ 1.0 / maxChromosomeLength, + /* percentGenesToAddOrDelete = */ 1.0 / maxChromosomeLength, + }); + algorithmRunner.run(algorithm); break; } } diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index 65a82eba4..07b7426ab 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -41,7 +41,7 @@ std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorith /** * Main class that controls yul-phaser based on command-line parameters. The class is responsible * for command-line parsing, initialisation of global objects (like the random number generator), - * creating instances of main components and running the genetic algorithm. + * creating instances of main components and feeding them into @a AlgorithmRunner. */ class Phaser {