Merge branch 'dev'
This commit is contained in:
commit
210fe1e785
210
Breeder.cpp
Normal file
210
Breeder.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
|
||||
|
||||
//
|
||||
#include "BitEvolver/Random.h"
|
||||
#include "BitEvolver/Chromosome.h"
|
||||
#include "BitEvolver/Breeder.h"
|
||||
|
||||
|
||||
//
|
||||
#include <iostream>
|
||||
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
//
|
||||
Breeder::Breeder(std::shared_ptr<Random> _random)
|
||||
{
|
||||
//
|
||||
this->random = _random;
|
||||
}
|
||||
|
||||
//
|
||||
std::shared_ptr<Chromosome> Breeder::Breed(
|
||||
std::shared_ptr<Chromosome> mama,
|
||||
std::shared_ptr<Chromosome> papa,
|
||||
Enums::CrossoverType crossover_type,
|
||||
Enums::CrossoverOrder crossover_order,
|
||||
Enums::CrossoverBounds crossover_bounds,
|
||||
double crossover_point,
|
||||
double crossover_point_std,
|
||||
double mutation_rate
|
||||
)
|
||||
{
|
||||
//
|
||||
std::shared_ptr<Chromosome>
|
||||
parent_primary,
|
||||
parent_secondary,
|
||||
kiddo
|
||||
;
|
||||
|
||||
// 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 primary parent first
|
||||
kiddo = std::shared_ptr<Chromosome>(new Chromosome(this->random, parent_primary->GetBitCount()));
|
||||
*kiddo = *parent_primary;
|
||||
|
||||
// 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);
|
||||
|
||||
// Reset kiddo's fitness
|
||||
kiddo->ResetFitness();
|
||||
|
||||
return kiddo;
|
||||
}
|
||||
|
||||
//
|
||||
void Breeder::Mutate(std::shared_ptr<Chromosome> chromosome, double mutation_rate)
|
||||
{
|
||||
//
|
||||
int
|
||||
i,
|
||||
size
|
||||
;
|
||||
|
||||
//
|
||||
size = chromosome->GetBitCount();
|
||||
for ( i=0; i<size; i++ ) {
|
||||
|
||||
//
|
||||
if ( this->random->RollBool(mutation_rate) ) {
|
||||
chromosome->FlipBit(i);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
chromosome->ResetFitness();
|
||||
}
|
||||
|
||||
//
|
||||
int Breeder::PickRandomCrossoverPoint(
|
||||
std::shared_ptr<Chromosome> chromosome,
|
||||
Enums::CrossoverBounds crossover_bounds,
|
||||
double crossover_point,
|
||||
double crossover_point_std
|
||||
)
|
||||
{
|
||||
//
|
||||
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 and std from the parameters.
|
||||
That way, there is still randomness to the crossover point,
|
||||
but it still generally sticks near the chosen point.
|
||||
*/
|
||||
random_double = this->random->GetNormal(crossover_point, crossover_point_std);
|
||||
|
||||
// Apply to the actual int length
|
||||
crossover_point_index = floor(random_double * 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;
|
||||
}
|
||||
}
|
||||
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_index;
|
||||
}
|
||||
|
||||
//
|
||||
void Breeder::ApplyCrossover(
|
||||
std::shared_ptr<Chromosome> kiddo,
|
||||
std::shared_ptr<Chromosome> parent,
|
||||
Enums::CrossoverType crossover_type,
|
||||
Enums::CrossoverBounds crossover_bounds,
|
||||
double crossover_point,
|
||||
double crossover_point_std
|
||||
)
|
||||
{
|
||||
//
|
||||
int
|
||||
bits_count,
|
||||
crossover_point_index,
|
||||
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)");
|
||||
}
|
||||
|
||||
// Pick random crossover point
|
||||
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_index; i<bits_count; i++) {
|
||||
kiddo->SetBit( i, parent->GetBit(i) );
|
||||
}
|
||||
|
||||
//
|
||||
kiddo->ResetFitness();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
57
Breeder.h
Normal file
57
Breeder.h
Normal file
@ -0,0 +1,57 @@
|
||||
|
||||
|
||||
//
|
||||
#pragma once
|
||||
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
class Breeder
|
||||
{
|
||||
//
|
||||
public:
|
||||
|
||||
//
|
||||
Breeder(std::shared_ptr<class Random> _random);
|
||||
|
||||
//
|
||||
std::shared_ptr<class Chromosome> Breed(
|
||||
std::shared_ptr<class Chromosome> mama,
|
||||
std::shared_ptr<class Chromosome> papa,
|
||||
Enums::CrossoverType crossover_type,
|
||||
Enums::CrossoverOrder crossover_order,
|
||||
Enums::CrossoverBounds crossover_bounds,
|
||||
double crossover_point,
|
||||
double crossover_point_std,
|
||||
double mutation_rate
|
||||
);
|
||||
|
||||
//
|
||||
void Mutate(std::shared_ptr<class Chromosome> chromosome, double mutation_rate);
|
||||
|
||||
//
|
||||
private:
|
||||
|
||||
//
|
||||
std::shared_ptr<class Random> random;
|
||||
|
||||
//
|
||||
int PickRandomCrossoverPoint(
|
||||
std::shared_ptr<class Chromosome> chromosome,
|
||||
Enums::CrossoverBounds crossover_bounds,
|
||||
double crossover_point,
|
||||
double crossover_point_std
|
||||
);
|
||||
void ApplyCrossover(
|
||||
std::shared_ptr<class Chromosome> kiddo,
|
||||
std::shared_ptr<class Chromosome> parent,
|
||||
Enums::CrossoverType crossover_type,
|
||||
Enums::CrossoverBounds crossover_bounds,
|
||||
double crossover_point,
|
||||
double crossover_point_std
|
||||
);
|
||||
};
|
||||
};
|
||||
|
228
Chromosome.cpp
Normal file
228
Chromosome.cpp
Normal file
@ -0,0 +1,228 @@
|
||||
|
||||
|
||||
//
|
||||
#include "BitEvolver/Chromosome.h"
|
||||
#include "BitEvolver/Random.h"
|
||||
|
||||
|
||||
//
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
using std::string;
|
||||
using std::to_string;
|
||||
using std::stringstream;
|
||||
|
||||
//
|
||||
Chromosome::Chromosome(std::shared_ptr<Random> _random, int _bits)
|
||||
{
|
||||
//
|
||||
this->random = _random;
|
||||
this->SetBitCount(_bits);
|
||||
|
||||
//
|
||||
this->Reset();
|
||||
}
|
||||
|
||||
//
|
||||
void Chromosome::Reset()
|
||||
{
|
||||
//
|
||||
this->Randomize();
|
||||
}
|
||||
|
||||
//
|
||||
void Chromosome::Randomize()
|
||||
{
|
||||
//
|
||||
std::unique_lock<std::recursive_mutex> lock(this->modification_mutex);
|
||||
int i;
|
||||
|
||||
//
|
||||
this->bits.clear();
|
||||
|
||||
//
|
||||
for ( i=0; i<this->bits_count_desired; i++ ) {
|
||||
this->bits.push_back(this->random->GetInt(0, 1));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
void Chromosome::SetBitCount(int count)
|
||||
{
|
||||
//
|
||||
std::unique_lock<std::recursive_mutex> lock(this->modification_mutex);
|
||||
|
||||
//
|
||||
this->bits_count_desired = count;
|
||||
this->Randomize();
|
||||
}
|
||||
|
||||
//
|
||||
int Chromosome::GetBitCount()
|
||||
{
|
||||
return (int)this->bits.size();
|
||||
}
|
||||
|
||||
//
|
||||
void Chromosome::FlipBit(int index)
|
||||
{
|
||||
//
|
||||
std::unique_lock<std::recursive_mutex> 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));
|
||||
}
|
||||
|
||||
//
|
||||
if ( this->bits[index] ) {
|
||||
this->bits[index] = false;
|
||||
}
|
||||
else{
|
||||
this->bits[index] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
bool Chromosome::GetBit(int index)
|
||||
{
|
||||
//
|
||||
std::unique_lock<std::recursive_mutex> lock(this->modification_mutex);
|
||||
|
||||
//
|
||||
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)
|
||||
{
|
||||
//
|
||||
std::unique_lock<std::recursive_mutex> lock(this->modification_mutex);
|
||||
|
||||
//
|
||||
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;
|
||||
}
|
||||
|
||||
//
|
||||
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()
|
||||
{
|
||||
//
|
||||
std::unique_lock<std::recursive_mutex> lock(this->modification_mutex);
|
||||
stringstream s;
|
||||
|
||||
//
|
||||
for ( bool b : this->bits ) {
|
||||
|
||||
//
|
||||
if ( b ) {
|
||||
s << "1";
|
||||
}
|
||||
else{
|
||||
s << "0";
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return s.str();
|
||||
}
|
||||
|
||||
//
|
||||
const Chromosome& Chromosome::operator=(const Chromosome& other)
|
||||
{
|
||||
//
|
||||
std::unique_lock<std::recursive_mutex> lock1(this->modification_mutex);
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
83
Chromosome.h
Normal file
83
Chromosome.h
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
|
||||
//
|
||||
#pragma once
|
||||
|
||||
|
||||
//
|
||||
#include "BitEvolver/Includes.h"
|
||||
|
||||
|
||||
//
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
class Chromosome
|
||||
{
|
||||
//
|
||||
public:
|
||||
|
||||
//
|
||||
Chromosome(std::shared_ptr<class Random> _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();
|
||||
|
||||
/**
|
||||
Error is just inverted fitness
|
||||
*/
|
||||
void ResetError();
|
||||
void SetError(double e);
|
||||
void AdjustError(double e);
|
||||
double GetError();
|
||||
|
||||
//
|
||||
std::string ToString();
|
||||
|
||||
//
|
||||
const Chromosome& operator=(const Chromosome& other);
|
||||
|
||||
//
|
||||
private:
|
||||
|
||||
// Random number generator
|
||||
std::shared_ptr<class Random> random;
|
||||
|
||||
//
|
||||
std::vector<bool> bits;
|
||||
int bits_count_desired;
|
||||
|
||||
// Fitness
|
||||
double fitness;
|
||||
|
||||
// Mutexes
|
||||
std::recursive_mutex modification_mutex;
|
||||
};
|
||||
};
|
||||
|
9
Defines.h
Normal file
9
Defines.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef BITEVOLVER_DEFINES_H
|
||||
#define BITEVOLVER_DEFINES_H
|
||||
|
||||
|
||||
// Nada for now
|
||||
|
||||
|
||||
|
||||
#endif
|
47
Enums.h
Normal file
47
Enums.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef BITEVOLVER_ENUMS_H
|
||||
#define BITEVOLVER_ENUMS_H
|
||||
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
namespace Enums
|
||||
{
|
||||
//
|
||||
enum class CrossoverType
|
||||
{
|
||||
//
|
||||
None,
|
||||
Sexual
|
||||
};
|
||||
|
||||
//
|
||||
enum class CrossoverOrder
|
||||
{
|
||||
//
|
||||
MamaPapa,
|
||||
ByFitness
|
||||
};
|
||||
|
||||
//
|
||||
enum class CrossoverBounds
|
||||
{
|
||||
//
|
||||
Clip,
|
||||
Wrap
|
||||
};
|
||||
|
||||
//
|
||||
enum class ElitismType
|
||||
{
|
||||
//
|
||||
None,
|
||||
Rate,
|
||||
Absolute
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif
|
14
ForwardDeclarations.h
Normal file
14
ForwardDeclarations.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef BITEVOLVER_FORWARD_DECLARATIONS_H
|
||||
#define BITEVOLVER_FORWARD_DECLARATIONS_H
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
class Population;
|
||||
class Breeder;
|
||||
class Chromosome;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
13
Includes.h
Normal file
13
Includes.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef BITEVOLVER_INCLUDES_H
|
||||
#define BITEVOLVER_INCLUDES_H
|
||||
|
||||
|
||||
//
|
||||
#include "BitEvolver/Defines.h"
|
||||
#include "BitEvolver/Enums.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
136
Makefile
136
Makefile
@ -1 +1,137 @@
|
||||
|
||||
# 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)RouletteWheel.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 $@)
|
||||
|
||||
|
||||
# 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 \
|
||||
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 $@)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
793
Population.cpp
Normal file
793
Population.cpp
Normal file
@ -0,0 +1,793 @@
|
||||
|
||||
|
||||
//
|
||||
#include "BitEvolver/Includes.h"
|
||||
#include "BitEvolver/Random.h"
|
||||
#include "BitEvolver/Population.h"
|
||||
#include "BitEvolver/Breeder.h"
|
||||
#include "BitEvolver/RouletteWheel.h"
|
||||
#include "BitEvolver/Chromosome.h"
|
||||
|
||||
|
||||
//
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
//
|
||||
Population::Population()
|
||||
{
|
||||
//
|
||||
this->InitRandomGenerator();
|
||||
this->InitRouletteWheel();
|
||||
this->InitBreeder();
|
||||
|
||||
//
|
||||
this->Reset();
|
||||
}
|
||||
|
||||
//
|
||||
void Population::Reset()
|
||||
{
|
||||
//
|
||||
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->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);
|
||||
|
||||
//
|
||||
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> chromosome;
|
||||
int i;
|
||||
|
||||
//
|
||||
this->ClearPopulation();
|
||||
for ( i=0; i<this->population_size; i++ ) {
|
||||
|
||||
//
|
||||
chromosome = std::shared_ptr<Chromosome>(
|
||||
new Chromosome( this->random, _bit_length )
|
||||
);
|
||||
this->chromosomes.push_back(chromosome);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
void Population::PopulationChanged()
|
||||
{
|
||||
//
|
||||
this->population_needs_sorting = true;
|
||||
}
|
||||
|
||||
//
|
||||
std::vector<std::shared_ptr<Chromosome>> Population::GetChromosomes()
|
||||
{
|
||||
return this->chromosomes;
|
||||
}
|
||||
|
||||
//
|
||||
void Population::GetChromosomes(std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes)
|
||||
{
|
||||
//
|
||||
_chromosomes->clear();
|
||||
for ( std::shared_ptr<Chromosome> chromosome : this->chromosomes) {
|
||||
_chromosomes->push_back(chromosome);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
std::shared_ptr<Chromosome> 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<std::shared_ptr<Chromosome>> _chromosomes)
|
||||
{
|
||||
//
|
||||
double fitness_sum;
|
||||
double fitness_average;
|
||||
|
||||
//
|
||||
fitness_sum = 0;
|
||||
for ( std::shared_ptr<Chromosome> chromosome : _chromosomes ) {
|
||||
fitness_sum += chromosome->GetFitness();
|
||||
}
|
||||
|
||||
//
|
||||
fitness_average = 0;
|
||||
if ( _chromosomes.size() > 0 ) {
|
||||
fitness_average = fitness_sum / _chromosomes.size();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
//
|
||||
this->crossover_point = p;
|
||||
}
|
||||
|
||||
//
|
||||
double Population::GetCrossoverPoint()
|
||||
{
|
||||
//
|
||||
return this->crossover_point;
|
||||
}
|
||||
|
||||
//
|
||||
void Population::SetCrossoverPointStandardDeviation(double std)
|
||||
{
|
||||
//
|
||||
this->crossover_point_std = std;
|
||||
}
|
||||
|
||||
//
|
||||
double Population::GetCrossoverPointStandardDeviation()
|
||||
{
|
||||
//
|
||||
return this->crossover_point_std;
|
||||
}
|
||||
|
||||
//
|
||||
void Population::SetMutationRate(double r)
|
||||
{
|
||||
//
|
||||
this->mutation_rate = r;
|
||||
}
|
||||
|
||||
//
|
||||
double Population::GetMutationRate()
|
||||
{
|
||||
//
|
||||
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<double(std::shared_ptr<Chromosome>)> evaluation_callback)
|
||||
{
|
||||
//
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_copy;
|
||||
std::vector<std::shared_ptr<std::thread>> threads;
|
||||
std::shared_ptr<std::thread> thread;
|
||||
int
|
||||
threads_count,
|
||||
i
|
||||
;
|
||||
|
||||
// Make a new vector containing all current chromosomes
|
||||
this->population_modification_mutex.lock();
|
||||
_chromosomes_copy = std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>>(
|
||||
new std::vector<std::shared_ptr<Chromosome>>()
|
||||
);
|
||||
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<threads_count; i++) {
|
||||
|
||||
//
|
||||
thread = std::shared_ptr<std::thread>(
|
||||
new std::thread(&Population::EvaluateFitness_Thread, this, _chromosomes_copy, evaluation_callback)
|
||||
);
|
||||
threads.push_back(thread);
|
||||
}
|
||||
|
||||
// Wait for threads to finish
|
||||
for ( i=0; i<threads_count; i++ ) {
|
||||
threads[i]->join();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
void Population::EvaluateError(std::function<double(std::shared_ptr<Chromosome>)> evaluation_callback)
|
||||
{
|
||||
//
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_copy;
|
||||
std::vector<std::shared_ptr<std::thread>> threads;
|
||||
std::shared_ptr<std::thread> thread;
|
||||
int
|
||||
threads_count,
|
||||
i
|
||||
;
|
||||
|
||||
// Make a new vector containing all current chromosomes
|
||||
this->population_modification_mutex.lock();
|
||||
_chromosomes_copy = std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>>(
|
||||
new std::vector<std::shared_ptr<Chromosome>>()
|
||||
);
|
||||
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<threads_count; i++) {
|
||||
|
||||
//
|
||||
thread = std::shared_ptr<std::thread>(
|
||||
new std::thread(&Population::EvaluateError_Thread, this, _chromosomes_copy, evaluation_callback)
|
||||
);
|
||||
threads.push_back(thread);
|
||||
}
|
||||
|
||||
// Wait for threads to finish
|
||||
for ( i=0; i<threads_count; i++ ) {
|
||||
threads[i]->join();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
void Population::Evolve()
|
||||
{
|
||||
//
|
||||
std::shared_ptr<std::vector< std::shared_ptr<Chromosome> > > population_new;
|
||||
|
||||
//
|
||||
if ( this->chromosomes.size() == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
this->EnsureSortedPopulation();
|
||||
|
||||
//
|
||||
population_new = std::shared_ptr<
|
||||
std::vector<
|
||||
std::shared_ptr<Chromosome>
|
||||
>
|
||||
>(
|
||||
new std::vector<std::shared_ptr<Chromosome>>()
|
||||
);
|
||||
|
||||
// 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<std::shared_ptr<Chromosome>> _chromosomes)
|
||||
{
|
||||
//
|
||||
for ( std::shared_ptr<Chromosome> chromosome : chromosomes ) {
|
||||
cout << chromosome->ToString() << endl;
|
||||
}
|
||||
cout << "Average Fitness --> " << this->GetAverageFitness(_chromosomes) << endl;
|
||||
}
|
||||
|
||||
//
|
||||
void Population::InitRandomGenerator()
|
||||
{
|
||||
//
|
||||
this->random = std::shared_ptr<Random>(
|
||||
new Random()
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
void Population::InitRouletteWheel()
|
||||
{
|
||||
//
|
||||
this->roulette_wheel = std::shared_ptr<RouletteWheel>(
|
||||
new RouletteWheel()
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
void Population::InitBreeder()
|
||||
{
|
||||
//
|
||||
if ( !this->random ) {
|
||||
throw std::runtime_error("Population::InitBreeder() - Should come after InitRandomGenerator()");
|
||||
}
|
||||
|
||||
//
|
||||
this->breeder = std::shared_ptr<Breeder>(
|
||||
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<Chromosome>& left, std::shared_ptr<Chromosome>& right ) -> bool
|
||||
{
|
||||
//
|
||||
if ( left->GetFitness() > right->GetFitness() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
//
|
||||
this->population_needs_sorting = false;
|
||||
}
|
||||
|
||||
//
|
||||
void Population::BreedNewPopulation(std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> population_new, int size)
|
||||
{
|
||||
//
|
||||
std::vector<std::shared_ptr<std::thread>> threads;
|
||||
std::shared_ptr<std::thread> thread;
|
||||
int
|
||||
thread_count,
|
||||
i
|
||||
;
|
||||
|
||||
// First, populate the roulette wheel
|
||||
this->roulette_wheel->SetChromosomes(this->chromosomes);
|
||||
|
||||
// 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<thread_count; i++) {
|
||||
thread = std::shared_ptr<std::thread>(
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
void Population::BreedNewPopulation_Thread(std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> population_new, int size)
|
||||
{
|
||||
//
|
||||
std::shared_ptr<Chromosome> 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();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
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<std::vector<std::shared_ptr<Chromosome>>> population_new)
|
||||
{
|
||||
//
|
||||
std::unique_lock<std::recursive_mutex> lock(this->population_modification_mutex);
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> elites;
|
||||
std::vector<std::shared_ptr<std::thread>> threads;
|
||||
std::shared_ptr<std::thread> thread;
|
||||
int
|
||||
elites_count,
|
||||
i
|
||||
;
|
||||
|
||||
// Determine how many elites to copy
|
||||
elites_count = this->DetermineEliteCount();
|
||||
elites = std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>>(
|
||||
new std::vector<std::shared_ptr<Chromosome>>()
|
||||
);
|
||||
|
||||
// First, copy over just the pointers
|
||||
for ( i=0; i<elites_count && i<(int)this->chromosomes.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<std::vector<std::shared_ptr<Chromosome>>> _chromosomes,
|
||||
std::function<double(std::shared_ptr<Chromosome>)> evaluation_callback
|
||||
)
|
||||
{
|
||||
//
|
||||
std::shared_ptr<Chromosome> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
void Population::EvaluateError_Thread(
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes,
|
||||
std::function<double(std::shared_ptr<Chromosome>)> evaluation_callback
|
||||
)
|
||||
{
|
||||
//
|
||||
std::shared_ptr<Chromosome> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
void Population::CopyChromosomes(
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_source,
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_destination
|
||||
)
|
||||
{
|
||||
//
|
||||
std::vector<std::shared_ptr<std::thread>> threads;
|
||||
std::shared_ptr<std::thread> thread;
|
||||
int
|
||||
threads_count,
|
||||
i
|
||||
;
|
||||
|
||||
// Spawn threads
|
||||
threads_count = this->GetThreadCountSuggestion();
|
||||
for ( i=0; i<threads_count; i++) {
|
||||
|
||||
//
|
||||
thread = std::shared_ptr<std::thread>(
|
||||
new std::thread(&Population::CopyChromosomes_Thread, this, _chromosomes_source, _chromosomes_destination)
|
||||
);
|
||||
threads.push_back(thread);
|
||||
}
|
||||
|
||||
// Wait for threads to finish
|
||||
for ( i=0; i<threads_count; i++ ) {
|
||||
threads[i]->join();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
void Population::CopyChromosomes_Thread(
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_source,
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_destination
|
||||
)
|
||||
{
|
||||
//
|
||||
std::shared_ptr<Chromosome>
|
||||
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<Chromosome>(
|
||||
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<Chromosome> Population::BreedChild()
|
||||
{
|
||||
//
|
||||
std::shared_ptr<Chromosome>
|
||||
mama, papa, kiddo
|
||||
;
|
||||
|
||||
// Pick two parents
|
||||
mama = this->roulette_wheel->Spin();
|
||||
papa = this->roulette_wheel->Spin();
|
||||
|
||||
//
|
||||
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
|
||||
);
|
||||
|
||||
return kiddo;
|
||||
}
|
||||
|
||||
//
|
||||
int Population::GetThreadCountSuggestion()
|
||||
{
|
||||
//
|
||||
int thread_count;
|
||||
|
||||
//
|
||||
thread_count = std::thread::hardware_concurrency();
|
||||
|
||||
return thread_count;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
178
Population.h
Normal file
178
Population.h
Normal file
@ -0,0 +1,178 @@
|
||||
|
||||
|
||||
//
|
||||
#pragma once
|
||||
|
||||
|
||||
//
|
||||
#include "BitEvolver/Includes.h"
|
||||
|
||||
|
||||
//
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
|
||||
//
|
||||
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<std::shared_ptr<class Chromosome>> GetChromosomes();
|
||||
void GetChromosomes(std::shared_ptr<std::vector<std::shared_ptr<class Chromosome>>> _chromosomes);
|
||||
std::shared_ptr<class Chromosome> GetChampion();
|
||||
|
||||
//
|
||||
double GetAverageFitness();
|
||||
double GetAverageFitness(std::vector<std::shared_ptr<class Chromosome>> _chromosomes);
|
||||
|
||||
//
|
||||
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();
|
||||
//
|
||||
void SetElitismType(Enums::ElitismType t);
|
||||
Enums::ElitismType GetElitismType();
|
||||
void SetElitismRate(double r);
|
||||
double GetElitismRate();
|
||||
void SetElitismCount(int c);
|
||||
int GetElitismCount();
|
||||
|
||||
//
|
||||
void EvaluateFitness(std::function<double(std::shared_ptr<Chromosome>)> evaluation_callback);
|
||||
void EvaluateError(std::function<double(std::shared_ptr<Chromosome>)> evaluation_callback);
|
||||
|
||||
//
|
||||
void Evolve();
|
||||
int GetEvolutionNumber();
|
||||
|
||||
//
|
||||
void PrintPopulation();
|
||||
void PrintPopulation(std::vector<std::shared_ptr<class Chromosome>> _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 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:
|
||||
|
||||
//
|
||||
std::shared_ptr<class Random> random;
|
||||
std::shared_ptr<class Breeder> breeder;
|
||||
|
||||
//
|
||||
std::vector<std::shared_ptr<class Chromosome>> chromosomes;
|
||||
int population_size;
|
||||
bool population_needs_sorting;
|
||||
int evolution_number;
|
||||
|
||||
//
|
||||
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;
|
||||
int elitism_count;
|
||||
|
||||
//
|
||||
std::shared_ptr<class RouletteWheel> roulette_wheel;
|
||||
|
||||
//
|
||||
std::recursive_mutex
|
||||
population_modification_mutex,
|
||||
breed_mutex,
|
||||
evaluate_fitness_mutex,
|
||||
copy_chromosomes_mutex
|
||||
;
|
||||
|
||||
//
|
||||
void InitRandomGenerator();
|
||||
void InitRouletteWheel();
|
||||
void InitBreeder();
|
||||
|
||||
//
|
||||
void EnsureSortedPopulation();
|
||||
|
||||
//
|
||||
void BreedNewPopulation(std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> population_new, int size);
|
||||
void BreedNewPopulation_Thread(std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> population_new, int size);
|
||||
|
||||
//
|
||||
int DetermineEliteCount();
|
||||
void SeedPopulationWithElites(std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> population_new);
|
||||
|
||||
//
|
||||
void EvaluateFitness_Thread(
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes,
|
||||
std::function<double(std::shared_ptr<Chromosome>)> evaluation_callback
|
||||
);
|
||||
void EvaluateError_Thread(
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes,
|
||||
std::function<double(std::shared_ptr<Chromosome>)> evaluation_callback
|
||||
);
|
||||
|
||||
//
|
||||
void CopyChromosomes(
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_source,
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_destination
|
||||
);
|
||||
void CopyChromosomes_Thread(
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_source,
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Chromosome>>> _chromosomes_destination
|
||||
);
|
||||
|
||||
//
|
||||
std::shared_ptr<Chromosome> BreedChild();
|
||||
|
||||
//
|
||||
int GetThreadCountSuggestion();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
86
Random.cpp
Normal file
86
Random.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
|
||||
//
|
||||
#include "BitEvolver/Random.h"
|
||||
|
||||
|
||||
//
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
Random::Random()
|
||||
{
|
||||
//
|
||||
this->InitializeGenerators();
|
||||
}
|
||||
|
||||
//
|
||||
int Random::GetInt(int min, int max)
|
||||
{
|
||||
//
|
||||
std::uniform_int_distribution<int> 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<double> 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<double> 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
41
Random.h
Normal file
41
Random.h
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
|
||||
//
|
||||
#pragma once
|
||||
|
||||
|
||||
//
|
||||
#include <random>
|
||||
|
||||
|
||||
//
|
||||
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();
|
||||
|
||||
};
|
||||
};
|
271
RouletteWheel.cpp
Normal file
271
RouletteWheel.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
|
||||
|
||||
|
||||
//
|
||||
#include "BitEvolver/Random.h"
|
||||
#include "BitEvolver/RouletteWheel.h"
|
||||
#include "BitEvolver/Chromosome.h"
|
||||
|
||||
|
||||
//
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
//
|
||||
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++ ) {
|
||||
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<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;
|
||||
pair.first *= pair.first; // Square to enhance the difference a little
|
||||
pair.second = chromosome;
|
||||
|
||||
//
|
||||
pairs.push_back(pair);
|
||||
|
||||
//
|
||||
//cout << "[" << pair.first << "]" << chromosome->ToString() << endl;
|
||||
}
|
||||
|
||||
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;
|
||||
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<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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
68
RouletteWheel.h
Normal file
68
RouletteWheel.h
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
|
||||
|
||||
//
|
||||
#pragma once
|
||||
|
||||
|
||||
//
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
|
||||
//
|
||||
namespace BitEvolver
|
||||
{
|
||||
//
|
||||
class RouletteWheel
|
||||
{
|
||||
//
|
||||
public:
|
||||
|
||||
//
|
||||
RouletteWheel();
|
||||
|
||||
//
|
||||
void Reset();
|
||||
|
||||
//
|
||||
void ClearChromosomes();
|
||||
void SetChromosomes(std::vector<std::shared_ptr<class Chromosome>> _chromosomes);
|
||||
void AddChromosome(std::shared_ptr<class Chromosome> _chromosome);
|
||||
void AddChromosomes(std::vector<std::shared_ptr<class Chromosome>> _chromosomes);
|
||||
|
||||
//
|
||||
std::shared_ptr<Chromosome> Spin();
|
||||
|
||||
//
|
||||
private:
|
||||
|
||||
//
|
||||
std::shared_ptr<class Random> random;
|
||||
|
||||
//
|
||||
std::vector<std::shared_ptr<class Chromosome>> chromosomes;
|
||||
std::recursive_mutex chromosomes_mutex;
|
||||
bool chromosomes_need_sorting;
|
||||
//
|
||||
std::vector<std::pair<double,std::shared_ptr<class Chromosome>>> wheel_slots;
|
||||
bool slots_need_population;
|
||||
|
||||
|
||||
//
|
||||
void Instantiate();
|
||||
|
||||
//
|
||||
std::vector<std::pair<double, std::shared_ptr<class Chromosome>>> GetNormalizedChromosomeFitness();
|
||||
void SortChromosomes();
|
||||
void PopulateSlots();
|
||||
|
||||
//
|
||||
void ChromosomesChanged();
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user