[yul-phaser] Change the design of crossover operators so that they produce a single chromosome rather than a pair

This commit is contained in:
Kamil Śliwak 2020-02-13 23:44:06 +01:00
parent a3e97108c5
commit 763bdb1d51
6 changed files with 74 additions and 65 deletions

View File

@ -214,11 +214,14 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_second_mutation_i
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_swap_chromosome_parts_at_random_point) BOOST_AUTO_TEST_CASE(randomPointCrossover_should_swap_chromosome_parts_at_random_point)
{ {
SimulationRNG::reset(1);
function<Crossover> crossover = randomPointCrossover(); function<Crossover> crossover = randomPointCrossover();
auto [result1, result2] = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); SimulationRNG::reset(1);
Chromosome result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc"));
BOOST_TEST(result1 == Chromosome("aaaccc")); BOOST_TEST(result1 == Chromosome("aaaccc"));
SimulationRNG::reset(1);
Chromosome result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa"));
BOOST_TEST(result2 == Chromosome("cccaaaaaaa")); BOOST_TEST(result2 == Chromosome("cccaaaaaaa"));
} }
@ -229,12 +232,19 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_only_consider_points_available_
for (size_t i = 0; i < 30; ++i) for (size_t i = 0; i < 30; ++i)
{ {
auto [result1, result2] = crossover(Chromosome("aaa"), Chromosome("TTTTTTTTTTTTTTTTTTTT")); Chromosome result1 = crossover(Chromosome("aaa"), Chromosome("TTTTTTTTTTTTTTTTTTTT"));
Chromosome result2 = crossover(Chromosome("TTTTTTTTTTTTTTTTTTTT"), Chromosome("aaa"));
BOOST_TEST(( BOOST_TEST((
(result1 == Chromosome("TTTTTTTTTTTTTTTTTTTT") && result2 == Chromosome("aaa")) || result1 == Chromosome("TTTTTTTTTTTTTTTTTTTT") ||
(result1 == Chromosome("aTTTTTTTTTTTTTTTTTTT") && result2 == Chromosome("Taa")) || result1 == Chromosome("aTTTTTTTTTTTTTTTTTTT") ||
(result1 == Chromosome("aaTTTTTTTTTTTTTTTTTT") && result2 == Chromosome("TTa")) || result1 == Chromosome("aaTTTTTTTTTTTTTTTTTT") ||
(result1 == Chromosome("aaaTTTTTTTTTTTTTTTTT") && result2 == Chromosome("TTT")) result1 == Chromosome("aaaTTTTTTTTTTTTTTTTT")
));
BOOST_TEST((
result2 == Chromosome("aaa") ||
result2 == Chromosome("Taa") ||
result2 == Chromosome("TTa") ||
result2 == Chromosome("TTT")
)); ));
} }
} }
@ -246,7 +256,8 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if
for (size_t i = 0; i < 30; ++i) for (size_t i = 0; i < 30; ++i)
{ {
auto [result1, result2] = crossover(Chromosome("aa"), Chromosome("TTTTTTTTTTTTTTTTTTTT")); Chromosome result1 = crossover(Chromosome("aa"), Chromosome("TTTTTTTTTTTTTTTTTTTT"));
Chromosome result2 = crossover(Chromosome("TTTTTTTTTTTTTTTTTTTT"), Chromosome("aa"));
BOOST_TEST(result1 != Chromosome("TTTTTTTTTTTTTTTTTTTT")); BOOST_TEST(result1 != Chromosome("TTTTTTTTTTTTTTTTTTTT"));
BOOST_TEST(result2 != Chromosome("aa")); BOOST_TEST(result2 != Chromosome("aa"));
} }
@ -259,7 +270,8 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if
for (size_t i = 0; i < 30; ++i) for (size_t i = 0; i < 30; ++i)
{ {
auto [result1, result2] = crossover(Chromosome("a"), Chromosome("T")); Chromosome result1 = crossover(Chromosome("a"), Chromosome("T"));
Chromosome result2 = crossover(Chromosome("T"), Chromosome("a"));
BOOST_TEST(result1 == Chromosome("a")); BOOST_TEST(result1 == Chromosome("a"));
BOOST_TEST(result2 == Chromosome("T")); BOOST_TEST(result2 == Chromosome("T"));
} }
@ -270,8 +282,8 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_work_even_if_one_chromosome_is_
function<Crossover> crossover = randomPointCrossover(); function<Crossover> crossover = randomPointCrossover();
SimulationRNG::reset(1); SimulationRNG::reset(1);
BOOST_CHECK(crossover(Chromosome("ff"), Chromosome("a")) == ChromosomePair(Chromosome("f"), Chromosome("af"))); BOOST_CHECK(crossover(Chromosome("ff"), Chromosome("a")) == Chromosome("f"));
BOOST_CHECK(crossover(Chromosome("a"), Chromosome("ff")) == ChromosomePair(Chromosome("af"), Chromosome("f"))); BOOST_CHECK(crossover(Chromosome("a"), Chromosome("ff")) == Chromosome("af"));
} }
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_split_at_position_zero_only_if_at_least_one_chromosome_is_empty) BOOST_AUTO_TEST_CASE(randomPointCrossover_should_split_at_position_zero_only_if_at_least_one_chromosome_is_empty)
@ -282,52 +294,59 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_split_at_position_zero_only_if_
function<Crossover> crossover = randomPointCrossover(); function<Crossover> crossover = randomPointCrossover();
SimulationRNG::reset(1); SimulationRNG::reset(1);
BOOST_CHECK(crossover(empty, empty) == ChromosomePair(empty, empty)); BOOST_CHECK(crossover(empty, empty) == empty);
BOOST_CHECK(crossover(unsplittable, empty) == ChromosomePair(empty, unsplittable)); BOOST_CHECK(crossover(unsplittable, empty) == empty);
BOOST_CHECK(crossover(empty, unsplittable) == ChromosomePair(unsplittable, empty)); BOOST_CHECK(crossover(empty, unsplittable) == unsplittable);
BOOST_CHECK(crossover(splittable, empty) == ChromosomePair(empty, splittable)); BOOST_CHECK(crossover(splittable, empty) == empty);
BOOST_CHECK(crossover(empty, splittable) == ChromosomePair(splittable, empty)); BOOST_CHECK(crossover(empty, splittable) == splittable);
} }
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_swap_chromosome_parts_at_given_point) BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_swap_chromosome_parts_at_given_point)
{ {
auto [result1, result2] = fixedPointCrossover(0.8)(Chromosome("aaaaaaaaaa"), Chromosome("cccccccccc")); Chromosome result1 = fixedPointCrossover(0.8)(Chromosome("aaaaaaaaaa"), Chromosome("cccccccccc"));
Chromosome result2 = fixedPointCrossover(0.8)(Chromosome("cccccccccc"), Chromosome("aaaaaaaaaa"));
BOOST_TEST(result1 == Chromosome("aaaaaaaacc")); BOOST_TEST(result1 == Chromosome("aaaaaaaacc"));
BOOST_TEST(result2 == Chromosome("ccccccccaa")); BOOST_TEST(result2 == Chromosome("ccccccccaa"));
} }
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_determine_crossover_point_based_on_length_of_shorter_chromosome) BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_determine_crossover_point_based_on_length_of_shorter_chromosome)
{ {
auto [result1, result2] = fixedPointCrossover(0.4)(Chromosome("aaaaa"), Chromosome("cccccccccc")); Chromosome result1 = fixedPointCrossover(0.4)(Chromosome("aaaaa"), Chromosome("cccccccccc"));
Chromosome result2 = fixedPointCrossover(0.4)(Chromosome("cccccccccc"), Chromosome("aaaaa"));
BOOST_TEST(result1 == Chromosome("aacccccccc")); BOOST_TEST(result1 == Chromosome("aacccccccc"));
BOOST_TEST(result2 == Chromosome("ccaaa")); BOOST_TEST(result2 == Chromosome("ccaaa"));
} }
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_round_split_point) BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_round_split_point)
{ {
auto [result1, result2] = fixedPointCrossover(0.49)(Chromosome("aaaaa"), Chromosome("ccccc")); Chromosome result1 = fixedPointCrossover(0.49)(Chromosome("aaaaa"), Chromosome("ccccc"));
Chromosome result2 = fixedPointCrossover(0.49)(Chromosome("ccccc"), Chromosome("aaaaa"));
BOOST_TEST(result1 == Chromosome("aaccc")); BOOST_TEST(result1 == Chromosome("aaccc"));
BOOST_TEST(result2 == Chromosome("ccaaa")); BOOST_TEST(result2 == Chromosome("ccaaa"));
auto [result3, result4] = fixedPointCrossover(0.50)(Chromosome("aaaaa"), Chromosome("ccccc")); Chromosome result3 = fixedPointCrossover(0.50)(Chromosome("aaaaa"), Chromosome("ccccc"));
Chromosome result4 = fixedPointCrossover(0.50)(Chromosome("ccccc"), Chromosome("aaaaa"));
BOOST_TEST(result3 == Chromosome("aaacc")); BOOST_TEST(result3 == Chromosome("aaacc"));
BOOST_TEST(result4 == Chromosome("cccaa")); BOOST_TEST(result4 == Chromosome("cccaa"));
auto [result5, result6] = fixedPointCrossover(0.51)(Chromosome("aaaaa"), Chromosome("ccccc")); Chromosome result5 = fixedPointCrossover(0.51)(Chromosome("aaaaa"), Chromosome("ccccc"));
Chromosome result6 = fixedPointCrossover(0.51)(Chromosome("ccccc"), Chromosome("aaaaa"));
BOOST_TEST(result5 == Chromosome("aaacc")); BOOST_TEST(result5 == Chromosome("aaacc"));
BOOST_TEST(result6 == Chromosome("cccaa")); BOOST_TEST(result6 == Chromosome("cccaa"));
} }
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_position_zero_if_explicitly_requested) BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_position_zero_if_explicitly_requested)
{ {
auto [result1, result2] = fixedPointCrossover(0.0)(Chromosome("aaaaa"), Chromosome("cccccccccc")); Chromosome result1 = fixedPointCrossover(0.0)(Chromosome("aaaaa"), Chromosome("cccccccccc"));
Chromosome result2 = fixedPointCrossover(0.0)(Chromosome("cccccccccc"), Chromosome("aaaaa"));
BOOST_TEST(result1 == Chromosome("cccccccccc")); BOOST_TEST(result1 == Chromosome("cccccccccc"));
BOOST_TEST(result2 == Chromosome("aaaaa")); BOOST_TEST(result2 == Chromosome("aaaaa"));
} }
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_end_of_shorter_chromosome_if_crossover_point_is_after_last_position) BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_end_of_shorter_chromosome_if_crossover_point_is_after_last_position)
{ {
auto [result1, result2] = fixedPointCrossover(1.0)(Chromosome("aaaaa"), Chromosome("cccccccccc")); Chromosome result1 = fixedPointCrossover(1.0)(Chromosome("aaaaa"), Chromosome("cccccccccc"));
Chromosome result2 = fixedPointCrossover(1.0)(Chromosome("cccccccccc"), Chromosome("aaaaa"));
BOOST_TEST(result1 == Chromosome("aaaaaccccc")); BOOST_TEST(result1 == Chromosome("aaaaaccccc"));
BOOST_TEST(result2 == Chromosome("ccccc")); BOOST_TEST(result2 == Chromosome("ccccc"));
} }
@ -335,16 +354,16 @@ BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_end_of_shorter_chromoso
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_select_correct_split_point_for_unsplittable_chromosomes) BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_select_correct_split_point_for_unsplittable_chromosomes)
{ {
function<Crossover> crossover00 = fixedPointCrossover(0.0); function<Crossover> crossover00 = fixedPointCrossover(0.0);
BOOST_CHECK(crossover00(Chromosome("fff"), Chromosome("a")) == ChromosomePair(Chromosome("a"), Chromosome("fff"))); BOOST_CHECK(crossover00(Chromosome("fff"), Chromosome("a")) == Chromosome("a"));
BOOST_CHECK(crossover00(Chromosome("a"), Chromosome("fff")) == ChromosomePair(Chromosome("fff"), Chromosome("a"))); BOOST_CHECK(crossover00(Chromosome("a"), Chromosome("fff")) == Chromosome("fff"));
BOOST_CHECK(crossover00(Chromosome("f"), Chromosome("a")) == ChromosomePair(Chromosome("a"), Chromosome("f"))); BOOST_CHECK(crossover00(Chromosome("f"), Chromosome("a")) == Chromosome("a"));
function<Crossover> crossover10 = fixedPointCrossover(1.0); function<Crossover> crossover10 = fixedPointCrossover(1.0);
BOOST_CHECK(crossover10(Chromosome("fff"), Chromosome("a")) == ChromosomePair(Chromosome("f"), Chromosome("aff"))); BOOST_CHECK(crossover10(Chromosome("fff"), Chromosome("a")) == Chromosome("f"));
BOOST_CHECK(crossover10(Chromosome("a"), Chromosome("fff")) == ChromosomePair(Chromosome("aff"), Chromosome("f"))); BOOST_CHECK(crossover10(Chromosome("a"), Chromosome("fff")) == Chromosome("aff"));
BOOST_CHECK(crossover10(Chromosome("f"), Chromosome("a")) == ChromosomePair(Chromosome("f"), Chromosome("a"))); BOOST_CHECK(crossover10(Chromosome("f"), Chromosome("a")) == Chromosome("f"));
} }
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_always_use_position_zero_as_split_point_when_chromosome_empty) BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_always_use_position_zero_as_split_point_when_chromosome_empty)
@ -354,18 +373,18 @@ BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_always_use_position_zero_as_spli
Chromosome splittable("aaaa"); Chromosome splittable("aaaa");
function<Crossover> crossover00 = fixedPointCrossover(0.0); function<Crossover> crossover00 = fixedPointCrossover(0.0);
BOOST_CHECK(crossover00(empty, empty) == ChromosomePair(empty, empty)); BOOST_CHECK(crossover00(empty, empty) == empty);
BOOST_CHECK(crossover00(unsplittable, empty) == ChromosomePair(empty, unsplittable)); BOOST_CHECK(crossover00(unsplittable, empty) == empty);
BOOST_CHECK(crossover00(empty, unsplittable) == ChromosomePair(unsplittable, empty)); BOOST_CHECK(crossover00(empty, unsplittable) == unsplittable);
BOOST_CHECK(crossover00(splittable, empty) == ChromosomePair(empty, splittable)); BOOST_CHECK(crossover00(splittable, empty) == empty);
BOOST_CHECK(crossover00(empty, splittable) == ChromosomePair(splittable, empty)); BOOST_CHECK(crossover00(empty, splittable) == splittable);
function<Crossover> crossover10 = fixedPointCrossover(1.0); function<Crossover> crossover10 = fixedPointCrossover(1.0);
BOOST_CHECK(crossover10(empty, empty) == ChromosomePair(empty, empty)); BOOST_CHECK(crossover10(empty, empty) == empty);
BOOST_CHECK(crossover10(unsplittable, empty) == ChromosomePair(empty, unsplittable)); BOOST_CHECK(crossover10(unsplittable, empty) == empty);
BOOST_CHECK(crossover10(empty, unsplittable) == ChromosomePair(unsplittable, empty)); BOOST_CHECK(crossover10(empty, unsplittable) == unsplittable);
BOOST_CHECK(crossover10(splittable, empty) == ChromosomePair(empty, splittable)); BOOST_CHECK(crossover10(splittable, empty) == empty);
BOOST_CHECK(crossover10(empty, splittable) == ChromosomePair(splittable, empty)); BOOST_CHECK(crossover10(empty, splittable) == splittable);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -263,10 +263,10 @@ BOOST_FIXTURE_TEST_CASE(mutate_should_return_empty_population_if_selection_is_em
BOOST_FIXTURE_TEST_CASE(crossover_should_return_population_containing_individuals_indicated_by_selection_with_crossover_applied, PopulationFixture) BOOST_FIXTURE_TEST_CASE(crossover_should_return_population_containing_individuals_indicated_by_selection_with_crossover_applied, PopulationFixture)
{ {
Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")});
PairMosaicSelection selection({{0, 1}, {2, 1}}, 0.5); PairMosaicSelection selection({{0, 1}, {2, 1}}, 1.0);
assert(selection.materialise(population.individuals().size()) == (vector<tuple<size_t, size_t>>{{0, 1}, {2, 1}})); assert(selection.materialise(population.individuals().size()) == (vector<tuple<size_t, size_t>>{{0, 1}, {2, 1}, {0, 1}, {2, 1}}));
Population expectedPopulation(m_fitnessMetric, {Chromosome("ac"), Chromosome("ca"), Chromosome("cg"), Chromosome("gc")}); Population expectedPopulation(m_fitnessMetric, {Chromosome("ac"), Chromosome("ac"), Chromosome("gc"), Chromosome("gc")});
BOOST_TEST(population.crossover(selection, fixedPointCrossover(0.5)) == expectedPopulation); BOOST_TEST(population.crossover(selection, fixedPointCrossover(0.5)) == expectedPopulation);
} }
@ -274,8 +274,8 @@ BOOST_FIXTURE_TEST_CASE(crossover_should_return_population_containing_individual
BOOST_FIXTURE_TEST_CASE(crossover_should_include_duplicates_if_selection_contains_duplicates, PopulationFixture) BOOST_FIXTURE_TEST_CASE(crossover_should_include_duplicates_if_selection_contains_duplicates, PopulationFixture)
{ {
Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")}); Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")});
PairMosaicSelection selection({{0, 0}, {1, 1}}, 1.0); PairMosaicSelection selection({{0, 0}, {1, 1}}, 2.0);
assert(selection.materialise(population.individuals().size()) == (vector<tuple<size_t, size_t>>{{0, 0}, {1, 1}})); assert(selection.materialise(population.individuals().size()) == (vector<tuple<size_t, size_t>>{{0, 0}, {1, 1}, {0, 0}, {1, 1}}));
BOOST_TEST( BOOST_TEST(
population.crossover(selection, fixedPointCrossover(0.5)) == population.crossover(selection, fixedPointCrossover(0.5)) ==

View File

@ -71,7 +71,7 @@ void GenerationalElitistWithExclusivePools::runNextRound()
) )
) + ) +
m_population.select(elite).crossover( m_population.select(elite).crossover(
RandomPairSelection(m_options.crossoverPoolSize / elitePoolSize / 2), RandomPairSelection(m_options.crossoverPoolSize / elitePoolSize),
randomPointCrossover() randomPointCrossover()
); );
} }

View File

@ -98,7 +98,7 @@ function<Mutation> phaser::alternativeMutations(
namespace namespace
{ {
ChromosomePair buildChromosomesBySwappingParts( Chromosome buildChromosomesBySwappingParts(
Chromosome const& _chromosome1, Chromosome const& _chromosome1,
Chromosome const& _chromosome2, Chromosome const& _chromosome2,
size_t _crossoverPoint size_t _crossoverPoint
@ -110,15 +110,9 @@ ChromosomePair buildChromosomesBySwappingParts(
auto begin1 = _chromosome1.optimisationSteps().begin(); auto begin1 = _chromosome1.optimisationSteps().begin();
auto begin2 = _chromosome2.optimisationSteps().begin(); auto begin2 = _chromosome2.optimisationSteps().begin();
return ChromosomePair( return Chromosome(
Chromosome( vector<string>(begin1, begin1 + _crossoverPoint) +
vector<string>(begin1, begin1 + _crossoverPoint) + vector<string>(begin2 + _crossoverPoint, _chromosome2.optimisationSteps().end())
vector<string>(begin2 + _crossoverPoint, _chromosome2.optimisationSteps().end())
),
Chromosome(
vector<string>(begin2, begin2 + _crossoverPoint) +
vector<string>(begin1 + _crossoverPoint, _chromosome1.optimisationSteps().end())
)
); );
} }

View File

@ -28,10 +28,8 @@
namespace solidity::phaser namespace solidity::phaser
{ {
using ChromosomePair = std::tuple<Chromosome, Chromosome>;
using Mutation = Chromosome(Chromosome const&); using Mutation = Chromosome(Chromosome const&);
using Crossover = ChromosomePair(Chromosome const&, Chromosome const&); using Crossover = Chromosome(Chromosome const&, Chromosome const&);
// MUTATIONS // MUTATIONS
@ -64,10 +62,9 @@ std::function<Mutation> alternativeMutations(
std::function<Crossover> randomPointCrossover(); std::function<Crossover> randomPointCrossover();
/// Creates a crossover operator that always chooses a point that lies at @a _crossoverPoint /// Creates a crossover operator that always chooses a point that lies at @a _crossoverPoint
/// percent of the length of the shorter chromosome. Then creates a pair of chromosomes by /// percent of the length of the shorter chromosome. Then creates a new chromosome by
/// splitting both inputs at the crossover point and stitching the resulting parts. The first /// splitting both inputs at the crossover point and stitching output from the first half or first
/// output is created from the first half or first input and the second half of the second input /// input and the second half of the second input.
/// The second output from the remaining two halves.
/// ///
/// Avoids selecting position 0 (since this just produces a chromosome identical to the second one) /// Avoids selecting position 0 (since this just produces a chromosome identical to the second one)
/// unless there is no other choice (i.e. one of the chromosomes is empty). /// unless there is no other choice (i.e. one of the chromosomes is empty).

View File

@ -108,12 +108,11 @@ Population Population::crossover(PairSelection const& _selection, function<Cross
vector<Individual> crossedIndividuals; vector<Individual> crossedIndividuals;
for (auto const& [i, j]: _selection.materialise(m_individuals.size())) for (auto const& [i, j]: _selection.materialise(m_individuals.size()))
{ {
auto [childChromosome1, childChromosome2] = _crossover( auto childChromosome = _crossover(
m_individuals[i].chromosome, m_individuals[i].chromosome,
m_individuals[j].chromosome m_individuals[j].chromosome
); );
crossedIndividuals.emplace_back(move(childChromosome1), *m_fitnessMetric); crossedIndividuals.emplace_back(move(childChromosome), *m_fitnessMetric);
crossedIndividuals.emplace_back(move(childChromosome2), *m_fitnessMetric);
} }
return Population(m_fitnessMetric, crossedIndividuals); return Population(m_fitnessMetric, crossedIndividuals);