[yul-phaser] Phaser: Accepting multiple input programs

- Use average of metric values for individual programs as the overall metric.
This commit is contained in:
Kamil Śliwak 2020-02-25 20:31:51 +01:00
parent 01050940fd
commit bc46323bed
3 changed files with 107 additions and 44 deletions

View File

@ -58,8 +58,16 @@ protected:
class FitnessMetricFactoryFixture class FitnessMetricFactoryFixture
{ {
protected: protected:
CharStream m_sourceStream = CharStream("{}", ""); vector<CharStream> m_sourceStreams = {
Program m_program = get<Program>(Program::load(m_sourceStream)); 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 = { FitnessMetricFactory::Options m_options = {
/* metric = */ MetricChoice::CodeSize, /* metric = */ MetricChoice::CodeSize,
/* relativeMetricScale = */ 5, /* relativeMetricScale = */ 5,
@ -144,22 +152,32 @@ BOOST_AUTO_TEST_SUITE(FitnessMetricFactoryTest)
BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMetricFactoryFixture) BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMetricFactoryFixture)
{ {
m_options.metric = MetricChoice::RelativeCodeSize; m_options.metric = MetricChoice::RelativeCodeSize;
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); BOOST_REQUIRE(metric != nullptr);
auto relativeProgramSizeMetric = dynamic_cast<RelativeProgramSize*>(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 relativeProgramSizeMetric = dynamic_cast<RelativeProgramSize*>(averageMetric->metrics()[0].get());
BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); 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) BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, FitnessMetricFactoryFixture)
{ {
m_options.metric = MetricChoice::CodeSize; m_options.metric = MetricChoice::CodeSize;
m_options.chromosomeRepetitions = 5; 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); 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_REQUIRE(programSizeMetric != nullptr);
BOOST_TEST(programSizeMetric->repetitionCount() == m_options.chromosomeRepetitions); 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.metric = MetricChoice::RelativeCodeSize;
m_options.relativeMetricScale = 10; m_options.relativeMetricScale = 10;
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); BOOST_REQUIRE(metric != nullptr);
auto relativeProgramSizeMetric = dynamic_cast<RelativeProgramSize*>(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 relativeProgramSizeMetric = dynamic_cast<RelativeProgramSize*>(averageMetric->metrics()[0].get());
BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); BOOST_REQUIRE(relativeProgramSizeMetric != nullptr);
BOOST_TEST(relativeProgramSizeMetric->fixedPointPrecision() == m_options.relativeMetricScale); 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_END()
BOOST_AUTO_TEST_SUITE(PopulationFactoryTest) 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_END()
BOOST_AUTO_TEST_SUITE(ProgramFactoryTest) 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; 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")); ofstream tmpFile(options.inputFiles[i]);
tmpFile << "{}" << endl; tmpFile << sources[i] << endl;
} }
ProgramFactory::Options options{/* inputFile = */ tempDir.memberPath("program.yul")}; vector<Program> programs = ProgramFactory::build(options);
CharStream expectedProgramSource("{}", "");
auto program = ProgramFactory::build(options); BOOST_TEST(programs.size() == sources.size());
for (size_t i = 0; i < sources.size(); ++i)
BOOST_TEST(toString(program) == toString(get<Program>(Program::load(expectedProgramSource)))); {
CharStream sourceStream(sources[i], options.inputFiles[i]);
BOOST_TEST(toString(programs[i]) == toString(get<Program>(Program::load(sourceStream))));
}
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -146,25 +146,39 @@ FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po:
unique_ptr<FitnessMetric> FitnessMetricFactory::build( unique_ptr<FitnessMetric> FitnessMetricFactory::build(
Options const& _options, Options const& _options,
Program _program vector<Program> _programs
) )
{ {
assert(_programs.size() > 0 && "Validations should prevent this from being executed with zero files.");
vector<shared_ptr<FitnessMetric>> metrics;
switch (_options.metric) switch (_options.metric)
{ {
case MetricChoice::CodeSize: case MetricChoice::CodeSize:
return make_unique<ProgramSize>( {
move(_program), for (Program& program: _programs)
metrics.push_back(make_unique<ProgramSize>(
move(program),
_options.chromosomeRepetitions _options.chromosomeRepetitions
); ));
break;
}
case MetricChoice::RelativeCodeSize: case MetricChoice::RelativeCodeSize:
return make_unique<RelativeProgramSize>( {
move(_program), for (Program& program: _programs)
metrics.push_back(make_unique<RelativeProgramSize>(
move(program),
_options.relativeMetricScale, _options.relativeMetricScale,
_options.chromosomeRepetitions _options.chromosomeRepetitions
); ));
break;
}
default: default:
assertThrow(false, solidity::util::Exception, "Invalid MetricChoice value."); assertThrow(false, solidity::util::Exception, "Invalid MetricChoice value.");
} }
return make_unique<FitnessMetricAverage>(move(metrics));
} }
PopulationFactory::Options PopulationFactory::Options::fromCommandLine(po::variables_map const& _arguments) 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) ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments)
{ {
return { return {
_arguments["input-file"].as<string>(), _arguments["input-files"].as<vector<string>>(),
}; };
} }
Program ProgramFactory::build(Options const& _options) vector<Program> ProgramFactory::build(Options const& _options)
{ {
CharStream sourceCode = loadSource(_options.inputFile); vector<Program> inputPrograms;
for (auto& path: _options.inputFiles)
{
CharStream sourceCode = loadSource(path);
variant<Program, ErrorList> programOrErrors = Program::load(sourceCode); variant<Program, ErrorList> programOrErrors = Program::load(sourceCode);
if (holds_alternative<ErrorList>(programOrErrors)) if (holds_alternative<ErrorList>(programOrErrors))
{ {
cerr << get<ErrorList>(programOrErrors) << endl; cerr << get<ErrorList>(programOrErrors) << endl;
assertThrow(false, InvalidProgram, "Failed to load program " + _options.inputFile); assertThrow(false, InvalidProgram, "Failed to load program " + path);
} }
return move(get<Program>(programOrErrors)); inputPrograms.push_back(move(get<Program>(programOrErrors)));
}
return inputPrograms;
} }
CharStream ProgramFactory::loadSource(string const& _sourcePath) CharStream ProgramFactory::loadSource(string const& _sourcePath)
@ -303,7 +323,7 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
po::options_description generalDescription("GENERAL", lineLength, minDescriptionLength); po::options_description generalDescription("GENERAL", lineLength, minDescriptionLength);
generalDescription.add_options() generalDescription.add_options()
("help", "Show help message and exit.") ("help", "Show help message and exit.")
("input-file", po::value<string>()->required()->value_name("<PATH>"), "Input file.") ("input-files", po::value<vector<string>>()->required()->value_name("<PATH>"), "Input files.")
("seed", po::value<uint32_t>()->value_name("<NUM>"), "Seed for the random number generator.") ("seed", po::value<uint32_t>()->value_name("<NUM>"), "Seed for the random number generator.")
( (
"rounds", "rounds",
@ -443,7 +463,7 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
keywordDescription.add(metricsDescription); keywordDescription.add(metricsDescription);
po::positional_options_description positionalDescription; po::positional_options_description positionalDescription;
positionalDescription.add("input-file", 1); positionalDescription.add("input-files", -1);
return {keywordDescription, positionalDescription}; return {keywordDescription, positionalDescription};
} }
@ -465,8 +485,8 @@ optional<po::variables_map> Phaser::parseCommandLine(int _argc, char** _argv)
return nullopt; return nullopt;
} }
if (arguments.count("input-file") == 0) if (arguments.count("input-files") == 0)
assertThrow(false, NoInputFiles, "Missing argument: input-file."); assertThrow(false, NoInputFiles, "Missing argument: input-files.");
return arguments; return arguments;
} }
@ -501,8 +521,8 @@ void Phaser::runAlgorithm(po::variables_map const& _arguments)
auto populationOptions = PopulationFactory::Options::fromCommandLine(_arguments); auto populationOptions = PopulationFactory::Options::fromCommandLine(_arguments);
auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments); auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments);
Program program = ProgramFactory::build(programOptions); vector<Program> programs = ProgramFactory::build(programOptions);
unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(metricOptions, move(program)); unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(metricOptions, move(programs));
Population population = PopulationFactory::build(populationOptions, move(fitnessMetric)); Population population = PopulationFactory::build(populationOptions, move(fitnessMetric));
unique_ptr<GeneticAlgorithm> geneticAlgorithm = GeneticAlgorithmFactory::build( unique_ptr<GeneticAlgorithm> geneticAlgorithm = GeneticAlgorithmFactory::build(

View File

@ -108,7 +108,7 @@ public:
static std::unique_ptr<FitnessMetric> build( static std::unique_ptr<FitnessMetric> build(
Options const& _options, Options const& _options,
Program _program std::vector<Program> _programs
); );
}; };
@ -157,12 +157,12 @@ class ProgramFactory
public: public:
struct Options struct Options
{ {
std::string inputFile; std::vector<std::string> inputFiles;
static Options fromCommandLine(boost::program_options::variables_map const& _arguments); static Options fromCommandLine(boost::program_options::variables_map const& _arguments);
}; };
static Program build(Options const& _options); static std::vector<Program> build(Options const& _options);
private: private:
static langutil::CharStream loadSource(std::string const& _sourcePath); static langutil::CharStream loadSource(std::string const& _sourcePath);