Merge pull request #8449 from imapp-pl/yul-phaser-multi-program-support

[yul-phaser] Multi-program support
This commit is contained in:
chriseth
2020-03-19 17:07:34 +01:00
committed by GitHub
9 changed files with 559 additions and 93 deletions
+159 -38
View File
@@ -22,22 +22,30 @@
#include <liblangutil/CharStream.h>
#include <libsolutil/CommonIO.h>
#include <boost/test/unit_test.hpp>
#include <cmath>
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&) override { return 0; }
};
class ProgramBasedMetricFixture
{
protected:
FitnessMetricFixture():
m_sourceStream(SampleSourceCode, ""),
m_program(get<Program>(Program::load(m_sourceStream))) {}
static constexpr char SampleSourceCode[] =
"{\n"
" function foo() -> result\n"
@@ -52,57 +60,171 @@ 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<string>{UnusedPruner::name, EquivalentFunctionCombiner::name}};
Program m_program = get<Program>(Program::load(m_sourceStream));
Program m_optimisedProgram = optimisedProgram(m_program);
};
class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture
{
protected:
vector<shared_ptr<FitnessMetric>> m_simpleMetrics = {
make_shared<ProgramSize>(m_program, 1),
make_shared<ProgramSize>(m_program, 2),
make_shared<ProgramSize>(m_program, 3),
};
vector<size_t> 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)
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)
{
Chromosome chromosome(vector<string>{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)
BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture)
{
Chromosome chromosome(vector<string>{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)
BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture)
{
Chromosome chromosome(vector<string>{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()
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>(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<double>(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(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()
@@ -110,4 +232,3 @@ BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
}
+79 -16
View File
@@ -58,9 +58,20 @@ protected:
class FitnessMetricFactoryFixture
{
protected:
CharStream m_sourceStream = CharStream("{}", "");
Program m_program = get<Program>(Program::load(m_sourceStream));
vector<CharStream> m_sourceStreams = {
CharStream("{}", ""),
CharStream("{{}}", ""),
CharStream("{{{}}}", ""),
};
vector<Program> m_programs = {
get<Program>(Program::load(m_sourceStreams[0])),
get<Program>(Program::load(m_sourceStreams[1])),
get<Program>(Program::load(m_sourceStreams[2])),
};
FitnessMetricFactory::Options m_options = {
/* metric = */ MetricChoice::CodeSize,
/* metricAggregator = */ MetricAggregatorChoice::Average,
/* relativeMetricScale = */ 5,
/* chromosomeRepetitions = */ 1,
};
};
@@ -141,25 +152,67 @@ BOOST_AUTO_TEST_SUITE(FitnessMetricFactoryTest)
BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMetricFactoryFixture)
{
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_program);
m_options.metric = MetricChoice::RelativeCodeSize;
m_options.metricAggregator = MetricAggregatorChoice::Sum;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]});
BOOST_REQUIRE(metric != nullptr);
auto programSizeMetric = dynamic_cast<ProgramSize*>(metric.get());
BOOST_REQUIRE(programSizeMetric != nullptr);
BOOST_TEST(toString(programSizeMetric->program()) == toString(m_program));
auto sumMetric = dynamic_cast<FitnessMetricSum*>(metric.get());
BOOST_REQUIRE(sumMetric != nullptr);
BOOST_REQUIRE(sumMetric->metrics().size() == 1);
BOOST_REQUIRE(sumMetric->metrics()[0] != nullptr);
auto relativeProgramSizeMetric = dynamic_cast<RelativeProgramSize*>(sumMetric->metrics()[0].get());
BOOST_REQUIRE(relativeProgramSizeMetric != nullptr);
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.metricAggregator = MetricAggregatorChoice::Average;
m_options.chromosomeRepetitions = 5;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_program);
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]});
BOOST_REQUIRE(metric != nullptr);
auto programSizeMetric = dynamic_cast<ProgramSize*>(metric.get());
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
BOOST_REQUIRE(averageMetric != nullptr);
BOOST_REQUIRE(averageMetric->metrics().size() == 1);
BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr);
auto programSizeMetric = dynamic_cast<ProgramSize*>(averageMetric->metrics()[0].get());
BOOST_REQUIRE(programSizeMetric != nullptr);
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.metricAggregator = MetricAggregatorChoice::Average;
m_options.relativeMetricScale = 10;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]});
BOOST_REQUIRE(metric != nullptr);
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
BOOST_REQUIRE(averageMetric != nullptr);
BOOST_REQUIRE(averageMetric->metrics().size() == 1);
BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr);
auto relativeProgramSizeMetric = dynamic_cast<RelativeProgramSize*>(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<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_programs);
BOOST_REQUIRE(metric != nullptr);
auto combinedMetric = dynamic_cast<FitnessMetricCombination*>(metric.get());
BOOST_REQUIRE(combinedMetric != nullptr);
BOOST_REQUIRE(combinedMetric->metrics().size() == m_programs.size());
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(PopulationFactoryTest)
@@ -267,20 +320,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<string> 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<Program> programs = ProgramFactory::build(options);
auto program = ProgramFactory::build(options);
BOOST_TEST(toString(program) == toString(get<Program>(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>(Program::load(sourceStream))));
}
}
BOOST_AUTO_TEST_SUITE_END()
+1 -1
View File
@@ -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