[yul-phaser] GeneticAlgorithms: Add ClassicGeneticAlgorithm

This commit is contained in:
Kamil Śliwak
2020-04-06 19:06:08 +02:00
parent 879f6e17e9
commit f6783c60b2
3 changed files with 321 additions and 0 deletions
+204
View File
@@ -31,6 +31,7 @@
using namespace std;
using namespace boost::unit_test::framework;
using namespace boost::test_tools;
using namespace solidity::util;
namespace solidity::phaser::test
{
@@ -41,6 +42,18 @@ protected:
shared_ptr<FitnessMetric> m_fitnessMetric = make_shared<ChromosomeLengthMetric>();
};
class ClassicGeneticAlgorithmFixture: public GeneticAlgorithmFixture
{
protected:
ClassicGeneticAlgorithm::Options m_options = {
/* elitePoolSize = */ 0.0,
/* crossoverChance = */ 0.0,
/* mutationChance = */ 0.0,
/* deletionChance = */ 0.0,
/* additionChance = */ 0.0,
};
};
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(GeneticAlgorithmsTest)
BOOST_AUTO_TEST_SUITE(RandomAlgorithmTest)
@@ -186,6 +199,197 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove
}));
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(ClassicGeneticAlgorithmTest)
BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_proportional_to_fitness, ClassicGeneticAlgorithmFixture)
{
constexpr double relativeTolerance = 0.1;
constexpr size_t populationSize = 1000;
assert(populationSize % 4 == 0 && "Choose a number divisible by 4 for this test");
auto population =
Population::makeRandom(m_fitnessMetric, populationSize / 4, 0, 0) +
Population::makeRandom(m_fitnessMetric, populationSize / 4, 1, 1) +
Population::makeRandom(m_fitnessMetric, populationSize / 4, 2, 2) +
Population::makeRandom(m_fitnessMetric, populationSize / 4, 3, 3);
map<size_t, double> expectedProbabilities = {
{0, 4.0 / (4 + 3 + 2 + 1)},
{1, 3.0 / (4 + 3 + 2 + 1)},
{2, 2.0 / (4 + 3 + 2 + 1)},
{3, 1.0 / (4 + 3 + 2 + 1)},
};
double const expectedValue = (
0.0 * expectedProbabilities[0] +
1.0 * expectedProbabilities[1] +
2.0 * expectedProbabilities[2] +
3.0 * expectedProbabilities[3]
);
double const variance = (
(0.0 - expectedValue) * (0.0 - expectedValue) * expectedProbabilities[0] +
(1.0 - expectedValue) * (1.0 - expectedValue) * expectedProbabilities[1] +
(2.0 - expectedValue) * (2.0 - expectedValue) * expectedProbabilities[2] +
(3.0 - expectedValue) * (3.0 - expectedValue) * expectedProbabilities[3]
);
ClassicGeneticAlgorithm algorithm(m_options);
Population newPopulation = algorithm.runNextRound(population);
BOOST_TEST(newPopulation.individuals().size() == population.individuals().size());
vector<size_t> newFitness = chromosomeLengths(newPopulation);
BOOST_TEST(abs(mean(newFitness) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(newFitness, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_only_individuals_existing_in_the_original_population, ClassicGeneticAlgorithmFixture)
{
constexpr size_t populationSize = 1000;
auto population = Population::makeRandom(m_fitnessMetric, populationSize, 1, 10);
set<string> originalSteps;
for (auto const& individual: population.individuals())
originalSteps.insert(toString(individual.chromosome));
ClassicGeneticAlgorithm algorithm(m_options);
Population newPopulation = algorithm.runNextRound(population);
for (auto const& individual: newPopulation.individuals())
BOOST_TEST(originalSteps.count(toString(individual.chromosome)) == 1);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_crossover, ClassicGeneticAlgorithmFixture)
{
auto population = Population(m_fitnessMetric, {
Chromosome("aa"), Chromosome("aa"), Chromosome("aa"),
Chromosome("ff"), Chromosome("ff"), Chromosome("ff"),
Chromosome("gg"), Chromosome("gg"), Chromosome("gg"),
});
set<string> originalSteps{"aa", "ff", "gg"};
set<string> crossedSteps{"af", "fa", "fg", "gf", "ga", "ag"};
m_options.crossoverChance = 0.8;
ClassicGeneticAlgorithm algorithm(m_options);
SimulationRNG::reset(1);
Population newPopulation = algorithm.runNextRound(population);
size_t totalCrossed = 0;
size_t totalUnchanged = 0;
for (auto const& individual: newPopulation.individuals())
{
totalCrossed += crossedSteps.count(toString(individual.chromosome));
totalUnchanged += originalSteps.count(toString(individual.chromosome));
}
BOOST_TEST(totalCrossed + totalUnchanged == newPopulation.individuals().size());
BOOST_TEST(totalCrossed >= 2);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_mutation, ClassicGeneticAlgorithmFixture)
{
m_options.mutationChance = 0.6;
ClassicGeneticAlgorithm algorithm(m_options);
constexpr size_t populationSize = 1000;
constexpr double relativeTolerance = 0.05;
double const expectedValue = m_options.mutationChance;
double const variance = m_options.mutationChance * (1 - m_options.mutationChance);
Chromosome chromosome("aaaaaaaaaa");
vector<Chromosome> chromosomes(populationSize, chromosome);
Population population(m_fitnessMetric, chromosomes);
SimulationRNG::reset(1);
Population newPopulation = algorithm.runNextRound(population);
vector<size_t> bernoulliTrials;
for (auto const& individual: newPopulation.individuals())
{
string steps = toString(individual.chromosome);
for (char step: steps)
bernoulliTrials.push_back(static_cast<size_t>(step != 'a'));
}
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithmFixture)
{
m_options.deletionChance = 0.6;
ClassicGeneticAlgorithm algorithm(m_options);
constexpr size_t populationSize = 1000;
constexpr double relativeTolerance = 0.05;
double const expectedValue = m_options.deletionChance;
double const variance = m_options.deletionChance * (1 - m_options.deletionChance);
Chromosome chromosome("aaaaaaaaaa");
vector<Chromosome> chromosomes(populationSize, chromosome);
Population population(m_fitnessMetric, chromosomes);
SimulationRNG::reset(1);
Population newPopulation = algorithm.runNextRound(population);
vector<size_t> bernoulliTrials;
for (auto const& individual: newPopulation.individuals())
{
string steps = toString(individual.chromosome);
for (size_t i = 0; i < chromosome.length(); ++i)
bernoulliTrials.push_back(static_cast<size_t>(i >= steps.size()));
}
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_addition, ClassicGeneticAlgorithmFixture)
{
m_options.additionChance = 0.6;
ClassicGeneticAlgorithm algorithm(m_options);
constexpr size_t populationSize = 1000;
constexpr double relativeTolerance = 0.05;
double const expectedValue = m_options.additionChance;
double const variance = m_options.additionChance * (1 - m_options.additionChance);
Chromosome chromosome("aaaaaaaaaa");
vector<Chromosome> chromosomes(populationSize, chromosome);
Population population(m_fitnessMetric, chromosomes);
SimulationRNG::reset(1);
Population newPopulation = algorithm.runNextRound(population);
vector<size_t> bernoulliTrials;
for (auto const& individual: newPopulation.individuals())
{
string steps = toString(individual.chromosome);
for (size_t i = 0; i < chromosome.length() + 1; ++i)
{
BOOST_REQUIRE(chromosome.length() <= steps.size() && steps.size() <= 2 * chromosome.length() + 1);
bernoulliTrials.push_back(static_cast<size_t>(i < steps.size() - chromosome.length()));
}
}
BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance);
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite, ClassicGeneticAlgorithmFixture)
{
auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 6, 5, 5);
assert((chromosomeLengths(population) == vector<size_t>{3, 3, 3, 3, 5, 5, 5, 5, 5, 5}));
m_options.elitePoolSize = 0.5;
m_options.deletionChance = 1.0;
ClassicGeneticAlgorithm algorithm(m_options);
Population newPopulation = algorithm.runNextRound(population);
BOOST_TEST((chromosomeLengths(newPopulation) == vector<size_t>{0, 0, 0, 0, 0, 3, 3, 3, 3, 5}));
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()