From 854ad5a1a38e697e8ef2d05375ae13d8f410e154 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 13 Apr 2018 23:59:20 -0700 Subject: [PATCH 01/17] First commit - Seems to pass "all 1's" evolution test --- Breeder.cpp | 152 +++++++++++++++++ Breeder.h | 41 +++++ Chromosome.cpp | 184 ++++++++++++++++++++ Chromosome.h | 71 ++++++++ Defines.h | 13 ++ Enums.h | 9 + ForwardDeclarations.h | 14 ++ Includes.h | 13 ++ Makefile | 122 +++++++++++++ Population.cpp | 389 ++++++++++++++++++++++++++++++++++++++++++ Population.h | 96 +++++++++++ Random.cpp | 86 ++++++++++ Random.h | 41 +++++ 13 files changed, 1231 insertions(+) create mode 100644 Breeder.cpp create mode 100644 Breeder.h create mode 100644 Chromosome.cpp create mode 100644 Chromosome.h create mode 100644 Defines.h create mode 100644 Enums.h create mode 100644 ForwardDeclarations.h create mode 100644 Includes.h create mode 100644 Population.cpp create mode 100644 Population.h create mode 100644 Random.cpp create mode 100644 Random.h diff --git a/Breeder.cpp b/Breeder.cpp new file mode 100644 index 0000000..f4d8fa0 --- /dev/null +++ b/Breeder.cpp @@ -0,0 +1,152 @@ + + +// +#include "BitEvolver/Random.h" +#include "BitEvolver/Chromosome.h" +#include "BitEvolver/Breeder.h" + + +// +#include + + +// +namespace BitEvolver +{ + // + using std::cout; + using std::endl; + + // + Breeder::Breeder(std::shared_ptr _random) + { + // + this->random = _random; + } + + // + std::shared_ptr Breeder::Breed( + std::shared_ptr mama, + std::shared_ptr papa, + double crossover_rate, + double mutation_rate + ) + { + // + std::shared_ptr kiddo; + int bit_length; + + // For now, don't crossover unless the bit lengths are identical + bit_length = mama->GetBitCount(); + if ( papa->GetBitCount() != bit_length ) { + throw std::runtime_error("Breeder::Breed() - Incompatible parents"); + } + + // Directly copy the mama + kiddo = std::shared_ptr(new Chromosome(this->random, bit_length)); + *kiddo = *mama; + + // Apply crossover + this->ApplyCrossover(kiddo, papa, crossover_rate); + + // Apply mutation + this->Mutate(kiddo, mutation_rate); + + return kiddo; + } + + // + void Breeder::Mutate(std::shared_ptr chromosome, double mutation_rate) + { + // + int i, size; + + // + #warning "For efficiency, let's instead roll the number of bits to flip in advance, and loop that many times (faster than rolling for every bit)" + + // + size = chromosome->GetBitCount(); + for ( i=0; irandom->RollBool(mutation_rate) ) { + chromosome->FlipBit(i); + } + } + + // + chromosome->ResetFitness(); + } + + // + int Breeder::PickRandomCrossoverPoint(std::shared_ptr chromosome, double crossover_rate) + { + // + double crossover_point_double; + int crossover_point; + int bit_count; + + // + bit_count = chromosome->GetBitCount(); + + /** + Choose a double between [0.0,1.0] for the crossover point. + Use normal distribution, with the mean to the default point, + and 1.0 as the std. + That way, there is still randomness to the crossover point, + but it still generally sticks near the default + */ + crossover_point_double = this->random->GetNormal(crossover_rate, 1.0); + + // Apply to the actual int length + crossover_point = crossover_point_double * bit_count; + + // Loop around to keep in bounds + while ( crossover_point < 0 ) + { + crossover_point += bit_count; + } + while ( crossover_point >= bit_count) + { + crossover_point -= bit_count; + } + + return crossover_point; + } + + // + void Breeder::ApplyCrossover(std::shared_ptr kiddo, std::shared_ptr parent, double crossover_rate) + { + // + int + bits_count, + crossover_point, + i + ; + + // + bits_count = kiddo->GetBitCount(); + if ( parent->GetBitCount() != bits_count ) { + throw std::runtime_error("Breeder::ApplyCrossover() - Parent incompatible with Kiddo (bit lengths don't match)"); + } + + // Pick random crossover point + crossover_point = this->PickRandomCrossoverPoint(kiddo, crossover_rate); + + // Begin copying the parent at the crossover point and beyond + // (not before) + for ( i=crossover_point; iSetBit( i, parent->GetBit(i) ); + } + + // + kiddo->ResetFitness(); + } +}; + + + + + + + diff --git a/Breeder.h b/Breeder.h new file mode 100644 index 0000000..7decf6e --- /dev/null +++ b/Breeder.h @@ -0,0 +1,41 @@ + + +// +#pragma once + + +// +namespace BitEvolver +{ + // + class Breeder + { + // + public: + + // + Breeder(std::shared_ptr _random); + + // + std::shared_ptr Breed( + std::shared_ptr mama, + std::shared_ptr papa, + double crossover_rate, + double mutation_rate + ); + + // + void Mutate(std::shared_ptr chromosome, double mutation_rate); + + // + private: + + // + std::shared_ptr random; + + // + int PickRandomCrossoverPoint(std::shared_ptr chromosome, double crossover_rate); + void ApplyCrossover(std::shared_ptr kiddo, std::shared_ptr parent, double crossover_rate); + }; +}; + diff --git a/Chromosome.cpp b/Chromosome.cpp new file mode 100644 index 0000000..0fde499 --- /dev/null +++ b/Chromosome.cpp @@ -0,0 +1,184 @@ + + +// +#include "BitEvolver/Chromosome.h" +#include "BitEvolver/Random.h" + + +// +#include +#include +#include +#include + + +// +namespace BitEvolver +{ + // + using std::string; + using std::to_string; + using std::stringstream; + + // + Chromosome::Chromosome(std::shared_ptr _random, int _bits) + { + // + this->random = _random; + this->SetBitCount(_bits); + + // + this->Reset(); + } + + // + void Chromosome::Reset() + { + // + this->Randomize(); + } + + // + void Chromosome::Randomize() + { + // + int i; + + // + this->bits.clear(); + + // + for ( i=0; ibits_count_desired; i++ ) { + this->bits.push_back(this->random->GetInt(0, 1)); + } + } + + // + void Chromosome::SetBitCount(int count) + { + // + this->bits_count_desired = count; + this->Randomize(); + } + + // + int Chromosome::GetBitCount() + { + return (int)this->bits.size(); + } + + // + void Chromosome::FlipBit(int index) + { + // + if ( index >= (int)this->bits.size() ) { + throw std::runtime_error("Chromosome::FlipBit() - Tried to flip out of range bit index: " + to_string(index)); + } + + // + if ( this->bits[index] ) { + this->bits[index] = false; + } + else{ + this->bits[index] = true; + } + } + + // + bool Chromosome::GetBit(int index) + { + // + if ( index >= (int)this->bits.size() ) { + throw std::runtime_error("Chromosome::GetBit() - Tried to access out of bounds bit"); + } + + // + return this->bits[index]; + } + + // + void Chromosome::SetBit(int index, bool b) + { + // + if ( index >= (int)this->bits.size() ) { + throw std::runtime_error("Chromosome::GetBit() - Tried to access out of bounds bit"); + } + + // + this->bits[index] = b; + } + + // + void Chromosome::ResetFitness() + { + // + this->SetFitness(0.0); + } + + // + void Chromosome::SetFitness(double d) + { + // + this->fitness = d; + } + + // + void Chromosome::AdjustFitness(double d) + { + // + this->fitness += d; + } + + // + double Chromosome::GetFitness() + { + // + return this->fitness; + } + + // + string Chromosome::ToString() + { + // + stringstream s; + + // + for ( bool b : this->bits ) { + + // + if ( b ) { + s << "1"; + } + else{ + s << "0"; + } + } + + // + return s.str(); + } + + // + const Chromosome& Chromosome::operator=(const Chromosome& other) + { + // + int i; + + // + this->random = other.random; + this->bits = other.bits; + this->bits_count_desired = other.bits_count_desired; + this->fitness = other.fitness; + + // Copy the bits! + this->bits.clear(); + for ( i=0; i<(int)other.bits.size(); i++ ) { + this->bits.push_back(other.bits[i]); + } + + return *this; + } +}; + + + diff --git a/Chromosome.h b/Chromosome.h new file mode 100644 index 0000000..a403b68 --- /dev/null +++ b/Chromosome.h @@ -0,0 +1,71 @@ + + +// +#pragma once + + +// +#include "BitEvolver/Includes.h" + + +// +#include +#include +#include +#include + + +// +namespace BitEvolver +{ + // + class Chromosome + { + // + public: + + // + Chromosome(std::shared_ptr _random, int _bits); + + // + void Reset(); + void Randomize(); + + // + void SetBitCount(int count); + int GetBitCount(); + + // + void FlipBit(int index); + + // + bool GetBit(int index); + void SetBit(int index, bool b); + + // + void ResetFitness(); + void SetFitness(double d); + void AdjustFitness(double d); + double GetFitness(); + + // + std::string ToString(); + + // + const Chromosome& operator=(const Chromosome& other); + + // + private: + + // Random number generator + std::shared_ptr random; + + // + std::vector bits; + int bits_count_desired; + + // Fitness + double fitness; + }; +}; + diff --git a/Defines.h b/Defines.h new file mode 100644 index 0000000..61a3ba4 --- /dev/null +++ b/Defines.h @@ -0,0 +1,13 @@ +#ifndef BITEVOLVER_DEFINES_H +#define BITEVOLVER_DEFINES_H + + + +// +#define BIT_EVOLVER_POPULATION_DEFAULT_POPULATION_SIZE 100 +#define BIT_EVOLVER_POPULATION_DEFAULT_MUTATE_RATE 0.01 +#define BIT_EVOLVER_POPULATION_DEFAULT_CROSSOVER 0.7 + + + +#endif \ No newline at end of file diff --git a/Enums.h b/Enums.h new file mode 100644 index 0000000..b108c2c --- /dev/null +++ b/Enums.h @@ -0,0 +1,9 @@ +#ifndef BITEVOLVER_ENUMS_H +#define BITEVOLVER_ENUMS_H + + +// + + + +#endif \ No newline at end of file diff --git a/ForwardDeclarations.h b/ForwardDeclarations.h new file mode 100644 index 0000000..25f710e --- /dev/null +++ b/ForwardDeclarations.h @@ -0,0 +1,14 @@ +#ifndef BITEVOLVER_FORWARD_DECLARATIONS_H +#define BITEVOLVER_FORWARD_DECLARATIONS_H + +// +namespace BitEvolver +{ + // + class Population; + class Breeder; + class Chromosome; +}; + + +#endif \ No newline at end of file diff --git a/Includes.h b/Includes.h new file mode 100644 index 0000000..da3cbb8 --- /dev/null +++ b/Includes.h @@ -0,0 +1,13 @@ +#ifndef BITEVOLVER_INCLUDES_H +#define BITEVOLVER_INCLUDES_H + + +// +#include "BitEvolver/Defines.h" +#include "BitEvolver/Enums.h" + + + + + +#endif \ No newline at end of file diff --git a/Makefile b/Makefile index 792d600..5b71764 100644 --- a/Makefile +++ b/Makefile @@ -1 +1,123 @@ + +# Custom functions +define say + $(info [BitEvolver] $1) +endef +define error + $(error [BitEvolver] $1) +endef +define die + $(call error,$1) + $(exit 1) +endef + + +# Demand BUILD_DIR and BIN_DIR +ifeq ($(BUILD_DIR),) +$(call die,Please provide BUILD_DIR) +endif +ifeq ($(BIN_DIR),) +$(call die,Please provide BIN_DIR) +endif + + # +CC=g++ +CFLAGS= -c -std=c++11 -Wall -I.. + + +# +OBJECT_PREFIX=BitEvolver_ + + +# +default: default-say +default: release +default: + $(call say,Default target finished) +default-say: + $(call say,Using default target: release) + + +# +release: release-say +release: CFLAGS+= -O2 +release: build-objects +release: + $(call say,Done building RELEASE) +release-say: + $(call say,Building RELEASE) + +# +debug: debug-say +debug: CFLAGS+= -g -g3 +debug: build-objects +debug: + $(call say,Done building DEBUG) +debug-say: + $(call say,Building DEBUG) + + +# +build-objects: \ + $(BUILD_DIR)/$(OBJECT_PREFIX)Random.o \ + $(BUILD_DIR)/$(OBJECT_PREFIX)Population.o \ + $(BUILD_DIR)/$(OBJECT_PREFIX)Breeder.o \ + $(BUILD_DIR)/$(OBJECT_PREFIX)Chromosome.o + $(call say,Done building objects) + + +# Population.o +$(BUILD_DIR)/$(OBJECT_PREFIX)Population.o: \ + Population.h \ + Population.cpp \ + Defines.h Enums.h Includes.h \ + Random.h \ + Chromosome.h + $(CC) -o $@ \ + Population.cpp \ + $(CFLAGS) + $(call say,Built $@) + + +# Breeder.o +$(BUILD_DIR)/$(OBJECT_PREFIX)Breeder.o: \ + Breeder.h \ + Breeder.cpp \ + Defines.h Enums.h Includes.h \ + Random.h \ + Chromosome.h + $(CC) -o $@ \ + Breeder.cpp \ + $(CFLAGS) + $(call say,Built $@) + + +# Chromosome.o +$(BUILD_DIR)/$(OBJECT_PREFIX)Chromosome.o: \ + Chromosome.h \ + Chromosome.cpp \ + Defines.h Enums.h Includes.h \ + Random.h + $(CC) -o $@ \ + Chromosome.cpp \ + $(CFLAGS) + $(call say,Built $@) + + +# Random.o +$(BUILD_DIR)/$(OBJECT_PREFIX)Random.o: \ + Random.h \ + Random.cpp \ + Defines.h Enums.h Includes.h + $(CC) -o $@ \ + Random.cpp \ + $(CFLAGS) + $(call say,Built $@) + + + + + + + diff --git a/Population.cpp b/Population.cpp new file mode 100644 index 0000000..1458be2 --- /dev/null +++ b/Population.cpp @@ -0,0 +1,389 @@ + + +// +#include "BitEvolver/Includes.h" +#include "BitEvolver/Random.h" +#include "BitEvolver/Population.h" +#include "BitEvolver/Breeder.h" +#include "BitEvolver/Chromosome.h" + + +// +#include +#include +#include +#include +#include +#include + + +// +namespace BitEvolver +{ + // + using std::cout; + using std::endl; + + // + Population::Population() + { + // + this->InitRandomGenerator(); + this->InitBreeder(); + + // + this->Reset(); + } + + // + void Population::Reset() + { + // + this->population_size = BIT_EVOLVER_POPULATION_DEFAULT_POPULATION_SIZE; + this->SetMutationRate(BIT_EVOLVER_POPULATION_DEFAULT_MUTATE_RATE); + this->evolution_number = 0; + + // + this->RandomizePopulation(this->population_size); + } + + // + void Population::ClearPopulation() + { + // + this->chromosomes.clear(); + this->evolution_number = 0; + } + + // + void Population::InitRandomPopulation(int _population_size, int _bit_length) + { + // + this->population_size = _population_size; + + // + this->RandomizePopulation(_bit_length); + } + + // + void Population::RandomizePopulation(int _bit_length) + { + // + std::shared_ptr chromosome; + int i; + + // + this->ClearPopulation(); + for ( i=0; ipopulation_size; i++ ) { + + // + chromosome = std::shared_ptr( + new Chromosome( this->random, _bit_length ) + ); + this->chromosomes.push_back(chromosome); + } + } + + // + void Population::PopulationChanged() + { + // + this->population_needs_sorting = true; + } + + // + std::vector> Population::GetChromosomes() + { + return this->chromosomes; + } + + // + void Population::GetChromosomes(std::shared_ptr>> _chromosomes) + { + // + _chromosomes->clear(); + for ( std::shared_ptr chromosome : this->chromosomes) { + _chromosomes->push_back(chromosome); + } + } + + // + std::shared_ptr Population::GetChampion() + { + // + this->EnsureSortedPopulation(); + + // + if ( this->chromosomes.size() > 0 ) { + return this->chromosomes[0]; + } + + return nullptr; + } + + // + double Population::GetAverageFitness() + { + return this->GetAverageFitness(this->chromosomes); + } + + // + double Population::GetAverageFitness(std::vector> _chromosomes) + { + // + double fitness_sum; + double fitness_average; + + // + fitness_sum = 0; + for ( std::shared_ptr chromosome : _chromosomes ) { + fitness_sum += chromosome->GetFitness(); + } + + // + fitness_average = 0; + if ( _chromosomes.size() > 0 ) { + fitness_average = fitness_sum / _chromosomes.size(); + } + + return fitness_average; + } + + // + void Population::SetMutationRate(double r) + { + // + this->mutation_rate = r; + } + + // + void Population::Evolve() + { + // + std::shared_ptr > > population_new; + + // + if ( this->chromosomes.size() == 0 ) { + return; + } + + // + this->EnsureSortedPopulation(); + + // + population_new = std::shared_ptr< + std::vector< + std::shared_ptr + > + >( + new std::vector>() + ); + + // Start the new population off with our champion, + // so the best score always carries over (elitism = 1 unit) + #warning "Elitism is only 1 right now" + population_new->push_back(this->chromosomes[0]); + + // Breed the new population + this->BreedNewPopulation(population_new, (int)this->chromosomes.size()); + + // Replace old population with the new + this->chromosomes = *population_new; + this->evolution_number++; + + // + this->PopulationChanged(); + } + + // + int Population::GetEvolutionNumber() + { + return this->evolution_number; + } + + // + void Population::PrintPopulation() + { + // + this->EnsureSortedPopulation(); + + this->PrintPopulation(this->chromosomes); + } + + // + void Population::PrintPopulation(std::vector> _chromosomes) + { + // + for ( std::shared_ptr chromosome : chromosomes ) { + cout << chromosome->ToString() << endl; + } + cout << "Average Fitness --> " << this->GetAverageFitness(_chromosomes) << endl; + } + + // + void Population::InitRandomGenerator() + { + // + this->random = std::shared_ptr( + new Random() + ); + } + + // + void Population::InitBreeder() + { + // + if ( !this->random ) { + throw std::runtime_error("Population::InitBreeder() - Should come after InitRandomGenerator()"); + } + + // + this->breeder = std::shared_ptr( + new Breeder( this->random ) + ); + } + + // + void Population::EnsureSortedPopulation() + { + // + if ( !this->population_needs_sorting ) { + return; + } + + // Yay std::sort + std::sort( + this->chromosomes.begin(), + this->chromosomes.end(), + []( std::shared_ptr& left, std::shared_ptr& right ) -> bool + { + // + if ( left->GetFitness() > right->GetFitness() ) { + return true; + } + + return false; + } + ); + + // + this->population_needs_sorting = false; + } + + // + void Population::BreedNewPopulation(std::shared_ptr>> population_new, int size) + { + // + std::vector> threads; + std::shared_ptr thread; + int + thread_count, + i + ; + + // + thread_count = std::thread::hardware_concurrency(); + + // + for ( i=0; i( + new std::thread(&Population::BreedNewPopulation_Thread, this, population_new, size) + ); + threads.push_back(thread); + } + + // + for ( i=0; i<(int)threads.size(); i++) { + threads[i]->join(); + } + } + + // + void Population::BreedNewPopulation_Thread(std::shared_ptr>> population_new, int size) + { + // + std::shared_ptr kiddo; + + // + while ( (int)population_new->size() < size ) + { + // + kiddo = this->BreedChild(); + + // Mutexed + this->breed_mutex.lock(); + if ( (int)population_new->size() < size ) { + population_new->push_back(kiddo); + } + this->breed_mutex.unlock(); + } + } + + // + std::shared_ptr Population::BreedChild() + { + // + std::shared_ptr + mama, papa, kiddo + ; + + // Pick two parents + mama = this->PickChromosomeForBreeding(); + papa = this->PickChromosomeForBreeding(); + + // + kiddo = this->breeder->Breed(mama, papa, BIT_EVOLVER_POPULATION_DEFAULT_CROSSOVER, this->mutation_rate); + + return kiddo; + } + + /** + (1) We want to pick the best chromosomes to repopulate + (2) [not in lecture] We want to add a bit of randomness + to the selection process, such that the worst chromosomes + can still possibly be picked, and the best can still + possibly be not-picked; Their fitness only increases + the probability they're picked. + */ + std::shared_ptr Population::PickChromosomeForBreeding() + { + // + double normal; + int chromosome_index; + + // + this->EnsureSortedPopulation(); + + /** + Grab normal with 0 at the mean, and the standard deviation equal + to 1/2 of the population size. Then make that an absolute value. + This will make top/best chromosomes more likely to be picked, + with the far/low end being much less likely + */ + #warning "Need to upgrade this to Roulette Wheel" + // Repeat as needed, since the normal generator might actually + // give us an out-of-bounds result sometimes + while ( true ) + { + // + normal = this->random->GetNormal(0, 0.5); + chromosome_index = abs( normal * this->chromosomes.size() + ); + if ( chromosome_index >= 0 && chromosome_index < (int)this->chromosomes.size() ) { + break; + } + } + + // + return this->chromosomes[chromosome_index]; + } +}; + + + + + + + + + diff --git a/Population.h b/Population.h new file mode 100644 index 0000000..2e0c703 --- /dev/null +++ b/Population.h @@ -0,0 +1,96 @@ + + +// +#pragma once + + +// +#include "BitEvolver/Includes.h" + + +// +#include +#include +#include + + +// +namespace BitEvolver +{ + // + class Population + { + // + public: + + // + Population(); + + // + void Reset(); + + // + void ClearPopulation(); + void InitRandomPopulation(int _population_size, int _bit_length); + void RandomizePopulation(int _bit_length); + + // + void PopulationChanged(); + + // + std::vector> GetChromosomes(); + void GetChromosomes(std::shared_ptr>> _chromosomes); + std::shared_ptr GetChampion(); + + // + double GetAverageFitness(); + double GetAverageFitness(std::vector> _chromosomes); + + // + void SetMutationRate(double r); + + // + void Evolve(); + int GetEvolutionNumber(); + + // + void PrintPopulation(); + void PrintPopulation(std::vector> _chromosomes); + + // + private: + + // + std::shared_ptr random; + std::shared_ptr breeder; + + // + std::vector> chromosomes; + int population_size; + bool population_needs_sorting; + double mutation_rate; + int evolution_number; + + // + std::mutex breed_mutex; + + // + void InitRandomGenerator(); + void InitBreeder(); + + // + void EnsureSortedPopulation(); + + // + void BreedNewPopulation(std::shared_ptr>> population_new, int size); + void BreedNewPopulation_Thread(std::shared_ptr>> population_new, int size); + + // + std::shared_ptr BreedChild(); + std::shared_ptr PickChromosomeForBreeding(); + + }; +}; + + + diff --git a/Random.cpp b/Random.cpp new file mode 100644 index 0000000..87726a5 --- /dev/null +++ b/Random.cpp @@ -0,0 +1,86 @@ + + +// +#include "BitEvolver/Random.h" + + +// +#include +#include + + +// +namespace BitEvolver +{ + // + Random::Random() + { + // + this->InitializeGenerators(); + } + + // + int Random::GetInt(int min, int max) + { + // + std::uniform_int_distribution distribution(min, max); + + // + int the_int = distribution(this->generator_mersenne_twister); + + return the_int; + } + + // + double Random::GetDouble(double min, double max) + { + // + std::uniform_real_distribution distribution(min, max); + + // + double the_double = distribution(this->generator_mersenne_twister); + + return the_double; + } + + // + double Random::GetNormal(double mean, double standard_deviation) + { + // + std::normal_distribution distribution(mean, standard_deviation); + + double d = distribution(this->generator_mersenne_twister); + + return d; + } + + // + bool Random::RollBool(double chance) + { + // + double d = this->GetDouble(0, 1); + if ( d <= chance ) { + return true; + } + + return false; + } + + // + void Random::InitializeGenerators() + { + // Mostly taken from + // http://www.cplusplus.com/reference/random/mersenne_twister_engine/seed/ + typedef std::chrono::high_resolution_clock myclock; + + // + myclock::time_point beginning = myclock::now(); + myclock::duration d = myclock::now() - beginning; + unsigned seed = d.count(); + + // Seed our internal generator + this->generator_mersenne_twister.seed(seed); + } +}; + + diff --git a/Random.h b/Random.h new file mode 100644 index 0000000..9d9d8d4 --- /dev/null +++ b/Random.h @@ -0,0 +1,41 @@ + + +// +#pragma once + + +// +#include + + +// +namespace BitEvolver +{ + // + class Random + { + // + public: + + // + Random(); + + // + int GetInt(int min, int max); + double GetDouble(double min, double max); + double GetNormal(double mean, double standard_deviation); + + // + bool RollBool(double chance); + + // + private: + + // + std::mt19937_64 generator_mersenne_twister; + + // + void InitializeGenerators(); + + }; +}; From 1d901ccc017b2ee50c968cff72b6477a45ed0ae8 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 00:28:50 -0700 Subject: [PATCH 02/17] Centralize thread count suggestion --- Population.cpp | 14 +++++++++++++- Population.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Population.cpp b/Population.cpp index 1458be2..6e5405f 100644 --- a/Population.cpp +++ b/Population.cpp @@ -282,7 +282,7 @@ namespace BitEvolver ; // - thread_count = std::thread::hardware_concurrency(); + thread_count = this->GetThreadCountSuggestion(); // for ( i=0; ichromosomes[chromosome_index]; } + + // + int Population::GetThreadCountSuggestion() + { + // + int thread_count; + + // + thread_count = std::thread::hardware_concurrency(); + + return thread_count; + } }; diff --git a/Population.h b/Population.h index 2e0c703..828422c 100644 --- a/Population.h +++ b/Population.h @@ -89,6 +89,8 @@ namespace BitEvolver std::shared_ptr BreedChild(); std::shared_ptr PickChromosomeForBreeding(); + // + int GetThreadCountSuggestion(); }; }; From 5f0b8a4c13202c0970dce1c4647daf4a2156833b Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 00:33:36 -0700 Subject: [PATCH 03/17] Add Error functions to mirror/inverse fitness logic --- Chromosome.cpp | 28 ++++++++++++++++++++++++++++ Chromosome.h | 8 ++++++++ 2 files changed, 36 insertions(+) diff --git a/Chromosome.cpp b/Chromosome.cpp index 0fde499..dc6c3c1 100644 --- a/Chromosome.cpp +++ b/Chromosome.cpp @@ -136,6 +136,34 @@ namespace BitEvolver return this->fitness; } + // + void Chromosome::ResetError() + { + // + this->ResetFitness(); + } + + // + void Chromosome::SetError(double e) + { + // + this->SetFitness(-e); + } + + // + void Chromosome::AdjustError(double e) + { + // + this->AdjustFitness(-e); + } + + // + double Chromosome::GetError() + { + // + return -this->GetFitness(); + } + // string Chromosome::ToString() { diff --git a/Chromosome.h b/Chromosome.h index a403b68..99ad214 100644 --- a/Chromosome.h +++ b/Chromosome.h @@ -48,6 +48,14 @@ namespace BitEvolver void AdjustFitness(double d); double GetFitness(); + /** + Error is just inverted fitness + */ + void ResetError(); + void SetError(double e); + void AdjustError(double e); + double GetError(); + // std::string ToString(); From b0da515139231fc9e9d585f29db3dc19c4de1a80 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 01:07:56 -0700 Subject: [PATCH 04/17] Added functions to control crossover rate and type --- Breeder.cpp | 22 +++++++++++++++------- Breeder.h | 8 +++++++- Enums.h | 15 ++++++++++++++- Population.cpp | 42 +++++++++++++++++++++++++++++++++++++++++- Population.h | 15 +++++++++++++++ 5 files changed, 92 insertions(+), 10 deletions(-) diff --git a/Breeder.cpp b/Breeder.cpp index f4d8fa0..0dce7a5 100644 --- a/Breeder.cpp +++ b/Breeder.cpp @@ -28,6 +28,7 @@ namespace BitEvolver std::shared_ptr Breeder::Breed( std::shared_ptr mama, std::shared_ptr papa, + Enums::CrossoverType crossover_type, double crossover_rate, double mutation_rate ) @@ -36,18 +37,15 @@ namespace BitEvolver std::shared_ptr kiddo; int bit_length; - // For now, don't crossover unless the bit lengths are identical + // bit_length = mama->GetBitCount(); - if ( papa->GetBitCount() != bit_length ) { - throw std::runtime_error("Breeder::Breed() - Incompatible parents"); - } // Directly copy the mama kiddo = std::shared_ptr(new Chromosome(this->random, bit_length)); *kiddo = *mama; // Apply crossover - this->ApplyCrossover(kiddo, papa, crossover_rate); + this->ApplyCrossover(kiddo, papa, crossover_type, crossover_rate); // Apply mutation this->Mutate(kiddo, mutation_rate); @@ -115,7 +113,12 @@ namespace BitEvolver } // - void Breeder::ApplyCrossover(std::shared_ptr kiddo, std::shared_ptr parent, double crossover_rate) + void Breeder::ApplyCrossover( + std::shared_ptr kiddo, + std::shared_ptr parent, + Enums::CrossoverType crossover_type, + double crossover_rate + ) { // int @@ -124,7 +127,12 @@ namespace BitEvolver i ; - // + // Only proceed if using sexual crossover + if (crossover_type != Enums::CrossoverType::Sexual) { + return; + } + + // For now, don't crossover unless the bit lengths are identical bits_count = kiddo->GetBitCount(); if ( parent->GetBitCount() != bits_count ) { throw std::runtime_error("Breeder::ApplyCrossover() - Parent incompatible with Kiddo (bit lengths don't match)"); diff --git a/Breeder.h b/Breeder.h index 7decf6e..40f4ed1 100644 --- a/Breeder.h +++ b/Breeder.h @@ -20,6 +20,7 @@ namespace BitEvolver std::shared_ptr Breed( std::shared_ptr mama, std::shared_ptr papa, + Enums::CrossoverType crossover_type, double crossover_rate, double mutation_rate ); @@ -35,7 +36,12 @@ namespace BitEvolver // int PickRandomCrossoverPoint(std::shared_ptr chromosome, double crossover_rate); - void ApplyCrossover(std::shared_ptr kiddo, std::shared_ptr parent, double crossover_rate); + void ApplyCrossover( + std::shared_ptr kiddo, + std::shared_ptr parent, + Enums::CrossoverType crossover_type, + double crossover_rate + ); }; }; diff --git a/Enums.h b/Enums.h index b108c2c..a3791c9 100644 --- a/Enums.h +++ b/Enums.h @@ -3,7 +3,20 @@ // - +namespace BitEvolver +{ + // + namespace Enums + { + // + enum class CrossoverType + { + // + None, + Sexual + }; + }; +}; #endif \ No newline at end of file diff --git a/Population.cpp b/Population.cpp index 6e5405f..31dfb91 100644 --- a/Population.cpp +++ b/Population.cpp @@ -149,6 +149,34 @@ namespace BitEvolver return fitness_average; } + // + void Population::SetCrossoverPoint(double p) + { + // + this->crossover_point = p; + } + + // + double Population::GetCrossoverPoint() + { + // + return this->crossover_point; + } + + // + void Population::SetCrossoverType(Enums::CrossoverType t) + { + // + this->crossover_type = t; + } + + // + Enums::CrossoverType Population::GetCrossoverType() + { + // + return this->crossover_type; + } + // void Population::SetMutationRate(double r) { @@ -156,6 +184,13 @@ namespace BitEvolver this->mutation_rate = r; } + // + double Population::GetMutationRate() + { + // + return this->mutation_rate; + } + // void Population::Evolve() { @@ -332,7 +367,12 @@ namespace BitEvolver papa = this->PickChromosomeForBreeding(); // - kiddo = this->breeder->Breed(mama, papa, BIT_EVOLVER_POPULATION_DEFAULT_CROSSOVER, this->mutation_rate); + kiddo = this->breeder->Breed( + mama, papa, + this->crossover_type, + this->crossover_point, + this->mutation_rate + ); return kiddo; } diff --git a/Population.h b/Population.h index 828422c..a37dd42 100644 --- a/Population.h +++ b/Population.h @@ -46,8 +46,14 @@ namespace BitEvolver double GetAverageFitness(); double GetAverageFitness(std::vector> _chromosomes); + // + void SetCrossoverPoint(double p); + double GetCrossoverPoint(); + void SetCrossoverType(Enums::CrossoverType t); + Enums::CrossoverType GetCrossoverType(); // void SetMutationRate(double r); + double GetMutationRate(); // void Evolve(); @@ -57,6 +63,13 @@ namespace BitEvolver void PrintPopulation(); void PrintPopulation(std::vector> _chromosomes); + // Constants + const static int DEFAULT_POPULATION_SIZE = 100; + const static int DEFAULT_MUTATION_RATE = 0.01; + // + const static Enums::CrossoverType DEFAULT_CROSSOVER_TYPE = Enums::CrossoverType::Sexual; + const static int DEFAULT_CROSSOVER_POINT = 0.7; + // private: @@ -68,6 +81,8 @@ namespace BitEvolver std::vector> chromosomes; int population_size; bool population_needs_sorting; + Enums::CrossoverType crossover_type; + double crossover_point; double mutation_rate; int evolution_number; From 0326bc4ddf3213520625cd8830515d28831f9fa0 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 01:08:22 -0700 Subject: [PATCH 05/17] Moved defines to constants in-class --- Defines.h | 6 +----- Population.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Defines.h b/Defines.h index 61a3ba4..bbfde70 100644 --- a/Defines.h +++ b/Defines.h @@ -2,11 +2,7 @@ #define BITEVOLVER_DEFINES_H - -// -#define BIT_EVOLVER_POPULATION_DEFAULT_POPULATION_SIZE 100 -#define BIT_EVOLVER_POPULATION_DEFAULT_MUTATE_RATE 0.01 -#define BIT_EVOLVER_POPULATION_DEFAULT_CROSSOVER 0.7 +// Nada for now diff --git a/Population.cpp b/Population.cpp index 31dfb91..5b5ea1c 100644 --- a/Population.cpp +++ b/Population.cpp @@ -39,9 +39,13 @@ namespace BitEvolver void Population::Reset() { // - this->population_size = BIT_EVOLVER_POPULATION_DEFAULT_POPULATION_SIZE; - this->SetMutationRate(BIT_EVOLVER_POPULATION_DEFAULT_MUTATE_RATE); this->evolution_number = 0; + this->population_size = Population::DEFAULT_POPULATION_SIZE; + + // + this->SetCrossoverType(Population::DEFAULT_CROSSOVER_TYPE); + this->SetCrossoverPoint(Population::DEFAULT_CROSSOVER_POINT); + this->SetMutationRate(Population::DEFAULT_MUTATION_RATE); // this->RandomizePopulation(this->population_size); From 35fa952da65b363e7caea5e8ecc98ae67b4727a9 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 02:23:29 -0700 Subject: [PATCH 06/17] Modification mutexes --- Chromosome.cpp | 16 ++++++++++++++++ Chromosome.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/Chromosome.cpp b/Chromosome.cpp index dc6c3c1..c6b7761 100644 --- a/Chromosome.cpp +++ b/Chromosome.cpp @@ -10,6 +10,7 @@ #include #include #include +#include // @@ -42,6 +43,7 @@ namespace BitEvolver void Chromosome::Randomize() { // + std::unique_lock lock(this->modification_mutex); int i; // @@ -56,6 +58,9 @@ namespace BitEvolver // void Chromosome::SetBitCount(int count) { + // + std::unique_lock lock(this->modification_mutex); + // this->bits_count_desired = count; this->Randomize(); @@ -70,6 +75,9 @@ namespace BitEvolver // void Chromosome::FlipBit(int index) { + // + std::unique_lock lock(this->modification_mutex); + // if ( index >= (int)this->bits.size() ) { throw std::runtime_error("Chromosome::FlipBit() - Tried to flip out of range bit index: " + to_string(index)); @@ -87,6 +95,9 @@ namespace BitEvolver // bool Chromosome::GetBit(int index) { + // + std::unique_lock lock(this->modification_mutex); + // if ( index >= (int)this->bits.size() ) { throw std::runtime_error("Chromosome::GetBit() - Tried to access out of bounds bit"); @@ -99,6 +110,9 @@ namespace BitEvolver // void Chromosome::SetBit(int index, bool b) { + // + std::unique_lock lock(this->modification_mutex); + // if ( index >= (int)this->bits.size() ) { throw std::runtime_error("Chromosome::GetBit() - Tried to access out of bounds bit"); @@ -168,6 +182,7 @@ namespace BitEvolver string Chromosome::ToString() { // + std::unique_lock lock(this->modification_mutex); stringstream s; // @@ -190,6 +205,7 @@ namespace BitEvolver const Chromosome& Chromosome::operator=(const Chromosome& other) { // + std::unique_lock lock1(this->modification_mutex); int i; // diff --git a/Chromosome.h b/Chromosome.h index 99ad214..b07eb37 100644 --- a/Chromosome.h +++ b/Chromosome.h @@ -13,6 +13,7 @@ #include #include #include +#include // @@ -74,6 +75,9 @@ namespace BitEvolver // Fitness double fitness; + + // Mutexes + std::recursive_mutex modification_mutex; }; }; From 4b38d1c4941b700ef726e3ec72e9f2ca35918e53 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 02:23:43 -0700 Subject: [PATCH 07/17] New class RouletteWheel ! --- RouletteWheel.cpp | 260 ++++++++++++++++++++++++++++++++++++++++++++++ RouletteWheel.h | 68 ++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 RouletteWheel.cpp create mode 100644 RouletteWheel.h diff --git a/RouletteWheel.cpp b/RouletteWheel.cpp new file mode 100644 index 0000000..6ba182d --- /dev/null +++ b/RouletteWheel.cpp @@ -0,0 +1,260 @@ + + + +// +#include "BitEvolver/Random.h" +#include "BitEvolver/RouletteWheel.h" +#include "BitEvolver/Chromosome.h" + + +// +#include +#include +#include +#include + + +// +namespace BitEvolver +{ + // + RouletteWheel::RouletteWheel() + { + // + this->Instantiate(); + + // + this->Reset(); + } + + // + void RouletteWheel::Reset() + { + // + this->ClearChromosomes(); + } + + // + void RouletteWheel::ClearChromosomes() + { + // + std::unique_lock lock(this->chromosomes_mutex); + + // + this->chromosomes.clear(); + this->ChromosomesChanged(); + } + + // + void RouletteWheel::SetChromosomes(std::vector> _chromosomes) + { + // + std::unique_lock lock(this->chromosomes_mutex); + + // + this->ClearChromosomes(); + + // + this->chromosomes = _chromosomes; + + // + this->ChromosomesChanged(); + } + + // + void RouletteWheel::AddChromosome(std::shared_ptr _chromosome) + { + // + std::unique_lock lock(this->chromosomes_mutex); + + // + this->chromosomes.push_back(_chromosome); + this->ChromosomesChanged(); + } + + // + void RouletteWheel::AddChromosomes(std::vector> _chromosomes) + { + // + std::unique_lock lock(this->chromosomes_mutex); + + // + for ( std::shared_ptr _chromosome : _chromosomes ) { + + // + this->chromosomes.push_back(_chromosome); + } + + // + this->ChromosomesChanged(); + } + + // + std::shared_ptr RouletteWheel::Spin() + { + // + std::unique_lock 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; iwheel_slots.size(); i++ ) { + if ( this->wheel_slots[i].first <= spin ) { + 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( + new Random() + ); + } + + // + std::vector>> RouletteWheel::GetNormalizedChromosomeFitness() + { + // + std::unique_lock lock(this->chromosomes_mutex); + std::vector< std::pair< double, std::shared_ptr > > pairs; + std::pair< double, std::shared_ptr > 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 : 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 : this->chromosomes ) { + + // + pair.first = chromosome->GetFitness() - fitness_low; + pair.second = chromosome; + + // + pairs.push_back(pair); + } + + return pairs; + } + + // + void RouletteWheel::SortChromosomes() + { + // + std::unique_lock lock(this->chromosomes_mutex); + + // + if ( !this->chromosomes_need_sorting ) { + return; + } + + // + std::sort( + this->chromosomes.begin(), + this->chromosomes.end(), + []( const std::shared_ptr& left, const std::shared_ptr& right ) -> bool + { + // + if ( left->GetFitness() > right->GetFitness() ) { + return true; + } + + return false; + } + ); + + // + this->chromosomes_need_sorting = false; + } + + // + void RouletteWheel::PopulateSlots() + { + // + std::unique_lock lock(this->chromosomes_mutex); + std::vector>> chromosomes_normalized_fitness; + std::pair> wheel_slot; + double slot_begin_value; + + // + if ( !this->slots_need_population ) { + return; + } + + // + this->SortChromosomes(); + + // + this->wheel_slots.clear(); + + // + slot_begin_value = 0; + chromosomes_normalized_fitness = this->GetNormalizedChromosomeFitness(); + for ( std::pair> 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; + } +}; + + + diff --git a/RouletteWheel.h b/RouletteWheel.h new file mode 100644 index 0000000..b31baaf --- /dev/null +++ b/RouletteWheel.h @@ -0,0 +1,68 @@ + + + +// +#pragma once + + +// +#include +#include +#include + + +// +namespace BitEvolver +{ + // + class RouletteWheel + { + // + public: + + // + RouletteWheel(); + + // + void Reset(); + + // + void ClearChromosomes(); + void SetChromosomes(std::vector> _chromosomes); + void AddChromosome(std::shared_ptr _chromosome); + void AddChromosomes(std::vector> _chromosomes); + + // + std::shared_ptr Spin(); + + // + private: + + // + std::shared_ptr random; + + // + std::vector> chromosomes; + std::recursive_mutex chromosomes_mutex; + bool chromosomes_need_sorting; + // + std::vector>> wheel_slots; + bool slots_need_population; + + + // + void Instantiate(); + + // + std::vector>> GetNormalizedChromosomeFitness(); + void SortChromosomes(); + void PopulateSlots(); + + // + void ChromosomesChanged(); + + }; +}; + + + From a0fc0257ef27bf95b3b7e95ec24818db9b0f55d0 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 02:24:05 -0700 Subject: [PATCH 08/17] New class: RouletteWheel --- Makefile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Makefile b/Makefile index 5b71764..2c6c801 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,7 @@ build-objects: \ $(BUILD_DIR)/$(OBJECT_PREFIX)Random.o \ $(BUILD_DIR)/$(OBJECT_PREFIX)Population.o \ $(BUILD_DIR)/$(OBJECT_PREFIX)Breeder.o \ + $(BUILD_DIR)/$(OBJECT_PREFIX)RouletteWheel.o \ $(BUILD_DIR)/$(OBJECT_PREFIX)Chromosome.o $(call say,Done building objects) @@ -93,6 +94,19 @@ $(BUILD_DIR)/$(OBJECT_PREFIX)Breeder.o: \ $(call say,Built $@) +# RouletteWheel.o +$(BUILD_DIR)/$(OBJECT_PREFIX)RouletteWheel.o: \ + RouletteWheel.h \ + RouletteWheel.cpp \ + Defines.h Enums.h Includes.h \ + Random.h + $(CC) -o $@ \ + RouletteWheel.cpp \ + $(CFLAGS) + $(call say,Built $@) + + + # Chromosome.o $(BUILD_DIR)/$(OBJECT_PREFIX)Chromosome.o: \ Chromosome.h \ From 4f1522ff4366a7aff81c0f02ed931c8900e53674 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 02:24:22 -0700 Subject: [PATCH 09/17] Switch to better roulette wheel selection --- Population.cpp | 60 ++++++++++++++------------------------------------ Population.h | 5 ++++- 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/Population.cpp b/Population.cpp index 5b5ea1c..27b1aa4 100644 --- a/Population.cpp +++ b/Population.cpp @@ -5,6 +5,7 @@ #include "BitEvolver/Random.h" #include "BitEvolver/Population.h" #include "BitEvolver/Breeder.h" +#include "BitEvolver/RouletteWheel.h" #include "BitEvolver/Chromosome.h" @@ -29,6 +30,7 @@ namespace BitEvolver { // this->InitRandomGenerator(); + this->InitRouletteWheel(); this->InitBreeder(); // @@ -268,6 +270,15 @@ namespace BitEvolver ); } + // + void Population::InitRouletteWheel() + { + // + this->roulette_wheel = std::shared_ptr( + new RouletteWheel() + ); + } + // void Population::InitBreeder() { @@ -313,6 +324,7 @@ namespace BitEvolver void Population::BreedNewPopulation(std::shared_ptr>> population_new, int size) { // + std::shared_ptr wheel; std::vector> threads; std::shared_ptr thread; int @@ -320,6 +332,9 @@ namespace BitEvolver i ; + // First, populate the roulette wheel + this->roulette_wheel->SetChromosomes(this->chromosomes); + // thread_count = this->GetThreadCountSuggestion(); @@ -367,8 +382,8 @@ namespace BitEvolver ; // Pick two parents - mama = this->PickChromosomeForBreeding(); - papa = this->PickChromosomeForBreeding(); + mama = this->roulette_wheel->Spin(); + papa = this->roulette_wheel->Spin(); // kiddo = this->breeder->Breed( @@ -381,47 +396,6 @@ namespace BitEvolver return kiddo; } - /** - (1) We want to pick the best chromosomes to repopulate - (2) [not in lecture] We want to add a bit of randomness - to the selection process, such that the worst chromosomes - can still possibly be picked, and the best can still - possibly be not-picked; Their fitness only increases - the probability they're picked. - */ - std::shared_ptr Population::PickChromosomeForBreeding() - { - // - double normal; - int chromosome_index; - - // - this->EnsureSortedPopulation(); - - /** - Grab normal with 0 at the mean, and the standard deviation equal - to 1/2 of the population size. Then make that an absolute value. - This will make top/best chromosomes more likely to be picked, - with the far/low end being much less likely - */ - #warning "Need to upgrade this to Roulette Wheel" - // Repeat as needed, since the normal generator might actually - // give us an out-of-bounds result sometimes - while ( true ) - { - // - normal = this->random->GetNormal(0, 0.5); - chromosome_index = abs( normal * this->chromosomes.size() - ); - if ( chromosome_index >= 0 && chromosome_index < (int)this->chromosomes.size() ) { - break; - } - } - - // - return this->chromosomes[chromosome_index]; - } - // int Population::GetThreadCountSuggestion() { diff --git a/Population.h b/Population.h index a37dd42..ad67dd3 100644 --- a/Population.h +++ b/Population.h @@ -86,11 +86,15 @@ namespace BitEvolver double mutation_rate; int evolution_number; + // + std::shared_ptr roulette_wheel; + // std::mutex breed_mutex; // void InitRandomGenerator(); + void InitRouletteWheel(); void InitBreeder(); // @@ -102,7 +106,6 @@ namespace BitEvolver // std::shared_ptr BreedChild(); - std::shared_ptr PickChromosomeForBreeding(); // int GetThreadCountSuggestion(); From b256a47cdecd4db80633d3187caecf46a49c126e Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 04:10:31 -0700 Subject: [PATCH 10/17] Convenience: Can now evaluate a lambda for fitness on every chromosome, using threads --- Population.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ Population.h | 16 ++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/Population.cpp b/Population.cpp index 27b1aa4..4519774 100644 --- a/Population.cpp +++ b/Population.cpp @@ -16,6 +16,7 @@ #include #include #include +#include // @@ -197,6 +198,45 @@ namespace BitEvolver return this->mutation_rate; } + // + void Population::EvaluateFitness(std::function)> evaluation_callback) + { + // + std::shared_ptr>> _chromosomes_copy; + std::vector> threads; + std::shared_ptr thread; + int + threads_count, + i + ; + + // Make a new vector containing all current chromosomes + this->population_modification_mutex.lock(); + _chromosomes_copy = std::shared_ptr>>( + new std::vector>() + ); + for ( i=0; i<(int)this->chromosomes.size(); i++ ) { + _chromosomes_copy->push_back( this->chromosomes[i] ); + } + this->population_modification_mutex.unlock(); + + // Spawn threads + threads_count = this->GetThreadCountSuggestion(); + for ( i=0; i( + new std::thread(&Population::EvaluateFitness_Thread, this, _chromosomes_copy, evaluation_callback) + ); + threads.push_back(thread); + } + + // Wait for threads to finish + for ( i=0; ijoin(); + } + } + // void Population::Evolve() { @@ -373,6 +413,41 @@ namespace BitEvolver } } + // + void Population::EvaluateFitness_Thread( + std::shared_ptr>> _chromosomes, + std::function)> evaluation_callback + ) + { + // + std::shared_ptr chromosome; + double fitness; + + // + while (true) + { + // Grab a free chromosome + this->evaluate_fitness_mutex.lock(); + chromosome = nullptr; + if ( _chromosomes->size() ) { + chromosome = _chromosomes->at(_chromosomes->size()-1); + _chromosomes->pop_back(); + } + this->evaluate_fitness_mutex.unlock(); + + // Call the evaluation callback + if ( chromosome != nullptr ) { + fitness = evaluation_callback(chromosome); + chromosome->SetFitness(fitness); + } + + // We're done if there was nothing to grab + else{ + break; + } + } + } + // std::shared_ptr Population::BreedChild() { diff --git a/Population.h b/Population.h index ad67dd3..24fc6fe 100644 --- a/Population.h +++ b/Population.h @@ -12,6 +12,7 @@ #include #include #include +#include // @@ -55,6 +56,9 @@ namespace BitEvolver void SetMutationRate(double r); double GetMutationRate(); + // + void EvaluateFitness(std::function)> evaluation_callback); + // void Evolve(); int GetEvolutionNumber(); @@ -90,7 +94,11 @@ namespace BitEvolver std::shared_ptr roulette_wheel; // - std::mutex breed_mutex; + std::recursive_mutex + population_modification_mutex, + breed_mutex, + evaluate_fitness_mutex + ; // void InitRandomGenerator(); @@ -104,6 +112,12 @@ namespace BitEvolver void BreedNewPopulation(std::shared_ptr>> population_new, int size); void BreedNewPopulation_Thread(std::shared_ptr>> population_new, int size); + // + void EvaluateFitness_Thread( + std::shared_ptr>> _chromosomes, + std::function)> evaluation_callback + ); + // std::shared_ptr BreedChild(); From a233802f6c382ed4550169cebd930a85fa9dbedd Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 04:18:05 -0700 Subject: [PATCH 11/17] Duplicated EvaluateFitness() to EvaluateError(), reversed --- Population.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ Population.h | 5 ++++ 2 files changed, 79 insertions(+) diff --git a/Population.cpp b/Population.cpp index 4519774..d36cdcd 100644 --- a/Population.cpp +++ b/Population.cpp @@ -237,6 +237,45 @@ namespace BitEvolver } } + // + void Population::EvaluateError(std::function)> evaluation_callback) + { + // + std::shared_ptr>> _chromosomes_copy; + std::vector> threads; + std::shared_ptr thread; + int + threads_count, + i + ; + + // Make a new vector containing all current chromosomes + this->population_modification_mutex.lock(); + _chromosomes_copy = std::shared_ptr>>( + new std::vector>() + ); + for ( i=0; i<(int)this->chromosomes.size(); i++ ) { + _chromosomes_copy->push_back( this->chromosomes[i] ); + } + this->population_modification_mutex.unlock(); + + // Spawn threads + threads_count = this->GetThreadCountSuggestion(); + for ( i=0; i( + new std::thread(&Population::EvaluateError_Thread, this, _chromosomes_copy, evaluation_callback) + ); + threads.push_back(thread); + } + + // Wait for threads to finish + for ( i=0; ijoin(); + } + } + // void Population::Evolve() { @@ -448,6 +487,41 @@ namespace BitEvolver } } + // + void Population::EvaluateError_Thread( + std::shared_ptr>> _chromosomes, + std::function)> evaluation_callback + ) + { + // + std::shared_ptr chromosome; + double error; + + // + while (true) + { + // Grab a free chromosome + this->evaluate_fitness_mutex.lock(); + chromosome = nullptr; + if ( _chromosomes->size() ) { + chromosome = _chromosomes->at(_chromosomes->size()-1); + _chromosomes->pop_back(); + } + this->evaluate_fitness_mutex.unlock(); + + // Call the evaluation callback + if ( chromosome != nullptr ) { + error = evaluation_callback(chromosome); + chromosome->SetError(error); + } + + // We're done if there was nothing to grab + else{ + break; + } + } + } + // std::shared_ptr Population::BreedChild() { diff --git a/Population.h b/Population.h index 24fc6fe..776c492 100644 --- a/Population.h +++ b/Population.h @@ -58,6 +58,7 @@ namespace BitEvolver // void EvaluateFitness(std::function)> evaluation_callback); + void EvaluateError(std::function)> evaluation_callback); // void Evolve(); @@ -117,6 +118,10 @@ namespace BitEvolver std::shared_ptr>> _chromosomes, std::function)> evaluation_callback ); + void EvaluateError_Thread( + std::shared_ptr>> _chromosomes, + std::function)> evaluation_callback + ); // std::shared_ptr BreedChild(); From dc75f730ab6d46e1a9d871bb8de747818eae932b Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 05:36:17 -0700 Subject: [PATCH 12/17] The Roulette wheel was broke lol --- Population.cpp | 4 ++++ RouletteWheel.cpp | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Population.cpp b/Population.cpp index d36cdcd..ce01929 100644 --- a/Population.cpp +++ b/Population.cpp @@ -534,6 +534,10 @@ namespace BitEvolver mama = this->roulette_wheel->Spin(); papa = this->roulette_wheel->Spin(); + // + //cout << "Roulette Wheel (mama): " << mama->ToString() << endl; + //cout << "Roulette Wheel (papa): " << papa->ToString() << endl; + // kiddo = this->breeder->Breed( mama, papa, diff --git a/RouletteWheel.cpp b/RouletteWheel.cpp index 6ba182d..bfa0d86 100644 --- a/RouletteWheel.cpp +++ b/RouletteWheel.cpp @@ -12,11 +12,16 @@ #include #include #include +#include // namespace BitEvolver { + // + using std::cout; + using std::endl; + // RouletteWheel::RouletteWheel() { @@ -115,7 +120,7 @@ namespace BitEvolver // Find the corresponding chromosome for ( i=0; iwheel_slots.size(); i++ ) { - if ( this->wheel_slots[i].first <= spin ) { + if ( this->wheel_slots[i].first >= spin ) { return this->wheel_slots[i].second; } } @@ -172,6 +177,9 @@ namespace BitEvolver // pairs.push_back(pair); + + // + //cout << "[" << pair.first << "]" << chromosome->ToString() << endl; } return pairs; From d2997d9a5c4b8f155536d8542bf993a5a4157203 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 06:31:34 -0700 Subject: [PATCH 13/17] Enhance fitness differences a bit --- RouletteWheel.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/RouletteWheel.cpp b/RouletteWheel.cpp index bfa0d86..cd18191 100644 --- a/RouletteWheel.cpp +++ b/RouletteWheel.cpp @@ -173,6 +173,7 @@ namespace BitEvolver // pair.first = chromosome->GetFitness() - fitness_low; + pair.first *= pair.first; // Square to enhance the difference a little pair.second = chromosome; // @@ -222,7 +223,9 @@ namespace BitEvolver std::unique_lock lock(this->chromosomes_mutex); std::vector>> chromosomes_normalized_fitness; std::pair> wheel_slot; - double slot_begin_value; + double + slot_begin_value + ; // if ( !this->slots_need_population ) { From 7db4140bddcf84253b4c42aa192c7e88567e13fa Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 07:18:47 -0700 Subject: [PATCH 14/17] Whoops ... --- Breeder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Breeder.cpp b/Breeder.cpp index 0dce7a5..656b640 100644 --- a/Breeder.cpp +++ b/Breeder.cpp @@ -50,6 +50,9 @@ namespace BitEvolver // Apply mutation this->Mutate(kiddo, mutation_rate); + // Reset kiddo's fitness + kiddo->ResetFitness(); + return kiddo; } From b2f164f4646b37cd7e560062a0fb3ead5898f6ce Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 08:33:25 -0700 Subject: [PATCH 15/17] Elitism and CopyChromosomes() --- Enums.h | 9 +++ Population.cpp | 197 ++++++++++++++++++++++++++++++++++++++++++++++--- Population.h | 38 +++++++++- 3 files changed, 229 insertions(+), 15 deletions(-) diff --git a/Enums.h b/Enums.h index a3791c9..0d524db 100644 --- a/Enums.h +++ b/Enums.h @@ -15,6 +15,15 @@ namespace BitEvolver None, Sexual }; + + // + enum class ElitismType + { + // + None, + Rate, + Absolute + }; }; }; diff --git a/Population.cpp b/Population.cpp index ce01929..5b58b64 100644 --- a/Population.cpp +++ b/Population.cpp @@ -17,12 +17,16 @@ #include #include #include +#include +#include // namespace BitEvolver { // + using std::string; + using std::stringstream; using std::cout; using std::endl; @@ -45,6 +49,11 @@ namespace BitEvolver this->evolution_number = 0; this->population_size = Population::DEFAULT_POPULATION_SIZE; + // + this->SetElitismType(Population::DEFAULT_ELITISM_TYPE); + this->SetElitismRate(Population::DEFAULT_ELITISM_RATE); + this->SetElitismCount(Population::DEFAULT_ELITISM_COUNT); + // this->SetCrossoverType(Population::DEFAULT_CROSSOVER_TYPE); this->SetCrossoverPoint(Population::DEFAULT_CROSSOVER_POINT); @@ -198,6 +207,48 @@ namespace BitEvolver return this->mutation_rate; } + // + void Population::SetElitismType(Enums::ElitismType t) + { + // + this->elitism_type = t; + } + + // + Enums::ElitismType Population::GetElitismType() + { + // + return this->elitism_type; + } + + // + void Population::SetElitismRate(double r) + { + // + this->elitism_rate = r; + } + + // + double Population::GetElitismRate() + { + // + return this->elitism_rate; + } + + // + void Population::SetElitismCount(int c) + { + // + this->elitism_count = c; + } + + // + int Population::GetElitismCount() + { + // + return this->elitism_count; + } + // void Population::EvaluateFitness(std::function)> evaluation_callback) { @@ -299,11 +350,6 @@ namespace BitEvolver new std::vector>() ); - // Start the new population off with our champion, - // so the best score always carries over (elitism = 1 unit) - #warning "Elitism is only 1 right now" - population_new->push_back(this->chromosomes[0]); - // Breed the new population this->BreedNewPopulation(population_new, (int)this->chromosomes.size()); @@ -403,7 +449,6 @@ namespace BitEvolver void Population::BreedNewPopulation(std::shared_ptr>> population_new, int size) { // - std::shared_ptr wheel; std::vector> threads; std::shared_ptr thread; int @@ -414,21 +459,25 @@ namespace BitEvolver // First, populate the roulette wheel this->roulette_wheel->SetChromosomes(this->chromosomes); - // - thread_count = this->GetThreadCountSuggestion(); + // Next, seed the population with elites + this->SeedPopulationWithElites(population_new); - // + // Next, breed until we've reached our new size + thread_count = this->GetThreadCountSuggestion(); for ( i=0; i( new std::thread(&Population::BreedNewPopulation_Thread, this, population_new, size) ); threads.push_back(thread); } - - // for ( i=0; i<(int)threads.size(); i++) { threads[i]->join(); } + + // Finally, reset the fitness of the new population + for ( i=0; i<(int)population_new->size(); i++ ) { + population_new->at(i)->ResetFitness(); + } } // @@ -452,6 +501,63 @@ namespace BitEvolver } } + // + int Population::DetermineEliteCount() + { + // + int count; + + // + switch( this->elitism_type ) + { + // + default: + case Enums::ElitismType::None: + count = 0; + break; + + // + case Enums::ElitismType::Absolute: + count = this->elitism_count; + break; + + // + case Enums::ElitismType::Rate: + count = floor( this->chromosomes.size() * this->elitism_rate ); + break; + } + + return count; + } + + // + void Population::SeedPopulationWithElites(std::shared_ptr>> population_new) + { + // + std::unique_lock lock(this->population_modification_mutex); + std::shared_ptr>> elites; + std::vector> threads; + std::shared_ptr thread; + int + elites_count, + i + ; + + // Determine how many elites to copy + elites_count = this->DetermineEliteCount(); + elites = std::shared_ptr>>( + new std::vector>() + ); + + // First, copy over just the pointers + for ( i=0; ichromosomes.size(); i++) { + elites->push_back( this->chromosomes[i] ); + } + + // Then, make them full copies (uses threads) + this->CopyChromosomes(elites, population_new); + } + // void Population::EvaluateFitness_Thread( std::shared_ptr>> _chromosomes, @@ -522,6 +628,75 @@ namespace BitEvolver } } + // + void Population::CopyChromosomes( + std::shared_ptr>> _chromosomes_source, + std::shared_ptr>> _chromosomes_destination + ) + { + // + std::vector> threads; + std::shared_ptr thread; + int + threads_count, + i + ; + + // Spawn threads + threads_count = this->GetThreadCountSuggestion(); + for ( i=0; i( + new std::thread(&Population::CopyChromosomes_Thread, this, _chromosomes_source, _chromosomes_destination) + ); + threads.push_back(thread); + } + + // Wait for threads to finish + for ( i=0; ijoin(); + } + } + + // + void Population::CopyChromosomes_Thread( + std::shared_ptr>> _chromosomes_source, + std::shared_ptr>> _chromosomes_destination + ) + { + // + std::shared_ptr + chromosome_original, + chromosome_copied + ; + stringstream ss; + + // + while ( _chromosomes_destination->size() < _chromosomes_source->size() ) + { + // Grab the next slot + this->copy_chromosomes_mutex.lock(); + chromosome_original = nullptr; + chromosome_copied = nullptr; + if ( _chromosomes_destination->size() < _chromosomes_source->size() ) { + + // + chromosome_copied = std::shared_ptr( + new Chromosome(this->random, 0) + ); + _chromosomes_destination->push_back(chromosome_copied); + chromosome_original = _chromosomes_source->at(_chromosomes_destination->size()-1); + } + this->copy_chromosomes_mutex.unlock(); + + // Make a full copy of the original, outside the lock + if ( chromosome_copied && chromosome_original ) { + *chromosome_copied = *chromosome_original; + } + } + } + // std::shared_ptr Population::BreedChild() { diff --git a/Population.h b/Population.h index 776c492..ca60a63 100644 --- a/Population.h +++ b/Population.h @@ -55,6 +55,13 @@ namespace BitEvolver // void SetMutationRate(double r); double GetMutationRate(); + // + void SetElitismType(Enums::ElitismType t); + Enums::ElitismType GetElitismType(); + void SetElitismRate(double r); + double GetElitismRate(); + void SetElitismCount(int c); + int GetElitismCount(); // void EvaluateFitness(std::function)> evaluation_callback); @@ -69,8 +76,11 @@ namespace BitEvolver void PrintPopulation(std::vector> _chromosomes); // Constants - const static int DEFAULT_POPULATION_SIZE = 100; - const static int DEFAULT_MUTATION_RATE = 0.01; + const static int DEFAULT_POPULATION_SIZE = 100; + const static Enums::ElitismType DEFAULT_ELITISM_TYPE = Enums::ElitismType::Rate; + constexpr static double DEFAULT_ELITISM_RATE = 0.01; + const static int DEFAULT_ELITISM_COUNT = 1; + constexpr static double DEFAULT_MUTATION_RATE = 0.01; // const static Enums::CrossoverType DEFAULT_CROSSOVER_TYPE = Enums::CrossoverType::Sexual; const static int DEFAULT_CROSSOVER_POINT = 0.7; @@ -86,10 +96,15 @@ namespace BitEvolver std::vector> chromosomes; int population_size; bool population_needs_sorting; + int evolution_number; + + // Enums::CrossoverType crossover_type; double crossover_point; double mutation_rate; - int evolution_number; + Enums::ElitismType elitism_type; + double elitism_rate; + int elitism_count; // std::shared_ptr roulette_wheel; @@ -98,7 +113,8 @@ namespace BitEvolver std::recursive_mutex population_modification_mutex, breed_mutex, - evaluate_fitness_mutex + evaluate_fitness_mutex, + copy_chromosomes_mutex ; // @@ -113,6 +129,10 @@ namespace BitEvolver void BreedNewPopulation(std::shared_ptr>> population_new, int size); void BreedNewPopulation_Thread(std::shared_ptr>> population_new, int size); + // + int DetermineEliteCount(); + void SeedPopulationWithElites(std::shared_ptr>> population_new); + // void EvaluateFitness_Thread( std::shared_ptr>> _chromosomes, @@ -123,6 +143,16 @@ namespace BitEvolver std::function)> evaluation_callback ); + // + void CopyChromosomes( + std::shared_ptr>> _chromosomes_source, + std::shared_ptr>> _chromosomes_destination + ); + void CopyChromosomes_Thread( + std::shared_ptr>> _chromosomes_source, + std::shared_ptr>> _chromosomes_destination + ); + // std::shared_ptr BreedChild(); From cb0f81b3e184196a197e5d021f5717c5809e1199 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 19:15:31 -0700 Subject: [PATCH 16/17] Removed bad idea --- Breeder.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Breeder.cpp b/Breeder.cpp index 656b640..b4f91b0 100644 --- a/Breeder.cpp +++ b/Breeder.cpp @@ -60,10 +60,10 @@ namespace BitEvolver void Breeder::Mutate(std::shared_ptr chromosome, double mutation_rate) { // - int i, size; - - // - #warning "For efficiency, let's instead roll the number of bits to flip in advance, and loop that many times (faster than rolling for every bit)" + int + i, + size + ; // size = chromosome->GetBitCount(); From 62ab4f9a16c125345b4e801c14f7221b28d70b13 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 14 Apr 2018 19:58:13 -0700 Subject: [PATCH 17/17] New crossover features: Order, Bounds, Standard deviation --- Breeder.cpp | 107 +++++++++++++++++++++++++++++++++++-------------- Breeder.h | 16 ++++++-- Enums.h | 16 ++++++++ Population.cpp | 62 ++++++++++++++++++++++++---- Population.h | 31 +++++++++----- 5 files changed, 182 insertions(+), 50 deletions(-) diff --git a/Breeder.cpp b/Breeder.cpp index b4f91b0..1a35347 100644 --- a/Breeder.cpp +++ b/Breeder.cpp @@ -29,23 +29,51 @@ namespace BitEvolver std::shared_ptr mama, std::shared_ptr papa, Enums::CrossoverType crossover_type, - double crossover_rate, + Enums::CrossoverOrder crossover_order, + Enums::CrossoverBounds crossover_bounds, + double crossover_point, + double crossover_point_std, double mutation_rate ) { // - std::shared_ptr kiddo; - int bit_length; + std::shared_ptr + parent_primary, + parent_secondary, + kiddo + ; - // - bit_length = mama->GetBitCount(); + // Choose primary / secondary parents + switch( crossover_order ) + { + // + case Enums::CrossoverOrder::MamaPapa: + parent_primary = mama; + parent_secondary = papa; + break; + + // + case Enums::CrossoverOrder::ByFitness: + if ( mama->GetFitness() > papa->GetFitness() ) { + parent_primary = mama; + parent_secondary = papa; + } + else{ + parent_primary = papa; + parent_secondary = mama; + } + break; + } - // Directly copy the mama - kiddo = std::shared_ptr(new Chromosome(this->random, bit_length)); - *kiddo = *mama; + // Directly copy the primary parent first + kiddo = std::shared_ptr(new Chromosome(this->random, parent_primary->GetBitCount())); + *kiddo = *parent_primary; - // Apply crossover - this->ApplyCrossover(kiddo, papa, crossover_type, crossover_rate); + // Apply crossover with the secondary parent + this->ApplyCrossover( + kiddo, parent_secondary, + crossover_type, crossover_bounds, crossover_point, crossover_point_std + ); // Apply mutation this->Mutate(kiddo, mutation_rate); @@ -80,39 +108,56 @@ namespace BitEvolver } // - int Breeder::PickRandomCrossoverPoint(std::shared_ptr chromosome, double crossover_rate) + int Breeder::PickRandomCrossoverPoint( + std::shared_ptr chromosome, + Enums::CrossoverBounds crossover_bounds, + double crossover_point, + double crossover_point_std + ) { // - double crossover_point_double; - int crossover_point; + int crossover_point_index; int bit_count; + double random_double; // bit_count = chromosome->GetBitCount(); /** Choose a double between [0.0,1.0] for the crossover point. - Use normal distribution, with the mean to the default point, - and 1.0 as the std. + Use normal distribution, with the mean and std from the parameters. That way, there is still randomness to the crossover point, - but it still generally sticks near the default + but it still generally sticks near the chosen point. */ - crossover_point_double = this->random->GetNormal(crossover_rate, 1.0); + random_double = this->random->GetNormal(crossover_point, crossover_point_std); // Apply to the actual int length - crossover_point = crossover_point_double * bit_count; + crossover_point_index = floor(random_double * bit_count); - // Loop around to keep in bounds - while ( crossover_point < 0 ) - { - crossover_point += bit_count; + // Loop around to keep in bounds? + if ( crossover_bounds == Enums::CrossoverBounds::Wrap ) { + while ( crossover_point_index < 0 ) + { + crossover_point_index += bit_count; + } + while ( crossover_point_index >= bit_count) + { + crossover_point_index -= bit_count; + } } - while ( crossover_point >= bit_count) - { - crossover_point -= bit_count; + else if ( crossover_bounds == Enums::CrossoverBounds::Clip ) { + if ( crossover_point_index < 0 ) { + crossover_point_index = 0; + } + else if ( crossover_point_index >= bit_count ) { + crossover_point_index = bit_count-1; + } + } + else{ + throw std::runtime_error("Breeder::PickRandomCrossoverPoint() - Invalid crossover_bounds"); } - return crossover_point; + return crossover_point_index; } // @@ -120,13 +165,15 @@ namespace BitEvolver std::shared_ptr kiddo, std::shared_ptr parent, Enums::CrossoverType crossover_type, - double crossover_rate + Enums::CrossoverBounds crossover_bounds, + double crossover_point, + double crossover_point_std ) { // int bits_count, - crossover_point, + crossover_point_index, i ; @@ -142,11 +189,11 @@ namespace BitEvolver } // Pick random crossover point - crossover_point = this->PickRandomCrossoverPoint(kiddo, crossover_rate); + crossover_point_index = this->PickRandomCrossoverPoint(kiddo, crossover_bounds, crossover_point, crossover_point_std); // Begin copying the parent at the crossover point and beyond // (not before) - for ( i=crossover_point; iSetBit( i, parent->GetBit(i) ); } diff --git a/Breeder.h b/Breeder.h index 40f4ed1..0b6b4fb 100644 --- a/Breeder.h +++ b/Breeder.h @@ -21,7 +21,10 @@ namespace BitEvolver std::shared_ptr mama, std::shared_ptr papa, Enums::CrossoverType crossover_type, - double crossover_rate, + Enums::CrossoverOrder crossover_order, + Enums::CrossoverBounds crossover_bounds, + double crossover_point, + double crossover_point_std, double mutation_rate ); @@ -35,12 +38,19 @@ namespace BitEvolver std::shared_ptr random; // - int PickRandomCrossoverPoint(std::shared_ptr chromosome, double crossover_rate); + int PickRandomCrossoverPoint( + std::shared_ptr chromosome, + Enums::CrossoverBounds crossover_bounds, + double crossover_point, + double crossover_point_std + ); void ApplyCrossover( std::shared_ptr kiddo, std::shared_ptr parent, Enums::CrossoverType crossover_type, - double crossover_rate + Enums::CrossoverBounds crossover_bounds, + double crossover_point, + double crossover_point_std ); }; }; diff --git a/Enums.h b/Enums.h index 0d524db..bf3410c 100644 --- a/Enums.h +++ b/Enums.h @@ -16,6 +16,22 @@ namespace BitEvolver Sexual }; + // + enum class CrossoverOrder + { + // + MamaPapa, + ByFitness + }; + + // + enum class CrossoverBounds + { + // + Clip, + Wrap + }; + // enum class ElitismType { diff --git a/Population.cpp b/Population.cpp index 5b58b64..85d82b7 100644 --- a/Population.cpp +++ b/Population.cpp @@ -56,7 +56,12 @@ namespace BitEvolver // this->SetCrossoverType(Population::DEFAULT_CROSSOVER_TYPE); + this->SetCrossoverOrder(Population::DEFAULT_CROSSOVER_ORDER); + this->SetCrossoverBounds(Population::DEFAULT_CROSSOVER_BOUNDS); this->SetCrossoverPoint(Population::DEFAULT_CROSSOVER_POINT); + this->SetCrossoverPointStandardDeviation(Population::DEFAULT_CROSSOVER_POINT_STD); + + // this->SetMutationRate(Population::DEFAULT_MUTATION_RATE); // @@ -165,6 +170,48 @@ namespace BitEvolver return fitness_average; } + // + void Population::SetCrossoverType(Enums::CrossoverType t) + { + // + this->crossover_type = t; + } + + // + Enums::CrossoverType Population::GetCrossoverType() + { + // + return this->crossover_type; + } + + // + void Population::SetCrossoverOrder(Enums::CrossoverOrder o) + { + // + this->crossover_order = o; + } + + // + Enums::CrossoverOrder Population::GetCrossoverOrder() + { + // + return this->crossover_order; + } + + // + void Population::SetCrossoverBounds(Enums::CrossoverBounds b) + { + // + this->crossover_bounds = b; + } + + // + Enums::CrossoverBounds Population::GetCrossoverBounds() + { + // + return this->crossover_bounds; + } + // void Population::SetCrossoverPoint(double p) { @@ -180,17 +227,17 @@ namespace BitEvolver } // - void Population::SetCrossoverType(Enums::CrossoverType t) + void Population::SetCrossoverPointStandardDeviation(double std) { // - this->crossover_type = t; + this->crossover_point_std = std; } // - Enums::CrossoverType Population::GetCrossoverType() + double Population::GetCrossoverPointStandardDeviation() { // - return this->crossover_type; + return this->crossover_point_std; } // @@ -709,15 +756,14 @@ namespace BitEvolver mama = this->roulette_wheel->Spin(); papa = this->roulette_wheel->Spin(); - // - //cout << "Roulette Wheel (mama): " << mama->ToString() << endl; - //cout << "Roulette Wheel (papa): " << papa->ToString() << endl; - // kiddo = this->breeder->Breed( mama, papa, this->crossover_type, + this->crossover_order, + this->crossover_bounds, this->crossover_point, + this->crossover_point_std, this->mutation_rate ); diff --git a/Population.h b/Population.h index ca60a63..e6ab36b 100644 --- a/Population.h +++ b/Population.h @@ -48,10 +48,17 @@ namespace BitEvolver double GetAverageFitness(std::vector> _chromosomes); // - void SetCrossoverPoint(double p); - double GetCrossoverPoint(); void SetCrossoverType(Enums::CrossoverType t); Enums::CrossoverType GetCrossoverType(); + void SetCrossoverOrder(Enums::CrossoverOrder o); + Enums::CrossoverOrder GetCrossoverOrder(); + void SetCrossoverBounds(Enums::CrossoverBounds b); + Enums::CrossoverBounds GetCrossoverBounds(); + void SetCrossoverPoint(double p); + double GetCrossoverPoint(); + void SetCrossoverPointStandardDeviation(double std); + double GetCrossoverPointStandardDeviation(); + // void SetMutationRate(double r); double GetMutationRate(); @@ -76,14 +83,17 @@ namespace BitEvolver void PrintPopulation(std::vector> _chromosomes); // Constants - const static int DEFAULT_POPULATION_SIZE = 100; - const static Enums::ElitismType DEFAULT_ELITISM_TYPE = Enums::ElitismType::Rate; - constexpr static double DEFAULT_ELITISM_RATE = 0.01; - const static int DEFAULT_ELITISM_COUNT = 1; - constexpr static double DEFAULT_MUTATION_RATE = 0.01; + const static int DEFAULT_POPULATION_SIZE = 100; + const static Enums::ElitismType DEFAULT_ELITISM_TYPE = Enums::ElitismType::Rate; + constexpr static double DEFAULT_ELITISM_RATE = 0.01; + const static int DEFAULT_ELITISM_COUNT = 1; + constexpr static double DEFAULT_MUTATION_RATE = 0.01; // - const static Enums::CrossoverType DEFAULT_CROSSOVER_TYPE = Enums::CrossoverType::Sexual; - const static int DEFAULT_CROSSOVER_POINT = 0.7; + const static Enums::CrossoverType DEFAULT_CROSSOVER_TYPE = Enums::CrossoverType::Sexual; + const static Enums::CrossoverOrder DEFAULT_CROSSOVER_ORDER = Enums::CrossoverOrder::MamaPapa; + const static Enums::CrossoverBounds DEFAULT_CROSSOVER_BOUNDS = Enums::CrossoverBounds::Wrap; + constexpr static double DEFAULT_CROSSOVER_POINT = 0.7; + constexpr static double DEFAULT_CROSSOVER_POINT_STD = 0.25; // private: @@ -100,7 +110,10 @@ namespace BitEvolver // Enums::CrossoverType crossover_type; + Enums::CrossoverOrder crossover_order; + Enums::CrossoverBounds crossover_bounds; double crossover_point; + double crossover_point_std; double mutation_rate; Enums::ElitismType elitism_type; double elitism_rate;