153 lines
3.1 KiB
C++
153 lines
3.1 KiB
C++
|
|
||
|
|
||
|
//
|
||
|
#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();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|