diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 7708e85ba..c03cf9369 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -68,10 +68,77 @@ ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) return _outputStream; } -namespace +GeneticAlgorithmFactory::Options GeneticAlgorithmFactory::Options::fromCommandLine(po::variables_map const& _arguments) { + return { + _arguments["algorithm"].as(), + }; +} -CharStream loadSource(string const& _sourcePath) +unique_ptr GeneticAlgorithmFactory::build( + Options const& _options, + size_t _populationSize, + size_t _minChromosomeLength, + size_t _maxChromosomeLength +) +{ + assert(_populationSize > 0); + + switch (_options.algorithm) + { + case Algorithm::Random: + return make_unique(RandomAlgorithm::Options{ + /* elitePoolSize = */ 1.0 / _populationSize, + /* minChromosomeLength = */ _minChromosomeLength, + /* maxChromosomeLength = */ _maxChromosomeLength, + }); + case Algorithm::GEWEP: + return make_unique(GenerationalElitistWithExclusivePools::Options{ + /* mutationPoolSize = */ 0.25, + /* crossoverPoolSize = */ 0.25, + /* randomisationChance = */ 0.9, + /* deletionVsAdditionChance = */ 0.5, + /* percentGenesToRandomise = */ 1.0 / _maxChromosomeLength, + /* percentGenesToAddOrDelete = */ 1.0 / _maxChromosomeLength, + }); + default: + assertThrow(false, solidity::util::Exception, "Invalid Algorithm value."); + } +} + +unique_ptr FitnessMetricFactory::build( + Program _program +) +{ + return make_unique(move(_program), RepetitionCount); +} + +Population PopulationFactory::build( + shared_ptr _fitnessMetric +) +{ + return Population::makeRandom( + move(_fitnessMetric), + PopulationSize, + MinChromosomeLength, + MaxChromosomeLength + ); +} + +ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["input-file"].as(), + }; +} + +Program ProgramFactory::build(Options const& _options) +{ + CharStream sourceCode = loadSource(_options.inputFile); + return Program::load(sourceCode); +} + +CharStream ProgramFactory::loadSource(string const& _sourcePath) { assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); @@ -79,8 +146,6 @@ CharStream loadSource(string const& _sourcePath) return CharStream(sourceCode, _sourcePath); } -} - int Phaser::main(int _argc, char** _argv) { CommandLineParsingResult parsingResult = parseCommandLine(_argc, _argv); @@ -89,10 +154,7 @@ int Phaser::main(int _argc, char** _argv) initialiseRNG(parsingResult.arguments); - runAlgorithm( - parsingResult.arguments["input-file"].as(), - parsingResult.arguments["algorithm"].as() - ); + runAlgorithm(parsingResult.arguments); return 0; } @@ -167,46 +229,22 @@ void Phaser::initialiseRNG(po::variables_map const& _arguments) cout << "Random seed: " << seed << endl; } -void Phaser::runAlgorithm(string const& _sourcePath, Algorithm _algorithm) +void Phaser::runAlgorithm(po::variables_map const& _arguments) { - constexpr size_t populationSize = 20; - constexpr size_t minChromosomeLength = 12; - constexpr size_t maxChromosomeLength = 30; + auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments); + auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments); - CharStream sourceCode = loadSource(_sourcePath); - shared_ptr fitnessMetric = make_shared(Program::load(sourceCode), 5); - auto population = Population::makeRandom( - fitnessMetric, - populationSize, - minChromosomeLength, - maxChromosomeLength + Program program = ProgramFactory::build(programOptions); + unique_ptr fitnessMetric = FitnessMetricFactory::build(move(program)); + Population population = PopulationFactory::build(move(fitnessMetric)); + + unique_ptr geneticAlgorithm = GeneticAlgorithmFactory::build( + algorithmOptions, + population.individuals().size(), + PopulationFactory::MinChromosomeLength, + PopulationFactory::MaxChromosomeLength ); AlgorithmRunner algorithmRunner(population, AlgorithmRunner::Options{}, cout); - switch (_algorithm) - { - case Algorithm::Random: - { - RandomAlgorithm algorithm({ - /* elitePoolSize = */ 1.0 / populationSize, - /* minChromosomeLength = */ minChromosomeLength, - /* maxChromosomeLength = */ maxChromosomeLength, - }); - algorithmRunner.run(algorithm); - break; - } - case Algorithm::GEWEP: - { - 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; - } - } + algorithmRunner.run(*geneticAlgorithm); } diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index 07b7426ab..1afd0836f 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -15,7 +15,8 @@ along with solidity. If not, see . */ /** - * Contains the main class that controls yul-phaser based on command-line parameters. + * Contains the main class that controls yul-phaser based on command-line parameters and + * associated factories for building instances of phaser's components. */ #pragma once @@ -23,12 +24,25 @@ #include #include +#include #include #include +namespace solidity::langutil +{ + +class CharStream; + +} + namespace solidity::phaser { +class FitnessMetric; +class GeneticAlgorithm; +class Population; +class Program; + enum class Algorithm { Random, @@ -38,10 +52,78 @@ enum class Algorithm std::istream& operator>>(std::istream& _inputStream, solidity::phaser::Algorithm& _algorithm); std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorithm _algorithm); +/** + * Builds and validates instances of @a GeneticAlgorithm and its derived classes. + */ +class GeneticAlgorithmFactory +{ +public: + struct Options + { + Algorithm algorithm; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::unique_ptr build( + Options const& _options, + size_t _populationSize, + size_t _minChromosomeLength, + size_t _maxChromosomeLength + ); +}; + +/** + * Builds and validates instances of @a FitnessMetric and its derived classes. + */ +class FitnessMetricFactory +{ +public: + static constexpr size_t RepetitionCount = 5; + + static std::unique_ptr build( + Program _program + ); +}; + +/** + * Builds and validates instances of @a Population. + */ +class PopulationFactory +{ +public: + static constexpr size_t PopulationSize = 20; + static constexpr size_t MinChromosomeLength = 12; + static constexpr size_t MaxChromosomeLength = 30; + + static Population build( + std::shared_ptr _fitnessMetric + ); +}; + +/** + * Builds and validates instances of @a Program. + */ +class ProgramFactory +{ +public: + struct Options + { + std::string inputFile; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static Program build(Options const& _options); + +private: + static langutil::CharStream loadSource(std::string const& _sourcePath); +}; + /** * 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 feeding them into @a AlgorithmRunner. + * creating instances of main components using factories and feeding them into @a AlgorithmRunner. */ class Phaser { @@ -58,7 +140,7 @@ private: static CommandLineParsingResult parseCommandLine(int _argc, char** _argv); static void initialiseRNG(boost::program_options::variables_map const& _arguments); - static void runAlgorithm(std::string const& _sourcePath, Algorithm _algorithm); + static void runAlgorithm(boost::program_options::variables_map const& _arguments); }; }