Species
Reproduction takes place mostly within a
single species, so that compatible organisms
can mate.
/* Species #1 : (Size 16) (AF 6.14618) (Age 13) */
/* Organism #0 Fitness: 8.98931 Error: 1.00178 */
genomestart 0
trait 1 0.1 0 0 0 0 0 0 0
trait 2 0.2 0.47343 0 0 0 0.388696 0 0
trait 3 0.3 0 0 0 0 0 0 0
node 1 3 1 3
node 2 2 1 1
node 3 1 1 1
node 4 2 0 2
gene 3 1 4 -1.53262 0 1 -1.53262 1
gene 1 2 4 3 0 2 3 1
gene 1 3 4 -3 0 3 -3 1
genomeend 0
/* Organism #1 Fitness: 8.25561 Error: 1.12674 */
genomestart 1
trait 1 0.1 0 0 0 0 0 0 0
trait 2 0.2 0.47343 0 0 0 0.388696 0 0
trait 3 0.3 0 0 0 0 0 0 0
node 1 3 1 3
node 2 2 1 1
node 3 1 1 1
node 4 2 0 2
gene 3 1 4 -0.392043 0 1 -0.392043 1
gene 1 2 4 3 0 2 3 1
gene 1 3 4 2.41432 0 3 2.41432 1
genomeend 1
...
A Species is a group of similar Organisms
////////////////////////////////////////////////////////////////////////////////////////
#ifndef _SPECIES_H_
#define _SPECIES_H_
#include "neat.h"
#include "organism.h"
#include "population.h"
namespace NEAT {
class Organism;
class Population;
// ---------------------------------------------
// SPECIES CLASS:
// A Species is a group of similar Organisms
// Reproduction takes place mostly within a
// single species, so that compatible organisms
// can mate.
// ---------------------------------------------
class Species {
public:
int id;
int age; //The age of the Species
double ave_fitness; //The average fitness of the Species
double max_fitness; //Max fitness of the Species
double max_fitness_ever; //The max it ever had
int expected_offspring;
bool novel;
bool checked;
bool obliterate; //Allows killing off in competitive coevolution stagnation
std::vector<Organism*> organisms; //The organisms in the Species
//std::vector<Organism*> reproduction_pool; //The organisms for reproduction- NOT NEEDED
int age_of_last_improvement; //If this is too long ago, the Species will goes extinct
double average_est; //When playing real-time allows estimating average fitness
bool add_Organism(Organism *o);
Organism *first();
bool print_to_file(std::ostream &outFile);
bool print_to_file(std::ofstream &outFile);
//Change the fitness of all the organisms in the species to possibly depend slightly on the age of the species
//and then divide it by the size of the species so that the organisms in the species "share" the fitness
void adjust_fitness();
double compute_average_fitness();
double compute_max_fitness();
//Counts the number of offspring expected from all its members skim is for keeping track of remaining
// fractional parts of offspring and distributing them among species
double count_offspring(double skim);
//Compute generations since last improvement
int last_improved() {
return age-age_of_last_improvement;
}
//Remove an organism from Species
bool remove_org(Organism *org);
double size() {
return organisms.size();
}
Organism *get_champ();
//Perform mating and mutation to form next generation
bool reproduce(int generation, Population *pop,std::vector<Species*> &sorted_species);
// *** Real-time methods ***
//Place organisms in this species in order by their fitness
bool rank();
//Compute an estimate of the average fitness of the species
//The result is left in variable average_est and returned
//New variable: average_est, NEAT::time_alive_minimum (const)
//Note: Initialization requires calling estimate_average() on all species
// Later it should be called only when a species changes
double estimate_average();
//Like the usual reproduce() method except only one offspring is produced
//Note that "generation" will be used to just count which offspring # this is over all evolution
//Here is how to get sorted species:
// Sort the Species by max fitness (Use an extra list to do this)
// These need to use ORIGINAL fitness
// sorted_species.sort(order_species);
Organism *reproduce_one(int generation, Population *pop,std::vector<Species*> &sorted_species);
// Organism *reproduce_one(int generation, Population *pop,Vector<Species*> &sorted_species, bool addAdv, Genome* adv);
Species(int i);
//Allows the creation of a Species that won't age (a novel one)
//This protects new Species from aging inside their first generation
Species(int i,bool n);
~Species();
};
// This is used for list sorting of Species by fitness of best organism highest fitness first
bool order_species(Species *x, Species *y);
bool order_new_species(Species *x, Species *y);
}
#endif
////////////////////////////////////////////////////////////////////////////////////////
#include "species.h"
#include "organism.h"
#include <cmath>
#include <iostream>
using namespace NEAT;
Species::Species(int i) {
id=i;
age=1;
ave_fitness=0.0;
expected_offspring=0;
novel=false;
age_of_last_improvement=0;
max_fitness=0;
max_fitness_ever=0;
obliterate=false;
average_est=0;
}
Species::Species(int i,bool n) {
id=i;
age=1;
ave_fitness=0.0;
expected_offspring=0;
novel=n;
age_of_last_improvement=0;
max_fitness=0;
max_fitness_ever=0;
obliterate=false;
average_est=0;
}
Species::~Species() {
std::vector<Organism*>::iterator curorg;
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
delete (*curorg);
}
}
bool Species::rank() {
//organisms.qsort(order_orgs);
std::sort(organisms.begin(), organisms.end(), order_orgs);
return true;
}
double Species::estimate_average() {
std::vector<Organism*>::iterator curorg;
double total = 0.0; //running total of fitnesses
//Note: Since evolution is happening in real-time, some organisms may not
//have been around long enough to count them in the fitness evaluation
double num_orgs = 0; //counts number of orgs above the time_alive threshold
for(curorg = organisms.begin(); curorg != organisms.end(); ++curorg) {
//New variable time_alive
if (((*curorg)->time_alive) >= NEAT::time_alive_minimum) {
total += (*curorg)->fitness;
++num_orgs;
}
}
if (num_orgs > 0)
average_est = total / num_orgs;
else {
average_est = 0;
}
return average_est;
}
Organism *Species::reproduce_one(int generation, Population *pop,std::vector<Species*> &sorted_species) {
//bool Species::reproduce(int generation, Population *pop,std::vector<Species*> &sorted_species) {
int count=generation; //This will assign genome id's according to the generation
std::vector<Organism*>::iterator curorg;
std::vector<Organism*> elig_orgs; //This list contains the eligible organisms (KEN)
int poolsize; //The number of Organisms in the old generation
int orgnum; //Random variable
int orgcount;
Organism *mom = 0; //Parent Organisms
Organism *dad = 0;
Organism *baby; //The new Organism
Genome *new_genome; //For holding baby's genes
std::vector<Species*>::iterator curspecies; //For adding baby
Species *newspecies; //For babies in new Species
Organism *comporg; //For Species determination through comparison
Species *randspecies; //For mating outside the Species
double randmult;
int randspeciesnum;
int spcount;
std::vector<Species*>::iterator cursp;
Network *net_analogue; //For adding link to test for recurrency
int pause;
bool outside;
bool found; //When a Species is found
bool champ_done=false; //Flag the preservation of the champion
Organism *thechamp;
int giveup; //For giving up finding a mate outside the species
bool mut_struct_baby;
bool mate_baby;
//The weight mutation power is species specific depending on its age
double mut_power=NEAT::weight_mut_power;
//Roulette wheel variables
double total_fitness=0.0;
double marble; //The marble will have a number between 0 and total_fitness
double spin; //Fitness total while the wheel is spinning
//printf("In reproduce_one");
//Check for a mistake
if ((organisms.size()==0)) {
// cout<<"ERROR: ATTEMPT TO REPRODUCE OUT OF EMPTY SPECIES"<<endl;
return false;
}
rank(); //Make sure organisms are ordered by rank
//ADDED CODE (Ken)
//Now transfer the list to elig_orgs without including the ones that are too young (Ken)
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
if ((*curorg)->time_alive >= NEAT::time_alive_minimum)
elig_orgs.push_back(*curorg);
}
//Now elig_orgs should be an ordered list of mature organisms
//Special case: if it's empty, then just include all the organisms (age doesn't matter in this case) (Ken)
if (elig_orgs.size()==0) {
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
elig_orgs.push_back(*curorg);
}
}
//std::cout<<"Eligible orgs: "<<elig_orgs.size()<<std::endl;
//Now elig_orgs is guaranteed to contain either an ordered list of mature orgs or all the orgs (Ken)
//We may also want to check to see if we are getting pools of >1 organism (to make sure our survival_thresh is sensible) (Ken)
//Only choose from among the top ranked orgs
poolsize=(elig_orgs.size() - 1) * NEAT::survival_thresh;
//poolsize=(organisms.size()-1)*.9;
//Compute total fitness of species for a roulette wheel
//Note: You don't get much advantage from a roulette here
// because the size of a species is relatively small.
// But you can use it by using the roulette code here
for(curorg=elig_orgs.begin();curorg!=elig_orgs.end();++curorg) {
total_fitness+=(*curorg)->fitness;
}
//In reproducing only one offspring, the champ shouldn't matter
//thechamp=(*(organisms.begin()));
//Create one offspring for the Species
mut_struct_baby=false;
mate_baby=false;
outside=false;
//First, decide whether to mate or mutate
//If there is only one organism in the pool, then always mutate
if ((randfloat()<NEAT::mutate_only_prob)||
poolsize == 0) {
//Choose the random parent
//RANDOM PARENT CHOOSER
orgnum=randint(0,poolsize);
curorg=elig_orgs.begin();
for(orgcount=0;orgcount<orgnum;orgcount++)
++curorg;
////Roulette Wheel
//marble=randfloat()*total_fitness;
//curorg=elig_orgs.begin();
//spin=(*curorg)->fitness;
//while(spin<marble) {
// ++curorg;
//Keep the wheel spinning
// spin+=(*curorg)->fitness;
//}
//Finished roulette
mom=(*curorg);
new_genome=(mom->gnome)->duplicate(count);
//Do the mutation depending on probabilities of
//various mutations
if (randfloat()<NEAT::mutate_add_node_prob) {
//cout<<"mutate add node"<<endl;
new_genome->mutate_add_node(pop->innovations,pop->cur_node_id,pop->cur_innov_num);
mut_struct_baby=true;
}
else if (randfloat()<NEAT::mutate_add_link_prob) {
//cout<<"mutate add link"<<endl;
net_analogue=new_genome->genesis(generation);
new_genome->mutate_add_link(pop->innovations,pop->cur_innov_num,NEAT::newlink_tries);
delete net_analogue;
mut_struct_baby=true;
}
//NOTE: A link CANNOT be added directly after a node was added because the phenotype
// will not be appropriately altered to reflect the change
else {
//If we didn't do a structural mutation, we do the other kinds
if (randfloat()<NEAT::mutate_random_trait_prob) {
//cout<<"mutate random trait"<<endl;
new_genome->mutate_random_trait();
}
if (randfloat()<NEAT::mutate_link_trait_prob) {
//cout<<"mutate_link_trait"<<endl;
new_genome->mutate_link_trait(1);
}
if (randfloat()<NEAT::mutate_node_trait_prob) {
//cout<<"mutate_node_trait"<<endl;
new_genome->mutate_node_trait(1);
}
if (randfloat()<NEAT::mutate_link_weights_prob) {
//cout<<"mutate_link_weights"<<endl;
new_genome->mutate_link_weights(mut_power,1.0,GAUSSIAN);
}
if (randfloat()<NEAT::mutate_toggle_enable_prob) {
//cout<<"mutate toggle enable"<<endl;
new_genome->mutate_toggle_enable(1);
}
if (randfloat()<NEAT::mutate_gene_reenable_prob) {
//cout<<"mutate gene reenable"<<endl;
new_genome->mutate_gene_reenable();
}
}
baby=new Organism(0.0,new_genome,generation);
}
//Otherwise we should mate
else {
//Choose the random mom
orgnum=randint(0,poolsize);
curorg=elig_orgs.begin();
for(orgcount=0;orgcount<orgnum;orgcount++)
++curorg;
////Roulette Wheel
//marble=randfloat()*total_fitness;
//curorg=elig_orgs.begin();
//spin=(*curorg)->fitness;
//while(spin<marble) {
// ++curorg;
//Keep the wheel spinning
// spin+=(*curorg)->fitness;
//}
//Finished roulette
mom=(*curorg);
//Choose random dad
if ((randfloat()>NEAT::interspecies_mate_rate)) {
//Mate within Species
orgnum=randint(0,poolsize);
curorg=elig_orgs.begin();
for(orgcount=0;orgcount<orgnum;orgcount++)
++curorg;
////Use a roulette wheel
//marble=randfloat()*total_fitness;
//curorg=elig_orgs.begin();
//spin=(*curorg)->fitness;
//while(spin<marble) {
// ++curorg;
//Keep the wheel spinning
// spin+=(*curorg)->fitness;
//}
////Finished roulette
dad=(*curorg);
}
else {
//Mate outside Species
randspecies=this;
//Select a random species
giveup=0; //Give up if you cant find a different Species
while((randspecies==this)&&(giveup<5)) {
//This old way just chose any old species
//randspeciesnum=randint(0,(pop->species).size()-1);
//Choose a random species tending towards better species
randmult=gaussrand()/4;
if (randmult>1.0) randmult=1.0;
//This tends to select better species
randspeciesnum=(int) floor((randmult*(sorted_species.size()-1.0))+0.5);
cursp=(sorted_species.begin());
for(spcount=0;spcount<randspeciesnum;spcount++)
++cursp;
randspecies=(*cursp);
++giveup;
}
//OLD WAY: Choose a random dad from the random species
//Select a random dad from the random Species
//NOTE: It is possible that a mating could take place
// here between the mom and a baby from the NEW
// generation in some other Species
//orgnum=randint(0,(randspecies->organisms).size()-1);
//curorg=(randspecies->organisms).begin();
//for(orgcount=0;orgcount<orgnum;orgcount++)
// ++curorg;
//dad=(*curorg);
//New way: Make dad be a champ from the random species
dad=(*((randspecies->organisms).begin()));
outside=true;
}
//Perform mating based on probabilities of differrent mating types
if (randfloat()<NEAT::mate_multipoint_prob) {
new_genome=(mom->gnome)->mate_multipoint(dad->gnome,count,mom->orig_fitness,dad->orig_fitness,outside);
}
else if (randfloat()<(NEAT::mate_multipoint_avg_prob/(NEAT::mate_multipoint_avg_prob+NEAT::mate_singlepoint_prob))) {
new_genome=(mom->gnome)->mate_multipoint_avg(dad->gnome,count,mom->orig_fitness,dad->orig_fitness,outside);
}
else {
new_genome=(mom->gnome)->mate_singlepoint(dad->gnome,count);
}
mate_baby=true;
//Determine whether to mutate the baby's Genome
//This is done randomly or if the mom and dad are the same organism
if ((randfloat()>NEAT::mate_only_prob)||
((dad->gnome)->genome_id==(mom->gnome)->genome_id)||
(((dad->gnome)->compatibility(mom->gnome))==0.0))
{
//Do the mutation depending on probabilities of
//various mutations
if (randfloat()<NEAT::mutate_add_node_prob) {
new_genome->mutate_add_node(pop->innovations,pop->cur_node_id,pop->cur_innov_num);
// cout<<"mutate_add_node: "<<new_genome<<endl;
mut_struct_baby=true;
}
else if (randfloat()<NEAT::mutate_add_link_prob) {
net_analogue=new_genome->genesis(generation);
new_genome->mutate_add_link(pop->innovations,pop->cur_innov_num,NEAT::newlink_tries);
delete net_analogue;
//cout<<"mutate_add_link: "<<new_genome<<endl;
mut_struct_baby=true;
}
else {
//Only do other mutations when not doing strurctural mutations
if (randfloat()<NEAT::mutate_random_trait_prob) {
new_genome->mutate_random_trait();
//cout<<"..mutate random trait: "<<new_genome<<endl;
}
if (randfloat()<NEAT::mutate_link_trait_prob) {
new_genome->mutate_link_trait(1);
//cout<<"..mutate link trait: "<<new_genome<<endl;
}
if (randfloat()<NEAT::mutate_node_trait_prob) {
new_genome->mutate_node_trait(1);
//cout<<"mutate_node_trait: "<<new_genome<<endl;
}
if (randfloat()<NEAT::mutate_link_weights_prob) {
new_genome->mutate_link_weights(mut_power,1.0,GAUSSIAN);
//cout<<"mutate_link_weights: "<<new_genome<<endl;
}
if (randfloat()<NEAT::mutate_toggle_enable_prob) {
new_genome->mutate_toggle_enable(1);
//cout<<"mutate_toggle_enable: "<<new_genome<<endl;
}
if (randfloat()<NEAT::mutate_gene_reenable_prob) {
new_genome->mutate_gene_reenable();
//cout<<"mutate_gene_reenable: "<<new_genome<<endl;
}
}
//Create the baby
baby=new Organism(0.0,new_genome,generation);
}
else {
//Create the baby without mutating first
baby=new Organism(0.0,new_genome,generation);
}
}
//Add the baby to its proper Species
//If it doesn't fit a Species, create a new one
baby->mut_struct_baby=mut_struct_baby;
baby->mate_baby=mate_baby;
curspecies=(pop->species).begin();
if (curspecies==(pop->species).end()){
//Create the first species
newspecies=new Species(++(pop->last_species),true);
(pop->species).push_back(newspecies);
newspecies->add_Organism(baby); //Add the baby
baby->species=newspecies; //Point the baby to its species
}
else {
comporg=(*curspecies)->first();
found=false;
// Testing out what happens when speciation is disabled
//found = true;
//(*curspecies)->add_Organism(baby);
//baby->species = (*curspecies);
while((curspecies!=(pop->species).end()) && (!found))
{
if (comporg==0) {
//Keep searching for a matching species
++curspecies;
if (curspecies!=(pop->species).end())
comporg=(*curspecies)->first();
}
else if (((baby->gnome)->compatibility(comporg->gnome))<NEAT::compat_threshold) {
//Found compatible species, so add this organism to it
(*curspecies)->add_Organism(baby);
baby->species=(*curspecies); //Point organism to its species
found=true; //Note the search is over
}
else {
//Keep searching for a matching species
++curspecies;
if (curspecies!=(pop->species).end())
comporg=(*curspecies)->first();
}
}
//If we didn't find a match, create a new species
if (found==false) {
newspecies=new Species(++(pop->last_species),true);
(pop->species).push_back(newspecies);
newspecies->add_Organism(baby); //Add the baby
baby->species=newspecies; //Point baby to its species
}
} //end else
//Put the baby also in the master organism list
(pop->organisms).push_back(baby);
return baby; //Return a pointer to the baby
}
bool Species::add_Organism(Organism *o){
organisms.push_back(o);
return true;
}
Organism *Species::get_champ() {
double champ_fitness=-1.0;
Organism *thechamp;
std::vector<Organism*>::iterator curorg;
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
//TODO: Remove DEBUG code
//cout<<"searching for champ...looking at org "<<(*curorg)->gnome->genome_id<<" fitness: "<<(*curorg)->fitness<<endl;
if (((*curorg)->fitness)>champ_fitness) {
thechamp=(*curorg);
champ_fitness=thechamp->fitness;
}
}
//cout<<"returning champ #"<<thechamp->gnome->genome_id<<endl;
return thechamp;
}
bool Species::remove_org(Organism *org) {
std::vector<Organism*>::iterator curorg;
curorg=organisms.begin();
while((curorg!=organisms.end())&&
((*curorg)!=org))
++curorg;
if (curorg==organisms.end()) {
//cout<<"ALERT: Attempt to remove nonexistent Organism from Species"<<endl;
return false;
}
else {
organisms.erase(curorg);
return true;
}
}
Organism *Species::first() {
return *(organisms.begin());
}
/*
bool Species::print_to_file(std::ostream &outFile) {
std::vector<Organism*>::iterator curorg;
//Print a comment on the Species info
//outFile<<endl<<"/* Species #"<<id<<" : (Size "<<organisms.size()<<") (AF "<<ave_fitness<<") (Age "<<age<<") *///"<<endl<<endl;
//char tempbuf[1024];
//sprintf(tempbuf, sizeof(tempbuf), "/* Species #%d : (Size %d) (AF %f) (Age %d) */\n\n", id, organisms.size(), average_est, age);
//sprintf(tempbuf, sizeof(tempbuf), "/* Species #%d : (Size %d) (AF %f) (Age %d) */\n\n", id, organisms.size(), ave_fitness, age);
//outFile.write(strlen(tempbuf), tempbuf);
//Show user what's going on
//cout<<endl<<"/* Species #"<<id<<" : (Size "<<organisms.size()<<") (AF "<<ave_fitness<<") (Age "<<age<<") */"<<endl;
//Print all the Organisms' Genomes to the outFile
//for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
//Put the fitness for each organism in a comment
//outFile<<endl<<"/* Organism #"<<((*curorg)->gnome)->genome_id<<" Fitness: "<<(*curorg)->fitness<<" Error: "<<(*curorg)->error<<" */"<<endl;
// char tempbuf2[1024];
// sprintf(tempbuf2, sizeof(tempbuf2), "/* Organism #%d Fitness: %f Error: %f */\n", ((*curorg)->gnome)->genome_id, (*curorg)->fitness, (*curorg)->error);
// outFile.write(strlen(tempbuf2), tempbuf2);
//If it is a winner, mark it in a comment
// if ((*curorg)->winner) {
// char tempbuf3[1024];
// sprintf(tempbuf3, sizeof(tempbuf3), "/* ##------$ WINNER %d SPECIES #%d $------## */\n", ((*curorg)->gnome)->genome_id, id);
//outFile<<"/* ##------$ WINNER "<<((*curorg)->gnome)->genome_id<<" SPECIES #"<<id<<" $------## */"<<endl;
// }
// ((*curorg)->gnome)->print_to_file(outFile);
//We can confirm by writing the genome #'s to the screen
//cout<<((*curorg)->gnome)->genome_id<<endl;
//}
//return true;
//}*/
//Print Species to a file outFile
bool Species::print_to_file(std::ofstream &outFile) {
std::vector<Organism*>::iterator curorg;
//Print a comment on the Species info
outFile<<std::endl<<"/* Species #"<<id<<" : (Size "<<organisms.size()<<") (AF "<<ave_fitness<<") (Age "<<age<<") */"<<std::endl<<std::endl;
//Show user what's going on
std::cout<<std::endl<<"/* Species #"<<id<<" : (Size "<<organisms.size()<<") (AF "<<ave_fitness<<") (Age "<<age<<") */"<<std::endl;
//Print all the Organisms' Genomes to the outFile
for(curorg=organisms.begin();curorg!=organisms.end();++curorg)
{
//Put the fitness for each organism in a comment
outFile<<std::endl<<"/* Organism #"<<((*curorg)->gnome)->genome_id<<" Fitness: "<<(*curorg)->fitness<<" Error: "<<(*curorg)->error<<" */"<<std::endl;
//If it is a winner, mark it in a comment
if ((*curorg)->winner)
outFile<<"/* ##------$ WINNER "<<((*curorg)->gnome)->genome_id<<" SPECIES #"<<id<<" $------## */"<<std::endl;
((*curorg)->gnome)->print_to_file(outFile);
//We can confirm by writing the genome #'s to the screen
//std::cout<<((*curorg)->gnome)->genome_id<<std::endl;
}
return true;
}
bool Species::print_to_file(std::ostream &outFile) {
std::vector<Organism*>::iterator curorg;
//Print a comment on the Species info
//outFile<<std::endl<<"/* Species #"<<id<<" : (Size "<<organisms.size()<<") (AF "<<ave_fitness<<") (Age "<<age<<") */"<<std::endl<<std::endl;
char tempbuf[1024];
sprintf(tempbuf,"/* Species #%d : (Size %d) (AF %f) (Age %d) */\n\n", id, organisms.size(), average_est, age);
//sprintf(tempbuf, "/* Species #%d : (Size %d) (AF %f) (Age %d) */\n\n", id, organisms.size(), ave_fitness, age);
outFile << tempbuf;
//Show user what's going on
//std::cout<<std::endl<<"/* Species #"<<id<<" : (Size "<<organisms.size()<<") (AF "<<ave_fitness<<") (Age "<<age<<") */"<<std::endl;
//Print all the Organisms' Genomes to the outFile
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
//Put the fitness for each organism in a comment
//outFile<<std::endl<<"/* Organism #"<<((*curorg)->gnome)->genome_id<<" Fitness: "<<(*curorg)->fitness<<" Error: "<<(*curorg)->error<<" */"<<std::endl;
char tempbuf2[1024];
sprintf(tempbuf2, "/* Organism #%d Fitness: %f Time: %d */\n", ((*curorg)->gnome)->genome_id, (*curorg)->fitness, (*curorg)->time_alive);
outFile << tempbuf2;
//If it is a winner, mark it in a comment
if ((*curorg)->winner) {
char tempbuf3[1024];
sprintf(tempbuf3, "/* ##------$ WINNER %d SPECIES #%d $------## */\n", ((*curorg)->gnome)->genome_id, id);
//outFile<<"/* ##------$ WINNER "<<((*curorg)->gnome)->genome_id<<" SPECIES #"<<id<<" $------## */"<<std::endl;
}
((*curorg)->gnome)->print_to_file(outFile);
//We can confirm by writing the genome #'s to the screen
//std::cout<<((*curorg)->gnome)->genome_id<<std::endl;
}
char tempbuf4[1024];
sprintf(tempbuf4, "\n\n");
outFile << tempbuf4;
return true;
}
//Prints the champions of each species to files
//starting with directory_prefix
//The file name are as follows: [prefix]g[generation_num]cs[species_num]
//Thus, they can be indexed by generation or species
//bool Population::print_species_champs_tofiles(char *directory_prefix, int generation) {
//
//ostrstream *fnamebuf; //File for output
//std::vector<Species*>::iterator curspecies;
//Organism *champ;
//int pause;
//
//std::cout<<generation<<std::endl;
//std::cout<<"Printing species champs to file"<<std::endl;
////cin>>pause;
//
////Step through the Species and print their champs to files
//for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
//
//std::cout<<"Printing species "<<(*curspecies)->id<<" champ to file"<<std::endl;
//
////cin>>pause;
//
////Get the champ of this species
//champ=(*curspecies)->get_champ();
//
////Revise the file name
//fnamebuf=new ostrstream();
//(*fnamebuf)<<directory_prefix<<"g"<<generation<<"cs"<<(*curspecies)->id<<ends; //needs end marker
//
////Print to file using organism printing (includes comments)
//champ->print_to_file(fnamebuf->str());
//
////Reset the name
//fnamebuf->clear();
//delete fnamebuf;
//}
//return true;
//}
void Species::adjust_fitness() {
std::vector<Organism*>::iterator curorg;
int num_parents;
int count;
int age_debt;
//std::cout<<"Species "<<id<<" last improved "<<(age-age_of_last_improvement)<<" steps ago when it moved up to "<<max_fitness_ever<<std::endl;
age_debt=(age-age_of_last_improvement+1)-NEAT::dropoff_age;
if (age_debt==0) age_debt=1;
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
//Remember the original fitness before it gets modified
(*curorg)->orig_fitness=(*curorg)->fitness;
//Make fitness decrease after a stagnation point dropoff_age
//Added an if to keep species pristine until the dropoff point
//obliterate is used in competitive coevolution to mark stagnation
//by obliterating the worst species over a certain age
if ((age_debt>=1)||obliterate) {
//Possible graded dropoff
//((*curorg)->fitness)=((*curorg)->fitness)*(-atan(age_debt));
//Extreme penalty for a long period of stagnation (divide fitness by 100)
((*curorg)->fitness)=((*curorg)->fitness)*0.01;
//std::cout<<"OBLITERATE Species "<<id<<" of age "<<age<<std::endl;
//std::cout<<"dropped fitness to "<<((*curorg)->fitness)<<std::endl;
}
//Give a fitness boost up to some young age (niching)
//The age_significance parameter is a system parameter
// if it is 1, then young species get no fitness boost
if (age<=10) ((*curorg)->fitness)=((*curorg)->fitness)*NEAT::age_significance;
//Do not allow negative fitness
if (((*curorg)->fitness)<0.0) (*curorg)->fitness=0.0001;
//Share fitness with the species
(*curorg)->fitness=((*curorg)->fitness)/(organisms.size());
}
//Sort the population and mark for death those after survival_thresh*pop_size
//organisms.qsort(order_orgs);
std::sort(organisms.begin(), organisms.end(), order_orgs);
//Update age_of_last_improvement here
if (((*(organisms.begin()))->orig_fitness)>
max_fitness_ever) {
age_of_last_improvement=age;
max_fitness_ever=((*(organisms.begin()))->orig_fitness);
}
//Decide how many get to reproduce based on survival_thresh*pop_size
//Adding 1.0 ensures that at least one will survive
num_parents=(int) floor((NEAT::survival_thresh*((double) organisms.size()))+1.0);
//Mark for death those who are ranked too low to be parents
curorg=organisms.begin();
(*curorg)->champion=true; //Mark the champ as such
for(count=1;count<=num_parents;count++) {
if (curorg!=organisms.end())
++curorg;
}
while(curorg!=organisms.end()) {
(*curorg)->eliminate=true; //Mark for elimination
//std::std::cout<<"marked org # "<<(*curorg)->gnome->genome_id<<" fitness = "<<(*curorg)->fitness<<std::std::endl;
++curorg;
}
}
double Species::compute_average_fitness() {
std::vector<Organism*>::iterator curorg;
double total=0.0;
//int pause; //DEBUG: Remove
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
total+=(*curorg)->fitness;
//std::cout<<"new total "<<total<<std::endl; //DEBUG: Remove
}
ave_fitness=total/(organisms.size());
//DEBUG: Remove
//std::cout<<"average of "<<(organisms.size())<<" organisms: "<<ave_fitness<<std::endl;
//cin>>pause;
return ave_fitness;
}
double Species::compute_max_fitness() {
double max=0.0;
std::vector<Organism*>::iterator curorg;
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
if (((*curorg)->fitness)>max)
max=(*curorg)->fitness;
}
max_fitness=max;
return max;
}
double Species::count_offspring(double skim) {
std::vector<Organism*>::iterator curorg;
int e_o_intpart; //The floor of an organism's expected offspring
double e_o_fracpart; //Expected offspring fractional part
double skim_intpart; //The whole offspring in the skim
expected_offspring=0;
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
e_o_intpart=(int) floor((*curorg)->expected_offspring);
e_o_fracpart=fmod((*curorg)->expected_offspring,1.0);
expected_offspring+=e_o_intpart;
//Skim off the fractional offspring
skim+=e_o_fracpart;
//NOTE: Some precision is lost by computer
// Must be remedied later
if (skim>1.0) {
skim_intpart=floor(skim);
expected_offspring+=(int) skim_intpart;
skim-=skim_intpart;
}
}
return skim;
}
bool Species::reproduce(int generation, Population *pop,std::vector<Species*> &sorted_species) {
int count;
std::vector<Organism*>::iterator curorg;
int poolsize; //The number of Organisms in the old generation
int orgnum; //Random variable
int orgcount;
Organism *mom; //Parent Organisms
Organism *dad;
Organism *baby; //The new Organism
Genome *new_genome; //For holding baby's genes
std::vector<Species*>::iterator curspecies; //For adding baby
Species *newspecies; //For babies in new Species
Organism *comporg; //For Species determination through comparison
Species *randspecies; //For mating outside the Species
double randmult;
int randspeciesnum;
int spcount;
std::vector<Species*>::iterator cursp;
Network *net_analogue; //For adding link to test for recurrency
int pause;
bool outside;
bool found; //When a Species is found
bool champ_done=false; //Flag the preservation of the champion
Organism *thechamp;
int giveup; //For giving up finding a mate outside the species
bool mut_struct_baby;
bool mate_baby;
//The weight mutation power is species specific depending on its age
double mut_power=NEAT::weight_mut_power;
//Roulette wheel variables
double total_fitness=0.0;
double marble; //The marble will have a number between 0 and total_fitness
double spin; //0Fitness total while the wheel is spinning
//Compute total fitness of species for a roulette wheel
//Note: You don't get much advantage from a roulette here
// because the size of a species is relatively small.
// But you can use it by using the roulette code here
//for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
// total_fitness+=(*curorg)->fitness;
//}
//Check for a mistake
if ((expected_offspring>0)&&
(organisms.size()==0)) {
// std::cout<<"ERROR: ATTEMPT TO REPRODUCE OUT OF EMPTY SPECIES"<<std::endl;
return false;
}
poolsize=organisms.size()-1;
thechamp=(*(organisms.begin()));
//Create the designated number of offspring for the Species
//one at a time
for (count=0;count<expected_offspring;count++) {
mut_struct_baby=false;
mate_baby=false;
outside=false;
//Debug Trap
if (expected_offspring>NEAT::pop_size) {
// std::cout<<"ALERT: EXPECTED OFFSPRING = "<<expected_offspring<<std::endl;
// cin>>pause;
}
//If we have a super_champ (Population champion), finish off some special clones
if ((thechamp->super_champ_offspring) > 0) {
mom=thechamp;
new_genome=(mom->gnome)->duplicate(count);
if ((thechamp->super_champ_offspring) == 1) {
}
//Most superchamp offspring will have their connection weights mutated only
//The last offspring will be an exact duplicate of this super_champ
//Note: Superchamp offspring only occur with stolen babies!
// Settings used for published experiments did not use this
if ((thechamp->super_champ_offspring) > 1) {
if ((randfloat()<0.8)||
(NEAT::mutate_add_link_prob==0.0))
//ABOVE LINE IS FOR:
//Make sure no links get added when the system has link adding disabled
new_genome->mutate_link_weights(mut_power,1.0,GAUSSIAN);
else {
//Sometimes we add a link to a superchamp
net_analogue=new_genome->genesis(generation);
new_genome->mutate_add_link(pop->innovations,pop->cur_innov_num,NEAT::newlink_tries);
delete net_analogue;
mut_struct_baby=true;
}
}
baby=new Organism(0.0,new_genome,generation);
if ((thechamp->super_champ_offspring) == 1) {
if (thechamp->pop_champ) {
//std::cout<<"The new org baby's genome is "<<baby->gnome<<std::endl;
baby->pop_champ_child=true;
baby->high_fit=mom->orig_fitness;
}
}
thechamp->super_champ_offspring--;
}
//If we have a Species champion, just clone it
else if ((!champ_done)&&
(expected_offspring>5)) {
mom=thechamp; //Mom is the champ
new_genome=(mom->gnome)->duplicate(count);
baby=new Organism(0.0,new_genome,generation); //Baby is just like mommy
champ_done=true;
}
//First, decide whether to mate or mutate
//If there is only one organism in the pool, then always mutate
else if ((randfloat()<NEAT::mutate_only_prob)||
poolsize== 0) {
//Choose the random parent
//RANDOM PARENT CHOOSER
orgnum=randint(0,poolsize);
curorg=organisms.begin();
for(orgcount=0;orgcount<orgnum;orgcount++)
++curorg;
////Roulette Wheel
//marble=randfloat()*total_fitness;
//curorg=organisms.begin();
//spin=(*curorg)->fitness;
//while(spin<marble) {
//++curorg;
////Keep the wheel spinning
//spin+=(*curorg)->fitness;
//}
////Finished roulette
//
mom=(*curorg);
new_genome=(mom->gnome)->duplicate(count);
//Do the mutation depending on probabilities of
//various mutations
if (randfloat()<NEAT::mutate_add_node_prob) {
//std::cout<<"mutate add node"<<std::endl;
new_genome->mutate_add_node(pop->innovations,pop->cur_node_id,pop->cur_innov_num);
mut_struct_baby=true;
}
else if (randfloat()<NEAT::mutate_add_link_prob) {
//std::cout<<"mutate add link"<<std::endl;
net_analogue=new_genome->genesis(generation);
new_genome->mutate_add_link(pop->innovations,pop->cur_innov_num,NEAT::newlink_tries);
delete net_analogue;
mut_struct_baby=true;
}
//NOTE: A link CANNOT be added directly after a node was added because the phenotype
// will not be appropriately altered to reflect the change
else {
//If we didn't do a structural mutation, we do the other kinds
if (randfloat()<NEAT::mutate_random_trait_prob) {
//std::cout<<"mutate random trait"<<std::endl;
new_genome->mutate_random_trait();
}
if (randfloat()<NEAT::mutate_link_trait_prob) {
//std::cout<<"mutate_link_trait"<<std::endl;
new_genome->mutate_link_trait(1);
}
if (randfloat()<NEAT::mutate_node_trait_prob) {
//std::cout<<"mutate_node_trait"<<std::endl;
new_genome->mutate_node_trait(1);
}
if (randfloat()<NEAT::mutate_link_weights_prob) {
//std::cout<<"mutate_link_weights"<<std::endl;
new_genome->mutate_link_weights(mut_power,1.0,GAUSSIAN);
}
if (randfloat()<NEAT::mutate_toggle_enable_prob) {
//std::cout<<"mutate toggle enable"<<std::endl;
new_genome->mutate_toggle_enable(1);
}
if (randfloat()<NEAT::mutate_gene_reenable_prob) {
//std::cout<<"mutate gene reenable"<<std::endl;
new_genome->mutate_gene_reenable();
}
}
baby=new Organism(0.0,new_genome,generation);
}
//Otherwise we should mate
else {
//Choose the random mom
orgnum=randint(0,poolsize);
curorg=organisms.begin();
for(orgcount=0;orgcount<orgnum;orgcount++)
++curorg;
////Roulette Wheel
//marble=randfloat()*total_fitness;
//curorg=organisms.begin();
//spin=(*curorg)->fitness;
//while(spin<marble) {
//++curorg;
////Keep the wheel spinning
//spin+=(*curorg)->fitness;
//}
////Finished roulette
//
mom=(*curorg);
//Choose random dad
if ((randfloat()>NEAT::interspecies_mate_rate)) {
//Mate within Species
orgnum=randint(0,poolsize);
curorg=organisms.begin();
for(orgcount=0;orgcount<orgnum;orgcount++)
++curorg;
////Use a roulette wheel
//marble=randfloat()*total_fitness;
//curorg=organisms.begin();
//spin=(*curorg)->fitness;
//while(spin<marble) {
//++curorg;
//}
////Keep the wheel spinning
//spin+=(*curorg)->fitness;
//}
////Finished roulette
//
dad=(*curorg);
}
else {
//Mate outside Species
randspecies=this;
//Select a random species
giveup=0; //Give up if you cant find a different Species
while((randspecies==this)&&(giveup<5)) {
//This old way just chose any old species
//randspeciesnum=randint(0,(pop->species).size()-1);
//Choose a random species tending towards better species
randmult=gaussrand()/4;
if (randmult>1.0) randmult=1.0;
//This tends to select better species
randspeciesnum=(int) floor((randmult*(sorted_species.size()-1.0))+0.5);
cursp=(sorted_species.begin());
for(spcount=0;spcount<randspeciesnum;spcount++)
++cursp;
randspecies=(*cursp);
++giveup;
}
//OLD WAY: Choose a random dad from the random species
//Select a random dad from the random Species
//NOTE: It is possible that a mating could take place
// here between the mom and a baby from the NEW
// generation in some other Species
//orgnum=randint(0,(randspecies->organisms).size()-1);
//curorg=(randspecies->organisms).begin();
//for(orgcount=0;orgcount<orgnum;orgcount++)
// ++curorg;
//dad=(*curorg);
//New way: Make dad be a champ from the random species
dad=(*((randspecies->organisms).begin()));
outside=true;
}
//Perform mating based on probabilities of differrent mating types
if (randfloat()<NEAT::mate_multipoint_prob) {
new_genome=(mom->gnome)->mate_multipoint(dad->gnome,count,mom->orig_fitness,dad->orig_fitness,outside);
}
else if (randfloat()<(NEAT::mate_multipoint_avg_prob/(NEAT::mate_multipoint_avg_prob+NEAT::mate_singlepoint_prob))) {
new_genome=(mom->gnome)->mate_multipoint_avg(dad->gnome,count,mom->orig_fitness,dad->orig_fitness,outside);
}
else {
new_genome=(mom->gnome)->mate_singlepoint(dad->gnome,count);
}
mate_baby=true;
//Determine whether to mutate the baby's Genome
//This is done randomly or if the mom and dad are the same organism
if ((randfloat()>NEAT::mate_only_prob)||
((dad->gnome)->genome_id==(mom->gnome)->genome_id)||
(((dad->gnome)->compatibility(mom->gnome))==0.0))
{
//Do the mutation depending on probabilities of
//various mutations
if (randfloat()<NEAT::mutate_add_node_prob) {
new_genome->mutate_add_node(pop->innovations,pop->cur_node_id,pop->cur_innov_num);
// std::cout<<"mutate_add_node: "<<new_genome<<std::endl;
mut_struct_baby=true;
}
else if (randfloat()<NEAT::mutate_add_link_prob) {
net_analogue=new_genome->genesis(generation);
new_genome->mutate_add_link(pop->innovations,pop->cur_innov_num,NEAT::newlink_tries);
delete net_analogue;
//std::cout<<"mutate_add_link: "<<new_genome<<std::endl;
mut_struct_baby=true;
}
else {
//Only do other mutations when not doing sturctural mutations
if (randfloat()<NEAT::mutate_random_trait_prob) {
new_genome->mutate_random_trait();
//std::cout<<"..mutate random trait: "<<new_genome<<std::endl;
}
if (randfloat()<NEAT::mutate_link_trait_prob) {
new_genome->mutate_link_trait(1);
//std::cout<<"..mutate link trait: "<<new_genome<<std::endl;
}
if (randfloat()<NEAT::mutate_node_trait_prob) {
new_genome->mutate_node_trait(1);
//std::cout<<"mutate_node_trait: "<<new_genome<<std::endl;
}
if (randfloat()<NEAT::mutate_link_weights_prob) {
new_genome->mutate_link_weights(mut_power,1.0,GAUSSIAN);
//std::cout<<"mutate_link_weights: "<<new_genome<<std::endl;
}
if (randfloat()<NEAT::mutate_toggle_enable_prob) {
new_genome->mutate_toggle_enable(1);
//std::cout<<"mutate_toggle_enable: "<<new_genome<<std::endl;
}
if (randfloat()<NEAT::mutate_gene_reenable_prob) {
new_genome->mutate_gene_reenable();
//std::cout<<"mutate_gene_reenable: "<<new_genome<<std::endl;
}
}
//Create the baby
baby=new Organism(0.0,new_genome,generation);
}
else {
//Create the baby without mutating first
baby=new Organism(0.0,new_genome,generation);
}
}
//Add the baby to its proper Species
//If it doesn't fit a Species, create a new one
baby->mut_struct_baby=mut_struct_baby;
baby->mate_baby=mate_baby;
curspecies=(pop->species).begin();
if (curspecies==(pop->species).end()){
//Create the first species
newspecies=new Species(++(pop->last_species),true);
(pop->species).push_back(newspecies);
newspecies->add_Organism(baby); //Add the baby
baby->species=newspecies; //Point the baby to its species
}
else {
comporg=(*curspecies)->first();
found=false;
while((curspecies!=(pop->species).end())&&
(!found)) {
if (comporg==0) {
//Keep searching for a matching species
++curspecies;
if (curspecies!=(pop->species).end())
comporg=(*curspecies)->first();
}
else if (((baby->gnome)->compatibility(comporg->gnome))<NEAT::compat_threshold) {
//Found compatible species, so add this organism to it
(*curspecies)->add_Organism(baby);
baby->species=(*curspecies); //Point organism to its species
found=true; //Note the search is over
}
else {
//Keep searching for a matching species
++curspecies;
if (curspecies!=(pop->species).end())
comporg=(*curspecies)->first();
}
}
//If we didn't find a match, create a new species
if (found==false) {
newspecies=new Species(++(pop->last_species),true);
//std::std::cout<<"CREATING NEW SPECIES "<<pop->last_species<<std::std::endl;
(pop->species).push_back(newspecies);
newspecies->add_Organism(baby); //Add the baby
baby->species=newspecies; //Point baby to its species
}
} //end else
}
return true;
}
bool NEAT::order_species(Species *x, Species *y) {
//std::cout<<"Comparing "<<((*((x->organisms).begin()))->orig_fitness)<<" and "<<((*((y->organisms).begin()))->orig_fitness)<<": "<<(((*((x->organisms).begin()))->orig_fitness) > ((*((y->organisms).begin()))->orig_fitness))<<std::endl;
return (((*((x->organisms).begin()))->orig_fitness) > ((*((y->organisms).begin()))->orig_fitness));
}
bool NEAT::order_new_species(Species *x, Species *y) {
return (x->compute_max_fitness() > y->compute_max_fitness());
}