mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8516 from imapp-pl/yul-phaser-crossover-operators
[yul-phaser] Crossover operators
This commit is contained in:
commit
0c5aa36e46
@ -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);
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user