[yul-phaser] Add RandomAlgorithm

This commit is contained in:
cameel 2020-02-05 14:58:48 +01:00 committed by Kamil Śliwak
parent 4665b7a7e4
commit 67fbafab8f
3 changed files with 110 additions and 0 deletions

View File

@ -89,6 +89,49 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, GeneticAlgorithmFix
BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(algorithm.population().individuals()[0].chromosome)) == 4);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(RandomAlgorithmTest)
BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_randomise_rest_of_population, GeneticAlgorithmFixture)
{
auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5);
RandomAlgorithm algorithm(population, m_output, {0.5, 1, 1});
assert((chromosomeLengths(algorithm.population()) == vector<size_t>{3, 3, 3, 3, 5, 5, 5, 5}));
algorithm.runNextRound();
BOOST_TEST((chromosomeLengths(algorithm.population()) == vector<size_t>{1, 1, 1, 1, 3, 3, 3, 3}));
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture)
{
auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5);
RandomAlgorithm algorithm(population, m_output, {0.5, 7, 7});
assert((chromosomeLengths(algorithm.population()) == vector<size_t>{3, 3, 3, 3, 5, 5, 5, 5}));
algorithm.runNextRound();
BOOST_TEST((chromosomeLengths(algorithm.population()) == vector<size_t>{3, 3, 3, 3, 7, 7, 7, 7}));
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_replace_all_chromosomes_if_zero_size_elite, GeneticAlgorithmFixture)
{
auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5);
RandomAlgorithm algorithm(population, m_output, {0.0, 1, 1});
assert((chromosomeLengths(algorithm.population()) == vector<size_t>{3, 3, 3, 3, 5, 5, 5, 5}));
algorithm.runNextRound();
BOOST_TEST((chromosomeLengths(algorithm.population()) == vector<size_t>{1, 1, 1, 1, 1, 1, 1, 1}));
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_any_chromosomes_if_whole_population_is_the_elite, GeneticAlgorithmFixture)
{
auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5);
RandomAlgorithm algorithm(population, m_output, {1.0, 1, 1});
assert((chromosomeLengths(algorithm.population()) == vector<size_t>{3, 3, 3, 3, 5, 5, 5, 5}));
algorithm.runNextRound();
BOOST_TEST((chromosomeLengths(algorithm.population()) == vector<size_t>{3, 3, 3, 3, 5, 5, 5, 5}));
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()

View File

@ -16,6 +16,7 @@
*/
#include <tools/yulPhaser/GeneticAlgorithms.h>
#include <tools/yulPhaser/Selections.h>
using namespace std;
using namespace solidity::phaser;
@ -30,3 +31,20 @@ void GeneticAlgorithm::run(optional<size_t> _numRounds)
m_outputStream << m_population;
}
}
void RandomAlgorithm::runNextRound()
{
RangeSelection elite(0.0, m_options.elitePoolSize);
Population elitePopulation = m_population.select(elite);
size_t replacementCount = m_population.individuals().size() - elitePopulation.individuals().size();
m_population =
move(elitePopulation) +
Population::makeRandom(
m_population.fitnessMetric(),
replacementCount,
m_options.minChromosomeLength,
m_options.maxChromosomeLength
);
}

View File

@ -63,4 +63,53 @@ private:
std::ostream& m_outputStream;
};
/**
* Completely random genetic algorithm,
*
* The algorithm simply replaces the worst chromosomes with entirely new ones, generated
* randomly and not based on any member of the current population. Only a constant proportion of the
* chromosomes (the elite) is preserved in each round.
*
* Preserves the size of the population. You can use @a elitePoolSize to make the algorithm
* generational (replacing most members in each round) or steady state (replacing only one member).
* Both versions are equivalent in terms of the outcome but the generational one converges in a
* smaller number of rounds while the steady state one does less work per round. This may matter
* in case of metrics that take a long time to compute though in case of this particular
* algorithm the same result could also be achieved by simply making the population smaller.
*/
class RandomAlgorithm: public GeneticAlgorithm
{
public:
struct Options
{
double elitePoolSize; ///< Percentage of the population treated as the elite
size_t minChromosomeLength; ///< Minimum length of newly generated chromosomes
size_t maxChromosomeLength; ///< Maximum length of newly generated chromosomes
bool isValid() const
{
return (
0 <= elitePoolSize && elitePoolSize <= 1.0 &&
minChromosomeLength <= maxChromosomeLength
);
}
};
explicit RandomAlgorithm(
Population _initialPopulation,
std::ostream& _outputStream,
Options const& _options
):
GeneticAlgorithm(_initialPopulation, _outputStream),
m_options(_options)
{
assert(_options.isValid());
}
void runNextRound() override;
private:
Options m_options;
};
}