Added a cache for track features, made the crossover ratio configurable, now using a fitness proportional selection

This commit is contained in:
emeric
2019-12-03 13:47:48 +01:00
parent 93cd3b6020
commit 74a5f6389d
6 changed files with 181 additions and 46 deletions
@@ -36,6 +36,7 @@ class GeneticAlgorithm
{
std::size_t nbWorkers {1};
std::size_t nbGenerations;
float crossoverRatio {0.5};
float mutationProbability {0.05};
BreedFunction breedFunction;
MutateFunction mutateFunction;
@@ -56,6 +57,8 @@ class GeneticAlgorithm
};
void scoreAndSortPopulation(std::vector<ScoredIndividual>& population);
Score getTotalScore(const std::vector<ScoredIndividual>& population) const;
typename std::vector<ScoredIndividual>::const_iterator pickRandomRouletteWheel(const std::vector<ScoredIndividual>& population);
Params _params;
};
@@ -66,10 +69,12 @@ GeneticAlgorithm<Individual>::GeneticAlgorithm(const Params& params)
{
}
template<typename Individual>
Individual
GeneticAlgorithm<Individual>::simulate(const std::vector<Individual>& initialPopulation)
{
const std::size_t childrenCountPerGeneration {static_cast<std::size_t>(initialPopulation.size() * _params.crossoverRatio)};
if (initialPopulation.size() < 10)
throw std::runtime_error("Initial population must has at least 10 elements");
@@ -83,36 +88,42 @@ GeneticAlgorithm<Individual>::simulate(const std::vector<Individual>& initialPop
for (std::size_t currentGeneration {}; currentGeneration < _params.nbGenerations; ++currentGeneration)
{
assert(scoredPopulation.size() == initialPopulation.size());
std::cout << "Processing generation " << currentGeneration << "..." << std::endl;
// parent selection (elitist selection)
scoredPopulation.resize(scoredPopulation.size() / 2);
// breed the remaining individuals
// breed
std::vector<ScoredIndividual> children;
children.reserve(initialPopulation.size() - scoredPopulation.size());
children.reserve(childrenCountPerGeneration);
while (children.size() + scoredPopulation.size() < initialPopulation.size())
while (children.size() < childrenCountPerGeneration)
{
// Select two random parents
const auto itParent1 {pickRandom(scoredPopulation)};
const auto itParent2 {pickRandom(scoredPopulation)};
// Select two random parents using their score as weight
const auto itParent1 {pickRandomRouletteWheel(scoredPopulation)};
const auto itParent2 {pickRandomRouletteWheel(scoredPopulation)};
if (itParent1 == itParent2)
continue;
std::cout << "Parent1 = " << std::distance(std::cbegin(scoredPopulation), itParent1) << std::endl;
std::cout << "Parent2 = " << std::distance(std::cbegin(scoredPopulation), itParent2) << std::endl;
ScoredIndividual child {_params.breedFunction(itParent1->individual, itParent2->individual)};
if (getRandom(0, 100) <= _params.mutationProbability * 100)
if (getRealRandom(float {}, float {1}) <= _params.mutationProbability)
_params.mutateFunction(child.individual);
children.emplace_back(std::move(child ));
children.emplace_back(std::move(child));
}
// Elitist selection
scoredPopulation.resize(initialPopulation.size() - childrenCountPerGeneration);
scoredPopulation.insert(std::end(scoredPopulation), std::make_move_iterator(std::begin(children)), std::make_move_iterator(std::end(children)));
assert(scoredPopulation.size() == initialPopulation.size());
scoreAndSortPopulation(scoredPopulation);
std::cout << "Mean score = " << getTotalScore(scoredPopulation) / scoredPopulation.size() << std::endl;
std::cout << "Current best score = " << *scoredPopulation.front().score << std::endl;
}
@@ -135,3 +146,31 @@ GeneticAlgorithm<Individual>::scoreAndSortPopulation(std::vector<ScoredIndividua
std::sort(std::begin(scoredPopulation), std::end(scoredPopulation), [](const ScoredIndividual& a, const ScoredIndividual& b) { return a.score > b.score; });
}
template<typename Individual>
typename GeneticAlgorithm<Individual>::Score
GeneticAlgorithm<Individual>::getTotalScore(const std::vector<ScoredIndividual>& scoredPopulation) const
{
return std::accumulate(std::cbegin(scoredPopulation), std::cend(scoredPopulation), Score {}, [](Score score, const ScoredIndividual& individual) { return score + *individual.score; });
}
template<typename Individual>
typename std::vector<typename GeneticAlgorithm<Individual>::ScoredIndividual>::const_iterator
GeneticAlgorithm<Individual>::pickRandomRouletteWheel(const std::vector<ScoredIndividual>& population)
{
const Score randomScore {getRealRandom(Score {}, getTotalScore(population))};
std::cout << "Random = " << randomScore << ", total = " << getTotalScore(population) << std::endl;
Score curScore{};
for (auto itScoredIndividual {std::cbegin(population)}; itScoredIndividual != std::cend(population); ++itScoredIndividual )
{
if (curScore + *itScoredIndividual->score > randomScore)
return itScoredIndividual;
curScore += *itScoredIndividual->score;
}
throw std::runtime_error("bad random or empty population");
}