2018-04-14 02:23:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
#include "BitEvolver/Random.h"
|
|
|
|
#include "BitEvolver/RouletteWheel.h"
|
|
|
|
#include "BitEvolver/Chromosome.h"
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
#include <vector>
|
|
|
|
#include <mutex>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <memory>
|
2018-04-14 05:36:17 -07:00
|
|
|
#include <iostream>
|
2018-04-14 02:23:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
namespace BitEvolver
|
|
|
|
{
|
2018-04-14 05:36:17 -07:00
|
|
|
//
|
|
|
|
using std::cout;
|
|
|
|
using std::endl;
|
|
|
|
|
2018-04-14 02:23:43 -07:00
|
|
|
//
|
|
|
|
RouletteWheel::RouletteWheel()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
this->Instantiate();
|
|
|
|
|
|
|
|
//
|
|
|
|
this->Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
void RouletteWheel::Reset()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
this->ClearChromosomes();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
void RouletteWheel::ClearChromosomes()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
std::unique_lock<std::recursive_mutex> lock(this->chromosomes_mutex);
|
|
|
|
|
|
|
|
//
|
|
|
|
this->chromosomes.clear();
|
|
|
|
this->ChromosomesChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
void RouletteWheel::SetChromosomes(std::vector<std::shared_ptr<Chromosome>> _chromosomes)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
std::unique_lock<std::recursive_mutex> lock(this->chromosomes_mutex);
|
|
|
|
|
|
|
|
//
|
|
|
|
this->ClearChromosomes();
|
|
|
|
|
|
|
|
//
|
|
|
|
this->chromosomes = _chromosomes;
|
|
|
|
|
|
|
|
//
|
|
|
|
this->ChromosomesChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
void RouletteWheel::AddChromosome(std::shared_ptr<Chromosome> _chromosome)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
std::unique_lock<std::recursive_mutex> lock(this->chromosomes_mutex);
|
|
|
|
|
|
|
|
//
|
|
|
|
this->chromosomes.push_back(_chromosome);
|
|
|
|
this->ChromosomesChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
void RouletteWheel::AddChromosomes(std::vector<std::shared_ptr<Chromosome>> _chromosomes)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
std::unique_lock<std::recursive_mutex> lock(this->chromosomes_mutex);
|
|
|
|
|
|
|
|
//
|
|
|
|
for ( std::shared_ptr<Chromosome> _chromosome : _chromosomes ) {
|
|
|
|
|
|
|
|
//
|
|
|
|
this->chromosomes.push_back(_chromosome);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
this->ChromosomesChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
std::shared_ptr<Chromosome> RouletteWheel::Spin()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
std::unique_lock<std::recursive_mutex> lock(this->chromosomes_mutex);
|
|
|
|
double
|
|
|
|
range_min, range_max,
|
|
|
|
spin
|
|
|
|
;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
//
|
|
|
|
this->PopulateSlots();
|
|
|
|
|
|
|
|
//
|
|
|
|
if ( !this->wheel_slots.size() ) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Spin a random number in our range
|
|
|
|
range_min = this->wheel_slots[0].first;
|
|
|
|
range_max = this->wheel_slots[this->wheel_slots.size()-1].first;
|
|
|
|
spin = this->random->GetDouble(range_min, range_max);
|
|
|
|
|
|
|
|
// Find the corresponding chromosome
|
|
|
|
for ( i=0; i<this->wheel_slots.size(); i++ ) {
|
2018-04-14 05:36:17 -07:00
|
|
|
if ( this->wheel_slots[i].first >= spin ) {
|
2018-04-14 02:23:43 -07:00
|
|
|
return this->wheel_slots[i].second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// By default, return the first one I guess
|
|
|
|
return this->wheel_slots[0].second;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
void RouletteWheel::Instantiate()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
this->random = std::shared_ptr<Random>(
|
|
|
|
new Random()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
std::vector<std::pair<double, std::shared_ptr<Chromosome>>> RouletteWheel::GetNormalizedChromosomeFitness()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
std::unique_lock<std::recursive_mutex> lock(this->chromosomes_mutex);
|
|
|
|
std::vector< std::pair< double, std::shared_ptr<Chromosome> > > pairs;
|
|
|
|
std::pair< double, std::shared_ptr<Chromosome> > pair;
|
|
|
|
double
|
|
|
|
fitness, fitness_low, fitness_high
|
|
|
|
;
|
|
|
|
|
|
|
|
//
|
|
|
|
if ( !this->chromosomes.size() ) {
|
|
|
|
return pairs;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Locate lowest and highest fitness
|
|
|
|
fitness_low = fitness_high = this->chromosomes[0]->GetFitness();
|
|
|
|
for ( std::shared_ptr<Chromosome> chromosome : this->chromosomes ) {
|
|
|
|
|
|
|
|
//
|
|
|
|
fitness = chromosome->GetFitness();
|
|
|
|
if ( fitness > fitness_high ) {
|
|
|
|
fitness_high = fitness;
|
|
|
|
}
|
|
|
|
if ( fitness < fitness_low ) {
|
|
|
|
fitness_low = fitness;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate normalized pairs
|
|
|
|
for ( std::shared_ptr<Chromosome> chromosome : this->chromosomes ) {
|
|
|
|
|
|
|
|
//
|
|
|
|
pair.first = chromosome->GetFitness() - fitness_low;
|
2018-04-14 06:31:34 -07:00
|
|
|
pair.first *= pair.first; // Square to enhance the difference a little
|
2018-04-14 02:23:43 -07:00
|
|
|
pair.second = chromosome;
|
|
|
|
|
|
|
|
//
|
|
|
|
pairs.push_back(pair);
|
2018-04-14 05:36:17 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
//cout << "[" << pair.first << "]" << chromosome->ToString() << endl;
|
2018-04-14 02:23:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return pairs;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
void RouletteWheel::SortChromosomes()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
std::unique_lock<std::recursive_mutex> lock(this->chromosomes_mutex);
|
|
|
|
|
|
|
|
//
|
|
|
|
if ( !this->chromosomes_need_sorting ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
std::sort(
|
|
|
|
this->chromosomes.begin(),
|
|
|
|
this->chromosomes.end(),
|
|
|
|
[]( const std::shared_ptr<Chromosome>& left, const std::shared_ptr<Chromosome>& right ) -> bool
|
|
|
|
{
|
|
|
|
//
|
|
|
|
if ( left->GetFitness() > right->GetFitness() ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
this->chromosomes_need_sorting = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
void RouletteWheel::PopulateSlots()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
std::unique_lock<std::recursive_mutex> lock(this->chromosomes_mutex);
|
|
|
|
std::vector<std::pair<double, std::shared_ptr<Chromosome>>> chromosomes_normalized_fitness;
|
|
|
|
std::pair<double,std::shared_ptr<Chromosome>> wheel_slot;
|
2018-04-14 06:31:34 -07:00
|
|
|
double
|
|
|
|
slot_begin_value
|
|
|
|
;
|
2018-04-14 02:23:43 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
if ( !this->slots_need_population ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
this->SortChromosomes();
|
|
|
|
|
|
|
|
//
|
|
|
|
this->wheel_slots.clear();
|
|
|
|
|
|
|
|
//
|
|
|
|
slot_begin_value = 0;
|
|
|
|
chromosomes_normalized_fitness = this->GetNormalizedChromosomeFitness();
|
|
|
|
for ( std::pair<double, std::shared_ptr<Chromosome>> pair: chromosomes_normalized_fitness ) {
|
|
|
|
|
|
|
|
//
|
|
|
|
wheel_slot.first = pair.first + slot_begin_value;
|
|
|
|
wheel_slot.second = pair.second;
|
|
|
|
|
|
|
|
//
|
|
|
|
this->wheel_slots.push_back(wheel_slot);
|
|
|
|
|
|
|
|
//
|
|
|
|
slot_begin_value += pair.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
this->slots_need_population = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
void RouletteWheel::ChromosomesChanged()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
this->chromosomes_need_sorting = true;
|
|
|
|
this->slots_need_population = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|