[yul-phaser] Add randomPointCrossover() and fixedPointCrossover() operators

This commit is contained in:
Kamil Śliwak 2020-02-06 04:34:09 +01:00
parent 3fdb4ca607
commit c941eaf5d6
3 changed files with 238 additions and 0 deletions

View File

@ -212,6 +212,162 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_second_mutation_i
BOOST_TEST(mutation(chromosome) == Chromosome("f"));
}
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_swap_chromosome_parts_at_random_point)
{
SimulationRNG::reset(1);
function<Crossover> crossover = randomPointCrossover();
auto [result1, result2] = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc"));
BOOST_TEST(result1 == Chromosome("aaaccc"));
BOOST_TEST(result2 == Chromosome("cccaaaaaaa"));
}
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_only_consider_points_available_on_both_chromosomes)
{
SimulationRNG::reset(1);
function<Crossover> crossover = randomPointCrossover();
for (size_t i = 0; i < 30; ++i)
{
auto [result1, result2] = crossover(Chromosome("aaa"), Chromosome("TTTTTTTTTTTTTTTTTTTT"));
BOOST_TEST((
(result1 == Chromosome("TTTTTTTTTTTTTTTTTTTT") && result2 == Chromosome("aaa")) ||
(result1 == Chromosome("aTTTTTTTTTTTTTTTTTTT") && result2 == Chromosome("Taa")) ||
(result1 == Chromosome("aaTTTTTTTTTTTTTTTTTT") && result2 == Chromosome("TTa")) ||
(result1 == Chromosome("aaaTTTTTTTTTTTTTTTTT") && result2 == Chromosome("TTT"))
));
}
}
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if_chromosomes_are_splittable)
{
SimulationRNG::reset(1);
function<Crossover> crossover = randomPointCrossover();
for (size_t i = 0; i < 30; ++i)
{
auto [result1, result2] = crossover(Chromosome("aa"), Chromosome("TTTTTTTTTTTTTTTTTTTT"));
BOOST_TEST(result1 != Chromosome("TTTTTTTTTTTTTTTTTTTT"));
BOOST_TEST(result2 != Chromosome("aa"));
}
}
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if_chromosomes_are_not_empty)
{
SimulationRNG::reset(1);
function<Crossover> crossover = randomPointCrossover();
for (size_t i = 0; i < 30; ++i)
{
auto [result1, result2] = crossover(Chromosome("a"), Chromosome("T"));
BOOST_TEST(result1 == Chromosome("a"));
BOOST_TEST(result2 == Chromosome("T"));
}
}
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_work_even_if_one_chromosome_is_unsplittable)
{
function<Crossover> crossover = randomPointCrossover();
SimulationRNG::reset(1);
BOOST_CHECK(crossover(Chromosome("ff"), Chromosome("a")) == ChromosomePair(Chromosome("f"), Chromosome("af")));
BOOST_CHECK(crossover(Chromosome("a"), Chromosome("ff")) == ChromosomePair(Chromosome("af"), Chromosome("f")));
}
BOOST_AUTO_TEST_CASE(randomPointCrossover_should_split_at_position_zero_only_if_at_least_one_chromosome_is_empty)
{
Chromosome empty("");
Chromosome unsplittable("a");
Chromosome splittable("aaaa");
function<Crossover> crossover = randomPointCrossover();
SimulationRNG::reset(1);
BOOST_CHECK(crossover(empty, empty) == ChromosomePair(empty, empty));
BOOST_CHECK(crossover(unsplittable, empty) == ChromosomePair(empty, unsplittable));
BOOST_CHECK(crossover(empty, unsplittable) == ChromosomePair(unsplittable, empty));
BOOST_CHECK(crossover(splittable, empty) == ChromosomePair(empty, splittable));
BOOST_CHECK(crossover(empty, splittable) == ChromosomePair(splittable, empty));
}
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_swap_chromosome_parts_at_given_point)
{
auto [result1, result2] = fixedPointCrossover(0.8)(Chromosome("aaaaaaaaaa"), Chromosome("cccccccccc"));
BOOST_TEST(result1 == Chromosome("aaaaaaaacc"));
BOOST_TEST(result2 == Chromosome("ccccccccaa"));
}
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"));
BOOST_TEST(result1 == Chromosome("aacccccccc"));
BOOST_TEST(result2 == Chromosome("ccaaa"));
}
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_round_split_point)
{
auto [result1, result2] = fixedPointCrossover(0.49)(Chromosome("aaaaa"), Chromosome("ccccc"));
BOOST_TEST(result1 == Chromosome("aaccc"));
BOOST_TEST(result2 == Chromosome("ccaaa"));
auto [result3, result4] = fixedPointCrossover(0.50)(Chromosome("aaaaa"), Chromosome("ccccc"));
BOOST_TEST(result3 == Chromosome("aaacc"));
BOOST_TEST(result4 == Chromosome("cccaa"));
auto [result5, result6] = fixedPointCrossover(0.51)(Chromosome("aaaaa"), Chromosome("ccccc"));
BOOST_TEST(result5 == Chromosome("aaacc"));
BOOST_TEST(result6 == Chromosome("cccaa"));
}
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_position_zero_if_explicitly_requested)
{
auto [result1, result2] = fixedPointCrossover(0.0)(Chromosome("aaaaa"), Chromosome("cccccccccc"));
BOOST_TEST(result1 == Chromosome("cccccccccc"));
BOOST_TEST(result2 == Chromosome("aaaaa"));
}
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"));
BOOST_TEST(result1 == Chromosome("aaaaaccccc"));
BOOST_TEST(result2 == Chromosome("ccccc"));
}
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_select_correct_split_point_for_unsplittable_chromosomes)
{
function<Crossover> crossover00 = fixedPointCrossover(0.0);
BOOST_CHECK(crossover00(Chromosome("fff"), Chromosome("a")) == ChromosomePair(Chromosome("a"), Chromosome("fff")));
BOOST_CHECK(crossover00(Chromosome("a"), Chromosome("fff")) == ChromosomePair(Chromosome("fff"), Chromosome("a")));
BOOST_CHECK(crossover00(Chromosome("f"), Chromosome("a")) == ChromosomePair(Chromosome("a"), Chromosome("f")));
function<Crossover> crossover10 = fixedPointCrossover(1.0);
BOOST_CHECK(crossover10(Chromosome("fff"), Chromosome("a")) == ChromosomePair(Chromosome("f"), Chromosome("aff")));
BOOST_CHECK(crossover10(Chromosome("a"), Chromosome("fff")) == ChromosomePair(Chromosome("aff"), Chromosome("f")));
BOOST_CHECK(crossover10(Chromosome("f"), Chromosome("a")) == ChromosomePair(Chromosome("f"), Chromosome("a")));
}
BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_always_use_position_zero_as_split_point_when_chromosome_empty)
{
Chromosome empty("");
Chromosome unsplittable("f");
Chromosome splittable("aaaa");
function<Crossover> crossover00 = fixedPointCrossover(0.0);
BOOST_CHECK(crossover00(empty, empty) == ChromosomePair(empty, empty));
BOOST_CHECK(crossover00(unsplittable, empty) == ChromosomePair(empty, unsplittable));
BOOST_CHECK(crossover00(empty, unsplittable) == ChromosomePair(unsplittable, empty));
BOOST_CHECK(crossover00(splittable, empty) == ChromosomePair(empty, splittable));
BOOST_CHECK(crossover00(empty, splittable) == ChromosomePair(splittable, empty));
function<Crossover> crossover10 = fixedPointCrossover(1.0);
BOOST_CHECK(crossover10(empty, empty) == ChromosomePair(empty, empty));
BOOST_CHECK(crossover10(unsplittable, empty) == ChromosomePair(empty, unsplittable));
BOOST_CHECK(crossover10(empty, unsplittable) == ChromosomePair(unsplittable, empty));
BOOST_CHECK(crossover10(splittable, empty) == ChromosomePair(empty, splittable));
BOOST_CHECK(crossover10(empty, splittable) == ChromosomePair(splittable, empty));
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()

View File

@ -19,6 +19,11 @@
#include <tools/yulPhaser/SimulationRNG.h>
#include <libsolutil/CommonData.h>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <string>
#include <vector>
@ -89,3 +94,60 @@ function<Mutation> phaser::alternativeMutations(
return _mutation2(_chromosome);
};
}
namespace
{
ChromosomePair buildChromosomesBySwappingParts(
Chromosome const& _chromosome1,
Chromosome const& _chromosome2,
size_t _crossoverPoint
)
{
assert(_crossoverPoint <= _chromosome1.length());
assert(_crossoverPoint <= _chromosome2.length());
auto begin1 = _chromosome1.optimisationSteps().begin();
auto begin2 = _chromosome2.optimisationSteps().begin();
return ChromosomePair(
Chromosome(
vector<string>(begin1, begin1 + _crossoverPoint) +
vector<string>(begin2 + _crossoverPoint, _chromosome2.optimisationSteps().end())
),
Chromosome(
vector<string>(begin2, begin2 + _crossoverPoint) +
vector<string>(begin1 + _crossoverPoint, _chromosome1.optimisationSteps().end())
)
);
}
}
function<Crossover> phaser::randomPointCrossover()
{
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 randomPoint = SimulationRNG::uniformInt(minPoint, minLength);
return buildChromosomesBySwappingParts(_chromosome1, _chromosome2, randomPoint);
};
}
function<Crossover> phaser::fixedPointCrossover(double _crossoverPoint)
{
assert(0.0 <= _crossoverPoint && _crossoverPoint <= 1.0);
return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2)
{
size_t minLength = min(_chromosome1.length(), _chromosome2.length());
size_t concretePoint = static_cast<size_t>(round(minLength * _crossoverPoint));
return buildChromosomesBySwappingParts(_chromosome1, _chromosome2, concretePoint);
};
}

View File

@ -23,11 +23,15 @@
#include <tools/yulPhaser/Chromosome.h>
#include <functional>
#include <tuple>
namespace solidity::phaser
{
using ChromosomePair = std::tuple<Chromosome, Chromosome>;
using Mutation = Chromosome(Chromosome const&);
using Crossover = ChromosomePair(Chromosome const&, Chromosome const&);
// MUTATIONS
@ -53,4 +57,20 @@ std::function<Mutation> alternativeMutations(
std::function<Mutation> _mutation2
);
// CROSSOVER
/// Creates a crossover operator that randomly selects a number between 0 and 1 and uses it as the
/// position at which to perform perform @a fixedPointCrossover.
std::function<Crossover> randomPointCrossover();
/// 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
/// splitting both inputs at the crossover point and stitching the resulting parts. The first
/// output is created from the first half or first 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)
/// unless there is no other choice (i.e. one of the chromosomes is empty).
std::function<Crossover> fixedPointCrossover(double _crossoverPoint);
}