First commit - Seems to pass "all 1's" evolution test
This commit is contained in:
parent
1ee1d3282b
commit
854ad5a1a3
152
Breeder.cpp
Normal file
152
Breeder.cpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#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,
|
||||||
|
double crossover_rate,
|
||||||
|
double mutation_rate
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
std::shared_ptr<Chromosome> 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<Chromosome>(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> 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; i<size; i++ ) {
|
||||||
|
|
||||||
|
//
|
||||||
|
if ( this->random->RollBool(mutation_rate) ) {
|
||||||
|
chromosome->FlipBit(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
chromosome->ResetFitness();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
int Breeder::PickRandomCrossoverPoint(std::shared_ptr<Chromosome> 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<Chromosome> kiddo, std::shared_ptr<Chromosome> 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; i<bits_count; i++) {
|
||||||
|
kiddo->SetBit( i, parent->GetBit(i) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
kiddo->ResetFitness();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
Breeder.h
Normal file
41
Breeder.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#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,
|
||||||
|
double crossover_rate,
|
||||||
|
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, double crossover_rate);
|
||||||
|
void ApplyCrossover(std::shared_ptr<class Chromosome> kiddo, std::shared_ptr<class Chromosome> parent, double crossover_rate);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
184
Chromosome.cpp
Normal file
184
Chromosome.cpp
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#include "BitEvolver/Chromosome.h"
|
||||||
|
#include "BitEvolver/Random.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
71
Chromosome.h
Normal file
71
Chromosome.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#include "BitEvolver/Includes.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
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();
|
||||||
|
|
||||||
|
//
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
13
Defines.h
Normal file
13
Defines.h
Normal file
@ -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
|
9
Enums.h
Normal file
9
Enums.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef BITEVOLVER_ENUMS_H
|
||||||
|
#define BITEVOLVER_ENUMS_H
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#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
|
122
Makefile
122
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 $@)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
389
Population.cpp
Normal file
389
Population.cpp
Normal file
@ -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 <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
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> 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::SetMutationRate(double r)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
this->mutation_rate = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
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>>()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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<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::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
|
||||||
|
;
|
||||||
|
|
||||||
|
//
|
||||||
|
thread_count = std::thread::hardware_concurrency();
|
||||||
|
|
||||||
|
//
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
std::shared_ptr<Chromosome> Population::BreedChild()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
std::shared_ptr<Chromosome>
|
||||||
|
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<Chromosome> 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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
96
Population.h
Normal file
96
Population.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#include "BitEvolver/Includes.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
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 SetMutationRate(double r);
|
||||||
|
|
||||||
|
//
|
||||||
|
void Evolve();
|
||||||
|
int GetEvolutionNumber();
|
||||||
|
|
||||||
|
//
|
||||||
|
void PrintPopulation();
|
||||||
|
void PrintPopulation(std::vector<std::shared_ptr<class Chromosome>> _chromosomes);
|
||||||
|
|
||||||
|
//
|
||||||
|
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;
|
||||||
|
double mutation_rate;
|
||||||
|
int evolution_number;
|
||||||
|
|
||||||
|
//
|
||||||
|
std::mutex breed_mutex;
|
||||||
|
|
||||||
|
//
|
||||||
|
void InitRandomGenerator();
|
||||||
|
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);
|
||||||
|
|
||||||
|
//
|
||||||
|
std::shared_ptr<Chromosome> BreedChild();
|
||||||
|
std::shared_ptr<Chromosome> PickChromosomeForBreeding();
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
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();
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user