Merge pull request #8516 from imapp-pl/yul-phaser-crossover-operators

[yul-phaser] Crossover operators
This commit is contained in:
chriseth 2020-04-23 13:50:39 +02:00 committed by GitHub
commit 0c5aa36e46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 460 additions and 6 deletions

View File

@ -51,6 +51,8 @@ protected:
/* mutationChance = */ 0.0,
/* deletionChance = */ 0.0,
/* additionChance = */ 0.0,
/* CrossoverChoice = */ CrossoverChoice::SinglePoint,
/* uniformCrossoverSwapChance= */ 0.5,
};
};
@ -113,6 +115,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_regenerate_rest_o
/* deletionVsAdditionChance = */ 1.0,
/* percentGenesToRandomise = */ 0.0,
/* percentGenesToAddOrDelete = */ 1.0,
/* CrossoverChoice = */ CrossoverChoice::SinglePoint,
/* uniformCrossoverSwapChance= */ 0.5,
};
GenerationalElitistWithExclusivePools algorithm(options);
@ -133,6 +137,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individ
/* deletionVsAdditionChance = */ 0.0,
/* percentGenesToRandomise = */ 0.0,
/* percentGenesToAddOrDelete = */ 1.0,
/* CrossoverChoice = */ CrossoverChoice::SinglePoint,
/* uniformCrossoverSwapChance= */ 0.5,
};
GenerationalElitistWithExclusivePools algorithm(options);
@ -152,6 +158,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove
/* deletionVsAdditionChance = */ 0.5,
/* percentGenesToRandomise = */ 1.0,
/* percentGenesToAddOrDelete = */ 1.0,
/* CrossoverChoice = */ CrossoverChoice::SinglePoint,
/* uniformCrossoverSwapChance= */ 0.5,
};
GenerationalElitistWithExclusivePools algorithm(options);
@ -179,6 +187,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove
/* deletionVsAdditionChance = */ 0.0,
/* percentGenesToRandomise = */ 0.0,
/* percentGenesToAddOrDelete = */ 0.0,
/* CrossoverChoice = */ CrossoverChoice::SinglePoint,
/* uniformCrossoverSwapChance= */ 0.5,
};
GenerationalElitistWithExclusivePools algorithm(options);

View File

@ -18,15 +18,17 @@
#include <test/yulPhaser/TestHelpers.h>
#include <tools/yulPhaser/Mutations.h>
#include <tools/yulPhaser/SimulationRNG.h>
#include <libsolutil/CommonIO.h>
#include <boost/test/unit_test.hpp>
#include <algorithm>
#include <vector>
using namespace std;
using namespace solidity::util;
namespace solidity::phaser::test
{
@ -434,6 +436,181 @@ BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_always_use_position_zero_as_spli
BOOST_CHECK(crossover10(empty, splittable) == splittable);
}
BOOST_AUTO_TEST_CASE(randomTwoPointCrossover_should_swap_chromosome_parts_between_two_random_points)
{
function<Crossover> crossover = randomTwoPointCrossover();
SimulationRNG::reset(1);
Chromosome result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc"));
BOOST_TEST(result1 == Chromosome("aaacccaaaa"));
SimulationRNG::reset(1);
Chromosome result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa"));
BOOST_TEST(result2 == Chromosome("cccaaa"));
}
BOOST_AUTO_TEST_CASE(symmetricRandomTwoPointCrossover_should_swap_chromosome_parts_at_random_point)
{
function<SymmetricCrossover> crossover = symmetricRandomTwoPointCrossover();
SimulationRNG::reset(1);
tuple<Chromosome, Chromosome> result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc"));
tuple<Chromosome, Chromosome> expectedPair1 = {Chromosome("aaacccaaaa"), Chromosome("cccaaa")};
BOOST_TEST(result1 == expectedPair1);
tuple<Chromosome, Chromosome> result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa"));
tuple<Chromosome, Chromosome> expectedPair2 = {Chromosome("ccccca"), Chromosome("aaaaacaaaa")};
BOOST_TEST(result2 == expectedPair2);
}
BOOST_AUTO_TEST_CASE(randomTwoPointCrossover_should_only_consider_points_available_on_both_chromosomes)
{
function<Crossover> crossover = randomTwoPointCrossover();
for (size_t i = 0; i < 30; ++i)
{
Chromosome result1 = crossover(Chromosome("aaa"), Chromosome("TTTTTTTTTTTTTTTTTTTT"));
Chromosome result2 = crossover(Chromosome("TTTTTTTTTTTTTTTTTTTT"), Chromosome("aaa"));
BOOST_TEST((
result1 == Chromosome("aaa") ||
result1 == Chromosome("Taa") ||
result1 == Chromosome("TTa") ||
result1 == Chromosome("TTT") ||
result1 == Chromosome("aTa") ||
result1 == Chromosome("aTT") ||
result1 == Chromosome("aaT")
));
BOOST_TEST((
result2 == Chromosome("TTTTTTTTTTTTTTTTTTTT") ||
result2 == Chromosome("aTTTTTTTTTTTTTTTTTTT") ||
result2 == Chromosome("aaTTTTTTTTTTTTTTTTTT") ||
result2 == Chromosome("aaaTTTTTTTTTTTTTTTTT") ||
result2 == Chromosome("TaTTTTTTTTTTTTTTTTTT") ||
result2 == Chromosome("TaaTTTTTTTTTTTTTTTTT") ||
result2 == Chromosome("TTaTTTTTTTTTTTTTTTTT")
));
}
}
BOOST_AUTO_TEST_CASE(uniformCrossover_should_swap_randomly_selected_genes)
{
function<Crossover> crossover = uniformCrossover(0.7);
SimulationRNG::reset(1);
Chromosome result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc"));
BOOST_TEST(result1 == Chromosome("caaacc"));
SimulationRNG::reset(1);
Chromosome result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa"));
BOOST_TEST(result2 == Chromosome("acccaaaaaa"));
}
BOOST_AUTO_TEST_CASE(symmetricUniformCrossover_should_swap_randomly_selected_genes)
{
function<SymmetricCrossover> crossover = symmetricUniformCrossover(0.7);
SimulationRNG::reset(1);
tuple<Chromosome, Chromosome> result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc"));
tuple<Chromosome, Chromosome> expectedPair1 = {Chromosome("caaacc"), Chromosome("acccaaaaaa")};
BOOST_TEST(result1 == expectedPair1);
tuple<Chromosome, Chromosome> result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa"));
tuple<Chromosome, Chromosome> expectedPair2 = {Chromosome("caaaaaaaaa"), Chromosome("accccc")};
BOOST_TEST(result2 == expectedPair2);
}
BOOST_AUTO_TEST_CASE(uniformCrossover_should_only_consider_points_available_on_both_chromosomes)
{
function<Crossover> crossover = uniformCrossover(0.7);
set<string> expectedPatterns = {
"TTTTTTTTTTTTTTTTTTTT",
"aTTTTTTTTTTTTTTTTTTT",
"TaTTTTTTTTTTTTTTTTTT",
"TTaTTTTTTTTTTTTTTTTT",
"aaTTTTTTTTTTTTTTTTTT",
"TaaTTTTTTTTTTTTTTTTT",
"aTaTTTTTTTTTTTTTTTTT",
"aaaTTTTTTTTTTTTTTTTT",
"aaa",
"Taa",
"aTa",
"aaT",
"TTa",
"aTT",
"TaT",
"TTT",
};
for (size_t i = 0; i < 30; ++i)
{
Chromosome result1 = crossover(Chromosome("aaa"), Chromosome("TTTTTTTTTTTTTTTTTTTT"));
Chromosome result2 = crossover(Chromosome("TTTTTTTTTTTTTTTTTTTT"), Chromosome("aaa"));
BOOST_TEST(expectedPatterns.count(toString(result1)) == 1);
BOOST_TEST(expectedPatterns.count(toString(result2)) == 1);
}
}
BOOST_AUTO_TEST_CASE(uniformCrossover_should_not_swap_anything_if_chance_is_zero)
{
BOOST_TEST(uniformCrossover(0.0)(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")) == Chromosome("aaaaaaaaaa"));
BOOST_TEST(uniformCrossover(0.0)(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")) == Chromosome("cccccc"));
}
BOOST_AUTO_TEST_CASE(uniformCrossover_should_swap_whole_chromosomes_if_chance_is_one)
{
BOOST_TEST(uniformCrossover(1.0)(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")) == Chromosome("cccccc"));
BOOST_TEST(uniformCrossover(1.0)(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")) == Chromosome("aaaaaaaaaa"));
}
BOOST_AUTO_TEST_CASE(uniformCrossover_should_swap_genes_with_uniform_probability)
{
constexpr size_t operationCount = 1000;
constexpr double swapChance = 0.8;
constexpr double relativeTolerance = 0.05;
double const expectedValue = swapChance;
double const variance = swapChance * (1 - swapChance);
function<Crossover> crossover = uniformCrossover(swapChance);
Chromosome chromosome1("aaaaaaaaaa");
Chromosome chromosome2("cccccccccc");
vector<size_t> bernoulliTrials;
for (size_t i = 0; i < operationCount; ++i)
{
string genes = toString(crossover(chromosome1, chromosome2));
for (size_t j = 0; j < chromosome1.length(); ++j)
bernoulliTrials.push_back(static_cast<size_t>(genes[j] == 'c'));
}
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_AUTO_TEST_CASE(uniformCrossover_should_swap_tail_with_uniform_probability)
{
constexpr size_t operationCount = 1000;
constexpr double swapChance = 0.3;
constexpr double relativeTolerance = 0.05;
double const expectedValue = swapChance;
double const variance = swapChance * (1 - swapChance);
function<Crossover> crossover = uniformCrossover(swapChance);
Chromosome chromosome1("aaaaa");
Chromosome chromosome2("cccccccccc");
vector<size_t> bernoulliTrials;
for (size_t i = 0; i < operationCount; ++i)
{
string genes = toString(crossover(chromosome1, chromosome2));
BOOST_REQUIRE(genes.size() == 5 || genes.size() == 10);
bernoulliTrials.push_back(static_cast<size_t>(genes.size() == 10));
}
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()

View File

@ -45,6 +45,8 @@ protected:
/* algorithm = */ Algorithm::Random,
/* minChromosomeLength = */ 50,
/* maxChromosomeLength = */ 100,
/* CrossoverChoice = */ CrossoverChoice::Uniform,
/* uniformCrossoverSwapChance = */ 0.5,
/* randomElitePoolSize = */ 0.5,
/* gewepMutationPoolSize = */ 0.1,
/* gewepCrossoverPoolSize = */ 0.1,
@ -121,6 +123,9 @@ BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_opt
auto gewepAlgorithm = dynamic_cast<GenerationalElitistWithExclusivePools*>(algorithm2.get());
BOOST_REQUIRE(gewepAlgorithm != nullptr);
BOOST_TEST(gewepAlgorithm->options().crossover == m_options.crossover);
BOOST_TEST(gewepAlgorithm->options().uniformCrossoverSwapChance.has_value());
BOOST_TEST(gewepAlgorithm->options().uniformCrossoverSwapChance.value() == m_options.uniformCrossoverSwapChance);
BOOST_TEST(gewepAlgorithm->options().mutationPoolSize == m_options.gewepMutationPoolSize);
BOOST_TEST(gewepAlgorithm->options().crossoverPoolSize == m_options.gewepCrossoverPoolSize);
BOOST_TEST(gewepAlgorithm->options().randomisationChance == m_options.gewepRandomisationChance);
@ -134,6 +139,8 @@ BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_opt
auto classicAlgorithm = dynamic_cast<ClassicGeneticAlgorithm*>(algorithm3.get());
BOOST_REQUIRE(classicAlgorithm != nullptr);
BOOST_TEST(classicAlgorithm->options().uniformCrossoverSwapChance.has_value());
BOOST_TEST(classicAlgorithm->options().uniformCrossoverSwapChance.value() == m_options.uniformCrossoverSwapChance);
BOOST_TEST(classicAlgorithm->options().elitePoolSize == m_options.classicElitePoolSize);
BOOST_TEST(classicAlgorithm->options().crossoverChance == m_options.classicCrossoverChance);
BOOST_TEST(classicAlgorithm->options().mutationChance == m_options.classicMutationChance);

View File

@ -21,8 +21,47 @@
#include <tools/yulPhaser/PairSelections.h>
using namespace std;
using namespace solidity;
using namespace solidity::phaser;
function<Crossover> phaser::buildCrossoverOperator(
CrossoverChoice _choice,
optional<double> _uniformCrossoverSwapChance
)
{
switch (_choice)
{
case CrossoverChoice::SinglePoint:
return randomPointCrossover();
case CrossoverChoice::TwoPoint:
return randomTwoPointCrossover();
case CrossoverChoice::Uniform:
assert(_uniformCrossoverSwapChance.has_value());
return uniformCrossover(_uniformCrossoverSwapChance.value());
default:
assertThrow(false, solidity::util::Exception, "Invalid CrossoverChoice value.");
};
}
function<SymmetricCrossover> phaser::buildSymmetricCrossoverOperator(
CrossoverChoice _choice,
optional<double> _uniformCrossoverSwapChance
)
{
switch (_choice)
{
case CrossoverChoice::SinglePoint:
return symmetricRandomPointCrossover();
case CrossoverChoice::TwoPoint:
return symmetricRandomTwoPointCrossover();
case CrossoverChoice::Uniform:
assert(_uniformCrossoverSwapChance.has_value());
return symmetricUniformCrossover(_uniformCrossoverSwapChance.value());
default:
assertThrow(false, solidity::util::Exception, "Invalid CrossoverChoice value.");
};
}
Population RandomAlgorithm::runNextRound(Population _population)
{
RangeSelection elite(0.0, m_options.elitePoolSize);
@ -57,7 +96,10 @@ Population GenerationalElitistWithExclusivePools::runNextRound(Population _popul
geneAddition(m_options.percentGenesToAddOrDelete)
)
);
std::function<Crossover> crossoverOperator = randomPointCrossover();
std::function<Crossover> crossoverOperator = buildCrossoverOperator(
m_options.crossover,
m_options.uniformCrossoverSwapChance
);
return
_population.select(elitePool) +
@ -72,10 +114,15 @@ Population ClassicGeneticAlgorithm::runNextRound(Population _population)
Population selectedPopulation = select(_population, rest.individuals().size());
std::function<SymmetricCrossover> crossoverOperator = buildSymmetricCrossoverOperator(
m_options.crossover,
m_options.uniformCrossoverSwapChance
);
Population crossedPopulation = Population::combine(
selectedPopulation.symmetricCrossoverWithRemainder(
PairsFromRandomSubset(m_options.crossoverChance),
symmetricRandomPointCrossover()
crossoverOperator
)
);

View File

@ -20,11 +20,31 @@
#pragma once
#include <tools/yulPhaser/Mutations.h>
#include <tools/yulPhaser/Population.h>
#include <optional>
namespace solidity::phaser
{
enum class CrossoverChoice
{
SinglePoint,
TwoPoint,
Uniform,
};
std::function<Crossover> buildCrossoverOperator(
CrossoverChoice _choice,
std::optional<double> _uniformCrossoverSwapChance
);
std::function<SymmetricCrossover> buildSymmetricCrossoverOperator(
CrossoverChoice _choice,
std::optional<double> _uniformCrossoverSwapChance
);
/**
* Abstract base class for genetic algorithms.
* The main feature is the @a runNextRound() method that executes one round of the algorithm,
@ -110,6 +130,8 @@ public:
double deletionVsAdditionChance; ///< The chance of choosing @a geneDeletion as the mutation if randomisation was not chosen.
double percentGenesToRandomise; ///< The chance of any given gene being mutated in gene randomisation.
double percentGenesToAddOrDelete; ///< The chance of a gene being added (or deleted) in gene addition (or deletion).
CrossoverChoice crossover; ///< The crossover operator to use.
std::optional<double> uniformCrossoverSwapChance; ///< Chance of a pair of genes being swapped in uniform crossover.
bool isValid() const
{
@ -120,6 +142,7 @@ public:
0 <= deletionVsAdditionChance && deletionVsAdditionChance <= 1.0 &&
0 <= percentGenesToRandomise && percentGenesToRandomise <= 1.0 &&
0 <= percentGenesToAddOrDelete && percentGenesToAddOrDelete <= 1.0 &&
0 <= uniformCrossoverSwapChance && uniformCrossoverSwapChance <= 1.0 &&
mutationPoolSize + crossoverPoolSize <= 1.0
);
}
@ -165,6 +188,8 @@ public:
double mutationChance; ///< The chance of a particular gene being randomised in @a geneRandomisation mutation.
double deletionChance; ///< The chance of a particular gene being deleted in @a geneDeletion mutation.
double additionChance; ///< The chance of a particular gene being added in @a geneAddition mutation.
CrossoverChoice crossover; ///< The crossover operator to use
std::optional<double> uniformCrossoverSwapChance; ///< Chance of a pair of genes being swapped in uniform crossover.
bool isValid() const
{
@ -173,7 +198,8 @@ public:
0 <= crossoverChance && crossoverChance <= 1.0 &&
0 <= mutationChance && mutationChance <= 1.0 &&
0 <= deletionChance && deletionChance <= 1.0 &&
0 <= additionChance && additionChance <= 1.0
0 <= additionChance && additionChance <= 1.0 &&
0 <= uniformCrossoverSwapChance && uniformCrossoverSwapChance <= 1.0
);
}
};

View File

@ -145,7 +145,7 @@ function<Crossover> phaser::randomPointCrossover()
size_t minLength = min(_chromosome1.length(), _chromosome2.length());
// Don't use position 0 (because this just swaps the values) unless it's the only choice.
size_t minPoint = (minLength > 0? 1 : 0);
size_t minPoint = (minLength > 0 ? 1 : 0);
assert(minPoint <= minLength);
size_t randomPoint = SimulationRNG::uniformInt(minPoint, minLength);
@ -160,7 +160,7 @@ function<SymmetricCrossover> phaser::symmetricRandomPointCrossover()
size_t minLength = min(_chromosome1.length(), _chromosome2.length());
// Don't use position 0 (because this just swaps the values) unless it's the only choice.
size_t minPoint = (minLength > 0? 1 : 0);
size_t minPoint = (minLength > 0 ? 1 : 0);
assert(minPoint <= minLength);
size_t randomPoint = SimulationRNG::uniformInt(minPoint, minLength);
@ -180,3 +180,138 @@ function<Crossover> phaser::fixedPointCrossover(double _crossoverPoint)
return get<0>(fixedPointSwap(_chromosome1, _chromosome2, concretePoint));
};
}
namespace
{
ChromosomePair fixedTwoPointSwap(
Chromosome const& _chromosome1,
Chromosome const& _chromosome2,
size_t _crossoverPoint1,
size_t _crossoverPoint2
)
{
assert(_crossoverPoint1 <= _chromosome1.length());
assert(_crossoverPoint1 <= _chromosome2.length());
assert(_crossoverPoint2 <= _chromosome1.length());
assert(_crossoverPoint2 <= _chromosome2.length());
size_t lowPoint = min(_crossoverPoint1, _crossoverPoint2);
size_t highPoint = max(_crossoverPoint1, _crossoverPoint2);
auto begin1 = _chromosome1.optimisationSteps().begin();
auto begin2 = _chromosome2.optimisationSteps().begin();
auto end1 = _chromosome1.optimisationSteps().end();
auto end2 = _chromosome2.optimisationSteps().end();
return {
Chromosome(
vector<string>(begin1, begin1 + lowPoint) +
vector<string>(begin2 + lowPoint, begin2 + highPoint) +
vector<string>(begin1 + highPoint, end1)
),
Chromosome(
vector<string>(begin2, begin2 + lowPoint) +
vector<string>(begin1 + lowPoint, begin1 + highPoint) +
vector<string>(begin2 + highPoint, end2)
),
};
}
}
function<Crossover> phaser::randomTwoPointCrossover()
{
return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2)
{
size_t minLength = min(_chromosome1.length(), _chromosome2.length());
// Don't use position 0 (because this just swaps the values) unless it's the only choice.
size_t minPoint = (minLength > 0 ? 1 : 0);
assert(minPoint <= minLength);
size_t randomPoint1 = SimulationRNG::uniformInt(minPoint, minLength);
size_t randomPoint2 = SimulationRNG::uniformInt(randomPoint1, minLength);
return get<0>(fixedTwoPointSwap(_chromosome1, _chromosome2, randomPoint1, randomPoint2));
};
}
function<SymmetricCrossover> phaser::symmetricRandomTwoPointCrossover()
{
return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2)
{
size_t minLength = min(_chromosome1.length(), _chromosome2.length());
// Don't use position 0 (because this just swaps the values) unless it's the only choice.
size_t minPoint = (minLength > 0 ? 1 : 0);
assert(minPoint <= minLength);
size_t randomPoint1 = SimulationRNG::uniformInt(minPoint, minLength);
size_t randomPoint2 = SimulationRNG::uniformInt(randomPoint1, minLength);
return fixedTwoPointSwap(_chromosome1, _chromosome2, randomPoint1, randomPoint2);
};
}
namespace
{
ChromosomePair uniformSwap(Chromosome const& _chromosome1, Chromosome const& _chromosome2, double _swapChance)
{
vector<string> steps1;
vector<string> steps2;
size_t minLength = min(_chromosome1.length(), _chromosome2.length());
for (size_t i = 0; i < minLength; ++i)
if (SimulationRNG::bernoulliTrial(_swapChance))
{
steps1.push_back(_chromosome2.optimisationSteps()[i]);
steps2.push_back(_chromosome1.optimisationSteps()[i]);
}
else
{
steps1.push_back(_chromosome1.optimisationSteps()[i]);
steps2.push_back(_chromosome2.optimisationSteps()[i]);
}
auto begin1 = _chromosome1.optimisationSteps().begin();
auto begin2 = _chromosome2.optimisationSteps().begin();
auto end1 = _chromosome1.optimisationSteps().end();
auto end2 = _chromosome2.optimisationSteps().end();
bool swapTail = SimulationRNG::bernoulliTrial(_swapChance);
if (_chromosome1.length() > minLength)
{
if (swapTail)
steps2.insert(steps2.end(), begin1 + minLength, end1);
else
steps1.insert(steps1.end(), begin1 + minLength, end1);
}
if (_chromosome2.length() > minLength)
{
if (swapTail)
steps1.insert(steps1.end(), begin2 + minLength, end2);
else
steps2.insert(steps2.end(), begin2 + minLength, end2);
}
return {Chromosome(steps1), Chromosome(steps2)};
}
}
function<Crossover> phaser::uniformCrossover(double _swapChance)
{
return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2)
{
return get<0>(uniformSwap(_chromosome1, _chromosome2, _swapChance));
};
}
function<SymmetricCrossover> phaser::symmetricUniformCrossover(double _swapChance)
{
return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2)
{
return uniformSwap(_chromosome1, _chromosome2, _swapChance);
};
}

View File

@ -80,4 +80,22 @@ std::function<SymmetricCrossover> symmetricRandomPointCrossover();
/// unless there is no other choice (i.e. one of the chromosomes is empty).
std::function<Crossover> fixedPointCrossover(double _crossoverPoint);
/// Creates a crossover operator that randomly selects two points between 0 and 1 and swaps genes
/// from the resulting interval. The interval may be empty in which case no genes are swapped.
std::function<Crossover> randomTwoPointCrossover();
/// Symmetric version of @a randomTwoPointCrossover(). Creates an operator that returns a pair
/// containing both possible results for the same crossover points.
std::function<SymmetricCrossover> symmetricRandomTwoPointCrossover();
/// Creates a crossover operator that goes over the length of the shorter chromosomes and for
/// each gene independently decides whether to swap it or not (with probability given by
/// @a _swapChance). The tail of the longer chromosome (the part that's past the length of the
/// shorter one) is treated as a single gene and can potentially be swapped too.
std::function<Crossover> uniformCrossover(double _swapChance);
/// Symmetric version of @a uniformCrossover(). Creates an operator that returns a pair
/// containing both possible results for the same set or swap decisions.
std::function<SymmetricCrossover> symmetricUniformCrossover(double _swapChance);
}

View File

@ -78,6 +78,14 @@ map<MetricAggregatorChoice, string> const MetricAggregatorChoiceToStringMap =
};
map<string, MetricAggregatorChoice> const StringToMetricAggregatorChoiceMap = invertMap(MetricAggregatorChoiceToStringMap);
map<CrossoverChoice, string> const CrossoverChoiceToStringMap =
{
{CrossoverChoice::SinglePoint, "single-point"},
{CrossoverChoice::TwoPoint, "two-point"},
{CrossoverChoice::Uniform, "uniform"},
};
map<string, CrossoverChoice> const StringToCrossoverChoiceMap = invertMap(CrossoverChoiceToStringMap);
}
istream& phaser::operator>>(istream& _inputStream, PhaserMode& _phaserMode) { return deserializeChoice(_inputStream, _phaserMode, StringToPhaserModeMap); }
@ -88,6 +96,8 @@ istream& phaser::operator>>(istream& _inputStream, MetricChoice& _metric) { retu
ostream& phaser::operator<<(ostream& _outputStream, MetricChoice _metric) { return serializeChoice(_outputStream, _metric, MetricChoiceToStringMap); }
istream& phaser::operator>>(istream& _inputStream, MetricAggregatorChoice& _aggregator) { return deserializeChoice(_inputStream, _aggregator, StringToMetricAggregatorChoiceMap); }
ostream& phaser::operator<<(ostream& _outputStream, MetricAggregatorChoice _aggregator) { return serializeChoice(_outputStream, _aggregator, MetricAggregatorChoiceToStringMap); }
istream& phaser::operator>>(istream& _inputStream, CrossoverChoice& _crossover) { return deserializeChoice(_inputStream, _crossover, StringToCrossoverChoiceMap); }
ostream& phaser::operator<<(ostream& _outputStream, CrossoverChoice _crossover) { return serializeChoice(_outputStream, _crossover, CrossoverChoiceToStringMap); }
GeneticAlgorithmFactory::Options GeneticAlgorithmFactory::Options::fromCommandLine(po::variables_map const& _arguments)
{
@ -95,6 +105,8 @@ GeneticAlgorithmFactory::Options GeneticAlgorithmFactory::Options::fromCommandLi
_arguments["algorithm"].as<Algorithm>(),
_arguments["min-chromosome-length"].as<size_t>(),
_arguments["max-chromosome-length"].as<size_t>(),
_arguments["crossover"].as<CrossoverChoice>(),
_arguments["uniform-crossover-swap-chance"].as<double>(),
_arguments.count("random-elite-pool-size") > 0 ?
_arguments["random-elite-pool-size"].as<double>() :
optional<double>{},
@ -155,6 +167,8 @@ unique_ptr<GeneticAlgorithm> GeneticAlgorithmFactory::build(
/* deletionVsAdditionChance = */ _options.gewepDeletionVsAdditionChance,
/* percentGenesToRandomise = */ percentGenesToRandomise,
/* percentGenesToAddOrDelete = */ percentGenesToAddOrDelete,
/* crossover = */ _options.crossover,
/* uniformCrossoverSwapChance = */ _options.uniformCrossoverSwapChance,
});
}
case Algorithm::Classic:
@ -165,6 +179,8 @@ unique_ptr<GeneticAlgorithm> GeneticAlgorithmFactory::build(
/* mutationChance = */ _options.classicMutationChance,
/* deletionChance = */ _options.classicDeletionChance,
/* additionChance = */ _options.classicAdditionChance,
/* crossover = */ _options.crossover,
/* uniformCrossoverSwapChance = */ _options.uniformCrossoverSwapChance,
});
}
default:
@ -451,6 +467,16 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
po::value<size_t>()->value_name("<NUM>")->default_value(30),
"Maximum length of randomly generated chromosomes."
)
(
"crossover",
po::value<CrossoverChoice>()->value_name("<NAME>")->default_value(CrossoverChoice::SinglePoint),
"Type of the crossover operator to use."
)
(
"uniform-crossover-swap-chance",
po::value<double>()->value_name("<PROBABILITY>")->default_value(0.5),
"Chance of two genes being swapped between chromosomes in uniform crossover."
)
;
keywordDescription.add(algorithmDescription);

View File

@ -22,6 +22,7 @@
#pragma once
#include <tools/yulPhaser/AlgorithmRunner.h>
#include <tools/yulPhaser/GeneticAlgorithms.h>
#include <boost/program_options.hpp>
@ -83,6 +84,8 @@ std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricCho
std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::MetricChoice _metric);
std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricAggregatorChoice& _aggregator);
std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::MetricAggregatorChoice _aggregator);
std::istream& operator>>(std::istream& _inputStream, solidity::phaser::CrossoverChoice& _crossover);
std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::CrossoverChoice _crossover);
/**
* Builds and validates instances of @a GeneticAlgorithm and its derived classes.
@ -95,13 +98,18 @@ public:
Algorithm algorithm;
size_t minChromosomeLength;
size_t maxChromosomeLength;
CrossoverChoice crossover;
double uniformCrossoverSwapChance;
std::optional<double> randomElitePoolSize;
double gewepMutationPoolSize;
double gewepCrossoverPoolSize;
double gewepRandomisationChance;
double gewepDeletionVsAdditionChance;
std::optional<double> gewepGenesToRandomise;
std::optional<double> gewepGenesToAddOrDelete;
double classicElitePoolSize;
double classicCrossoverChance;
double classicMutationChance;