From bdc4d1ccd19561a7a658794075ca068ff4034fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 4 Mar 2020 19:46:18 +0100 Subject: [PATCH 01/10] [yul-phaser] ProgramSize metric: Refactor repeating parts of test code - Also replace test fixture constructor with direct member initialisation --- test/yulPhaser/FitnessMetrics.cpp | 62 ++++++++++++++----------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index ecb628c6b..e7a389be7 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -34,10 +34,6 @@ namespace solidity::phaser::test class FitnessMetricFixture { protected: - FitnessMetricFixture(): - m_sourceStream(SampleSourceCode, ""), - m_program(get(Program::load(m_sourceStream))) {} - static constexpr char SampleSourceCode[] = "{\n" " function foo() -> result\n" @@ -52,8 +48,22 @@ protected: " mstore(foo(), bar())\n" "}\n"; - CharStream m_sourceStream; - Program m_program; + Program optimisedProgram(Program _program) const + { + [[maybe_unused]] size_t originalSize = _program.codeSize(); + Program result = move(_program); + result.optimise(m_chromosome.optimisationSteps()); + + // Make sure that the program and the chromosome we have chosen are suitable for the test + assert(result.codeSize() != originalSize); + + return result; + } + + CharStream m_sourceStream = CharStream(SampleSourceCode, ""); + Chromosome m_chromosome{vector{UnusedPruner::name, EquivalentFunctionCombiner::name}}; + Program m_program = get(Program::load(m_sourceStream)); + Program m_optimisedProgram = optimisedProgram(m_program); }; BOOST_AUTO_TEST_SUITE(Phaser) @@ -62,47 +72,32 @@ BOOST_AUTO_TEST_SUITE(ProgramSizeTest) BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, FitnessMetricFixture) { - Chromosome chromosome(vector{UnusedPruner::name, EquivalentFunctionCombiner::name}); + size_t fitness = ProgramSize(m_program).evaluate(m_chromosome); - Program optimisedProgram = m_program; - optimisedProgram.optimise(chromosome.optimisationSteps()); - assert(m_program.codeSize() != optimisedProgram.codeSize()); - - BOOST_TEST(ProgramSize(m_program).evaluate(chromosome) != m_program.codeSize()); - BOOST_TEST(ProgramSize(m_program).evaluate(chromosome) == optimisedProgram.codeSize()); + BOOST_TEST(fitness != m_program.codeSize()); + BOOST_TEST(fitness == m_optimisedProgram.codeSize()); } BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, FitnessMetricFixture) { - Chromosome chromosome(vector{UnusedPruner::name, EquivalentFunctionCombiner::name}); - - Program programOptimisedOnce = m_program; - programOptimisedOnce.optimise(chromosome.optimisationSteps()); - Program programOptimisedTwice = programOptimisedOnce; - programOptimisedTwice.optimise(chromosome.optimisationSteps()); - assert(m_program.codeSize() != programOptimisedOnce.codeSize()); - assert(m_program.codeSize() != programOptimisedTwice.codeSize()); - assert(programOptimisedOnce.codeSize() != programOptimisedTwice.codeSize()); + Program const& programOptimisedOnce = m_optimisedProgram; + Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); ProgramSize metric(m_program, 2); + size_t fitness = metric.evaluate(m_chromosome); - BOOST_TEST(metric.evaluate(chromosome) != m_program.codeSize()); - BOOST_TEST(metric.evaluate(chromosome) != programOptimisedOnce.codeSize()); - BOOST_TEST(metric.evaluate(chromosome) == programOptimisedTwice.codeSize()); + BOOST_TEST(fitness != m_program.codeSize()); + BOOST_TEST(fitness != programOptimisedOnce.codeSize()); + BOOST_TEST(fitness == programOptimisedTwice.codeSize()); } BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, FitnessMetricFixture) { - Chromosome chromosome(vector{UnusedPruner::name, EquivalentFunctionCombiner::name}); - - Program optimisedProgram = m_program; - optimisedProgram.optimise(chromosome.optimisationSteps()); - assert(m_program.codeSize() != optimisedProgram.codeSize()); - ProgramSize metric(m_program, 0); + size_t fitness = metric.evaluate(m_chromosome); - BOOST_TEST(metric.evaluate(chromosome) == m_program.codeSize()); - BOOST_TEST(metric.evaluate(chromosome) != optimisedProgram.codeSize()); + BOOST_TEST(fitness == m_program.codeSize()); + BOOST_TEST(fitness != m_optimisedProgram.codeSize()); } BOOST_AUTO_TEST_SUITE_END() @@ -110,4 +105,3 @@ BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() } - From 0913fd1aac820637d8f28790124cf908300cedba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 26 Feb 2020 20:47:11 +0100 Subject: [PATCH 02/10] [yul-phaser] Extract ProgramBasedMetric base class from ProgramSize --- test/yulPhaser/FitnessMetrics.cpp | 29 +++++++++++++++++++++++++---- tools/yulPhaser/FitnessMetrics.cpp | 9 +++++++-- tools/yulPhaser/FitnessMetrics.h | 30 +++++++++++++++++++++++++----- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index e7a389be7..293fac426 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -22,16 +22,26 @@ #include +#include + #include using namespace std; using namespace solidity::langutil; +using namespace solidity::util; using namespace solidity::yul; namespace solidity::phaser::test { -class FitnessMetricFixture +class DummyProgramBasedMetric: public ProgramBasedMetric +{ +public: + using ProgramBasedMetric::ProgramBasedMetric; + size_t evaluate(Chromosome const&) const override { return 0; } +}; + +class ProgramBasedMetricFixture { protected: static constexpr char SampleSourceCode[] = @@ -68,9 +78,20 @@ protected: BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(FitnessMetricsTest) +BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest) + +BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(m_program).optimisedProgram(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); +} + +BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(ProgramSizeTest) -BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, FitnessMetricFixture) +BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, ProgramBasedMetricFixture) { size_t fitness = ProgramSize(m_program).evaluate(m_chromosome); @@ -78,7 +99,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, F BOOST_TEST(fitness == m_optimisedProgram.codeSize()); } -BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, FitnessMetricFixture) +BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture) { Program const& programOptimisedOnce = m_optimisedProgram; Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); @@ -91,7 +112,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number BOOST_TEST(fitness == programOptimisedTwice.codeSize()); } -BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, FitnessMetricFixture) +BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) { ProgramSize metric(m_program, 0); size_t fitness = metric.evaluate(m_chromosome); diff --git a/tools/yulPhaser/FitnessMetrics.cpp b/tools/yulPhaser/FitnessMetrics.cpp index be7ea5497..a3a344eb9 100644 --- a/tools/yulPhaser/FitnessMetrics.cpp +++ b/tools/yulPhaser/FitnessMetrics.cpp @@ -20,11 +20,16 @@ using namespace std; using namespace solidity::phaser; -size_t ProgramSize::evaluate(Chromosome const& _chromosome) const +Program ProgramBasedMetric::optimisedProgram(Chromosome const& _chromosome) const { Program programCopy = m_program; for (size_t i = 0; i < m_repetitionCount; ++i) programCopy.optimise(_chromosome.optimisationSteps()); - return programCopy.codeSize(); + return programCopy; +} + +size_t ProgramSize::evaluate(Chromosome const& _chromosome) const +{ + return optimisedProgram(_chromosome).codeSize(); } diff --git a/tools/yulPhaser/FitnessMetrics.h b/tools/yulPhaser/FitnessMetrics.h index 95608e89b..180fa322c 100644 --- a/tools/yulPhaser/FitnessMetrics.h +++ b/tools/yulPhaser/FitnessMetrics.h @@ -47,24 +47,44 @@ public: }; /** - * Fitness metric based on the size of a specific program after applying the optimisations from the - * chromosome to it. + * Abstract base class for fitness metrics that return values based on program size. + * + * The class provides utilities for optimising programs according to the information stored in + * chromosomes. + * + * It can also store weights for the @a CodeSize metric. It does not do anything with + * them because it does not actually compute the code size but they are readily available for use + * by derived classes. */ -class ProgramSize: public FitnessMetric +class ProgramBasedMetric: public FitnessMetric { public: - explicit ProgramSize(Program _program, size_t _repetitionCount = 1): + explicit ProgramBasedMetric( + Program _program, + size_t _repetitionCount = 1 + ): m_program(std::move(_program)), m_repetitionCount(_repetitionCount) {} Program const& program() const { return m_program; } size_t repetitionCount() const { return m_repetitionCount; } - size_t evaluate(Chromosome const& _chromosome) const override; + Program optimisedProgram(Chromosome const& _chromosome) const; private: Program m_program; size_t m_repetitionCount; }; +/** + * Fitness metric based on the size of a specific program after applying the optimisations from the + * chromosome to it. + */ +class ProgramSize: public ProgramBasedMetric +{ +public: + using ProgramBasedMetric::ProgramBasedMetric; + size_t evaluate(Chromosome const& _chromosome) const override; +}; + } From 7edbbe4edda845e8032029c19b195fff888a1dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 26 Feb 2020 19:58:14 +0100 Subject: [PATCH 03/10] [yul-phaser] Make FitnessMetric::evaluate() non-const --- test/yulPhaser/FitnessMetrics.cpp | 2 +- test/yulPhaser/TestHelpers.h | 2 +- tools/yulPhaser/FitnessMetrics.cpp | 2 +- tools/yulPhaser/FitnessMetrics.h | 4 ++-- tools/yulPhaser/Population.cpp | 6 +++--- tools/yulPhaser/Population.h | 16 ++++++++-------- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index 293fac426..8e4b14185 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -38,7 +38,7 @@ class DummyProgramBasedMetric: public ProgramBasedMetric { public: using ProgramBasedMetric::ProgramBasedMetric; - size_t evaluate(Chromosome const&) const override { return 0; } + size_t evaluate(Chromosome const&) override { return 0; } }; class ProgramBasedMetricFixture diff --git a/test/yulPhaser/TestHelpers.h b/test/yulPhaser/TestHelpers.h index aa76ae6da..2bf755b18 100644 --- a/test/yulPhaser/TestHelpers.h +++ b/test/yulPhaser/TestHelpers.h @@ -51,7 +51,7 @@ class ChromosomeLengthMetric: public FitnessMetric { public: using FitnessMetric::FitnessMetric; - size_t evaluate(Chromosome const& _chromosome) const override { return _chromosome.length(); } + size_t evaluate(Chromosome const& _chromosome) override { return _chromosome.length(); } }; // MUTATIONS diff --git a/tools/yulPhaser/FitnessMetrics.cpp b/tools/yulPhaser/FitnessMetrics.cpp index a3a344eb9..0992b9be0 100644 --- a/tools/yulPhaser/FitnessMetrics.cpp +++ b/tools/yulPhaser/FitnessMetrics.cpp @@ -29,7 +29,7 @@ Program ProgramBasedMetric::optimisedProgram(Chromosome const& _chromosome) cons return programCopy; } -size_t ProgramSize::evaluate(Chromosome const& _chromosome) const +size_t ProgramSize::evaluate(Chromosome const& _chromosome) { return optimisedProgram(_chromosome).codeSize(); } diff --git a/tools/yulPhaser/FitnessMetrics.h b/tools/yulPhaser/FitnessMetrics.h index 180fa322c..b50c26c8e 100644 --- a/tools/yulPhaser/FitnessMetrics.h +++ b/tools/yulPhaser/FitnessMetrics.h @@ -43,7 +43,7 @@ public: FitnessMetric& operator=(FitnessMetric const&) = delete; virtual ~FitnessMetric() = default; - virtual size_t evaluate(Chromosome const& _chromosome) const = 0; + virtual size_t evaluate(Chromosome const& _chromosome) = 0; }; /** @@ -84,7 +84,7 @@ class ProgramSize: public ProgramBasedMetric { public: using ProgramBasedMetric::ProgramBasedMetric; - size_t evaluate(Chromosome const& _chromosome) const override; + size_t evaluate(Chromosome const& _chromosome) override; }; } diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index ec63eb3bb..87df4e59b 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -59,7 +59,7 @@ bool phaser::isFitter(Individual const& a, Individual const& b) } Population Population::makeRandom( - shared_ptr _fitnessMetric, + shared_ptr _fitnessMetric, size_t _size, function _chromosomeLengthGenerator ) @@ -72,7 +72,7 @@ Population Population::makeRandom( } Population Population::makeRandom( - shared_ptr _fitnessMetric, + shared_ptr _fitnessMetric, size_t _size, size_t _minChromosomeLength, size_t _maxChromosomeLength @@ -145,7 +145,7 @@ ostream& phaser::operator<<(ostream& _stream, Population const& _population) } vector Population::chromosomesToIndividuals( - FitnessMetric const& _fitnessMetric, + FitnessMetric& _fitnessMetric, vector _chromosomes ) { diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index 29c82efed..f8f12632e 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -55,7 +55,7 @@ struct Individual Individual(Chromosome _chromosome, size_t _fitness): chromosome(std::move(_chromosome)), fitness(_fitness) {} - Individual(Chromosome _chromosome, FitnessMetric const& _fitnessMetric): + Individual(Chromosome _chromosome, FitnessMetric& _fitnessMetric): chromosome(std::move(_chromosome)), fitness(_fitnessMetric.evaluate(chromosome)) {} @@ -85,7 +85,7 @@ class Population { public: explicit Population( - std::shared_ptr _fitnessMetric, + std::shared_ptr _fitnessMetric, std::vector _chromosomes = {} ): Population( @@ -94,12 +94,12 @@ public: ) {} static Population makeRandom( - std::shared_ptr _fitnessMetric, + std::shared_ptr _fitnessMetric, size_t _size, std::function _chromosomeLengthGenerator ); static Population makeRandom( - std::shared_ptr _fitnessMetric, + std::shared_ptr _fitnessMetric, size_t _size, size_t _minChromosomeLength, size_t _maxChromosomeLength @@ -110,7 +110,7 @@ public: Population crossover(PairSelection const& _selection, std::function _crossover) const; friend Population (::operator+)(Population _a, Population _b); - std::shared_ptr fitnessMetric() const { return m_fitnessMetric; } + std::shared_ptr fitnessMetric() { return m_fitnessMetric; } std::vector const& individuals() const { return m_individuals; } static size_t uniformChromosomeLength(size_t _min, size_t _max) { return SimulationRNG::uniformInt(_min, _max); } @@ -122,17 +122,17 @@ public: friend std::ostream& operator<<(std::ostream& _stream, Population const& _population); private: - explicit Population(std::shared_ptr _fitnessMetric, std::vector _individuals): + explicit Population(std::shared_ptr _fitnessMetric, std::vector _individuals): m_fitnessMetric(std::move(_fitnessMetric)), m_individuals{sortedIndividuals(std::move(_individuals))} {} static std::vector chromosomesToIndividuals( - FitnessMetric const& _fitnessMetric, + FitnessMetric& _fitnessMetric, std::vector _chromosomes ); static std::vector sortedIndividuals(std::vector _individuals); - std::shared_ptr m_fitnessMetric; + std::shared_ptr m_fitnessMetric; std::vector m_individuals; }; From 0e03839e6217bb37abdaa0e61c197e9d2f038d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 20 Feb 2020 19:36:46 +0100 Subject: [PATCH 04/10] [yul-phaser] Add RelativeProgramSize metric --- test/yulPhaser/FitnessMetrics.cpp | 52 ++++++++++++++++++++++++++++++ tools/yulPhaser/FitnessMetrics.cpp | 17 ++++++++++ tools/yulPhaser/FitnessMetrics.h | 26 +++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index 8e4b14185..27997c791 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -26,6 +26,8 @@ #include +#include + using namespace std; using namespace solidity::langutil; using namespace solidity::util; @@ -121,6 +123,56 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is BOOST_TEST(fitness != m_optimisedProgram.codeSize()); } +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(RelativeProgramSizeTest) + +BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised_program_and_original_program, ProgramBasedMetricFixture) +{ + BOOST_TEST(RelativeProgramSize(m_program, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture) +{ + Program const& programOptimisedOnce = m_optimisedProgram; + Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); + + RelativeProgramSize metric(m_program, 3, 2); + size_t fitness = metric.evaluate(m_chromosome); + + BOOST_TEST(fitness != 1000); + BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, 3, 1).evaluate(m_chromosome)); + BOOST_TEST(fitness == round(1000.0 * programOptimisedTwice.codeSize() / m_program.codeSize())); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) +{ + RelativeProgramSize metric(m_program, 3, 0); + + BOOST_TEST(metric.evaluate(m_chromosome) == 1000); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_is_zero, ProgramBasedMetricFixture) +{ + CharStream sourceStream = CharStream("{}", ""); + Program program = get(Program::load(sourceStream)); + + RelativeProgramSize metric(program, 3); + + BOOST_TEST(metric.evaluate(m_chromosome) == 1000); + BOOST_TEST(metric.evaluate(Chromosome("")) == 1000); + BOOST_TEST(metric.evaluate(Chromosome("afcxjLTLTDoO")) == 1000); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, ProgramBasedMetricFixture) +{ + double sizeRatio = static_cast(m_optimisedProgram.codeSize()) / m_program.codeSize(); + BOOST_TEST(RelativeProgramSize(m_program, 0).evaluate(m_chromosome) == round(1.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, 1).evaluate(m_chromosome) == round(10.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, 2).evaluate(m_chromosome) == round(100.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, 3).evaluate(m_chromosome) == round(1000.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio)); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/FitnessMetrics.cpp b/tools/yulPhaser/FitnessMetrics.cpp index 0992b9be0..02c3b457f 100644 --- a/tools/yulPhaser/FitnessMetrics.cpp +++ b/tools/yulPhaser/FitnessMetrics.cpp @@ -17,6 +17,8 @@ #include +#include + using namespace std; using namespace solidity::phaser; @@ -33,3 +35,18 @@ size_t ProgramSize::evaluate(Chromosome const& _chromosome) { return optimisedProgram(_chromosome).codeSize(); } + +size_t RelativeProgramSize::evaluate(Chromosome const& _chromosome) +{ + size_t const scalingFactor = pow(10, m_fixedPointPrecision); + + size_t unoptimisedSize = optimisedProgram(Chromosome("")).codeSize(); + if (unoptimisedSize == 0) + return scalingFactor; + + size_t optimisedSize = optimisedProgram(_chromosome).codeSize(); + + return static_cast(round( + static_cast(optimisedSize) / unoptimisedSize * scalingFactor + )); +} diff --git a/tools/yulPhaser/FitnessMetrics.h b/tools/yulPhaser/FitnessMetrics.h index b50c26c8e..3544dba75 100644 --- a/tools/yulPhaser/FitnessMetrics.h +++ b/tools/yulPhaser/FitnessMetrics.h @@ -87,4 +87,30 @@ public: size_t evaluate(Chromosome const& _chromosome) override; }; +/** + * Fitness metric based on the size of a specific program after applying the optimisations from the + * chromosome to it in relation to the original, unoptimised program. + * + * Since metric values are integers, the class multiplies the ratio by 10^@a _fixedPointPrecision + * before rounding it. + */ +class RelativeProgramSize: public ProgramBasedMetric +{ +public: + explicit RelativeProgramSize( + Program _program, + size_t _fixedPointPrecision, + size_t _repetitionCount = 1 + ): + ProgramBasedMetric(std::move(_program), _repetitionCount), + m_fixedPointPrecision(_fixedPointPrecision) {} + + size_t fixedPointPrecision() const { return m_fixedPointPrecision; } + + size_t evaluate(Chromosome const& _chromosome) override; + +private: + size_t m_fixedPointPrecision; +}; + } From e4a360947b6b77e7a2844af0db97048bc282c403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 25 Feb 2020 20:16:47 +0100 Subject: [PATCH 05/10] [yul-phaser] Add FitnessMetricCombination and derived classes for average, sum, min and max --- test/yulPhaser/FitnessMetrics.cpp | 54 ++++++++++++++++++++++++++++ tools/yulPhaser/FitnessMetrics.cpp | 44 +++++++++++++++++++++++ tools/yulPhaser/FitnessMetrics.h | 56 ++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index 27997c791..8b62b03b7 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -78,6 +78,21 @@ protected: Program m_optimisedProgram = optimisedProgram(m_program); }; +class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture +{ +protected: + vector> m_simpleMetrics = { + make_shared(m_program, 1), + make_shared(m_program, 2), + make_shared(m_program, 3), + }; + vector m_fitness = { + m_simpleMetrics[0]->evaluate(m_chromosome), + m_simpleMetrics[1]->evaluate(m_chromosome), + m_simpleMetrics[2]->evaluate(m_chromosome), + }; +}; + BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(FitnessMetricsTest) BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest) @@ -173,6 +188,45 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, P BOOST_TEST(RelativeProgramSize(m_program, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio)); } +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(FitnessMetricCombinationTest) + +BOOST_FIXTURE_TEST_CASE(FitnessMetricAverage_evaluate_should_compute_average_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricAverage metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == (m_fitness[0] + m_fitness[1] + m_fitness[2]) / 3); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + +BOOST_FIXTURE_TEST_CASE(FitnessMetricSum_evaluate_should_compute_sum_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricSum metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == m_fitness[0] + m_fitness[1] + m_fitness[2]); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + +BOOST_FIXTURE_TEST_CASE(FitnessMetricMaximum_evaluate_should_compute_maximum_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricMaximum metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == max(m_fitness[0], max(m_fitness[1], m_fitness[2]))); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + +BOOST_FIXTURE_TEST_CASE(FitnessMetricMinimum_evaluate_should_compute_minimum_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricMinimum metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == min(m_fitness[0], min(m_fitness[1], m_fitness[2]))); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/FitnessMetrics.cpp b/tools/yulPhaser/FitnessMetrics.cpp index 02c3b457f..49482d0af 100644 --- a/tools/yulPhaser/FitnessMetrics.cpp +++ b/tools/yulPhaser/FitnessMetrics.cpp @@ -50,3 +50,47 @@ size_t RelativeProgramSize::evaluate(Chromosome const& _chromosome) static_cast(optimisedSize) / unoptimisedSize * scalingFactor )); } + +size_t FitnessMetricAverage::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t total = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + total += m_metrics[i]->evaluate(_chromosome); + + return total / m_metrics.size(); +} + +size_t FitnessMetricSum::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t total = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + total += m_metrics[i]->evaluate(_chromosome); + + return total; +} + +size_t FitnessMetricMaximum::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t maximum = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + maximum = max(maximum, m_metrics[i]->evaluate(_chromosome)); + + return maximum; +} + +size_t FitnessMetricMinimum::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t minimum = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + minimum = min(minimum, m_metrics[i]->evaluate(_chromosome)); + + return minimum; +} diff --git a/tools/yulPhaser/FitnessMetrics.h b/tools/yulPhaser/FitnessMetrics.h index 3544dba75..e79d99ff4 100644 --- a/tools/yulPhaser/FitnessMetrics.h +++ b/tools/yulPhaser/FitnessMetrics.h @@ -113,4 +113,60 @@ private: size_t m_fixedPointPrecision; }; +/** + * Abstract base class for fitness metrics that compute their value based on values of multiple + * other, nested metrics. + */ +class FitnessMetricCombination: public FitnessMetric +{ +public: + explicit FitnessMetricCombination(std::vector> _metrics): + m_metrics(std::move(_metrics)) {} + + std::vector> const& metrics() const { return m_metrics; } + +protected: + std::vector> m_metrics; +}; + +/** + * Fitness metric that returns the average of values of its nested metrics. + */ +class FitnessMetricAverage: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric that returns the sum of values of its nested metrics. + */ +class FitnessMetricSum: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric that returns the highest of values of its nested metrics. + */ +class FitnessMetricMaximum: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric that returns the lowest of values of its nested metrics. + */ +class FitnessMetricMinimum: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + } From 8e64c5c6f058778fc3c89a3ad26c8e0ff52d0570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 25 Feb 2020 18:56:07 +0100 Subject: [PATCH 06/10] [yul-phaser] Add --metric option --- test/yulPhaser/Phaser.cpp | 9 ++++++--- tools/yulPhaser/Phaser.cpp | 32 +++++++++++++++++++++++++++++++- tools/yulPhaser/Phaser.h | 9 +++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp index 26fe3c55e..a99c464da 100644 --- a/test/yulPhaser/Phaser.cpp +++ b/test/yulPhaser/Phaser.cpp @@ -61,6 +61,7 @@ protected: CharStream m_sourceStream = CharStream("{}", ""); Program m_program = get(Program::load(m_sourceStream)); FitnessMetricFactory::Options m_options = { + /* metric = */ MetricChoice::CodeSize, /* chromosomeRepetitions = */ 1, }; }; @@ -141,16 +142,18 @@ BOOST_AUTO_TEST_SUITE(FitnessMetricFactoryTest) BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMetricFactoryFixture) { + m_options.metric = MetricChoice::RelativeCodeSize; unique_ptr metric = FitnessMetricFactory::build(m_options, m_program); BOOST_REQUIRE(metric != nullptr); - auto programSizeMetric = dynamic_cast(metric.get()); - BOOST_REQUIRE(programSizeMetric != nullptr); - BOOST_TEST(toString(programSizeMetric->program()) == toString(m_program)); + auto relativeProgramSizeMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); + BOOST_TEST(toString(relativeProgramSizeMetric->program()) == toString(m_program)); } BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, FitnessMetricFactoryFixture) { + m_options.metric = MetricChoice::CodeSize; m_options.chromosomeRepetitions = 5; unique_ptr metric = FitnessMetricFactory::build(m_options, m_program); BOOST_REQUIRE(metric != nullptr); diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 7640fbf28..e8f2cae50 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -53,10 +53,19 @@ map const AlgorithmToStringMap = }; map const StringToAlgorithmMap = invertMap(AlgorithmToStringMap); +map MetricChoiceToStringMap = +{ + {MetricChoice::CodeSize, "code-size"}, + {MetricChoice::RelativeCodeSize, "relative-code-size"}, +}; +map const StringToMetricChoiceMap = invertMap(MetricChoiceToStringMap); + } istream& phaser::operator>>(istream& _inputStream, Algorithm& _algorithm) { return deserializeChoice(_inputStream, _algorithm, StringToAlgorithmMap); } ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) { return serializeChoice(_outputStream, _algorithm, AlgorithmToStringMap); } +istream& phaser::operator>>(istream& _inputStream, MetricChoice& _metric) { return deserializeChoice(_inputStream, _metric, StringToMetricChoiceMap); } +ostream& phaser::operator<<(ostream& _outputStream, MetricChoice _metric) { return serializeChoice(_outputStream, _metric, MetricChoiceToStringMap); } GeneticAlgorithmFactory::Options GeneticAlgorithmFactory::Options::fromCommandLine(po::variables_map const& _arguments) { @@ -129,6 +138,7 @@ unique_ptr GeneticAlgorithmFactory::build( FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po::variables_map const& _arguments) { return { + _arguments["metric"].as(), _arguments["chromosome-repetitions"].as(), }; } @@ -138,7 +148,22 @@ unique_ptr FitnessMetricFactory::build( Program _program ) { - return make_unique(move(_program), _options.chromosomeRepetitions); + switch (_options.metric) + { + case MetricChoice::CodeSize: + return make_unique( + move(_program), + _options.chromosomeRepetitions + ); + case MetricChoice::RelativeCodeSize: + return make_unique( + move(_program), + 3, + _options.chromosomeRepetitions + ); + default: + assertThrow(false, solidity::util::Exception, "Invalid MetricChoice value."); + } } PopulationFactory::Options PopulationFactory::Options::fromCommandLine(po::variables_map const& _arguments) @@ -391,6 +416,11 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() po::options_description metricsDescription("METRICS", lineLength, minDescriptionLength); metricsDescription.add_options() + ( + "metric", + po::value()->value_name("")->default_value(MetricChoice::CodeSize), + "Metric used to evaluate the fitness of a chromosome." + ) ( "chromosome-repetitions", po::value()->value_name("")->default_value(1), diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index 9631aa4cb..effa2ffe6 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -52,8 +52,16 @@ enum class Algorithm GEWEP, }; +enum class MetricChoice +{ + CodeSize, + RelativeCodeSize, +}; + std::istream& operator>>(std::istream& _inputStream, solidity::phaser::Algorithm& _algorithm); std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorithm _algorithm); +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricChoice& _metric); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::MetricChoice _metric); /** * Builds and validates instances of @a GeneticAlgorithm and its derived classes. @@ -91,6 +99,7 @@ class FitnessMetricFactory public: struct Options { + MetricChoice metric; size_t chromosomeRepetitions; static Options fromCommandLine(boost::program_options::variables_map const& _arguments); From 01050940fd68a23a0453fbe52db2aa5effe0f287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 25 Feb 2020 19:33:54 +0100 Subject: [PATCH 07/10] [yul-phaser] Add --relative-metric-scale option --- test/yulPhaser/Phaser.cpp | 13 +++++++++++++ tools/yulPhaser/Phaser.cpp | 15 ++++++++++++++- tools/yulPhaser/Phaser.h | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp index a99c464da..800301ecf 100644 --- a/test/yulPhaser/Phaser.cpp +++ b/test/yulPhaser/Phaser.cpp @@ -62,6 +62,7 @@ protected: Program m_program = get(Program::load(m_sourceStream)); FitnessMetricFactory::Options m_options = { /* metric = */ MetricChoice::CodeSize, + /* relativeMetricScale = */ 5, /* chromosomeRepetitions = */ 1, }; }; @@ -163,6 +164,18 @@ BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, Fitn BOOST_TEST(programSizeMetric->repetitionCount() == m_options.chromosomeRepetitions); } +BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFactoryFixture) +{ + m_options.metric = MetricChoice::RelativeCodeSize; + m_options.relativeMetricScale = 10; + unique_ptr metric = FitnessMetricFactory::build(m_options, m_program); + BOOST_REQUIRE(metric != nullptr); + + auto relativeProgramSizeMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); + BOOST_TEST(relativeProgramSizeMetric->fixedPointPrecision() == m_options.relativeMetricScale); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(PopulationFactoryTest) diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index e8f2cae50..a0a787f7f 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -139,6 +139,7 @@ FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po: { return { _arguments["metric"].as(), + _arguments["relative-metric-scale"].as(), _arguments["chromosome-repetitions"].as(), }; } @@ -158,7 +159,7 @@ unique_ptr FitnessMetricFactory::build( case MetricChoice::RelativeCodeSize: return make_unique( move(_program), - 3, + _options.relativeMetricScale, _options.chromosomeRepetitions ); default: @@ -421,6 +422,18 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() po::value()->value_name("")->default_value(MetricChoice::CodeSize), "Metric used to evaluate the fitness of a chromosome." ) + ( + "relative-metric-scale", + po::value()->value_name("")->default_value(3), + "Scaling factor for values produced by relative fitness metrics. \n" + "Since all metrics must produce integer values, the fractional part of the result is discarded. " + "To keep the numbers meaningful, a relative metric multiples its values by a scaling factor " + "and this option specifies the exponent of this factor. " + "For example with value of 3 the factor is 10^3 = 1000 and the metric will return " + "500 to represent 0.5, 1000 for 1.0, 2000 for 2.0 and so on. " + "Using a bigger factor allows discerning smaller relative differences between chromosomes " + "but makes the numbers less readable and may also lose precision if the numbers are very large." + ) ( "chromosome-repetitions", po::value()->value_name("")->default_value(1), diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index effa2ffe6..07f1eaea1 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -100,6 +100,7 @@ public: struct Options { MetricChoice metric; + size_t relativeMetricScale; size_t chromosomeRepetitions; static Options fromCommandLine(boost::program_options::variables_map const& _arguments); From bc46323bed66d8cbab49b8d8bc363a2a6e6ccce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 25 Feb 2020 20:31:51 +0100 Subject: [PATCH 08/10] [yul-phaser] Phaser: Accepting multiple input programs - Use average of metric values for individual programs as the overall metric. --- test/yulPhaser/Phaser.cpp | 77 +++++++++++++++++++++++++++++--------- tools/yulPhaser/Phaser.cpp | 68 +++++++++++++++++++++------------ tools/yulPhaser/Phaser.h | 6 +-- 3 files changed, 107 insertions(+), 44 deletions(-) diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp index 800301ecf..ea06dd771 100644 --- a/test/yulPhaser/Phaser.cpp +++ b/test/yulPhaser/Phaser.cpp @@ -58,8 +58,16 @@ protected: class FitnessMetricFactoryFixture { protected: - CharStream m_sourceStream = CharStream("{}", ""); - Program m_program = get(Program::load(m_sourceStream)); + vector m_sourceStreams = { + CharStream("{}", ""), + CharStream("{{}}", ""), + CharStream("{{{}}}", ""), + }; + vector m_programs = { + get(Program::load(m_sourceStreams[0])), + get(Program::load(m_sourceStreams[1])), + get(Program::load(m_sourceStreams[2])), + }; FitnessMetricFactory::Options m_options = { /* metric = */ MetricChoice::CodeSize, /* relativeMetricScale = */ 5, @@ -144,22 +152,32 @@ BOOST_AUTO_TEST_SUITE(FitnessMetricFactoryTest) BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMetricFactoryFixture) { m_options.metric = MetricChoice::RelativeCodeSize; - unique_ptr metric = FitnessMetricFactory::build(m_options, m_program); + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}); BOOST_REQUIRE(metric != nullptr); - auto relativeProgramSizeMetric = dynamic_cast(metric.get()); + auto averageMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(averageMetric != nullptr); + BOOST_REQUIRE(averageMetric->metrics().size() == 1); + BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr); + + auto relativeProgramSizeMetric = dynamic_cast(averageMetric->metrics()[0].get()); BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); - BOOST_TEST(toString(relativeProgramSizeMetric->program()) == toString(m_program)); + BOOST_TEST(toString(relativeProgramSizeMetric->program()) == toString(m_programs[0])); } BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, FitnessMetricFactoryFixture) { m_options.metric = MetricChoice::CodeSize; m_options.chromosomeRepetitions = 5; - unique_ptr metric = FitnessMetricFactory::build(m_options, m_program); + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}); BOOST_REQUIRE(metric != nullptr); - auto programSizeMetric = dynamic_cast(metric.get()); + auto averageMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(averageMetric != nullptr); + BOOST_REQUIRE(averageMetric->metrics().size() == 1); + BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr); + + auto programSizeMetric = dynamic_cast(averageMetric->metrics()[0].get()); BOOST_REQUIRE(programSizeMetric != nullptr); BOOST_TEST(programSizeMetric->repetitionCount() == m_options.chromosomeRepetitions); } @@ -168,14 +186,29 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac { m_options.metric = MetricChoice::RelativeCodeSize; m_options.relativeMetricScale = 10; - unique_ptr metric = FitnessMetricFactory::build(m_options, m_program); + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}); BOOST_REQUIRE(metric != nullptr); - auto relativeProgramSizeMetric = dynamic_cast(metric.get()); + auto averageMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(averageMetric != nullptr); + BOOST_REQUIRE(averageMetric->metrics().size() == 1); + BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr); + + auto relativeProgramSizeMetric = dynamic_cast(averageMetric->metrics()[0].get()); BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); BOOST_TEST(relativeProgramSizeMetric->fixedPointPrecision() == m_options.relativeMetricScale); } +BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, FitnessMetricFactoryFixture) +{ + unique_ptr metric = FitnessMetricFactory::build(m_options, m_programs); + BOOST_REQUIRE(metric != nullptr); + + auto combinedMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(combinedMetric != nullptr); + BOOST_REQUIRE(combinedMetric->metrics().size() == m_programs.size()); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(PopulationFactoryTest) @@ -283,20 +316,30 @@ BOOST_FIXTURE_TEST_CASE(build_should_combine_populations_from_all_sources, Poula BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(ProgramFactoryTest) -BOOST_AUTO_TEST_CASE(build_should_load_program_from_file) +BOOST_AUTO_TEST_CASE(build_should_load_programs_from_files) { TemporaryDirectory tempDir; + vector sources{"{}", "{{}}", "{{{}}}"}; + ProgramFactory::Options options{/* inputFiles = */ { + tempDir.memberPath("program1.yul"), + tempDir.memberPath("program2.yul"), + tempDir.memberPath("program3.yul"), + }}; + + for (size_t i = 0; i < sources.size(); ++i) { - ofstream tmpFile(tempDir.memberPath("program.yul")); - tmpFile << "{}" << endl; + ofstream tmpFile(options.inputFiles[i]); + tmpFile << sources[i] << endl; } - ProgramFactory::Options options{/* inputFile = */ tempDir.memberPath("program.yul")}; - CharStream expectedProgramSource("{}", ""); + vector programs = ProgramFactory::build(options); - auto program = ProgramFactory::build(options); - - BOOST_TEST(toString(program) == toString(get(Program::load(expectedProgramSource)))); + BOOST_TEST(programs.size() == sources.size()); + for (size_t i = 0; i < sources.size(); ++i) + { + CharStream sourceStream(sources[i], options.inputFiles[i]); + BOOST_TEST(toString(programs[i]) == toString(get(Program::load(sourceStream)))); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index a0a787f7f..d0d9d9a1e 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -146,25 +146,39 @@ FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po: unique_ptr FitnessMetricFactory::build( Options const& _options, - Program _program + vector _programs ) { + assert(_programs.size() > 0 && "Validations should prevent this from being executed with zero files."); + + vector> metrics; switch (_options.metric) { case MetricChoice::CodeSize: - return make_unique( - move(_program), - _options.chromosomeRepetitions - ); + { + for (Program& program: _programs) + metrics.push_back(make_unique( + move(program), + _options.chromosomeRepetitions + )); + + break; + } case MetricChoice::RelativeCodeSize: - return make_unique( - move(_program), - _options.relativeMetricScale, - _options.chromosomeRepetitions - ); + { + for (Program& program: _programs) + metrics.push_back(make_unique( + move(program), + _options.relativeMetricScale, + _options.chromosomeRepetitions + )); + break; + } default: assertThrow(false, solidity::util::Exception, "Invalid MetricChoice value."); } + + return make_unique(move(metrics)); } PopulationFactory::Options PopulationFactory::Options::fromCommandLine(po::variables_map const& _arguments) @@ -246,20 +260,26 @@ Population PopulationFactory::buildFromFile( ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments) { return { - _arguments["input-file"].as(), + _arguments["input-files"].as>(), }; } -Program ProgramFactory::build(Options const& _options) +vector ProgramFactory::build(Options const& _options) { - CharStream sourceCode = loadSource(_options.inputFile); - variant programOrErrors = Program::load(sourceCode); - if (holds_alternative(programOrErrors)) + vector inputPrograms; + for (auto& path: _options.inputFiles) { - cerr << get(programOrErrors) << endl; - assertThrow(false, InvalidProgram, "Failed to load program " + _options.inputFile); + CharStream sourceCode = loadSource(path); + variant programOrErrors = Program::load(sourceCode); + if (holds_alternative(programOrErrors)) + { + cerr << get(programOrErrors) << endl; + assertThrow(false, InvalidProgram, "Failed to load program " + path); + } + inputPrograms.push_back(move(get(programOrErrors))); } - return move(get(programOrErrors)); + + return inputPrograms; } CharStream ProgramFactory::loadSource(string const& _sourcePath) @@ -303,7 +323,7 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() po::options_description generalDescription("GENERAL", lineLength, minDescriptionLength); generalDescription.add_options() ("help", "Show help message and exit.") - ("input-file", po::value()->required()->value_name(""), "Input file.") + ("input-files", po::value>()->required()->value_name(""), "Input files.") ("seed", po::value()->value_name(""), "Seed for the random number generator.") ( "rounds", @@ -443,7 +463,7 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() keywordDescription.add(metricsDescription); po::positional_options_description positionalDescription; - positionalDescription.add("input-file", 1); + positionalDescription.add("input-files", -1); return {keywordDescription, positionalDescription}; } @@ -465,8 +485,8 @@ optional Phaser::parseCommandLine(int _argc, char** _argv) return nullopt; } - if (arguments.count("input-file") == 0) - assertThrow(false, NoInputFiles, "Missing argument: input-file."); + if (arguments.count("input-files") == 0) + assertThrow(false, NoInputFiles, "Missing argument: input-files."); return arguments; } @@ -501,8 +521,8 @@ void Phaser::runAlgorithm(po::variables_map const& _arguments) auto populationOptions = PopulationFactory::Options::fromCommandLine(_arguments); auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments); - Program program = ProgramFactory::build(programOptions); - unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, move(program)); + vector programs = ProgramFactory::build(programOptions); + unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, move(programs)); Population population = PopulationFactory::build(populationOptions, move(fitnessMetric)); unique_ptr geneticAlgorithm = GeneticAlgorithmFactory::build( diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index 07f1eaea1..55c808f07 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -108,7 +108,7 @@ public: static std::unique_ptr build( Options const& _options, - Program _program + std::vector _programs ); }; @@ -157,12 +157,12 @@ class ProgramFactory public: struct Options { - std::string inputFile; + std::vector inputFiles; static Options fromCommandLine(boost::program_options::variables_map const& _arguments); }; - static Program build(Options const& _options); + static std::vector build(Options const& _options); private: static langutil::CharStream loadSource(std::string const& _sourcePath); From d86652cb9618907b70ec4c1039b3dabf53219a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 26 Feb 2020 16:32:49 +0100 Subject: [PATCH 09/10] [yul-phaser] Add --metric-aggregator option --- test/yulPhaser/Phaser.cpp | 14 +++++++++----- tools/yulPhaser/Phaser.cpp | 32 +++++++++++++++++++++++++++++++- tools/yulPhaser/Phaser.h | 11 +++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp index ea06dd771..aab34db61 100644 --- a/test/yulPhaser/Phaser.cpp +++ b/test/yulPhaser/Phaser.cpp @@ -70,6 +70,7 @@ protected: }; FitnessMetricFactory::Options m_options = { /* metric = */ MetricChoice::CodeSize, + /* metricAggregator = */ MetricAggregatorChoice::Average, /* relativeMetricScale = */ 5, /* chromosomeRepetitions = */ 1, }; @@ -152,15 +153,16 @@ BOOST_AUTO_TEST_SUITE(FitnessMetricFactoryTest) BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMetricFactoryFixture) { m_options.metric = MetricChoice::RelativeCodeSize; + m_options.metricAggregator = MetricAggregatorChoice::Sum; unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}); BOOST_REQUIRE(metric != nullptr); - auto averageMetric = dynamic_cast(metric.get()); - BOOST_REQUIRE(averageMetric != nullptr); - BOOST_REQUIRE(averageMetric->metrics().size() == 1); - BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr); + auto sumMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(sumMetric != nullptr); + BOOST_REQUIRE(sumMetric->metrics().size() == 1); + BOOST_REQUIRE(sumMetric->metrics()[0] != nullptr); - auto relativeProgramSizeMetric = dynamic_cast(averageMetric->metrics()[0].get()); + auto relativeProgramSizeMetric = dynamic_cast(sumMetric->metrics()[0].get()); BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); BOOST_TEST(toString(relativeProgramSizeMetric->program()) == toString(m_programs[0])); } @@ -168,6 +170,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMet BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, FitnessMetricFactoryFixture) { m_options.metric = MetricChoice::CodeSize; + m_options.metricAggregator = MetricAggregatorChoice::Average; m_options.chromosomeRepetitions = 5; unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}); BOOST_REQUIRE(metric != nullptr); @@ -185,6 +188,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, Fitn BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFactoryFixture) { m_options.metric = MetricChoice::RelativeCodeSize; + m_options.metricAggregator = MetricAggregatorChoice::Average; m_options.relativeMetricScale = 10; unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}); BOOST_REQUIRE(metric != nullptr); diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index d0d9d9a1e..c62b62dc3 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -60,12 +60,23 @@ map MetricChoiceToStringMap = }; map const StringToMetricChoiceMap = invertMap(MetricChoiceToStringMap); +map const MetricAggregatorChoiceToStringMap = +{ + {MetricAggregatorChoice::Average, "average"}, + {MetricAggregatorChoice::Sum, "sum"}, + {MetricAggregatorChoice::Maximum, "maximum"}, + {MetricAggregatorChoice::Minimum, "minimum"}, +}; +map const StringToMetricAggregatorChoiceMap = invertMap(MetricAggregatorChoiceToStringMap); + } istream& phaser::operator>>(istream& _inputStream, Algorithm& _algorithm) { return deserializeChoice(_inputStream, _algorithm, StringToAlgorithmMap); } ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) { return serializeChoice(_outputStream, _algorithm, AlgorithmToStringMap); } istream& phaser::operator>>(istream& _inputStream, MetricChoice& _metric) { return deserializeChoice(_inputStream, _metric, StringToMetricChoiceMap); } ostream& phaser::operator<<(ostream& _outputStream, MetricChoice _metric) { return serializeChoice(_outputStream, _metric, MetricChoiceToStringMap); } +istream& phaser::operator>>(istream& _inputStream, MetricAggregatorChoice& _aggregator) { return deserializeChoice(_inputStream, _aggregator, StringToMetricAggregatorChoiceMap); } +ostream& phaser::operator<<(ostream& _outputStream, MetricAggregatorChoice _aggregator) { return serializeChoice(_outputStream, _aggregator, MetricAggregatorChoiceToStringMap); } GeneticAlgorithmFactory::Options GeneticAlgorithmFactory::Options::fromCommandLine(po::variables_map const& _arguments) { @@ -139,6 +150,7 @@ FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po: { return { _arguments["metric"].as(), + _arguments["metric-aggregator"].as(), _arguments["relative-metric-scale"].as(), _arguments["chromosome-repetitions"].as(), }; @@ -178,7 +190,19 @@ unique_ptr FitnessMetricFactory::build( assertThrow(false, solidity::util::Exception, "Invalid MetricChoice value."); } - return make_unique(move(metrics)); + switch (_options.metricAggregator) + { + case MetricAggregatorChoice::Average: + return make_unique(move(metrics)); + case MetricAggregatorChoice::Sum: + return make_unique(move(metrics)); + case MetricAggregatorChoice::Maximum: + return make_unique(move(metrics)); + case MetricAggregatorChoice::Minimum: + return make_unique(move(metrics)); + default: + assertThrow(false, solidity::util::Exception, "Invalid MetricAggregatorChoice value."); + } } PopulationFactory::Options PopulationFactory::Options::fromCommandLine(po::variables_map const& _arguments) @@ -442,6 +466,12 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() po::value()->value_name("")->default_value(MetricChoice::CodeSize), "Metric used to evaluate the fitness of a chromosome." ) + ( + "metric-aggregator", + po::value()->value_name("")->default_value(MetricAggregatorChoice::Average), + "Operator used to combine multiple fitness metric obtained by evaluating a chromosome " + "separately for each input program." + ) ( "relative-metric-scale", po::value()->value_name("")->default_value(3), diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index 55c808f07..2e72c31f6 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -58,10 +58,20 @@ enum class MetricChoice RelativeCodeSize, }; +enum class MetricAggregatorChoice +{ + Average, + Sum, + Maximum, + Minimum, +}; + std::istream& operator>>(std::istream& _inputStream, solidity::phaser::Algorithm& _algorithm); std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorithm _algorithm); std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricChoice& _metric); std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::MetricChoice _metric); +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricAggregatorChoice& _aggregator); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::MetricAggregatorChoice _aggregator); /** * Builds and validates instances of @a GeneticAlgorithm and its derived classes. @@ -100,6 +110,7 @@ public: struct Options { MetricChoice metric; + MetricAggregatorChoice metricAggregator; size_t relativeMetricScale; size_t chromosomeRepetitions; From 905147321b7896508108060d00aa2dd51f58ccf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 27 Feb 2020 00:12:54 +0100 Subject: [PATCH 10/10] [yul-phaser] Phaser: Change default metric to RelativeCodeSize --- tools/yulPhaser/Phaser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index c62b62dc3..da0296eff 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -463,7 +463,7 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() metricsDescription.add_options() ( "metric", - po::value()->value_name("")->default_value(MetricChoice::CodeSize), + po::value()->value_name("")->default_value(MetricChoice::RelativeCodeSize), "Metric used to evaluate the fitness of a chromosome." ) (