diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp index 4fb796c3e..1b8e76da0 100644 --- a/test/yulPhaser/Population.cpp +++ b/test/yulPhaser/Population.cpp @@ -42,19 +42,6 @@ using namespace boost::unit_test::framework; namespace solidity::phaser::test { -namespace -{ - bool fitnessNotSet(Individual const& individual) - { - return !individual.fitness.has_value(); - } - - bool fitnessSet(Individual const& individual) - { - return individual.fitness.has_value(); - } -} - class PopulationFixture { protected: @@ -94,7 +81,7 @@ BOOST_AUTO_TEST_CASE(isFitter_should_return_false_for_identical_individuals) BOOST_TEST(!isFitter(Individual{Chromosome("acT"), 0}, Individual{Chromosome("acT"), 0})); } -BOOST_FIXTURE_TEST_CASE(constructor_should_copy_chromosomes_and_not_compute_fitness, PopulationFixture) +BOOST_FIXTURE_TEST_CASE(constructor_should_copy_chromosomes_and_compute_fitness, PopulationFixture) { vector chromosomes = { Chromosome::makeRandom(5), @@ -106,8 +93,8 @@ BOOST_FIXTURE_TEST_CASE(constructor_should_copy_chromosomes_and_not_compute_fitn BOOST_TEST(population.individuals()[0].chromosome == chromosomes[0]); BOOST_TEST(population.individuals()[1].chromosome == chromosomes[1]); - auto fitnessNotSet = [](auto const& individual){ return !individual.fitness.has_value(); }; - BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet)); + BOOST_TEST(population.individuals()[0].fitness == m_fitnessMetric->evaluate(population.individuals()[0].chromosome)); + BOOST_TEST(population.individuals()[1].fitness == m_fitnessMetric->evaluate(population.individuals()[1].chromosome)); } BOOST_FIXTURE_TEST_CASE(makeRandom_should_get_chromosome_lengths_from_specified_generator, PopulationFixture) @@ -181,22 +168,13 @@ BOOST_FIXTURE_TEST_CASE(makeRandom_should_return_population_with_random_chromoso BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } -BOOST_FIXTURE_TEST_CASE(makeRandom_should_not_compute_fitness, PopulationFixture) +BOOST_FIXTURE_TEST_CASE(makeRandom_should_compute_fitness, PopulationFixture) { auto population = Population::makeRandom(m_fitnessMetric, 3, 5, 10); - BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet)); -} - -BOOST_FIXTURE_TEST_CASE(run_should_evaluate_fitness, PopulationFixture) -{ - stringstream output; - auto population = Population::makeRandom(m_fitnessMetric, 5, 5, 10); - assert(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet)); - - population.run(1, output); - - BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessSet)); + BOOST_TEST(population.individuals()[0].fitness == m_fitnessMetric->evaluate(population.individuals()[0].chromosome)); + BOOST_TEST(population.individuals()[1].fitness == m_fitnessMetric->evaluate(population.individuals()[1].chromosome)); + BOOST_TEST(population.individuals()[2].fitness == m_fitnessMetric->evaluate(population.individuals()[2].chromosome)); } BOOST_FIXTURE_TEST_CASE(run_should_not_make_fitness_of_top_chromosomes_worse, PopulationFixture) @@ -220,12 +198,10 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_make_fitness_of_top_chromosomes_worse, Po { population.run(1, output); BOOST_TEST(population.individuals().size() == 5); - BOOST_TEST(fitnessSet(population.individuals()[0])); - BOOST_TEST(fitnessSet(population.individuals()[1])); size_t currentTopFitness[2] = { - population.individuals()[0].fitness.value(), - population.individuals()[1].fitness.value(), + population.individuals()[0].fitness, + population.individuals()[1].fitness, }; BOOST_TEST(currentTopFitness[0] <= initialTopFitness[0]); BOOST_TEST(currentTopFitness[1] <= initialTopFitness[1]); diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index 58309299a..d258fbf3d 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -41,11 +41,7 @@ ostream& operator<<(ostream& _stream, Population const& _population); ostream& phaser::operator<<(ostream& _stream, Individual const& _individual) { - _stream << "Fitness: "; - if (_individual.fitness.has_value()) - _stream << _individual.fitness.value(); - else - _stream << ""; + _stream << "Fitness: " << _individual.fitness; _stream << ", optimisations: " << _individual.chromosome; return _stream; @@ -53,12 +49,10 @@ ostream& phaser::operator<<(ostream& _stream, Individual const& _individual) bool phaser::isFitter(Individual const& a, Individual const& b) { - assert(a.fitness.has_value() && b.fitness.has_value()); - return ( - (a.fitness.value() < b.fitness.value()) || - (a.fitness.value() == b.fitness.value() && a.chromosome.length() < b.chromosome.length()) || - (a.fitness.value() == b.fitness.value() && a.chromosome.length() == b.chromosome.length() && toString(a.chromosome) < toString(b.chromosome)) + (a.fitness < b.fitness) || + (a.fitness == b.fitness && a.chromosome.length() < b.chromosome.length()) || + (a.fitness == b.fitness && a.chromosome.length() == b.chromosome.length() && toString(a.chromosome) < toString(b.chromosome)) ); } @@ -68,11 +62,11 @@ Population Population::makeRandom( function _chromosomeLengthGenerator ) { - vector individuals; + vector chromosomes; for (size_t i = 0; i < _size; ++i) - individuals.push_back({Chromosome::makeRandom(_chromosomeLengthGenerator())}); + chromosomes.push_back(Chromosome::makeRandom(_chromosomeLengthGenerator())); - return Population(move(_fitnessMetric), move(individuals)); + return Population(move(_fitnessMetric), move(chromosomes)); } Population Population::makeRandom( @@ -91,12 +85,10 @@ Population Population::makeRandom( void Population::run(optional _numRounds, ostream& _outputStream) { - doEvaluation(); for (size_t round = 0; !_numRounds.has_value() || round < _numRounds.value(); ++round) { doMutation(); doSelection(); - doEvaluation(); _outputStream << "---------- ROUND " << round << " ----------" << endl; _outputStream << *this; @@ -134,20 +126,14 @@ void Population::doMutation() // TODO: Implement mutation and crossover } -void Population::doEvaluation() -{ - for (auto& individual: m_individuals) - if (!individual.fitness.has_value()) - individual.fitness = m_fitnessMetric->evaluate(individual.chromosome); -} - void Population::doSelection() { m_individuals = sortedIndividuals(move(m_individuals)); - randomizeWorstChromosomes(m_individuals, m_individuals.size() / 2); + randomizeWorstChromosomes(*m_fitnessMetric, m_individuals, m_individuals.size() / 2); } void Population::randomizeWorstChromosomes( + FitnessMetric const& _fitnessMetric, vector& _individuals, size_t _count ) @@ -158,25 +144,29 @@ void Population::randomizeWorstChromosomes( auto individual = _individuals.begin() + (_individuals.size() - _count); for (; individual != _individuals.end(); ++individual) { - *individual = {Chromosome::makeRandom(binomialChromosomeLength(MaxChromosomeLength))}; + auto chromosome = Chromosome::makeRandom(binomialChromosomeLength(MaxChromosomeLength)); + size_t fitness = _fitnessMetric.evaluate(chromosome); + *individual = {move(chromosome), fitness}; } } vector Population::chromosomesToIndividuals( + FitnessMetric const& _fitnessMetric, vector _chromosomes ) { vector individuals; for (auto& chromosome: _chromosomes) - individuals.push_back({move(chromosome)}); + { + size_t fitness = _fitnessMetric.evaluate(chromosome); + individuals.push_back({move(chromosome), fitness}); + } return individuals; } vector Population::sortedIndividuals(vector _individuals) { - assert(all_of(_individuals.begin(), _individuals.end(), [](auto& i){ return i.fitness.has_value(); })); - sort(_individuals.begin(), _individuals.end(), isFitter); return _individuals; } diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index 5387cea28..0dbac3235 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -46,7 +46,7 @@ namespace solidity::phaser struct Individual { Chromosome chromosome; - std::optional fitness = std::nullopt; + size_t fitness; bool operator==(Individual const& _other) const { return fitness == _other.fitness && chromosome == _other.chromosome; } bool operator!=(Individual const& _other) const { return !(*this == _other); } @@ -67,6 +67,7 @@ bool isFitter(Individual const& a, Individual const& b); * An individual is a sequence of optimiser steps represented by a @a Chromosome instance. * Individuals are stored together with a fitness value that can be computed by the fitness metric * associated with the population. + * The fitness is computed using the metric as soon as an individual is inserted into the population. */ class Population { @@ -78,8 +79,8 @@ public: std::vector _chromosomes = {} ): Population( - std::move(_fitnessMetric), - chromosomesToIndividuals(std::move(_chromosomes)) + _fitnessMetric, + chromosomesToIndividuals(*_fitnessMetric, std::move(_chromosomes)) ) {} static Population makeRandom( @@ -114,14 +115,15 @@ private: m_individuals{std::move(_individuals)} {} void doMutation(); - void doEvaluation(); void doSelection(); static void randomizeWorstChromosomes( + FitnessMetric const& _fitnessMetric, std::vector& _individuals, size_t _count ); static std::vector chromosomesToIndividuals( + FitnessMetric const& _fitnessMetric, std::vector _chromosomes ); static std::vector sortedIndividuals(std::vector _individuals);