Merge pull request #8451 from imapp-pl/yul-phaser-program-cache

[yul-phaser] Program cache
This commit is contained in:
chriseth
2020-03-24 23:02:53 +01:00
committed by GitHub
14 changed files with 697 additions and 48 deletions
+2
View File
@@ -154,6 +154,7 @@ set(yul_phaser_sources
yulPhaser/Phaser.cpp
yulPhaser/Population.cpp
yulPhaser/Program.cpp
yulPhaser/ProgramCache.cpp
yulPhaser/Selections.cpp
yulPhaser/SimulationRNG.cpp
@@ -170,6 +171,7 @@ set(yul_phaser_sources
../tools/yulPhaser/Phaser.cpp
../tools/yulPhaser/Population.cpp
../tools/yulPhaser/Program.cpp
../tools/yulPhaser/ProgramCache.cpp
../tools/yulPhaser/Selections.cpp
../tools/yulPhaser/SimulationRNG.cpp
)
+38 -7
View File
@@ -20,6 +20,8 @@
#include <tools/yulPhaser/AlgorithmRunner.h>
#include <tools/yulPhaser/Common.h>
#include <liblangutil/CharStream.h>
#include <libsolutil/CommonIO.h>
#include <boost/filesystem.hpp>
@@ -29,6 +31,7 @@
using namespace std;
using namespace boost::unit_test::framework;
using namespace boost::test_tools;
using namespace solidity::langutil;
using namespace solidity::util;
namespace fs = boost::filesystem;
@@ -92,7 +95,7 @@ BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest)
BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRunnerFixture)
{
m_options.maxRounds = 5;
AlgorithmRunner runner(Population(m_fitnessMetric), m_options, m_output);
AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output);
CountingAlgorithm algorithm;
@@ -112,6 +115,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, AlgorithmRunnerFixt
// NOTE: Chromosomes chosen so that they're not substrings of each other and are not
// words likely to appear in the output in normal circumstances.
Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}),
{},
m_options,
m_output
);
@@ -131,7 +135,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_f
{
m_options.maxRounds = 0;
m_options.populationAutosaveFile = m_autosavePath;
AlgorithmRunner runner(m_population, m_options, m_output);
AlgorithmRunner runner(m_population, {}, m_options, m_output);
assert(!fs::exists(m_autosavePath));
runner.run(m_algorithm);
@@ -145,7 +149,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_save_population_to_file_if_autosave_file_spec
{
m_options.maxRounds = 1;
m_options.populationAutosaveFile = m_autosavePath;
AlgorithmRunner runner(m_population, m_options, m_output);
AlgorithmRunner runner(m_population, {}, m_options, m_output);
assert(!fs::exists(m_autosavePath));
runner.run(m_algorithm);
@@ -159,7 +163,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_overwrite_existing_file_if_autosave_file_spec
{
m_options.maxRounds = 5;
m_options.populationAutosaveFile = m_autosavePath;
AlgorithmRunner runner(m_population, m_options, m_output);
AlgorithmRunner runner(m_population, {}, m_options, m_output);
assert(!fs::exists(m_autosavePath));
vector<string> originalContent = {"Original content"};
@@ -180,7 +184,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_save_population_to_file_if_autosave_file_
{
m_options.maxRounds = 5;
m_options.populationAutosaveFile = nullopt;
AlgorithmRunner runner(m_population, m_options, m_output);
AlgorithmRunner runner(m_population, {}, m_options, m_output);
assert(!fs::exists(m_autosavePath));
runner.run(m_algorithm);
@@ -198,7 +202,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_randomise_duplicate_chromosomes_if_requested,
m_options.randomiseDuplicates = true;
m_options.minChromosomeLength = 50;
m_options.maxChromosomeLength = 50;
AlgorithmRunner runner(population, m_options, m_output);
AlgorithmRunner runner(population, {}, m_options, m_output);
runner.run(algorithm);
@@ -227,7 +231,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_re
m_options.maxRounds = 1;
m_options.randomiseDuplicates = false;
AlgorithmRunner runner(population, m_options, m_output);
AlgorithmRunner runner(population, {}, m_options, m_output);
runner.run(algorithm);
@@ -237,6 +241,33 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_re
BOOST_TEST(runner.population().individuals()[2].chromosome == duplicate);
}
BOOST_FIXTURE_TEST_CASE(run_should_clear_cache_at_the_beginning_and_update_it_before_each_round, AlgorithmRunnerFixture)
{
CharStream sourceStream = CharStream("{}", current_test_case().p_name);
vector<shared_ptr<ProgramCache>> caches = {
make_shared<ProgramCache>(get<Program>(Program::load(sourceStream))),
make_shared<ProgramCache>(get<Program>(Program::load(sourceStream))),
};
m_options.maxRounds = 10;
AlgorithmRunner runner(Population(m_fitnessMetric), caches, m_options, m_output);
CountingAlgorithm algorithm;
BOOST_TEST(algorithm.m_currentRound == 0);
BOOST_TEST(caches[0]->currentRound() == 0);
BOOST_TEST(caches[1]->currentRound() == 0);
runner.run(algorithm);
BOOST_TEST(algorithm.m_currentRound == 10);
BOOST_TEST(caches[0]->currentRound() == 10);
BOOST_TEST(caches[1]->currentRound() == 10);
runner.run(algorithm);
BOOST_TEST(algorithm.m_currentRound == 20);
BOOST_TEST(caches[0]->currentRound() == 10);
BOOST_TEST(caches[1]->currentRound() == 10);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
+60 -18
View File
@@ -76,15 +76,16 @@ protected:
Chromosome m_chromosome{vector<string>{UnusedPruner::name, EquivalentFunctionCombiner::name}};
Program m_program = get<Program>(Program::load(m_sourceStream));
Program m_optimisedProgram = optimisedProgram(m_program);
shared_ptr<ProgramCache> m_programCache = make_shared<ProgramCache>(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),
make_shared<ProgramSize>(m_program, nullptr, 1),
make_shared<ProgramSize>(m_program, nullptr, 2),
make_shared<ProgramSize>(m_program, nullptr, 3),
};
vector<size_t> m_fitness = {
m_simpleMetrics[0]->evaluate(m_chromosome),
@@ -97,31 +98,66 @@ 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)
BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture)
{
string code = toString(DummyProgramBasedMetric(m_program).optimisedProgram(m_chromosome));
string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgram(m_chromosome));
BOOST_TEST(code != toString(m_program));
BOOST_TEST(code == toString(m_optimisedProgram));
}
BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramBasedMetricFixture)
{
string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgram(m_chromosome));
BOOST_TEST(code != toString(m_program));
BOOST_TEST(code == toString(m_optimisedProgram));
BOOST_TEST(m_programCache->size() == m_chromosome.length());
}
BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture)
{
string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgramNoCache(m_chromosome));
BOOST_TEST(code != toString(m_program));
BOOST_TEST(code == toString(m_optimisedProgram));
}
BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_not_use_cache_even_if_available, ProgramBasedMetricFixture)
{
string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgramNoCache(m_chromosome));
BOOST_TEST(code != toString(m_program));
BOOST_TEST(code == toString(m_optimisedProgram));
BOOST_TEST(m_programCache->size() == 0);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(ProgramSizeTest)
BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, ProgramBasedMetricFixture)
{
size_t fitness = ProgramSize(m_program).evaluate(m_chromosome);
size_t fitness = ProgramSize(m_program, nullptr).evaluate(m_chromosome);
BOOST_TEST(fitness != m_program.codeSize());
BOOST_TEST(fitness == m_optimisedProgram.codeSize());
}
BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture)
{
size_t fitness = ProgramSize(nullopt, m_programCache).evaluate(m_chromosome);
BOOST_TEST(fitness != m_program.codeSize());
BOOST_TEST(fitness == m_optimisedProgram.codeSize());
BOOST_TEST(m_programCache->size() == m_chromosome.length());
}
BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture)
{
Program const& programOptimisedOnce = m_optimisedProgram;
Program programOptimisedTwice = optimisedProgram(programOptimisedOnce);
ProgramSize metric(m_program, 2);
ProgramSize metric(m_program, nullptr, 2);
size_t fitness = metric.evaluate(m_chromosome);
BOOST_TEST(fitness != m_program.codeSize());
@@ -131,7 +167,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number
BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture)
{
ProgramSize metric(m_program, 0);
ProgramSize metric(m_program, nullptr, 0);
size_t fitness = metric.evaluate(m_chromosome);
BOOST_TEST(fitness == m_program.codeSize());
@@ -143,7 +179,13 @@ 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_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize()));
}
BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture)
{
BOOST_TEST(RelativeProgramSize(nullopt, m_programCache, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize()));
BOOST_TEST(m_programCache->size() == m_chromosome.length());
}
BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture)
@@ -151,17 +193,17 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number
Program const& programOptimisedOnce = m_optimisedProgram;
Program programOptimisedTwice = optimisedProgram(programOptimisedOnce);
RelativeProgramSize metric(m_program, 3, 2);
RelativeProgramSize metric(m_program, nullptr, 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 != RelativeProgramSize(programOptimisedTwice, nullptr, 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);
RelativeProgramSize metric(m_program, nullptr, 3, 0);
BOOST_TEST(metric.evaluate(m_chromosome) == 1000);
}
@@ -171,7 +213,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_
CharStream sourceStream = CharStream("{}", "");
Program program = get<Program>(Program::load(sourceStream));
RelativeProgramSize metric(program, 3);
RelativeProgramSize metric(program, nullptr, 3);
BOOST_TEST(metric.evaluate(m_chromosome) == 1000);
BOOST_TEST(metric.evaluate(Chromosome("")) == 1000);
@@ -181,11 +223,11 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_
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_TEST(RelativeProgramSize(m_program, nullptr, 0).evaluate(m_chromosome) == round(1.0 * sizeRatio));
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 1).evaluate(m_chromosome) == round(10.0 * sizeRatio));
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 2).evaluate(m_chromosome) == round(100.0 * sizeRatio));
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * sizeRatio));
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio));
}
BOOST_AUTO_TEST_SUITE_END()
+68 -5
View File
@@ -55,7 +55,7 @@ protected:
};
};
class FitnessMetricFactoryFixture
class FixtureWithPrograms
{
protected:
vector<CharStream> m_sourceStreams = {
@@ -68,6 +68,11 @@ protected:
get<Program>(Program::load(m_sourceStreams[1])),
get<Program>(Program::load(m_sourceStreams[2])),
};
};
class FitnessMetricFactoryFixture: public FixtureWithPrograms
{
protected:
FitnessMetricFactory::Options m_options = {
/* metric = */ MetricChoice::CodeSize,
/* metricAggregator = */ MetricAggregatorChoice::Average,
@@ -154,7 +159,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMet
{
m_options.metric = MetricChoice::RelativeCodeSize;
m_options.metricAggregator = MetricAggregatorChoice::Sum;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]});
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr});
BOOST_REQUIRE(metric != nullptr);
auto sumMetric = dynamic_cast<FitnessMetricSum*>(metric.get());
@@ -172,7 +177,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, Fitn
m_options.metric = MetricChoice::CodeSize;
m_options.metricAggregator = MetricAggregatorChoice::Average;
m_options.chromosomeRepetitions = 5;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]});
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr});
BOOST_REQUIRE(metric != nullptr);
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
@@ -190,7 +195,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac
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]});
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr});
BOOST_REQUIRE(metric != nullptr);
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
@@ -205,7 +210,11 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac
BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, FitnessMetricFactoryFixture)
{
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_programs);
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(
m_options,
m_programs,
vector<shared_ptr<ProgramCache>>(m_programs.size(), nullptr)
);
BOOST_REQUIRE(metric != nullptr);
auto combinedMetric = dynamic_cast<FitnessMetricCombination*>(metric.get());
@@ -213,6 +222,31 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, Fitne
BOOST_REQUIRE(combinedMetric->metrics().size() == m_programs.size());
}
BOOST_FIXTURE_TEST_CASE(build_should_pass_program_caches_to_metrics, FitnessMetricFactoryFixture)
{
assert(m_programs.size() == 3);
vector<shared_ptr<ProgramCache>> caches = {
make_shared<ProgramCache>(m_programs[0]),
make_shared<ProgramCache>(m_programs[1]),
make_shared<ProgramCache>(m_programs[2]),
};
m_options.metric = MetricChoice::RelativeCodeSize;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_programs, caches);
BOOST_REQUIRE(metric != nullptr);
auto combinedMetric = dynamic_cast<FitnessMetricCombination*>(metric.get());
BOOST_REQUIRE(combinedMetric != nullptr);
BOOST_REQUIRE(combinedMetric->metrics().size() == caches.size());
for (size_t i = 0; i < caches.size(); ++i)
{
auto programBasedMetric = dynamic_cast<ProgramBasedMetric*>(combinedMetric->metrics()[i].get());
BOOST_REQUIRE(programBasedMetric != nullptr);
BOOST_TEST(programBasedMetric->programCache() == caches[i].get());
}
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(PopulationFactoryTest)
@@ -317,6 +351,35 @@ BOOST_FIXTURE_TEST_CASE(build_should_combine_populations_from_all_sources, Poula
BOOST_TEST(count(begin, end, Individual(Chromosome("fcL"), *m_fitnessMetric)) >= 2);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(ProgramCacheFactoryTest)
BOOST_FIXTURE_TEST_CASE(build_should_create_cache_for_each_input_program_if_cache_enabled, FixtureWithPrograms)
{
ProgramCacheFactory::Options options{/* programCacheEnabled = */ true};
vector<shared_ptr<ProgramCache>> caches = ProgramCacheFactory::build(options, m_programs);
assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful");
BOOST_TEST(caches.size() == m_programs.size());
for (size_t i = 0; i < m_programs.size(); ++i)
{
BOOST_REQUIRE(caches[i] != nullptr);
BOOST_TEST(toString(caches[i]->program()) == toString(m_programs[i]));
}
}
BOOST_FIXTURE_TEST_CASE(build_should_return_nullptr_for_each_input_program_if_cache_disabled, FixtureWithPrograms)
{
ProgramCacheFactory::Options options{/* programCacheEnabled = */ false};
vector<shared_ptr<ProgramCache>> caches = ProgramCacheFactory::build(options, m_programs);
assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful");
BOOST_TEST(caches.size() == m_programs.size());
for (size_t i = 0; i < m_programs.size(); ++i)
BOOST_TEST(caches[i] == nullptr);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(ProgramFactoryTest)
+207
View File
@@ -0,0 +1,207 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <tools/yulPhaser/ProgramCache.h>
#include <tools/yulPhaser/Chromosome.h>
#include <liblangutil/CharStream.h>
#include <libsolutil/CommonIO.h>
#include <boost/test/unit_test.hpp>
#include <string>
#include <set>
using namespace std;
using namespace solidity::util;
using namespace solidity::langutil;
using namespace solidity::yul;
namespace solidity::phaser::test
{
class ProgramCacheFixture
{
protected:
static constexpr char SampleSourceCode[] =
"{\n"
" for { let i := 0 } not(eq(i, 15)) { i := add(i, 1) }\n"
" {\n"
" let x := 1\n"
" mstore(i, 2)\n"
" }\n"
"}\n";
Program optimisedProgram(Program _program, string _abbreviatedOptimisationSteps) const
{
Program result = move(_program);
result.optimise(Chromosome(_abbreviatedOptimisationSteps).optimisationSteps());
return result;
}
static set<string> cachedKeys(ProgramCache const& _programCache)
{
set<string> keys;
for (auto pair = _programCache.entries().begin(); pair != _programCache.entries().end(); ++pair)
keys.insert(pair->first);
return keys;
}
CharStream m_sourceStream = CharStream(SampleSourceCode, "program-cache-test");
Program m_program = get<Program>(Program::load(m_sourceStream));
ProgramCache m_programCache{m_program};
};
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(ProgramCacheTest)
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_apply_optimisation_steps_to_program, ProgramCacheFixture)
{
Program expectedProgram = optimisedProgram(m_program, "IuO");
assert(toString(expectedProgram) != toString(m_program));
Program cachedProgram = m_programCache.optimiseProgram("IuO");
BOOST_TEST(toString(cachedProgram) == toString(expectedProgram));
}
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_store_programs_for_all_prefixes, ProgramCacheFixture)
{
Program programI = optimisedProgram(m_program, "I");
Program programIu = optimisedProgram(programI, "u");
Program programIuO = optimisedProgram(programIu, "O");
assert(toString(m_program) != toString(programI));
assert(toString(m_program) != toString(programIu));
assert(toString(m_program) != toString(programIuO));
assert(toString(programI) != toString(programIu));
assert(toString(programI) != toString(programIuO));
assert(toString(programIu) != toString(programIuO));
BOOST_REQUIRE(m_programCache.size() == 0);
Program cachedProgram = m_programCache.optimiseProgram("IuO");
BOOST_TEST(toString(cachedProgram) == toString(programIuO));
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "IuO"}));
BOOST_TEST(toString(*m_programCache.find("I")) == toString(programI));
BOOST_TEST(toString(*m_programCache.find("Iu")) == toString(programIu));
BOOST_TEST(toString(*m_programCache.find("IuO")) == toString(programIuO));
}
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_repeat_the_chromosome_requested_number_of_times, ProgramCacheFixture)
{
string steps = "IuOIuO";
Program cachedProgram = m_programCache.optimiseProgram("IuO", 2);
ProgramCache cacheNoRepetitions(m_program);
Program cachedProgramNoRepetitions = cacheNoRepetitions.optimiseProgram("IuOIuO");
BOOST_TEST(toString(cachedProgram) == toString(cachedProgramNoRepetitions));
for (size_t size = 1; size <= 6; ++size)
{
BOOST_REQUIRE(m_programCache.contains(steps.substr(0, size)));
BOOST_REQUIRE(cacheNoRepetitions.contains(steps.substr(0, size)));
BOOST_TEST(
toString(*cacheNoRepetitions.find(steps.substr(0, size))) ==
toString(*m_programCache.find(steps.substr(0, size)))
);
}
}
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_reuse_the_longest_prefix_and_move_it_to_the_next_round, ProgramCacheFixture)
{
BOOST_TEST(m_programCache.currentRound() == 0);
m_programCache.optimiseProgram("Iu");
m_programCache.optimiseProgram("Ia");
m_programCache.startRound(1);
BOOST_TEST(m_programCache.currentRound() == 1);
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "Ia"}));
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0);
m_programCache.optimiseProgram("IuOI");
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "Ia", "IuO", "IuOI"}));
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 1);
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 1);
BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("IuO")->second.roundNumber == 1);
BOOST_TEST(m_programCache.entries().find("IuOI")->second.roundNumber == 1);
}
BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, ProgramCacheFixture)
{
BOOST_TEST(m_programCache.currentRound() == 0);
BOOST_TEST(m_programCache.size() == 0);
m_programCache.optimiseProgram("Iu");
BOOST_TEST(m_programCache.currentRound() == 0);
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu"}));
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
m_programCache.optimiseProgram("a");
BOOST_TEST(m_programCache.currentRound() == 0);
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "a"}));
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0);
m_programCache.startRound(1);
BOOST_TEST(m_programCache.currentRound() == 1);
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "a"}));
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0);
m_programCache.optimiseProgram("af");
BOOST_TEST(m_programCache.currentRound() == 1);
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "a", "af"}));
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1);
BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1);
m_programCache.startRound(2);
BOOST_TEST(m_programCache.currentRound() == 2);
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"a", "af"}));
BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1);
BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1);
m_programCache.startRound(3);
BOOST_TEST(m_programCache.currentRound() == 3);
BOOST_TEST(m_programCache.size() == 0);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
}