mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8423 from imapp-pl/yul-phaser-population-and-algorithm-options
[yul-phaser] Population and algorithm options
This commit is contained in:
commit
1c25104741
@ -151,6 +151,7 @@ set(yul_phaser_sources
|
|||||||
yulPhaser/GeneticAlgorithms.cpp
|
yulPhaser/GeneticAlgorithms.cpp
|
||||||
yulPhaser/Mutations.cpp
|
yulPhaser/Mutations.cpp
|
||||||
yulPhaser/PairSelections.cpp
|
yulPhaser/PairSelections.cpp
|
||||||
|
yulPhaser/Phaser.cpp
|
||||||
yulPhaser/Population.cpp
|
yulPhaser/Population.cpp
|
||||||
yulPhaser/Program.cpp
|
yulPhaser/Program.cpp
|
||||||
yulPhaser/Selections.cpp
|
yulPhaser/Selections.cpp
|
||||||
@ -160,11 +161,13 @@ set(yul_phaser_sources
|
|||||||
# My current workaround is just to include its source files here but this introduces
|
# My current workaround is just to include its source files here but this introduces
|
||||||
# unnecessary duplication. Create a library or find a way to reuse the list in both places.
|
# unnecessary duplication. Create a library or find a way to reuse the list in both places.
|
||||||
../tools/yulPhaser/AlgorithmRunner.cpp
|
../tools/yulPhaser/AlgorithmRunner.cpp
|
||||||
|
../tools/yulPhaser/Common.cpp
|
||||||
../tools/yulPhaser/Chromosome.cpp
|
../tools/yulPhaser/Chromosome.cpp
|
||||||
../tools/yulPhaser/FitnessMetrics.cpp
|
../tools/yulPhaser/FitnessMetrics.cpp
|
||||||
../tools/yulPhaser/GeneticAlgorithms.cpp
|
../tools/yulPhaser/GeneticAlgorithms.cpp
|
||||||
../tools/yulPhaser/Mutations.cpp
|
../tools/yulPhaser/Mutations.cpp
|
||||||
../tools/yulPhaser/PairSelections.cpp
|
../tools/yulPhaser/PairSelections.cpp
|
||||||
|
../tools/yulPhaser/Phaser.cpp
|
||||||
../tools/yulPhaser/Population.cpp
|
../tools/yulPhaser/Population.cpp
|
||||||
../tools/yulPhaser/Program.cpp
|
../tools/yulPhaser/Program.cpp
|
||||||
../tools/yulPhaser/Selections.cpp
|
../tools/yulPhaser/Selections.cpp
|
||||||
|
@ -18,9 +18,11 @@
|
|||||||
#include <test/yulPhaser/TestHelpers.h>
|
#include <test/yulPhaser/TestHelpers.h>
|
||||||
|
|
||||||
#include <tools/yulPhaser/AlgorithmRunner.h>
|
#include <tools/yulPhaser/AlgorithmRunner.h>
|
||||||
|
#include <tools/yulPhaser/Common.h>
|
||||||
|
|
||||||
#include <libsolutil/CommonIO.h>
|
#include <libsolutil/CommonIO.h>
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <boost/test/tools/output_test_stream.hpp>
|
#include <boost/test/tools/output_test_stream.hpp>
|
||||||
|
|
||||||
@ -29,10 +31,12 @@ using namespace boost::unit_test::framework;
|
|||||||
using namespace boost::test_tools;
|
using namespace boost::test_tools;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
namespace solidity::phaser::test
|
namespace solidity::phaser::test
|
||||||
{
|
{
|
||||||
|
|
||||||
class DummyAlgorithm: public GeneticAlgorithm
|
class CountingAlgorithm: public GeneticAlgorithm
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using GeneticAlgorithm::GeneticAlgorithm;
|
using GeneticAlgorithm::GeneticAlgorithm;
|
||||||
@ -45,6 +49,16 @@ public:
|
|||||||
size_t m_currentRound = 0;
|
size_t m_currentRound = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RandomisingAlgorithm: public GeneticAlgorithm
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using GeneticAlgorithm::GeneticAlgorithm;
|
||||||
|
Population runNextRound(Population _population) override
|
||||||
|
{
|
||||||
|
return Population::makeRandom(_population.fitnessMetric(), _population.individuals().size(), 10, 20);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class AlgorithmRunnerFixture
|
class AlgorithmRunnerFixture
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@ -53,6 +67,25 @@ protected:
|
|||||||
AlgorithmRunner::Options m_options;
|
AlgorithmRunner::Options m_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AlgorithmRunnerAutosaveFixture: public AlgorithmRunnerFixture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static vector<string> chromosomeStrings(Population const& _population)
|
||||||
|
{
|
||||||
|
vector<string> lines;
|
||||||
|
for (auto const& individual: _population.individuals())
|
||||||
|
lines.push_back(toString(individual.chromosome));
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TemporaryDirectory m_tempDir;
|
||||||
|
string const m_autosavePath = m_tempDir.memberPath("population-autosave.txt");
|
||||||
|
Population const m_population = Population::makeRandom(m_fitnessMetric, 5, 0, 20);
|
||||||
|
RandomisingAlgorithm m_algorithm;
|
||||||
|
};
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(Phaser)
|
BOOST_AUTO_TEST_SUITE(Phaser)
|
||||||
BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest)
|
BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest)
|
||||||
|
|
||||||
@ -60,7 +93,8 @@ BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRu
|
|||||||
{
|
{
|
||||||
m_options.maxRounds = 5;
|
m_options.maxRounds = 5;
|
||||||
AlgorithmRunner runner(Population(m_fitnessMetric), m_options, m_output);
|
AlgorithmRunner runner(Population(m_fitnessMetric), m_options, m_output);
|
||||||
DummyAlgorithm algorithm;
|
|
||||||
|
CountingAlgorithm algorithm;
|
||||||
|
|
||||||
BOOST_TEST(algorithm.m_currentRound == 0);
|
BOOST_TEST(algorithm.m_currentRound == 0);
|
||||||
runner.run(algorithm);
|
runner.run(algorithm);
|
||||||
@ -82,7 +116,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, AlgorithmRunnerFixt
|
|||||||
m_output
|
m_output
|
||||||
);
|
);
|
||||||
|
|
||||||
DummyAlgorithm algorithm;
|
CountingAlgorithm algorithm;
|
||||||
|
|
||||||
BOOST_TEST(m_output.is_empty());
|
BOOST_TEST(m_output.is_empty());
|
||||||
runner.run(algorithm);
|
runner.run(algorithm);
|
||||||
@ -93,6 +127,116 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, AlgorithmRunnerFixt
|
|||||||
BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(runner.population().individuals()[0].chromosome)) == 4);
|
BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(runner.population().individuals()[0].chromosome)) == 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 0;
|
||||||
|
m_options.populationAutosaveFile = m_autosavePath;
|
||||||
|
AlgorithmRunner runner(m_population, m_options, m_output);
|
||||||
|
assert(!fs::exists(m_autosavePath));
|
||||||
|
|
||||||
|
runner.run(m_algorithm);
|
||||||
|
assert(runner.population() == m_population);
|
||||||
|
|
||||||
|
BOOST_TEST(fs::is_regular_file(m_autosavePath));
|
||||||
|
BOOST_TEST(readLinesFromFile(m_autosavePath) == chromosomeStrings(runner.population()));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_save_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 1;
|
||||||
|
m_options.populationAutosaveFile = m_autosavePath;
|
||||||
|
AlgorithmRunner runner(m_population, m_options, m_output);
|
||||||
|
assert(!fs::exists(m_autosavePath));
|
||||||
|
|
||||||
|
runner.run(m_algorithm);
|
||||||
|
assert(runner.population() != m_population);
|
||||||
|
|
||||||
|
BOOST_TEST(fs::is_regular_file(m_autosavePath));
|
||||||
|
BOOST_TEST(readLinesFromFile(m_autosavePath) == chromosomeStrings(runner.population()));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_overwrite_existing_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 5;
|
||||||
|
m_options.populationAutosaveFile = m_autosavePath;
|
||||||
|
AlgorithmRunner runner(m_population, m_options, m_output);
|
||||||
|
assert(!fs::exists(m_autosavePath));
|
||||||
|
|
||||||
|
vector<string> originalContent = {"Original content"};
|
||||||
|
{
|
||||||
|
ofstream tmpFile(m_autosavePath);
|
||||||
|
tmpFile << originalContent[0] << endl;
|
||||||
|
}
|
||||||
|
assert(fs::exists(m_autosavePath));
|
||||||
|
assert(readLinesFromFile(m_autosavePath) == originalContent);
|
||||||
|
|
||||||
|
runner.run(m_algorithm);
|
||||||
|
|
||||||
|
BOOST_TEST(fs::is_regular_file(m_autosavePath));
|
||||||
|
BOOST_TEST(readLinesFromFile(m_autosavePath) != originalContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_not_save_population_to_file_if_autosave_file_not_specified, AlgorithmRunnerAutosaveFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 5;
|
||||||
|
m_options.populationAutosaveFile = nullopt;
|
||||||
|
AlgorithmRunner runner(m_population, m_options, m_output);
|
||||||
|
assert(!fs::exists(m_autosavePath));
|
||||||
|
|
||||||
|
runner.run(m_algorithm);
|
||||||
|
|
||||||
|
BOOST_TEST(!fs::exists(m_autosavePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_randomise_duplicate_chromosomes_if_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
Chromosome duplicate("afc");
|
||||||
|
Population population(m_fitnessMetric, {duplicate, duplicate, duplicate});
|
||||||
|
CountingAlgorithm algorithm;
|
||||||
|
|
||||||
|
m_options.maxRounds = 1;
|
||||||
|
m_options.randomiseDuplicates = true;
|
||||||
|
m_options.minChromosomeLength = 50;
|
||||||
|
m_options.maxChromosomeLength = 50;
|
||||||
|
AlgorithmRunner runner(population, m_options, m_output);
|
||||||
|
|
||||||
|
runner.run(algorithm);
|
||||||
|
|
||||||
|
auto const& newIndividuals = runner.population().individuals();
|
||||||
|
|
||||||
|
BOOST_TEST(newIndividuals.size() == 3);
|
||||||
|
BOOST_TEST((
|
||||||
|
newIndividuals[0].chromosome == duplicate ||
|
||||||
|
newIndividuals[1].chromosome == duplicate ||
|
||||||
|
newIndividuals[2].chromosome == duplicate
|
||||||
|
));
|
||||||
|
BOOST_TEST(newIndividuals[0] != newIndividuals[1]);
|
||||||
|
BOOST_TEST(newIndividuals[0] != newIndividuals[2]);
|
||||||
|
BOOST_TEST(newIndividuals[1] != newIndividuals[2]);
|
||||||
|
|
||||||
|
BOOST_TEST((newIndividuals[0].chromosome.length() == 50 || newIndividuals[0].chromosome == duplicate));
|
||||||
|
BOOST_TEST((newIndividuals[1].chromosome.length() == 50 || newIndividuals[1].chromosome == duplicate));
|
||||||
|
BOOST_TEST((newIndividuals[2].chromosome.length() == 50 || newIndividuals[2].chromosome == duplicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
Chromosome duplicate("afc");
|
||||||
|
Population population(m_fitnessMetric, {duplicate, duplicate, duplicate});
|
||||||
|
CountingAlgorithm algorithm;
|
||||||
|
|
||||||
|
m_options.maxRounds = 1;
|
||||||
|
m_options.randomiseDuplicates = false;
|
||||||
|
AlgorithmRunner runner(population, m_options, m_output);
|
||||||
|
|
||||||
|
runner.run(algorithm);
|
||||||
|
|
||||||
|
BOOST_TEST(runner.population().individuals().size() == 3);
|
||||||
|
BOOST_TEST(runner.population().individuals()[0].chromosome == duplicate);
|
||||||
|
BOOST_TEST(runner.population().individuals()[1].chromosome == duplicate);
|
||||||
|
BOOST_TEST(runner.population().individuals()[2].chromosome == duplicate);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <test/yulPhaser/TestHelpers.h>
|
||||||
|
|
||||||
#include <tools/yulPhaser/Common.h>
|
#include <tools/yulPhaser/Common.h>
|
||||||
|
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
@ -22,6 +24,7 @@
|
|||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <boost/test/tools/output_test_stream.hpp>
|
#include <boost/test/tools/output_test_stream.hpp>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -32,6 +35,12 @@ using namespace solidity::util;
|
|||||||
namespace solidity::phaser::test
|
namespace solidity::phaser::test
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ReadLinesFromFileFixture
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
TemporaryDirectory m_tempDir;
|
||||||
|
};
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -60,6 +69,17 @@ map<string, TestEnum> const StringToTestEnumMap = invertMap(TestEnumToStringMap)
|
|||||||
BOOST_AUTO_TEST_SUITE(Phaser)
|
BOOST_AUTO_TEST_SUITE(Phaser)
|
||||||
BOOST_AUTO_TEST_SUITE(CommonTest)
|
BOOST_AUTO_TEST_SUITE(CommonTest)
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(readLinesFromFile_should_return_all_lines_from_a_text_file_as_strings_without_newlines, ReadLinesFromFileFixture)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
ofstream tmpFile(m_tempDir.memberPath("test-file.txt"));
|
||||||
|
tmpFile << endl << "Line 1" << endl << endl << endl << "Line 2" << endl << "#" << endl << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> lines = readLinesFromFile(m_tempDir.memberPath("test-file.txt"));
|
||||||
|
BOOST_TEST((lines == vector<string>{"", "Line 1", "", "", "Line 2", "#", ""}));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(deserializeChoice_should_convert_string_to_enum)
|
BOOST_AUTO_TEST_CASE(deserializeChoice_should_convert_string_to_enum)
|
||||||
{
|
{
|
||||||
istringstream aStream("a");
|
istringstream aStream("a");
|
||||||
|
290
test/yulPhaser/Phaser.cpp
Normal file
290
test/yulPhaser/Phaser.cpp
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
/*
|
||||||
|
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 <test/yulPhaser/TestHelpers.h>
|
||||||
|
|
||||||
|
#include <tools/yulPhaser/Exceptions.h>
|
||||||
|
#include <tools/yulPhaser/Phaser.h>
|
||||||
|
|
||||||
|
#include <liblangutil/CharStream.h>
|
||||||
|
|
||||||
|
#include <libsolutil/CommonIO.h>
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
namespace solidity::phaser::test
|
||||||
|
{
|
||||||
|
|
||||||
|
class GeneticAlgorithmFactoryFixture
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
GeneticAlgorithmFactory::Options m_options = {
|
||||||
|
/* algorithm = */ Algorithm::Random,
|
||||||
|
/* minChromosomeLength = */ 50,
|
||||||
|
/* maxChromosomeLength = */ 100,
|
||||||
|
/* randomElitePoolSize = */ 0.5,
|
||||||
|
/* gewepMutationPoolSize = */ 0.1,
|
||||||
|
/* gewepCrossoverPoolSize = */ 0.1,
|
||||||
|
/* gewepRandomisationChance = */ 0.6,
|
||||||
|
/* gewepDeletionVsAdditionChance = */ 0.3,
|
||||||
|
/* gewepGenesToRandomise = */ 0.4,
|
||||||
|
/* gewepGenesToAddOrDelete = */ 0.2,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class FitnessMetricFactoryFixture
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
CharStream m_sourceStream = CharStream("{}", "");
|
||||||
|
Program m_program = get<Program>(Program::load(m_sourceStream));
|
||||||
|
FitnessMetricFactory::Options m_options = {
|
||||||
|
/* chromosomeRepetitions = */ 1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class PoulationFactoryFixture
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
shared_ptr<FitnessMetric> m_fitnessMetric = make_shared<ChromosomeLengthMetric>();
|
||||||
|
PopulationFactory::Options m_options = {
|
||||||
|
/* minChromosomeLength = */ 0,
|
||||||
|
/* maxChromosomeLength = */ 0,
|
||||||
|
/* population = */ {},
|
||||||
|
/* randomPopulation = */ {},
|
||||||
|
/* populationFromFile = */ {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(Phaser)
|
||||||
|
BOOST_AUTO_TEST_SUITE(PhaserTest)
|
||||||
|
BOOST_AUTO_TEST_SUITE(GeneticAlgorithmFactoryTest)
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_options_to_it, GeneticAlgorithmFactoryFixture)
|
||||||
|
{
|
||||||
|
m_options.algorithm = Algorithm::Random;
|
||||||
|
unique_ptr<GeneticAlgorithm> algorithm1 = GeneticAlgorithmFactory::build(m_options, 100);
|
||||||
|
BOOST_REQUIRE(algorithm1 != nullptr);
|
||||||
|
|
||||||
|
auto randomAlgorithm = dynamic_cast<RandomAlgorithm*>(algorithm1.get());
|
||||||
|
BOOST_REQUIRE(randomAlgorithm != nullptr);
|
||||||
|
BOOST_TEST(randomAlgorithm->options().elitePoolSize == m_options.randomElitePoolSize.value());
|
||||||
|
BOOST_TEST(randomAlgorithm->options().minChromosomeLength == m_options.minChromosomeLength);
|
||||||
|
BOOST_TEST(randomAlgorithm->options().maxChromosomeLength == m_options.maxChromosomeLength);
|
||||||
|
|
||||||
|
m_options.algorithm = Algorithm::GEWEP;
|
||||||
|
unique_ptr<GeneticAlgorithm> algorithm2 = GeneticAlgorithmFactory::build(m_options, 100);
|
||||||
|
BOOST_REQUIRE(algorithm2 != nullptr);
|
||||||
|
|
||||||
|
auto gewepAlgorithm = dynamic_cast<GenerationalElitistWithExclusivePools*>(algorithm2.get());
|
||||||
|
BOOST_REQUIRE(gewepAlgorithm != nullptr);
|
||||||
|
BOOST_TEST(gewepAlgorithm->options().mutationPoolSize == m_options.gewepMutationPoolSize);
|
||||||
|
BOOST_TEST(gewepAlgorithm->options().crossoverPoolSize == m_options.gewepCrossoverPoolSize);
|
||||||
|
BOOST_TEST(gewepAlgorithm->options().randomisationChance == m_options.gewepRandomisationChance);
|
||||||
|
BOOST_TEST(gewepAlgorithm->options().deletionVsAdditionChance == m_options.gewepDeletionVsAdditionChance);
|
||||||
|
BOOST_TEST(gewepAlgorithm->options().percentGenesToRandomise == m_options.gewepGenesToRandomise.value());
|
||||||
|
BOOST_TEST(gewepAlgorithm->options().percentGenesToAddOrDelete == m_options.gewepGenesToAddOrDelete.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_set_random_algorithm_elite_pool_size_based_on_population_size_if_not_specified, GeneticAlgorithmFactoryFixture)
|
||||||
|
{
|
||||||
|
m_options.algorithm = Algorithm::Random;
|
||||||
|
m_options.randomElitePoolSize = nullopt;
|
||||||
|
unique_ptr<GeneticAlgorithm> algorithm = GeneticAlgorithmFactory::build(m_options, 100);
|
||||||
|
BOOST_REQUIRE(algorithm != nullptr);
|
||||||
|
|
||||||
|
auto randomAlgorithm = dynamic_cast<RandomAlgorithm*>(algorithm.get());
|
||||||
|
BOOST_REQUIRE(randomAlgorithm != nullptr);
|
||||||
|
BOOST_TEST(randomAlgorithm->options().elitePoolSize == 1.0 / 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_set_gewep_mutation_percentages_based_on_maximum_chromosome_length_if_not_specified, GeneticAlgorithmFactoryFixture)
|
||||||
|
{
|
||||||
|
m_options.algorithm = Algorithm::GEWEP;
|
||||||
|
m_options.gewepGenesToRandomise = nullopt;
|
||||||
|
m_options.gewepGenesToAddOrDelete = nullopt;
|
||||||
|
m_options.maxChromosomeLength = 125;
|
||||||
|
|
||||||
|
unique_ptr<GeneticAlgorithm> algorithm = GeneticAlgorithmFactory::build(m_options, 100);
|
||||||
|
BOOST_REQUIRE(algorithm != nullptr);
|
||||||
|
|
||||||
|
auto gewepAlgorithm = dynamic_cast<GenerationalElitistWithExclusivePools*>(algorithm.get());
|
||||||
|
BOOST_REQUIRE(gewepAlgorithm != nullptr);
|
||||||
|
BOOST_TEST(gewepAlgorithm->options().percentGenesToRandomise == 1.0 / 125.0);
|
||||||
|
BOOST_TEST(gewepAlgorithm->options().percentGenesToAddOrDelete == 1.0 / 125.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
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);
|
||||||
|
BOOST_REQUIRE(metric != nullptr);
|
||||||
|
|
||||||
|
auto programSizeMetric = dynamic_cast<ProgramSize*>(metric.get());
|
||||||
|
BOOST_REQUIRE(programSizeMetric != nullptr);
|
||||||
|
BOOST_TEST(toString(programSizeMetric->program()) == toString(m_program));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, FitnessMetricFactoryFixture)
|
||||||
|
{
|
||||||
|
m_options.chromosomeRepetitions = 5;
|
||||||
|
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_program);
|
||||||
|
BOOST_REQUIRE(metric != nullptr);
|
||||||
|
|
||||||
|
auto programSizeMetric = dynamic_cast<ProgramSize*>(metric.get());
|
||||||
|
BOOST_REQUIRE(programSizeMetric != nullptr);
|
||||||
|
BOOST_TEST(programSizeMetric->repetitionCount() == m_options.chromosomeRepetitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
BOOST_AUTO_TEST_SUITE(PopulationFactoryTest)
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_create_an_empty_population_if_no_specific_options_given, PoulationFactoryFixture)
|
||||||
|
{
|
||||||
|
m_options.population = {};
|
||||||
|
m_options.randomPopulation = {};
|
||||||
|
m_options.populationFromFile = {};
|
||||||
|
BOOST_TEST(
|
||||||
|
PopulationFactory::build(m_options, m_fitnessMetric) ==
|
||||||
|
Population(m_fitnessMetric, vector<Chromosome>{})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_respect_population_option, PoulationFactoryFixture)
|
||||||
|
{
|
||||||
|
m_options.population = {"a", "afc", "xadd"};
|
||||||
|
BOOST_TEST(
|
||||||
|
PopulationFactory::build(m_options, m_fitnessMetric) ==
|
||||||
|
Population(m_fitnessMetric, {Chromosome("a"), Chromosome("afc"), Chromosome("xadd")})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_respect_random_population_option, PoulationFactoryFixture)
|
||||||
|
{
|
||||||
|
m_options.randomPopulation = {5, 3, 2};
|
||||||
|
m_options.minChromosomeLength = 5;
|
||||||
|
m_options.maxChromosomeLength = 10;
|
||||||
|
|
||||||
|
auto population = PopulationFactory::build(m_options, m_fitnessMetric);
|
||||||
|
|
||||||
|
BOOST_TEST(population.individuals().size() == 10);
|
||||||
|
BOOST_TEST(all_of(
|
||||||
|
population.individuals().begin(),
|
||||||
|
population.individuals().end(),
|
||||||
|
[](auto const& individual){ return 5 <= individual.chromosome.length() && individual.chromosome.length() <= 10; }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_respect_population_from_file_option, PoulationFactoryFixture)
|
||||||
|
{
|
||||||
|
map<string, vector<string>> fileContent = {
|
||||||
|
{"a.txt", {"a", "fff", "", "jxccLTa"}},
|
||||||
|
{"b.txt", {}},
|
||||||
|
{"c.txt", {""}},
|
||||||
|
{"d.txt", {"c", "T"}},
|
||||||
|
};
|
||||||
|
|
||||||
|
TemporaryDirectory tempDir;
|
||||||
|
for (auto const& [fileName, chromosomes]: fileContent)
|
||||||
|
{
|
||||||
|
ofstream tmpFile(tempDir.memberPath(fileName));
|
||||||
|
for (auto const& chromosome: chromosomes)
|
||||||
|
tmpFile << chromosome << endl;
|
||||||
|
|
||||||
|
m_options.populationFromFile.push_back(tempDir.memberPath(fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_TEST(
|
||||||
|
PopulationFactory::build(m_options, m_fitnessMetric) ==
|
||||||
|
Population(m_fitnessMetric, {
|
||||||
|
Chromosome("a"),
|
||||||
|
Chromosome("fff"),
|
||||||
|
Chromosome(""),
|
||||||
|
Chromosome("jxccLTa"),
|
||||||
|
Chromosome(""),
|
||||||
|
Chromosome("c"),
|
||||||
|
Chromosome("T"),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_throw_FileOpenError_if_population_file_does_not_exist, PoulationFactoryFixture)
|
||||||
|
{
|
||||||
|
m_options.populationFromFile = {"a-file-that-does-not-exist.abcdefgh"};
|
||||||
|
assert(!fs::exists(m_options.populationFromFile[0]));
|
||||||
|
|
||||||
|
BOOST_CHECK_THROW(PopulationFactory::build(m_options, m_fitnessMetric), FileOpenError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_combine_populations_from_all_sources, PoulationFactoryFixture)
|
||||||
|
{
|
||||||
|
TemporaryDirectory tempDir;
|
||||||
|
{
|
||||||
|
ofstream tmpFile(tempDir.memberPath("population.txt"));
|
||||||
|
tmpFile << "axc" << endl << "fcL" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_options.population = {"axc", "fcL"};
|
||||||
|
m_options.randomPopulation = {2};
|
||||||
|
m_options.populationFromFile = {tempDir.memberPath("population.txt")};
|
||||||
|
m_options.minChromosomeLength = 3;
|
||||||
|
m_options.maxChromosomeLength = 3;
|
||||||
|
|
||||||
|
auto population = PopulationFactory::build(m_options, m_fitnessMetric);
|
||||||
|
|
||||||
|
auto begin = population.individuals().begin();
|
||||||
|
auto end = population.individuals().end();
|
||||||
|
BOOST_TEST(population.individuals().size() == 6);
|
||||||
|
BOOST_TEST(all_of(begin, end, [](auto const& individual){ return individual.chromosome.length() == 3; }));
|
||||||
|
BOOST_TEST(count(begin, end, Individual(Chromosome("axc"), *m_fitnessMetric)) >= 2);
|
||||||
|
BOOST_TEST(count(begin, end, Individual(Chromosome("fcL"), *m_fitnessMetric)) >= 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
BOOST_AUTO_TEST_SUITE(ProgramFactoryTest)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(build_should_load_program_from_file)
|
||||||
|
{
|
||||||
|
TemporaryDirectory tempDir;
|
||||||
|
{
|
||||||
|
ofstream tmpFile(tempDir.memberPath("program.yul"));
|
||||||
|
tmpFile << "{}" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramFactory::Options options{/* inputFile = */ tempDir.memberPath("program.yul")};
|
||||||
|
CharStream expectedProgramSource("{}", "");
|
||||||
|
|
||||||
|
auto program = ProgramFactory::build(options);
|
||||||
|
|
||||||
|
BOOST_TEST(toString(program) == toString(get<Program>(Program::load(expectedProgramSource))));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
}
|
@ -19,12 +19,18 @@
|
|||||||
|
|
||||||
#include <libyul/optimiser/Suite.h>
|
#include <libyul/optimiser/Suite.h>
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace solidity::phaser;
|
using namespace solidity::phaser;
|
||||||
|
using namespace solidity::phaser::test;
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
function<Mutation> phaser::test::wholeChromosomeReplacement(Chromosome _newChromosome)
|
function<Mutation> phaser::test::wholeChromosomeReplacement(Chromosome _newChromosome)
|
||||||
{
|
{
|
||||||
@ -71,6 +77,40 @@ size_t phaser::test::countDifferences(Chromosome const& _chromosome1, Chromosome
|
|||||||
return count + abs(static_cast<int>(_chromosome1.length() - _chromosome2.length()));
|
return count + abs(static_cast<int>(_chromosome1.length() - _chromosome2.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TemporaryDirectory::TemporaryDirectory(std::string const& _prefix):
|
||||||
|
m_path((fs::temp_directory_path() / fs::unique_path(_prefix + "%%%%-%%%%-%%%%-%%%%")).string())
|
||||||
|
{
|
||||||
|
// Prefix should just be a file name and not contain anything that would make us step out of /tmp.
|
||||||
|
assert(fs::path(_prefix) == fs::path(_prefix).stem());
|
||||||
|
|
||||||
|
fs::create_directory(m_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
TemporaryDirectory::~TemporaryDirectory()
|
||||||
|
{
|
||||||
|
// A few paranoid sanity checks just to be extra sure we're not deleting someone's homework.
|
||||||
|
assert(m_path.find(fs::temp_directory_path().string()) == 0);
|
||||||
|
assert(fs::path(m_path) != fs::temp_directory_path());
|
||||||
|
assert(fs::path(m_path) != fs::path(m_path).root_path());
|
||||||
|
assert(!fs::path(m_path).empty());
|
||||||
|
|
||||||
|
boost::system::error_code errorCode;
|
||||||
|
uintmax_t numRemoved = fs::remove_all(m_path, errorCode);
|
||||||
|
if (errorCode.value() != boost::system::errc::success)
|
||||||
|
{
|
||||||
|
cerr << "Failed to completely remove temporary directory '" << m_path << "'. ";
|
||||||
|
cerr << "Only " << numRemoved << " files were actually removed." << endl;
|
||||||
|
cerr << "Reason: " << errorCode.message() << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string TemporaryDirectory::memberPath(string const& _relativePath) const
|
||||||
|
{
|
||||||
|
assert(fs::path(_relativePath).is_relative());
|
||||||
|
|
||||||
|
return (fs::path(m_path) / _relativePath).string();
|
||||||
|
}
|
||||||
|
|
||||||
string phaser::test::stripWhitespace(string const& input)
|
string phaser::test::stripWhitespace(string const& input)
|
||||||
{
|
{
|
||||||
regex whitespaceRegex("\\s+");
|
regex whitespaceRegex("\\s+");
|
||||||
|
@ -79,6 +79,31 @@ size_t countDifferences(Chromosome const& _chromosome1, Chromosome const& _chrom
|
|||||||
/// integers.
|
/// integers.
|
||||||
std::map<std::string, size_t> enumerateOptmisationSteps();
|
std::map<std::string, size_t> enumerateOptmisationSteps();
|
||||||
|
|
||||||
|
// FILESYSTEM UTILITIES
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that creates a unique temporary directory and automatically deletes it and its
|
||||||
|
* content upon being destroyed.
|
||||||
|
*
|
||||||
|
* The directory is guaranteed to be newly created and empty. Directory names are generated
|
||||||
|
* randomly. If a directory with the same name already exists (very unlikely but possible) the
|
||||||
|
* object won't reuse it and will fail with an exception instead.
|
||||||
|
*/
|
||||||
|
class TemporaryDirectory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TemporaryDirectory(std::string const& _prefix = "yul-phaser-test-");
|
||||||
|
~TemporaryDirectory();
|
||||||
|
|
||||||
|
std::string const& path() const { return m_path; }
|
||||||
|
|
||||||
|
/// Converts a path relative to the directory held by the object into an absolute one.
|
||||||
|
std::string memberPath(std::string const& _relativePath) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_path;
|
||||||
|
};
|
||||||
|
|
||||||
// STRING UTILITIES
|
// STRING UTILITIES
|
||||||
|
|
||||||
/// Returns the input string with all the whitespace characters (spaces, line endings, etc.) removed.
|
/// Returns the input string with all the whitespace characters (spaces, line endings, etc.) removed.
|
||||||
|
@ -19,14 +19,18 @@
|
|||||||
|
|
||||||
#include <libyul/optimiser/Suite.h>
|
#include <libyul/optimiser/Suite.h>
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace boost::test_tools;
|
using namespace boost::test_tools;
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
namespace solidity::phaser::test
|
namespace solidity::phaser::test
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -114,6 +118,63 @@ BOOST_AUTO_TEST_CASE(enumerateOptimisationSteps_should_assing_indices_to_all_ava
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(TemporaryDirectory_should_create_and_delete_a_unique_and_empty_directory)
|
||||||
|
{
|
||||||
|
fs::path dirPath;
|
||||||
|
{
|
||||||
|
TemporaryDirectory tempDir("temporary-directory-test-");
|
||||||
|
dirPath = tempDir.path();
|
||||||
|
|
||||||
|
BOOST_TEST(dirPath.stem().string().find("temporary-directory-test-") == 0);
|
||||||
|
BOOST_TEST(fs::equivalent(dirPath.parent_path(), fs::temp_directory_path()));
|
||||||
|
BOOST_TEST(fs::is_directory(dirPath));
|
||||||
|
BOOST_TEST(fs::is_empty(dirPath));
|
||||||
|
}
|
||||||
|
BOOST_TEST(!fs::exists(dirPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(TemporaryDirectory_should_delete_its_directory_even_if_not_empty)
|
||||||
|
{
|
||||||
|
fs::path dirPath;
|
||||||
|
{
|
||||||
|
TemporaryDirectory tempDir("temporary-directory-test-");
|
||||||
|
dirPath = tempDir.path();
|
||||||
|
|
||||||
|
BOOST_TEST(fs::is_directory(dirPath));
|
||||||
|
|
||||||
|
{
|
||||||
|
ofstream tmpFile((dirPath / "test-file.txt").string());
|
||||||
|
tmpFile << "Delete me!" << endl;
|
||||||
|
}
|
||||||
|
assert(fs::is_regular_file(dirPath / "test-file.txt"));
|
||||||
|
}
|
||||||
|
BOOST_TEST(!fs::exists(dirPath / "test-file.txt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(TemporaryDirectory_memberPath_should_construct_paths_relative_to_the_temporary_directory)
|
||||||
|
{
|
||||||
|
TemporaryDirectory tempDir("temporary-directory-test-");
|
||||||
|
|
||||||
|
BOOST_TEST(fs::equivalent(tempDir.memberPath(""), tempDir.path()));
|
||||||
|
BOOST_TEST(fs::equivalent(tempDir.memberPath("."), tempDir.path() / fs::path(".")));
|
||||||
|
BOOST_TEST(fs::equivalent(tempDir.memberPath(".."), tempDir.path() / fs::path("..")));
|
||||||
|
|
||||||
|
// NOTE: fs::equivalent() only works with paths that actually exist
|
||||||
|
{
|
||||||
|
ofstream file;
|
||||||
|
file.open(tempDir.memberPath("file.txt"), ios::out);
|
||||||
|
}
|
||||||
|
BOOST_TEST(fs::equivalent(tempDir.memberPath("file.txt"), tempDir.path() / fs::path("file.txt")));
|
||||||
|
|
||||||
|
{
|
||||||
|
fs::create_directories(tempDir.memberPath("a/b/"));
|
||||||
|
|
||||||
|
ofstream file;
|
||||||
|
file.open(tempDir.memberPath("a/b/file.txt"), ios::out);
|
||||||
|
}
|
||||||
|
BOOST_TEST(fs::equivalent(tempDir.memberPath("a/b/file.txt"), tempDir.path() / fs::path("a") / fs::path("b") / fs::path("file.txt")));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(stripWhitespace_should_remove_all_whitespace_characters_from_a_string)
|
BOOST_AUTO_TEST_CASE(stripWhitespace_should_remove_all_whitespace_characters_from_a_string)
|
||||||
{
|
{
|
||||||
BOOST_TEST(stripWhitespace("") == "");
|
BOOST_TEST(stripWhitespace("") == "");
|
||||||
|
@ -16,6 +16,7 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
|||||||
add_executable(yul-phaser
|
add_executable(yul-phaser
|
||||||
yulPhaser/main.cpp
|
yulPhaser/main.cpp
|
||||||
yulPhaser/Common.h
|
yulPhaser/Common.h
|
||||||
|
yulPhaser/Common.cpp
|
||||||
yulPhaser/AlgorithmRunner.h
|
yulPhaser/AlgorithmRunner.h
|
||||||
yulPhaser/AlgorithmRunner.cpp
|
yulPhaser/AlgorithmRunner.cpp
|
||||||
yulPhaser/Phaser.h
|
yulPhaser/Phaser.h
|
||||||
|
@ -17,16 +17,89 @@
|
|||||||
|
|
||||||
#include <tools/yulPhaser/AlgorithmRunner.h>
|
#include <tools/yulPhaser/AlgorithmRunner.h>
|
||||||
|
|
||||||
|
#include <tools/yulPhaser/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolutil/Assertions.h>
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity::phaser;
|
using namespace solidity::phaser;
|
||||||
|
|
||||||
void AlgorithmRunner::run(GeneticAlgorithm& _algorithm)
|
void AlgorithmRunner::run(GeneticAlgorithm& _algorithm)
|
||||||
{
|
{
|
||||||
|
populationAutosave();
|
||||||
|
|
||||||
for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round)
|
for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round)
|
||||||
{
|
{
|
||||||
m_population = _algorithm.runNextRound(m_population);
|
m_population = _algorithm.runNextRound(m_population);
|
||||||
|
randomiseDuplicates();
|
||||||
|
|
||||||
m_outputStream << "---------- ROUND " << round + 1 << " ----------" << endl;
|
m_outputStream << "---------- ROUND " << round + 1 << " ----------" << endl;
|
||||||
m_outputStream << m_population;
|
m_outputStream << m_population;
|
||||||
|
|
||||||
|
populationAutosave();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AlgorithmRunner::populationAutosave() const
|
||||||
|
{
|
||||||
|
if (!m_options.populationAutosaveFile.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ofstream outputStream(m_options.populationAutosaveFile.value(), ios::out | ios::trunc);
|
||||||
|
assertThrow(
|
||||||
|
outputStream.is_open(),
|
||||||
|
FileOpenError,
|
||||||
|
"Could not open file '" + m_options.populationAutosaveFile.value() + "': " + strerror(errno)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto& individual: m_population.individuals())
|
||||||
|
outputStream << individual.chromosome << endl;
|
||||||
|
|
||||||
|
assertThrow(
|
||||||
|
!outputStream.bad(),
|
||||||
|
FileWriteError,
|
||||||
|
"Error while writing to file '" + m_options.populationAutosaveFile.value() + "': " + strerror(errno)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlgorithmRunner::randomiseDuplicates()
|
||||||
|
{
|
||||||
|
if (m_options.randomiseDuplicates)
|
||||||
|
{
|
||||||
|
assert(m_options.minChromosomeLength.has_value());
|
||||||
|
assert(m_options.maxChromosomeLength.has_value());
|
||||||
|
|
||||||
|
m_population = randomiseDuplicates(
|
||||||
|
m_population,
|
||||||
|
m_options.minChromosomeLength.value(),
|
||||||
|
m_options.maxChromosomeLength.value()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Population AlgorithmRunner::randomiseDuplicates(
|
||||||
|
Population _population,
|
||||||
|
size_t _minChromosomeLength,
|
||||||
|
size_t _maxChromosomeLength
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (_population.individuals().size() == 0)
|
||||||
|
return _population;
|
||||||
|
|
||||||
|
vector<Chromosome> chromosomes{_population.individuals()[0].chromosome};
|
||||||
|
size_t duplicateCount = 0;
|
||||||
|
for (size_t i = 1; i < _population.individuals().size(); ++i)
|
||||||
|
if (_population.individuals()[i].chromosome == _population.individuals()[i - 1].chromosome)
|
||||||
|
++duplicateCount;
|
||||||
|
else
|
||||||
|
chromosomes.push_back(_population.individuals()[i].chromosome);
|
||||||
|
|
||||||
|
return (
|
||||||
|
Population(_population.fitnessMetric(), chromosomes) +
|
||||||
|
Population::makeRandom(_population.fitnessMetric(), duplicateCount, _minChromosomeLength, _maxChromosomeLength)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -42,6 +42,10 @@ public:
|
|||||||
struct Options
|
struct Options
|
||||||
{
|
{
|
||||||
std::optional<size_t> maxRounds = std::nullopt;
|
std::optional<size_t> maxRounds = std::nullopt;
|
||||||
|
std::optional<std::string> populationAutosaveFile = std::nullopt;
|
||||||
|
bool randomiseDuplicates = false;
|
||||||
|
std::optional<size_t> minChromosomeLength = std::nullopt;
|
||||||
|
std::optional<size_t> maxChromosomeLength = std::nullopt;
|
||||||
};
|
};
|
||||||
|
|
||||||
AlgorithmRunner(
|
AlgorithmRunner(
|
||||||
@ -59,6 +63,14 @@ public:
|
|||||||
Population const& population() const { return m_population; }
|
Population const& population() const { return m_population; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void populationAutosave() const;
|
||||||
|
void randomiseDuplicates();
|
||||||
|
static Population randomiseDuplicates(
|
||||||
|
Population _population,
|
||||||
|
size_t _minChromosomeLength,
|
||||||
|
size_t _maxChromosomeLength
|
||||||
|
);
|
||||||
|
|
||||||
Population m_population;
|
Population m_population;
|
||||||
Options m_options;
|
Options m_options;
|
||||||
std::ostream& m_outputStream;
|
std::ostream& m_outputStream;
|
||||||
|
45
tools/yulPhaser/Common.cpp
Normal file
45
tools/yulPhaser/Common.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
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/Common.h>
|
||||||
|
|
||||||
|
#include <tools/yulPhaser/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolutil/Assertions.h>
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::phaser;
|
||||||
|
|
||||||
|
vector<string> phaser::readLinesFromFile(string const& _path)
|
||||||
|
{
|
||||||
|
ifstream inputStream(_path);
|
||||||
|
assertThrow(inputStream.is_open(), FileOpenError, "Could not open file '" + _path + "': " + strerror(errno));
|
||||||
|
|
||||||
|
string line;
|
||||||
|
vector<string> lines;
|
||||||
|
while (!getline(inputStream, line).fail())
|
||||||
|
lines.push_back(line);
|
||||||
|
|
||||||
|
assertThrow(!inputStream.bad(), FileReadError, "Error while reading from file '" + _path + "': " + strerror(errno));
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
@ -22,10 +22,19 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace solidity::phaser
|
namespace solidity::phaser
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// Loads the whole file into memory, splits the content into lines, strips newlines and
|
||||||
|
/// returns the result as a list of strings.
|
||||||
|
///
|
||||||
|
/// Throws FileOpenError if the file does not exist or cannot be opened for reading.
|
||||||
|
/// Throws FileReadError if any read operation fails during the whole process.
|
||||||
|
std::vector<std::string> readLinesFromFile(std::string const& _path);
|
||||||
|
|
||||||
/// Reads a token from the input stream and translates it to a string using a map.
|
/// Reads a token from the input stream and translates it to a string using a map.
|
||||||
/// Sets the failbit in the stream if there's no matching value in the map.
|
/// Sets the failbit in the stream if there's no matching value in the map.
|
||||||
template <typename C>
|
template <typename C>
|
||||||
|
@ -27,4 +27,8 @@ struct InvalidProgram: virtual BadInput {};
|
|||||||
struct NoInputFiles: virtual BadInput {};
|
struct NoInputFiles: virtual BadInput {};
|
||||||
struct MissingFile: virtual BadInput {};
|
struct MissingFile: virtual BadInput {};
|
||||||
|
|
||||||
|
struct FileOpenError: virtual util::Exception {};
|
||||||
|
struct FileReadError: virtual util::Exception {};
|
||||||
|
struct FileWriteError: virtual util::Exception {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,9 @@ public:
|
|||||||
m_program(std::move(_program)),
|
m_program(std::move(_program)),
|
||||||
m_repetitionCount(_repetitionCount) {}
|
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;
|
size_t evaluate(Chromosome const& _chromosome) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -81,6 +81,8 @@ public:
|
|||||||
assert(_options.isValid());
|
assert(_options.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Options const& options() const { return m_options; }
|
||||||
|
|
||||||
Population runNextRound(Population _population) override;
|
Population runNextRound(Population _population) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -129,6 +131,8 @@ public:
|
|||||||
assert(_options.isValid());
|
assert(_options.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Options const& options() const { return m_options; }
|
||||||
|
|
||||||
Population runNextRound(Population _population) override;
|
Population runNextRound(Population _population) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -62,14 +62,27 @@ GeneticAlgorithmFactory::Options GeneticAlgorithmFactory::Options::fromCommandLi
|
|||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
_arguments["algorithm"].as<Algorithm>(),
|
_arguments["algorithm"].as<Algorithm>(),
|
||||||
|
_arguments["min-chromosome-length"].as<size_t>(),
|
||||||
|
_arguments["max-chromosome-length"].as<size_t>(),
|
||||||
|
_arguments.count("random-elite-pool-size") > 0 ?
|
||||||
|
_arguments["random-elite-pool-size"].as<double>() :
|
||||||
|
optional<double>{},
|
||||||
|
_arguments["gewep-mutation-pool-size"].as<double>(),
|
||||||
|
_arguments["gewep-crossover-pool-size"].as<double>(),
|
||||||
|
_arguments["gewep-randomisation-chance"].as<double>(),
|
||||||
|
_arguments["gewep-deletion-vs-addition-chance"].as<double>(),
|
||||||
|
_arguments.count("gewep-genes-to-randomise") > 0 ?
|
||||||
|
_arguments["gewep-genes-to-randomise"].as<double>() :
|
||||||
|
optional<double>{},
|
||||||
|
_arguments.count("gewep-genes-to-add-or-delete") > 0 ?
|
||||||
|
_arguments["gewep-genes-to-add-or-delete"].as<double>() :
|
||||||
|
optional<double>{},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
unique_ptr<GeneticAlgorithm> GeneticAlgorithmFactory::build(
|
unique_ptr<GeneticAlgorithm> GeneticAlgorithmFactory::build(
|
||||||
Options const& _options,
|
Options const& _options,
|
||||||
size_t _populationSize,
|
size_t _populationSize
|
||||||
size_t _minChromosomeLength,
|
|
||||||
size_t _maxChromosomeLength
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
assert(_populationSize > 0);
|
assert(_populationSize > 0);
|
||||||
@ -77,44 +90,133 @@ unique_ptr<GeneticAlgorithm> GeneticAlgorithmFactory::build(
|
|||||||
switch (_options.algorithm)
|
switch (_options.algorithm)
|
||||||
{
|
{
|
||||||
case Algorithm::Random:
|
case Algorithm::Random:
|
||||||
|
{
|
||||||
|
double elitePoolSize = 1.0 / _populationSize;
|
||||||
|
|
||||||
|
if (_options.randomElitePoolSize.has_value())
|
||||||
|
elitePoolSize = _options.randomElitePoolSize.value();
|
||||||
|
|
||||||
return make_unique<RandomAlgorithm>(RandomAlgorithm::Options{
|
return make_unique<RandomAlgorithm>(RandomAlgorithm::Options{
|
||||||
/* elitePoolSize = */ 1.0 / _populationSize,
|
/* elitePoolSize = */ elitePoolSize,
|
||||||
/* minChromosomeLength = */ _minChromosomeLength,
|
/* minChromosomeLength = */ _options.minChromosomeLength,
|
||||||
/* maxChromosomeLength = */ _maxChromosomeLength,
|
/* maxChromosomeLength = */ _options.maxChromosomeLength,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
case Algorithm::GEWEP:
|
case Algorithm::GEWEP:
|
||||||
|
{
|
||||||
|
double percentGenesToRandomise = 1.0 / _options.maxChromosomeLength;
|
||||||
|
double percentGenesToAddOrDelete = percentGenesToRandomise;
|
||||||
|
|
||||||
|
if (_options.gewepGenesToRandomise.has_value())
|
||||||
|
percentGenesToRandomise = _options.gewepGenesToRandomise.value();
|
||||||
|
if (_options.gewepGenesToAddOrDelete.has_value())
|
||||||
|
percentGenesToAddOrDelete = _options.gewepGenesToAddOrDelete.value();
|
||||||
|
|
||||||
return make_unique<GenerationalElitistWithExclusivePools>(GenerationalElitistWithExclusivePools::Options{
|
return make_unique<GenerationalElitistWithExclusivePools>(GenerationalElitistWithExclusivePools::Options{
|
||||||
/* mutationPoolSize = */ 0.25,
|
/* mutationPoolSize = */ _options.gewepMutationPoolSize,
|
||||||
/* crossoverPoolSize = */ 0.25,
|
/* crossoverPoolSize = */ _options.gewepCrossoverPoolSize,
|
||||||
/* randomisationChance = */ 0.9,
|
/* randomisationChance = */ _options.gewepRandomisationChance,
|
||||||
/* deletionVsAdditionChance = */ 0.5,
|
/* deletionVsAdditionChance = */ _options.gewepDeletionVsAdditionChance,
|
||||||
/* percentGenesToRandomise = */ 1.0 / _maxChromosomeLength,
|
/* percentGenesToRandomise = */ percentGenesToRandomise,
|
||||||
/* percentGenesToAddOrDelete = */ 1.0 / _maxChromosomeLength,
|
/* percentGenesToAddOrDelete = */ percentGenesToAddOrDelete,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
assertThrow(false, solidity::util::Exception, "Invalid Algorithm value.");
|
assertThrow(false, solidity::util::Exception, "Invalid Algorithm value.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po::variables_map const& _arguments)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
_arguments["chromosome-repetitions"].as<size_t>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
unique_ptr<FitnessMetric> FitnessMetricFactory::build(
|
unique_ptr<FitnessMetric> FitnessMetricFactory::build(
|
||||||
|
Options const& _options,
|
||||||
Program _program
|
Program _program
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return make_unique<ProgramSize>(move(_program), RepetitionCount);
|
return make_unique<ProgramSize>(move(_program), _options.chromosomeRepetitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
PopulationFactory::Options PopulationFactory::Options::fromCommandLine(po::variables_map const& _arguments)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
_arguments["min-chromosome-length"].as<size_t>(),
|
||||||
|
_arguments["max-chromosome-length"].as<size_t>(),
|
||||||
|
_arguments.count("population") > 0 ?
|
||||||
|
_arguments["population"].as<vector<string>>() :
|
||||||
|
vector<string>{},
|
||||||
|
_arguments.count("random-population") > 0 ?
|
||||||
|
_arguments["random-population"].as<vector<size_t>>() :
|
||||||
|
vector<size_t>{},
|
||||||
|
_arguments.count("population-from-file") > 0 ?
|
||||||
|
_arguments["population-from-file"].as<vector<string>>() :
|
||||||
|
vector<string>{},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Population PopulationFactory::build(
|
Population PopulationFactory::build(
|
||||||
|
Options const& _options,
|
||||||
|
shared_ptr<FitnessMetric> _fitnessMetric
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Population population = buildFromStrings(_options.population, _fitnessMetric);
|
||||||
|
|
||||||
|
size_t combinedSize = 0;
|
||||||
|
for (size_t populationSize: _options.randomPopulation)
|
||||||
|
combinedSize += populationSize;
|
||||||
|
|
||||||
|
population = move(population) + buildRandom(
|
||||||
|
combinedSize,
|
||||||
|
_options.minChromosomeLength,
|
||||||
|
_options.maxChromosomeLength,
|
||||||
|
_fitnessMetric
|
||||||
|
);
|
||||||
|
|
||||||
|
for (string const& populationFilePath: _options.populationFromFile)
|
||||||
|
population = move(population) + buildFromFile(populationFilePath, _fitnessMetric);
|
||||||
|
|
||||||
|
return population;
|
||||||
|
}
|
||||||
|
|
||||||
|
Population PopulationFactory::buildFromStrings(
|
||||||
|
vector<string> const& _geneSequences,
|
||||||
|
shared_ptr<FitnessMetric> _fitnessMetric
|
||||||
|
)
|
||||||
|
{
|
||||||
|
vector<Chromosome> chromosomes;
|
||||||
|
for (string const& geneSequence: _geneSequences)
|
||||||
|
chromosomes.emplace_back(geneSequence);
|
||||||
|
|
||||||
|
return Population(move(_fitnessMetric), move(chromosomes));
|
||||||
|
}
|
||||||
|
|
||||||
|
Population PopulationFactory::buildRandom(
|
||||||
|
size_t _populationSize,
|
||||||
|
size_t _minChromosomeLength,
|
||||||
|
size_t _maxChromosomeLength,
|
||||||
shared_ptr<FitnessMetric> _fitnessMetric
|
shared_ptr<FitnessMetric> _fitnessMetric
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return Population::makeRandom(
|
return Population::makeRandom(
|
||||||
move(_fitnessMetric),
|
move(_fitnessMetric),
|
||||||
PopulationSize,
|
_populationSize,
|
||||||
MinChromosomeLength,
|
_minChromosomeLength,
|
||||||
MaxChromosomeLength
|
_maxChromosomeLength
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Population PopulationFactory::buildFromFile(
|
||||||
|
string const& _filePath,
|
||||||
|
shared_ptr<FitnessMetric> _fitnessMetric
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return buildFromStrings(readLinesFromFile(_filePath), move(_fitnessMetric));
|
||||||
|
}
|
||||||
|
|
||||||
ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments)
|
ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
@ -177,6 +279,11 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
|
|||||||
("help", "Show help message and exit.")
|
("help", "Show help message and exit.")
|
||||||
("input-file", po::value<string>()->required()->value_name("<PATH>"), "Input file.")
|
("input-file", po::value<string>()->required()->value_name("<PATH>"), "Input file.")
|
||||||
("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",
|
||||||
|
po::value<size_t>()->value_name("<NUM>"),
|
||||||
|
"The number of rounds after which the algorithm should stop. (default=no limit)."
|
||||||
|
)
|
||||||
;
|
;
|
||||||
keywordDescription.add(generalDescription);
|
keywordDescription.add(generalDescription);
|
||||||
|
|
||||||
@ -187,9 +294,110 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
|
|||||||
po::value<Algorithm>()->value_name("<NAME>")->default_value(Algorithm::GEWEP),
|
po::value<Algorithm>()->value_name("<NAME>")->default_value(Algorithm::GEWEP),
|
||||||
"Algorithm"
|
"Algorithm"
|
||||||
)
|
)
|
||||||
|
(
|
||||||
|
"no-randomise-duplicates",
|
||||||
|
po::bool_switch(),
|
||||||
|
"By default, after each round of the algorithm duplicate chromosomes are removed from"
|
||||||
|
"the population and replaced with randomly generated ones. "
|
||||||
|
"This option disables this postprocessing."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"min-chromosome-length",
|
||||||
|
po::value<size_t>()->value_name("<NUM>")->default_value(12),
|
||||||
|
"Minimum length of randomly generated chromosomes."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"max-chromosome-length",
|
||||||
|
po::value<size_t>()->value_name("<NUM>")->default_value(30),
|
||||||
|
"Maximum length of randomly generated chromosomes."
|
||||||
|
)
|
||||||
;
|
;
|
||||||
keywordDescription.add(algorithmDescription);
|
keywordDescription.add(algorithmDescription);
|
||||||
|
|
||||||
|
po::options_description gewepAlgorithmDescription("GEWEP ALGORITHM", lineLength, minDescriptionLength);
|
||||||
|
gewepAlgorithmDescription.add_options()
|
||||||
|
(
|
||||||
|
"gewep-mutation-pool-size",
|
||||||
|
po::value<double>()->value_name("<FRACTION>")->default_value(0.25),
|
||||||
|
"Percentage of population to regenerate using mutations in each round."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"gewep-crossover-pool-size",
|
||||||
|
po::value<double>()->value_name("<FRACTION>")->default_value(0.25),
|
||||||
|
"Percentage of population to regenerate using crossover in each round."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"gewep-randomisation-chance",
|
||||||
|
po::value<double>()->value_name("<PROBABILITY>")->default_value(0.9),
|
||||||
|
"The chance of choosing gene randomisation as the mutation to perform."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"gewep-deletion-vs-addition-chance",
|
||||||
|
po::value<double>()->value_name("<PROBABILITY>")->default_value(0.5),
|
||||||
|
"The chance of choosing gene deletion as the mutation if randomisation was not chosen."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"gewep-genes-to-randomise",
|
||||||
|
po::value<double>()->value_name("<PROBABILITY>"),
|
||||||
|
"The chance of any given gene being mutated in gene randomisation. "
|
||||||
|
"(default=1/max-chromosome-length)"
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"gewep-genes-to-add-or-delete",
|
||||||
|
po::value<double>()->value_name("<PROBABILITY>"),
|
||||||
|
"The chance of a gene being added (or deleted) in gene addition (or deletion). "
|
||||||
|
"(default=1/max-chromosome-length)"
|
||||||
|
)
|
||||||
|
;
|
||||||
|
keywordDescription.add(gewepAlgorithmDescription);
|
||||||
|
|
||||||
|
po::options_description randomAlgorithmDescription("RANDOM ALGORITHM", lineLength, minDescriptionLength);
|
||||||
|
randomAlgorithmDescription.add_options()
|
||||||
|
(
|
||||||
|
"random-elite-pool-size",
|
||||||
|
po::value<double>()->value_name("<FRACTION>"),
|
||||||
|
"Percentage of the population preserved in each round. "
|
||||||
|
"(default=one individual, regardless of population size)"
|
||||||
|
)
|
||||||
|
;
|
||||||
|
keywordDescription.add(randomAlgorithmDescription);
|
||||||
|
|
||||||
|
po::options_description populationDescription("POPULATION", lineLength, minDescriptionLength);
|
||||||
|
populationDescription.add_options()
|
||||||
|
(
|
||||||
|
"population",
|
||||||
|
po::value<vector<string>>()->multitoken()->value_name("<CHROMOSOMES>"),
|
||||||
|
"List of chromosomes to be included in the initial population. "
|
||||||
|
"You can specify multiple values separated with spaces or invoke the option multiple times "
|
||||||
|
"and all the values will be included."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"random-population",
|
||||||
|
po::value<vector<size_t>>()->value_name("<SIZE>"),
|
||||||
|
"The number of randomly generated chromosomes to be included in the initial population."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"population-from-file",
|
||||||
|
po::value<vector<string>>()->value_name("<FILE>"),
|
||||||
|
"A text file with a list of chromosomes (one per line) to be included in the initial population."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"population-autosave",
|
||||||
|
po::value<string>()->value_name("<FILE>"),
|
||||||
|
"If specified, the population is saved in the specified file after each round. (default=autosave disabled)"
|
||||||
|
)
|
||||||
|
;
|
||||||
|
keywordDescription.add(populationDescription);
|
||||||
|
|
||||||
|
po::options_description metricsDescription("METRICS", lineLength, minDescriptionLength);
|
||||||
|
metricsDescription.add_options()
|
||||||
|
(
|
||||||
|
"chromosome-repetitions",
|
||||||
|
po::value<size_t>()->value_name("<COUNT>")->default_value(1),
|
||||||
|
"Number of times to repeat the sequence optimisation steps represented by a chromosome."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
keywordDescription.add(metricsDescription);
|
||||||
|
|
||||||
po::positional_options_description positionalDescription;
|
po::positional_options_description positionalDescription;
|
||||||
positionalDescription.add("input-file", 1);
|
positionalDescription.add("input-file", 1);
|
||||||
@ -232,22 +440,33 @@ void Phaser::initialiseRNG(po::variables_map const& _arguments)
|
|||||||
cout << "Random seed: " << seed << endl;
|
cout << "Random seed: " << seed << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map const& _arguments)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
_arguments.count("rounds") > 0 ? static_cast<optional<size_t>>(_arguments["rounds"].as<size_t>()) : nullopt,
|
||||||
|
_arguments.count("population-autosave") > 0 ? static_cast<optional<string>>(_arguments["population-autosave"].as<string>()) : nullopt,
|
||||||
|
!_arguments["no-randomise-duplicates"].as<bool>(),
|
||||||
|
_arguments["min-chromosome-length"].as<size_t>(),
|
||||||
|
_arguments["max-chromosome-length"].as<size_t>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void Phaser::runAlgorithm(po::variables_map const& _arguments)
|
void Phaser::runAlgorithm(po::variables_map const& _arguments)
|
||||||
{
|
{
|
||||||
auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments);
|
auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments);
|
||||||
|
auto metricOptions = FitnessMetricFactory::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);
|
Program program = ProgramFactory::build(programOptions);
|
||||||
unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(move(program));
|
unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(metricOptions, move(program));
|
||||||
Population population = PopulationFactory::build(move(fitnessMetric));
|
Population population = PopulationFactory::build(populationOptions, move(fitnessMetric));
|
||||||
|
|
||||||
unique_ptr<GeneticAlgorithm> geneticAlgorithm = GeneticAlgorithmFactory::build(
|
unique_ptr<GeneticAlgorithm> geneticAlgorithm = GeneticAlgorithmFactory::build(
|
||||||
algorithmOptions,
|
algorithmOptions,
|
||||||
population.individuals().size(),
|
population.individuals().size()
|
||||||
PopulationFactory::MinChromosomeLength,
|
|
||||||
PopulationFactory::MaxChromosomeLength
|
|
||||||
);
|
);
|
||||||
|
|
||||||
AlgorithmRunner algorithmRunner(population, AlgorithmRunner::Options{}, cout);
|
AlgorithmRunner algorithmRunner(population, buildAlgorithmRunnerOptions(_arguments), cout);
|
||||||
algorithmRunner.run(*geneticAlgorithm);
|
algorithmRunner.run(*geneticAlgorithm);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <tools/yulPhaser/AlgorithmRunner.h>
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
#include <istream>
|
#include <istream>
|
||||||
@ -62,15 +64,22 @@ public:
|
|||||||
struct Options
|
struct Options
|
||||||
{
|
{
|
||||||
Algorithm algorithm;
|
Algorithm algorithm;
|
||||||
|
size_t minChromosomeLength;
|
||||||
|
size_t maxChromosomeLength;
|
||||||
|
std::optional<double> randomElitePoolSize;
|
||||||
|
double gewepMutationPoolSize;
|
||||||
|
double gewepCrossoverPoolSize;
|
||||||
|
double gewepRandomisationChance;
|
||||||
|
double gewepDeletionVsAdditionChance;
|
||||||
|
std::optional<double> gewepGenesToRandomise;
|
||||||
|
std::optional<double> gewepGenesToAddOrDelete;
|
||||||
|
|
||||||
static Options fromCommandLine(boost::program_options::variables_map const& _arguments);
|
static Options fromCommandLine(boost::program_options::variables_map const& _arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::unique_ptr<GeneticAlgorithm> build(
|
static std::unique_ptr<GeneticAlgorithm> build(
|
||||||
Options const& _options,
|
Options const& _options,
|
||||||
size_t _populationSize,
|
size_t _populationSize
|
||||||
size_t _minChromosomeLength,
|
|
||||||
size_t _maxChromosomeLength
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -80,9 +89,15 @@ public:
|
|||||||
class FitnessMetricFactory
|
class FitnessMetricFactory
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr size_t RepetitionCount = 5;
|
struct Options
|
||||||
|
{
|
||||||
|
size_t chromosomeRepetitions;
|
||||||
|
|
||||||
|
static Options fromCommandLine(boost::program_options::variables_map const& _arguments);
|
||||||
|
};
|
||||||
|
|
||||||
static std::unique_ptr<FitnessMetric> build(
|
static std::unique_ptr<FitnessMetric> build(
|
||||||
|
Options const& _options,
|
||||||
Program _program
|
Program _program
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -93,11 +108,33 @@ public:
|
|||||||
class PopulationFactory
|
class PopulationFactory
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr size_t PopulationSize = 20;
|
struct Options
|
||||||
static constexpr size_t MinChromosomeLength = 12;
|
{
|
||||||
static constexpr size_t MaxChromosomeLength = 30;
|
size_t minChromosomeLength;
|
||||||
|
size_t maxChromosomeLength;
|
||||||
|
std::vector<std::string> population;
|
||||||
|
std::vector<size_t> randomPopulation;
|
||||||
|
std::vector<std::string> populationFromFile;
|
||||||
|
|
||||||
|
static Options fromCommandLine(boost::program_options::variables_map const& _arguments);
|
||||||
|
};
|
||||||
|
|
||||||
static Population build(
|
static Population build(
|
||||||
|
Options const& _options,
|
||||||
|
std::shared_ptr<FitnessMetric> _fitnessMetric
|
||||||
|
);
|
||||||
|
static Population buildFromStrings(
|
||||||
|
std::vector<std::string> const& _geneSequences,
|
||||||
|
std::shared_ptr<FitnessMetric> _fitnessMetric
|
||||||
|
);
|
||||||
|
static Population buildRandom(
|
||||||
|
size_t _populationSize,
|
||||||
|
size_t _minChromosomeLength,
|
||||||
|
size_t _maxChromosomeLength,
|
||||||
|
std::shared_ptr<FitnessMetric> _fitnessMetric
|
||||||
|
);
|
||||||
|
static Population buildFromFile(
|
||||||
|
std::string const& _filePath,
|
||||||
std::shared_ptr<FitnessMetric> _fitnessMetric
|
std::shared_ptr<FitnessMetric> _fitnessMetric
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -141,6 +178,7 @@ private:
|
|||||||
static CommandLineDescription buildCommandLineDescription();
|
static CommandLineDescription buildCommandLineDescription();
|
||||||
static std::optional<boost::program_options::variables_map> parseCommandLine(int _argc, char** _argv);
|
static std::optional<boost::program_options::variables_map> parseCommandLine(int _argc, char** _argv);
|
||||||
static void initialiseRNG(boost::program_options::variables_map const& _arguments);
|
static void initialiseRNG(boost::program_options::variables_map const& _arguments);
|
||||||
|
static AlgorithmRunner::Options buildAlgorithmRunnerOptions(boost::program_options::variables_map const& _arguments);
|
||||||
|
|
||||||
static void runAlgorithm(boost::program_options::variables_map const& _arguments);
|
static void runAlgorithm(boost::program_options::variables_map const& _arguments);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user