Population
A Population is a group of Organisms including their species
////////////////////////////////////////////////////////////////////////////////////////
#ifndef _POPULATION_H_
#define _POPULATION_H_
#include <cmath>
#include <vector>
#include "innovation.h"
#include "genome.h"
#include "species.h"
#include "organism.h"
namespace NEAT {
class Species;
class Organism;
// ---------------------------------------------
// POPULATION CLASS:
// A Population is a group of Organisms
// including their species
// ---------------------------------------------
class Population {
protected:
// A Population can be spawned off of a single Genome
// There will be size Genomes added to the Population
// The Population does not have to be empty to add Genomes
bool spawn(Genome *g,int size);
public:
std::vector<Organism*> organisms; //The organisms in the Population
std::vector<Species*> species; // Species in the Population. Note that the species should comprise all the genomes
// ******* Member variables used during reproduction *******
std::vector<Innovation*> innovations; // For holding the genetic innovations of the newest generation
int cur_node_id; //Current label number available
double cur_innov_num;
int last_species; //The highest species number
// ******* Fitness Statistics *******
double mean_fitness;
double variance;
double standard_deviation;
int winnergen; //An integer that when above zero tells when the first winner appeared
// ******* When do we need to delta code? *******
double highest_fitness; //Stagnation detector
int highest_last_changed; //If too high, leads to delta coding
// Separate the Organisms into species
bool speciate();
// Print Population to a file specified by a string
bool print_to_file(std::ostream& outFile);
// Print Population to a file in speciated order with comments separating each species
bool print_to_file_by_species(std::ostream& outFile);
bool print_to_file_by_species(char *filename);
// 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 print_species_champs_tofiles(char *directory_prefix,int generation);
// Run verify on all Genomes in this Population (Debugging)
bool verify();
// Turnover the population to a new generation using fitness
// The generation argument is the next generation
bool epoch(int generation);
// *** Real-time methods ***
// Places the organisms in species in order from best to worst fitness
bool rank_within_species();
// Estimates average fitness for all existing species
void estimate_all_averages();
//Reproduce only out of the pop champ
Organism* reproduce_champ(int generation);
// Probabilistically choose a species to reproduce
// Note that this method is effectively real-time fitness sharing in that the
// species will tend to produce offspring in an amount proportional
// to their average fitness, which approximates the generational
// method of producing the next generation of the species en masse
// based on its average (shared) fitness.
Species *choose_parent_species();
//Remove a species from the species list (sometimes called by remove_worst when a species becomes empty)
bool remove_species(Species *spec);
// Removes worst member of population that has been around for a minimum amount of time and returns
// a pointer to the Organism that was removed (note that the pointer will not point to anything at all,
// since the Organism it was pointing to has been deleted from memory)
Organism* remove_worst();
//Warning: rtNEAT does not behave like regular NEAT if you remove the worst probabilistically
//You really should just use "remove_worst," which removes the org with worst adjusted fitness.
Organism* remove_worst_probabilistic();
//KEN: New 2/17/04
//This method takes an Organism and reassigns what Species it belongs to
//It is meant to be used so that we can reasses where Organisms should belong
//as the speciation threshold changes.
void reassign_species(Organism *org);
//Move an Organism from one Species to another (called by reassign_species)
void switch_species(Organism *org, Species *orig_species, Species *new_species);
// Construct off of a single spawning Genome
Population(Genome *g,int size);
// Construct off of a single spawning Genome without mutation
Population(Genome *g,int size, float power);
//MSC Addition
// Construct off of a vector of genomes with a mutation rate of "power"
Population(std::vector<Genome*> genomeList, float power);
bool clone(Genome *g,int size, float power);
//// Special constructor to create a population of random topologies
//// uses Genome(int i, int o, int n,int nmax, bool r, double linkprob)
//// See the Genome constructor for the argument specifications
//Population(int size,int i,int o, int nmax, bool r, double linkprob);
// Construct off of a file of Genomes
Population(const char *filename);
// It can delete a Population in two ways:
// -delete by killing off the species
// -delete by killing off the organisms themselves (if not speciated)
// It does the latter if it sees the species list is empty
~Population();
};
} // namespace NEAT
#endif
////////////////////////////////////////////////////////////////////////////////////////
#include "population.h"
#include "organism.h"
#include <iostream>
#include <sstream>
#include <fstream>
using namespace NEAT;
extern int NEAT::time_alive_minimum;
Population::Population(Genome *g,int size) {
winnergen=0;
highest_fitness=0.0;
highest_last_changed=0;
spawn(g,size);
}
Population::Population(Genome *g,int size, float power) {
winnergen=0;
highest_fitness=0.0;
highest_last_changed=0;
clone(g, size, power);
}
//Population::Population(int size,int i,int o, int nmax, bool r, double linkprob) {
//int count;
//Genome *new_genome;
//cout<<"Making a random pop"<<endl;
//winnergen=0;
//highest_fitness=0.0;
//highest_last_changed=0;
//for(count=0;count<size;count++) {
//new_genome=new Genome(count,i,o,randint(0,nmax),nmax,r,linkprob);
//organisms.push_back(new Organism(0,new_genome,1));
//}
//cur_node_id=i+o+nmax+1;;
//cur_innov_num=(i+o+nmax)*(i+o+nmax)+1;
//cout<<"Calling speciate"<<endl;
//speciate();
//}
//MSC Addition
//Added the ability for a population to be spawned
//off of a vector of Genomes. Useful when converging.
Population::Population(std::vector<Genome*> genomeList, float power) {
winnergen=0;
highest_fitness=0.0;
highest_last_changed=0;
int count;
Genome *new_genome;
Organism *new_organism;
//Create size copies of the Genome
//Start with perturbed linkweights
for (std::vector<Genome*>::iterator iter = genomeList.begin(); iter != genomeList.end(); ++iter)
{
new_genome=(*iter);
if(power>0)
new_genome->mutate_link_weights(power,1.0,GAUSSIAN);
//new_genome->mutate_link_weights(1.0,1.0,COLDGAUSSIAN);
new_genome->randomize_traits();
new_organism=new Organism(0.0,new_genome,1);
organisms.push_back(new_organism);
}
//Keep a record of the innovation and node number we are on
cur_node_id=new_genome->get_last_node_id();
cur_innov_num=new_genome->get_last_gene_innovnum();
//Separate the new Population into species
speciate();
}
Population::Population(const char *filename) {
char curword[128]; //max word size of 128 characters
char curline[1024]; //max line size of 1024 characters
char delimiters[] = " \n";
Genome *new_genome;
winnergen=0;
highest_fitness=0.0;
highest_last_changed=0;
cur_node_id=0;
cur_innov_num=0.0;
int curwordnum = 0;
std::ifstream iFile(filename);
if (!iFile) {
printf("Can't open genomes file for input");
return;
}
else {
bool md = false;
char metadata[128];
//Loop until file is finished, parsing each line
while (!iFile.eof())
{
iFile.getline(curline, sizeof(curline));
std::stringstream ss(curline);
//strcpy(curword, NEAT::getUnit(curline, 0, delimiters));
ss >> curword;
//std::cout << curline << std::endl;
//Check for next
if (strcmp(curword,"genomestart")==0)
{
//strcpy(curword, NEAT::getUnit(curline, 1, delimiters));
//int idcheck = atoi(curword);
int idcheck;
ss >> idcheck;
// If there isn't metadata, set metadata to ""
if(md == false) {
strcpy(metadata, "");
}
md = false;
new_genome=new Genome(idcheck,iFile);
organisms.push_back(new Organism(0,new_genome,1, metadata));
if (cur_node_id<(new_genome->get_last_node_id()))
cur_node_id=new_genome->get_last_node_id();
if (cur_innov_num<(new_genome->get_last_gene_innovnum()))
cur_innov_num=new_genome->get_last_gene_innovnum();
}
else if (strcmp(curword,"/*")==0)
{
// New metadata possibly, so clear out the metadata
strcpy(metadata, "");
curwordnum=1;
//strcpy(curword, NEAT::getUnit(curline, curwordnum++, delimiters));
ss >> curword;
while(strcmp(curword,"*/")!=0)
{
// If we've started to form the metadata, put a space in the front
if(md) {
strncat(metadata, " ", 128 - strlen(metadata));
}
// Append the next word to the metadata, and say that there is metadata
strncat(metadata, curword, 128 - strlen(metadata));
md = true;
//strcpy(curword, NEAT::getUnit(curline, curwordnum++, delimiters));
ss >> curword;
}
}
//Ignore comments - they get printed to screen
//else if (strcmp(curword,"/*")==0) {
// iFile>>curword;
// while (strcmp(curword,"*/")!=0) {
//cout<<curword<<" ";
// iFile>>curword;
// }
// cout<<endl;
//}
//Ignore comments surrounded by - they get printed to screen
}
iFile.close();
speciate();
}
}
Population::~Population() {
std::vector<Species*>::iterator curspec;
std::vector<Organism*>::iterator curorg;
//std::vector<Generation_viz*>::iterator cursnap;
if (species.begin()!=species.end()) {
for(curspec=species.begin();curspec!=species.end();++curspec) {
delete (*curspec);
}
}
else {
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
delete (*curorg);
}
}
for (std::vector<Innovation*>::iterator iter = innovations.begin(); iter != innovations.end(); ++iter)
delete *iter;
//Delete the snapshots
// for(cursnap=generation_snapshots.begin();cursnap!=generation_snapshots.end();++cursnap) {
// delete (*cursnap);
// }
}
bool Population::verify() {
std::vector<Organism*>::iterator curorg;
bool verification;
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
verification=((*curorg)->gnome)->verify();
}
return verification;
}
bool Population::clone(Genome *g,int size, float power) {
int count;
Genome *new_genome;
Organism *new_organism;
new_genome = g->duplicate(1);
new_organism = new Organism(0.0,new_genome,1);
organisms.push_back(new_organism);
//Create size copies of the Genome
//Start with perturbed linkweights
for(count=2;count<=size;count++) {
//cout<<"CREATING ORGANISM "<<count<<endl;
new_genome=g->duplicate(count);
if(power>0)
new_genome->mutate_link_weights(power,1.0,GAUSSIAN);
new_genome->randomize_traits();
new_organism=new Organism(0.0,new_genome,1);
organisms.push_back(new_organism);
}
//Keep a record of the innovation and node number we are on
cur_node_id=new_genome->get_last_node_id();
cur_innov_num=new_genome->get_last_gene_innovnum();
//Separate the new Population into species
speciate();
return true;
}
bool Population::spawn(Genome *g,int size) {
int count;
Genome *new_genome;
Organism *new_organism;
//Create size copies of the Genome
//Start with perturbed linkweights
for(count=1;count<=size;count++) {
//cout<<"CREATING ORGANISM "<<count<<endl;
new_genome=g->duplicate(count);
//new_genome->mutate_link_weights(1.0,1.0,GAUSSIAN);
new_genome->mutate_link_weights(1.0,1.0,COLDGAUSSIAN);
new_genome->randomize_traits();
new_organism=new Organism(0.0,new_genome,1);
organisms.push_back(new_organism);
}
//Keep a record of the innovation and node number we are on
cur_node_id=new_genome->get_last_node_id();
cur_innov_num=new_genome->get_last_gene_innovnum();
//Separate the new Population into species
speciate();
return true;
}
bool Population::speciate() {
std::vector<Organism*>::iterator curorg; //For stepping through Population
std::vector<Species*>::iterator curspecies; //Steps through species
Organism *comporg=0; //Organism for comparison
Species *newspecies; //For adding a new species
int counter=0; //Species counter
//Step through all existing organisms
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
//For each organism, search for a species it is compatible to
curspecies=species.begin();
if (curspecies==species.end()){
//Create the first species
newspecies=new Species(++counter);
species.push_back(newspecies);
newspecies->add_Organism(*curorg); //Add the current organism
(*curorg)->species=newspecies; //Point organism to its species
}
else {
comporg=(*curspecies)->first();
while((comporg!=0)&&
(curspecies!=species.end())) {
if ((((*curorg)->gnome)->compatibility(comporg->gnome))<NEAT::compat_threshold) {
//Found compatible species, so add this organism to it
(*curspecies)->add_Organism(*curorg);
(*curorg)->species=(*curspecies); //Point organism to its species
comporg=0; //Note the search is over
}
else {
//Keep searching for a matching species
++curspecies;
if (curspecies!=species.end())
comporg=(*curspecies)->first();
}
}
//If we didn't find a match, create a new species
if (comporg!=0) {
newspecies=new Species(++counter);
species.push_back(newspecies);
newspecies->add_Organism(*curorg); //Add the current organism
(*curorg)->species=newspecies; //Point organism to its species
}
} //end else
} //end for
last_species=counter; //Keep track of highest species
return true;
}
bool Population::print_to_file_by_species(char *filename) {
std::vector<Species*>::iterator curspecies;
std::ofstream outFile(filename,std::ios::out);
//Make sure it worked
if (!outFile) {
std::cerr<<"Can't open "<<filename<<" for output"<<std::endl;
return false;
}
//Step through the Species and print them to the file
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
(*curspecies)->print_to_file(outFile);
}
outFile.close();
return true;
}
bool Population::print_to_file_by_species(std::ostream& outFile) {
std::vector<Species*>::iterator curspecies;
//ofstream outFile(filename,ios::out);
//std::ostream outFile;
//ResourceManager->openFileForWrite(outFile, fileName, std::ostream::Write);
//Make sure it worked
//if (!outFile) {
// cerr<<"Can't open "<<filename<<" for output"<<endl;
// return false;
//}
//Step through the Species and print them to the file
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
(*curspecies)->print_to_file(outFile);
}
return true;
}
bool Population::epoch(int generation) {
std::vector<Species*>::iterator curspecies;
std::vector<Species*>::iterator deadspecies; //For removing empty Species
std::vector<Organism*>::iterator curorg;
std::vector<Organism*>::iterator deadorg;
std::vector<Innovation*>::iterator curinnov;
std::vector<Innovation*>::iterator deadinnov; //For removing old Innovs
double total=0.0; //Used to compute average fitness over all Organisms
double overall_average; //The average modified fitness among ALL organisms
int orgcount;
//The fractional parts of expected offspring that can be
//Used only when they accumulate above 1 for the purposes of counting
//Offspring
double skim;
int total_expected; //precision checking
int total_organisms=organisms.size();
int max_expected;
Species *best_species;
int final_expected;
int pause;
//Rights to make babies can be stolen from inferior species
//and given to their superiors, in order to concentrate exploration on
//the best species
int NUM_STOLEN=NEAT::babies_stolen; //Number of babies to steal
int one_fifth_stolen;
int one_tenth_stolen;
std::vector<Species*> sorted_species; //Species sorted by max fit org in Species
int stolen_babies; //Babies taken from the bad species and given to the champs
int half_pop;
int best_species_num; //Used in debugging to see why (if) best species dies
bool best_ok;
//We can try to keep the number of species constant at this number
int num_species_target=4;
int num_species=species.size();
double compat_mod=0.3; //Modify compat thresh to control speciation
//Keeping species diverse
//This commented out code forces the system to aim for
// num_species species at all times, enforcing diversity
//This tinkers with the compatibility threshold, which
// normally would be held constant
/*
if (generation>1) {
if (num_species<num_species_target)
NEAT::compat_threshold-=compat_mod;
else if (num_species>num_species_target)
NEAT::compat_threshold+=compat_mod;
if (NEAT::compat_threshold<0.3) NEAT::compat_threshold=0.3;
}
*/
//Stick the Species pointers into a new Species list for sorting
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
sorted_species.push_back(*curspecies);
}
//Sort the Species by max fitness (Use an extra list to do this)
//These need to use ORIGINAL fitness
//sorted_species.qsort(order_species);
std::sort(sorted_species.begin(), sorted_species.end(), order_species);
//Flag the lowest performing species over age 20 every 30 generations
//NOTE: THIS IS FOR COMPETITIVE COEVOLUTION STAGNATION DETECTION
curspecies=sorted_species.end();
curspecies--;
while((curspecies!=sorted_species.begin())&&
((*curspecies)->age<20))
--curspecies;
if ((generation%30)==0)
(*curspecies)->obliterate=true;
std::cout<<"Number of Species: "<<num_species<<std::endl;
std::cout<<"compat_thresh: "<<compat_threshold<<std::endl;
//Use Species' ages to modify the objective fitness of organisms
// in other words, make it more fair for younger species
// so they have a chance to take hold
//Also penalize stagnant species
//Then adjust the fitness using the species size to "share" fitness
//within a species.
//Then, within each Species, mark for death
//those below survival_thresh*average
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
(*curspecies)->adjust_fitness();
}
//Go through the organisms and add up their fitnesses to compute the
//overall average
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
total+=(*curorg)->fitness;
}
overall_average=total/total_organisms;
std::cout<<"Generation "<<generation<<": "<<"overall_average = "<<overall_average<<std::endl;
//Now compute expected number of offspring for each individual organism
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
(*curorg)->expected_offspring=(((*curorg)->fitness)/overall_average);
}
//Now add those offspring up within each Species to get the number of
//offspring per Species
skim=0.0;
total_expected=0;
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
skim=(*curspecies)->count_offspring(skim);
total_expected+=(*curspecies)->expected_offspring;
}
//Need to make up for lost foating point precision in offspring assignment
//If we lost precision, give an extra baby to the best Species
if (total_expected<total_organisms) {
//Find the Species expecting the most
max_expected=0;
final_expected=0;
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
if ((*curspecies)->expected_offspring>=max_expected) {
max_expected=(*curspecies)->expected_offspring;
best_species=(*curspecies);
}
final_expected+=(*curspecies)->expected_offspring;
}
//Give the extra offspring to the best species
++(best_species->expected_offspring);
final_expected++;
//If we still arent at total, there is a problem
//Note that this can happen if a stagnant Species
//dominates the population and then gets killed off by its age
//Then the whole population plummets in fitness
//If the average fitness is allowed to hit 0, then we no longer have
//an average we can use to assign offspring.
if (final_expected<total_organisms) {
// cout<<"Population died!"<<endl;
//cin>>pause;
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
(*curspecies)->expected_offspring=0;
}
best_species->expected_offspring=total_organisms;
}
}
//Sort the Species by max fitness (Use an extra list to do this)
//These need to use ORIGINAL fitness
//sorted_species.qsort(order_species);
std::sort(sorted_species.begin(), sorted_species.end(), order_species);
best_species_num=(*(sorted_species.begin()))->id;
for(curspecies=sorted_species.begin();curspecies!=sorted_species.end();++curspecies) {
//Print out for Debugging/viewing what's going on
std::cout<<"orig fitness of Species"<<(*curspecies)->id<<"(Size "<<(*curspecies)->organisms.size()<<"): "<<(*((*curspecies)->organisms).begin())->orig_fitness<<" last improved "<<((*curspecies)->age-(*curspecies)->age_of_last_improvement)<<std::endl;
}
//Check for Population-level stagnation
curspecies=sorted_species.begin();
(*(((*curspecies)->organisms).begin()))->pop_champ=true; //DEBUG marker of the best of pop
if (((*(((*curspecies)->organisms).begin()))->orig_fitness)>
highest_fitness) {
highest_fitness=((*(((*curspecies)->organisms).begin()))->orig_fitness);
highest_last_changed=0;
std::cout<<"NEW POPULATION RECORD FITNESS: "<<highest_fitness<<std::endl;
}
else {
++highest_last_changed;
std::cout<<highest_last_changed<<" generations since last population fitness record: "<<highest_fitness<<std::endl;
}
//Check for stagnation- if there is stagnation, perform delta-coding
if (highest_last_changed>=NEAT::dropoff_age+5) {
// cout<<"PERFORMING DELTA CODING"<<endl;
highest_last_changed=0;
half_pop=NEAT::pop_size/2;
// cout<<"half_pop"<<half_pop<<" pop_size-halfpop: "<<pop_size-half_pop<<endl;
curspecies=sorted_species.begin();
(*(((*curspecies)->organisms).begin()))->super_champ_offspring=half_pop;
(*curspecies)->expected_offspring=half_pop;
(*curspecies)->age_of_last_improvement=(*curspecies)->age;
++curspecies;
if (curspecies!=sorted_species.end()) {
(*(((*curspecies)->organisms).begin()))->super_champ_offspring=NEAT::pop_size-half_pop;
(*curspecies)->expected_offspring=NEAT::pop_size-half_pop;
(*curspecies)->age_of_last_improvement=(*curspecies)->age;
++curspecies;
//Get rid of all species under the first 2
while(curspecies!=sorted_species.end()) {
(*curspecies)->expected_offspring=0;
++curspecies;
}
}
else {
curspecies=sorted_species.begin();
(*(((*curspecies)->organisms).begin()))->super_champ_offspring+=NEAT::pop_size-half_pop;
(*curspecies)->expected_offspring=NEAT::pop_size-half_pop;
}
}
//STOLEN BABIES: The system can take expected offspring away from
// worse species and give them to superior species depending on
// the system parameter babies_stolen (when babies_stolen > 0)
else if (NEAT::babies_stolen>0) {
//Take away a constant number of expected offspring from the worst few species
stolen_babies=0;
curspecies=sorted_species.end();
curspecies--;
while ((stolen_babies<NUM_STOLEN)&&
(curspecies!=sorted_species.begin())) {
//cout<<"Considering Species "<<(*curspecies)->id<<": age "<<(((*curspecies)->age))<<" expected offspring "<<(((*curspecies)->expected_offspring))<<endl;
if ((((*curspecies)->age)>5)&&
(((*curspecies)->expected_offspring)>2)) {
//cout<<"STEALING!"<<endl;
//This species has enough to finish off the stolen pool
if (((*curspecies)->expected_offspring-1)>=(NUM_STOLEN-stolen_babies)) {
(*curspecies)->expected_offspring-=(NUM_STOLEN-stolen_babies);
stolen_babies=NUM_STOLEN;
}
//Not enough here to complete the pool of stolen
else {
stolen_babies+=(*curspecies)->expected_offspring-1;
(*curspecies)->expected_offspring=1;
}
}
curspecies--;
//if (stolen_babies>0)
//cout<<"stolen babies so far: "<<stolen_babies<<endl;
}
//cout<<"STOLEN BABIES: "<<stolen_babies<<endl;
//Mark the best champions of the top species to be the super champs
//who will take on the extra offspring for cloning or mutant cloning
curspecies=sorted_species.begin();
//Determine the exact number that will be given to the top three
//They get , in order, 1/5 1/5 and 1/10 of the stolen babies
one_fifth_stolen=NEAT::babies_stolen/5;
one_tenth_stolen=NEAT::babies_stolen/10;
//Don't give to dying species even if they are champs
while(((*curspecies)->last_improved()>NEAT::dropoff_age)&&(curspecies!=sorted_species.end()))
++curspecies;
//Concentrate A LOT on the number one species
if ((stolen_babies>=one_fifth_stolen)&&(curspecies!=sorted_species.end())) {
(*(((*curspecies)->organisms).begin()))->super_champ_offspring=one_fifth_stolen;
(*curspecies)->expected_offspring+=one_fifth_stolen;
stolen_babies-=one_fifth_stolen;
//cout<<"Gave "<<one_fifth_stolen<<" babies to Species "<<(*curspecies)->id<<endl;
// cout<<"The best superchamp is "<<(*(((*curspecies)->organisms).begin()))->gnome->genome_id<<endl;
//Print this champ to file "champ" for observation if desired
//IMPORTANT: This causes generational file output
//print_Genome_tofile((*(((*curspecies)->organisms).begin()))->gnome,"champ");
curspecies++;
}
//Don't give to dying species even if they are champs
while(((*curspecies)->last_improved()>NEAT::dropoff_age)&&(curspecies!=sorted_species.end()))
++curspecies;
if ((curspecies!=sorted_species.end())) {
if (stolen_babies>=one_fifth_stolen) {
(*(((*curspecies)->organisms).begin()))->super_champ_offspring=one_fifth_stolen;
(*curspecies)->expected_offspring+=one_fifth_stolen;
stolen_babies-=one_fifth_stolen;
//cout<<"Gave "<<one_fifth_stolen<<" babies to Species "<<(*curspecies)->id<<endl;
curspecies++;
}
}
//Don't give to dying species even if they are champs
while(((*curspecies)->last_improved()>NEAT::dropoff_age)&&(curspecies!=sorted_species.end()))
++curspecies;
if (curspecies!=sorted_species.end())
if (stolen_babies>=one_tenth_stolen) {
(*(((*curspecies)->organisms).begin()))->super_champ_offspring=one_tenth_stolen;
(*curspecies)->expected_offspring+=one_tenth_stolen;
stolen_babies-=one_tenth_stolen;
//cout<<"Gave "<<one_tenth_stolen<<" babies to Species "<<(*curspecies)->id<<endl;
curspecies++;
}
//Don't give to dying species even if they are champs
while(((*curspecies)->last_improved()>NEAT::dropoff_age)&&(curspecies!=sorted_species.end()))
++curspecies;
while((stolen_babies>0)&&
(curspecies!=sorted_species.end())) {
//Randomize a little which species get boosted by a super champ
if (randfloat()>0.1)
if (stolen_babies>3) {
(*(((*curspecies)->organisms).begin()))->super_champ_offspring=3;
(*curspecies)->expected_offspring+=3;
stolen_babies-=3;
//cout<<"Gave 3 babies to Species "<<(*curspecies)->id<<endl;
}
else {
//cout<<"3 or less babies available"<<endl;
(*(((*curspecies)->organisms).begin()))->super_champ_offspring=stolen_babies;
(*curspecies)->expected_offspring+=stolen_babies;
//cout<<"Gave "<<stolen_babies<<" babies to Species "<<(*curspecies)->id<<endl;
stolen_babies=0;
}
curspecies++;
//Don't give to dying species even if they are champs
while((curspecies!=sorted_species.end())&&((*curspecies)->last_improved()>NEAT::dropoff_age))
++curspecies;
}
//cout<<"Done giving back babies"<<endl;
//If any stolen babies aren't taken, give them to species #1's champ
if (stolen_babies>0) {
//cout<<"Not all given back, giving to best Species"<<endl;
curspecies=sorted_species.begin();
(*(((*curspecies)->organisms).begin()))->super_champ_offspring+=stolen_babies;
(*curspecies)->expected_offspring+=stolen_babies;
stolen_babies=0;
}
}
//Kill off all Organisms marked for death. The remainder
//will be allowed to reproduce.
curorg=organisms.begin();
while(curorg!=organisms.end()) {
if (((*curorg)->eliminate)) {
//Remove the organism from its Species
((*curorg)->species)->remove_org(*curorg);
//Delete the organism from memory
delete (*curorg);
//Remember where we are
deadorg=curorg;
++curorg;
//iter2 = v.erase(iter);
//Remove the organism from the master list
curorg=organisms.erase(deadorg);
}
else {
++curorg;
}
}
//cout<<"Reproducing"<<endl;
//Perform reproduction. Reproduction is done on a per-Species
//basis. (So this could be paralellized potentially.)
// for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
//KENHACK
// for(std::vector<Species*>::iterator curspecies2=species.begin();curspecies2!=species.end();++curspecies2) {
// std::cout<<"PRE in repro specloop SPEC EXISTING number "<<(*curspecies2)->id<<std::endl;
// }
// (*curspecies)->reproduce(generation,this,sorted_species);
//}
curspecies=species.begin();
int last_id=(*curspecies)->id;
while(curspecies!=species.end()) {
(*curspecies)->reproduce(generation,this,sorted_species);
//Set the current species to the id of the last species checked
//(the iterator must be reset because there were possibly vector insertions during reproduce)
std::vector<Species*>::iterator curspecies2=species.begin();
while(curspecies2!=species.end()) {
if (((*curspecies2)->id)==last_id)
curspecies=curspecies2;
curspecies2++;
}
//Move to the next on the list
curspecies++;
//Record where we are
if (curspecies!=species.end())
last_id=(*curspecies)->id;
}
//cout<<"Reproduction Complete"<<endl;
//Destroy and remove the old generation from the organisms and species
curorg=organisms.begin();
while(curorg!=organisms.end()) {
//Remove the organism from its Species
((*curorg)->species)->remove_org(*curorg);
//std::cout<<"deleting org # "<<(*curorg)->gnome->genome_id<<std::endl;
//Delete the organism from memory
delete (*curorg);
//Remember where we are
deadorg=curorg;
++curorg;
//std::cout<<"next org # "<<(*curorg)->gnome->genome_id<<std::endl;
//Remove the organism from the master list
curorg=organisms.erase(deadorg);
//std::cout<<"nnext org # "<<(*curorg)->gnome->genome_id<<std::endl;
}
//Remove all empty Species and age ones that survive
//As this happens, create master organism list for the new generation
curspecies=species.begin();
orgcount=0;
while(curspecies!=species.end()) {
if (((*curspecies)->organisms.size())==0) {
delete (*curspecies);
deadspecies=curspecies;
++curspecies;
curspecies=species.erase(deadspecies);
}
//Age surviving Species and
//Rebuild master Organism list: NUMBER THEM as they are added to the list
else {
//Age any Species that is not newly created in this generation
if ((*curspecies)->novel) {
(*curspecies)->novel=false;
}
else ++((*curspecies)->age);
//Go through the organisms of the curspecies and add them to
//the master list
for(curorg=((*curspecies)->organisms).begin();curorg!=((*curspecies)->organisms).end();++curorg) {
((*curorg)->gnome)->genome_id=orgcount++;
organisms.push_back(*curorg);
}
++curspecies;
}
}
//Remove the innovations of the current generation
curinnov=innovations.begin();
while(curinnov!=innovations.end()) {
delete (*curinnov);
deadinnov=curinnov;
++curinnov;
curinnov=innovations.erase(deadinnov);
}
//DEBUG: Check to see if the best species died somehow
// We don't want this to happen
curspecies=species.begin();
best_ok=false;
while(curspecies!=species.end()) {
if (((*curspecies)->id)==best_species_num) best_ok=true;
++curspecies;
}
if (!best_ok) {
//cout<<"ERROR: THE BEST SPECIES DIED!"<<endl;
}
else {
//cout<<"The best survived: "<<best_species_num<<endl;
}
//DEBUG: Checking the top organism's duplicate in the next gen
//This prints the champ's child to the screen
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
if ((*curorg)->pop_champ_child) {
//cout<<"At end of reproduction cycle, the child of the pop champ is: "<<(*curorg)->gnome<<endl;
}
}
//cout<<"babies_stolen at end: "<<babies_stolen<<endl;
//cout<<"Epoch complete"<<endl;
return true;
}
bool Population::rank_within_species() {
std::vector<Species*>::iterator curspecies;
//Add each Species in this generation to the snapshot
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
(*curspecies)->rank();
}
return true;
}
void Population::estimate_all_averages() {
std::vector<Species*>::iterator curspecies;
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
(*curspecies)->estimate_average();
}
}
Species *Population::choose_parent_species() {
double total_fitness=0;
std::vector<Species*>::iterator curspecies;
double marble; //The roulette marble
double spin; //Spins until the marble reaches its chosen point
//We can try to keep the number of species constant at this number
int num_species_target=4;
int num_species=species.size();
double compat_mod=0.3; //Modify compat thresh to control speciation
//Keeping species diverse
//This commented out code forces the system to aim for
// num_species species at all times, enforcing diversity
//This tinkers with the compatibility threshold, which
// normally would be held constant
//if (num_species<num_species_target)
// NEAT::compat_threshold-=compat_mod;
//else if (num_species>num_species_target)
// NEAT::compat_threshold+=compat_mod;
//if (NEAT::compat_threshold<0.3) NEAT::compat_threshold=0.3;
//Use the roulette method to choose the species
//Sum all the average fitness estimates of the different species
//for the purposes of the roulette
for(curspecies=species.begin();curspecies!=species.end();++curspecies) {
total_fitness+=(*curspecies)->average_est;
}
marble=randfloat()*total_fitness;
curspecies=species.begin();
spin=(*curspecies)->average_est;
while(spin<marble) {
++curspecies;
//Keep the wheel spinning
spin+=(*curspecies)->average_est;
}
//Finished roulette
// cout<<"Chose random species "<<(*curspecies)->id<<endl;
//printf("Chose random species %d.",(*curspecies)->id);
//Return the chosen species
return (*curspecies);
}
bool Population::remove_species(Species *spec) {
std::vector<Species*>::iterator curspec;
curspec=species.begin();
while((curspec!=species.end())&&
((*curspec)!=spec))
++curspec;
if (curspec==species.end()) {
// cout<<"ALERT: Attempt to remove nonexistent Species from Population"<<endl;
return false;
}
else {
species.erase(curspec);
return true;
}
}
Organism* Population::remove_worst() {
double adjusted_fitness;
double min_fitness=999999;
std::vector<Organism*>::iterator curorg;
Organism *org_to_kill = 0;
std::vector<Organism*>::iterator deadorg;
Species *orgs_species; //The species of the dead organism
//Make sure the organism is deleted from its species and the population
//First find the organism with minimum *adjusted* fitness
for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
adjusted_fitness=((*curorg)->fitness)/((*curorg)->species->organisms.size());
if ((adjusted_fitness<min_fitness)&&
(((*curorg)->time_alive) >= NEAT::time_alive_minimum))
{
min_fitness=adjusted_fitness;
org_to_kill=(*curorg);
deadorg=curorg;
orgs_species=(*curorg)->species;
}
}
if (org_to_kill) {
// printf("Org to kill: species = %d",org_to_kill->species->id);
//Remove the organism from its species and the population
orgs_species->remove_org(org_to_kill); //Remove from species
delete org_to_kill; //Delete the organism itself
organisms.erase(deadorg); //Remove from population list
//Did the species become empty?
if ((orgs_species->organisms.size())==0) {
remove_species(orgs_species);
delete orgs_species;
}
//If not, re-estimate the species average after removing the organism
else {
orgs_species->estimate_average();
}
}
return org_to_kill;
}
//Warning: rtNEAT does not behave like regular NEAT if you remove the worst probabilistically
//You really should just use "remove_worst," which removes the org with worst adjusted fitness.
Organism* Population::remove_worst_probabilistic() {
double adjusted_fitness;
double min_fitness=999999;
std::vector<Organism*>::iterator curorg;
Organism *org_to_kill = 0;
std::vector<Organism*>::iterator deadorg;
Species *orgs_species; //The species of the dead organism
//Make sure the organism is deleted from its species and the population
std::vector<Organism*> sorted_adjusted_orgs;
for(curorg = organisms.begin(); curorg != organisms.end(); ++curorg) {
if ((*curorg)->time_alive >= NEAT::time_alive_minimum)
sorted_adjusted_orgs.push_back(*curorg);
}
if (sorted_adjusted_orgs.size() == 0)
return 0;
//sorted_adjusted_orgs.qsort(order_orgs_by_adjusted_fit);
std::sort(sorted_adjusted_orgs.begin(), sorted_adjusted_orgs.end(), order_orgs_by_adjusted_fit);
int size_bottom_10_percent = ceil((float)sorted_adjusted_orgs.size() * 0.10);
int randorgnum = NEAT::randint(sorted_adjusted_orgs.size() - size_bottom_10_percent, sorted_adjusted_orgs.size() - 1);
curorg = sorted_adjusted_orgs.begin();
curorg += randorgnum;
org_to_kill = *curorg;
orgs_species=(org_to_kill)->species;
curorg = organisms.begin();
while (*curorg != org_to_kill) {
++curorg;
}
deadorg = curorg;
//First find the organism with minimum *adjusted* fitness
//for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
// adjusted_fitness=((*curorg)->fitness)/((*curorg)->species->organisms.size());
// if ((adjusted_fitness<min_fitness)&&
// (((*curorg)->time_alive) >= NEAT::time_alive_minimum))
// {
// min_fitness=adjusted_fitness;
// org_to_kill=(*curorg);
// deadorg=curorg;
// orgs_species=(*curorg)->species;
// }
//}
//printf("Org to kill time_alive = %d and fitness = %f",org_to_kill->time_alive,org_to_kill->fitness);
if (org_to_kill) {
//Remove the organism from its species and the population
orgs_species->remove_org(org_to_kill); //Remove from species
delete org_to_kill; //Delete the organism itself
organisms.erase(deadorg); //Remove from population list
//Did the species become empty?
if ((orgs_species->organisms.size())==0) {
remove_species(orgs_species);
delete orgs_species;
}
//If not, re-estimate the species average after removing the organism
else {
orgs_species->estimate_average();
}
}
return org_to_kill;
}
Organism* Population::reproduce_champ(int generation) {
std::vector<Organism*>::iterator curorg;
double max_fitness=0;
Organism *champ;
Organism *baby;
Genome *new_genome;
std::vector<Species*>::iterator curspecies;
Species *newspecies;
Organism *comporg;
bool found;
//The weight mutation power is species specific depending on its age
double mut_power=NEAT::weight_mut_power;
champ=*(organisms.begin()); //Make sure at least something is chosen
//Find the population champ
for(curorg = organisms.begin(); curorg != organisms.end(); ++curorg) {
if (((*curorg)->fitness>max_fitness)&&
((*curorg)->time_alive >= NEAT::time_alive_minimum)){
champ=(*curorg);
max_fitness=champ->fitness;
}
}
//Now reproduce the pop champ as a new org
new_genome=(champ->gnome)->duplicate(generation);
//Maybe mutate its link weights
//if (randfloat()<NEAT::mutate_link_weights_prob) {
if (randfloat()<0.5) {
//cout<<"mutate_link_weights"<<endl;
new_genome->mutate_link_weights(mut_power,1.0,GAUSSIAN);
}
baby=new Organism(0.0,new_genome,generation);
curspecies=(species).begin();
if (curspecies==(species).end()){
//Create the first species
newspecies=new Species(++(last_species),true);
(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!=(species).end()) && (!found))
{
if (comporg==0) {
//Keep searching for a matching species
++curspecies;
if (curspecies!=(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!=(species).end())
comporg=(*curspecies)->first();
}
}
//If we didn't find a match, create a new species
if (found==false) {
newspecies=new Species(++(last_species),true);
(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
(organisms).push_back(baby);
//printf("CHAMPBABY --- species: %i fitness: %f ", champ->species->id, champ->fitness );
//printf("----------------------------");
return baby; //Return a pointer to the baby
}
//KEN: New 2/17/04
//This method takes an Organism and reassigns what Species it belongs to
//It is meant to be used so that we can reasses where Organisms should belong
//as the speciation threshold changes.
void Population::reassign_species(Organism *org) {
Organism *comporg;
bool found=false; //Note we don't really need this flag but it
//might be useful if we change how this function works
std::vector<Species*>::iterator curspecies;
Species *newspecies;
Species *orig_species;
curspecies=(species).begin();
comporg=(*curspecies)->first();
while((curspecies!=(species).end()) && (!found))
{
if (comporg==0) {
//Keep searching for a matching species
++curspecies;
if (curspecies!=(species).end())
comporg=(*curspecies)->first();
}
else if (((org->gnome)->compatibility(comporg->gnome))<NEAT::compat_threshold) {
//If we found the same species it's already in, return 0
if ((*curspecies)==org->species) {
found=true;
break;
}
//Found compatible species
else {
switch_species(org,org->species,(*curspecies));
found=true; //Note the search is over
}
}
else {
//Keep searching for a matching species
++curspecies;
if (curspecies!=(species).end())
comporg=(*curspecies)->first();
}
}
//If we didn't find a match, create a new species, move the org to
// that species, check if the old species is empty,
//re-estimate averages, and return 0
if (found==false) {
//Create a new species for the org
newspecies=new Species(++(last_species),true);
(species).push_back(newspecies);
switch_species(org,org->species,newspecies);
}
}
//Move an Organism from one Species to another
void Population::switch_species(Organism *org, Species *orig_species, Species *new_species) {
//Remove organism from the species we want to remove it from
orig_species->remove_org(org);
//Add the organism to the new species it is being moved to
new_species->add_Organism(org);
org->species=new_species;
//KEN: Delete orig_species if empty, and remove it from pop
if ((orig_species->organisms.size())==0) {
remove_species(orig_species);
delete orig_species;
//Re-estimate the average of the species that now has a new member
new_species->estimate_average();
}
//If not, re-estimate the species average after removing the organism
// AND the new species with the new member
else {
orig_species->estimate_average();
new_species->estimate_average();
}
}
//Organism* Population::remove_worst_probabilistic() {
//
// double adjusted_fitness;
// double min_fitness=999999;
// std::vector<Organism*>::iterator curorg;
// Organism *org_to_kill = 0;
// std::vector<Organism*>::iterator deadorg;
// Species *orgs_species; //The species of the dead organism
//
// // Select a random species from which to remove an organism
// //std::vector<Species*>::iterator curspecies = species.begin();
// //int randspecies = NEAT::randint(0, species.size() - 1);
// //curspecies += randspecies;
//
// float sum_of_averages = 0;
//
// for (std::vector<NEAT::Species*>::iterator curspec = species.begin(); curspec != species.end(); ++curspec) {
// (*curspec)->estimate_average();
// sum_of_averages += (*curspec)->average_est;
// }
//
// float marble = randfloat() * sum_of_averages;
// std::vector<Species*>::iterator curspecies=species.begin();
// float spin = (*curspecies)->average_est * (*curspecies)->organisms.size();
// while(spin<marble) {
// ++curspecies;
//
// //Keep the wheel spinning
// spin += (*curspecies)->average_est * (*curspecies)->organisms.size();
// }
//
//
// int num_below_min=0; //# orgs below minimum age
//
// //Make sure the organism is deleted from its species and the population
//
// //Find how many organisms in this species are below minimum age
// for(curorg=(*curspecies)->organisms.begin();curorg!=(*curspecies)->organisms.end();++curorg) {
// if ((*curorg)->time_alive < NEAT::time_alive_minimum)
// num_below_min++;
// }
// if (num_below_min == (*curspecies)->organisms.size())
// return 0;
//
// int speciessize = (*curspecies)->organisms.size() - num_below_min;
//
// //First find the organism with minimum *adjusted* fitness within the selected species
// //for(curorg=(*curspecies)->organisms.begin();curorg!=(*curspecies)->organisms.end();++curorg) {
// //adjusted_fitness=((*curorg)->fitness)/((*curorg)->species->organisms.size());
// (*curspecies)->organisms.qsort(order_orgs);
// int size_bottom_10_percent = mCeil((float)speciessize * 0.10);
// int randorgnum = NEAT::randint(speciessize - size_bottom_10_percent - 1, speciessize - 1);
//
// curorg = (*curspecies)->organisms.begin();
// while (randorgnum > 0) {
// if ((*curorg)->time_alive >= NEAT::time_alive_minimum)
// --randorgnum;
// ++curorg;
// }
// org_to_kill = *curorg;
// orgs_species=(org_to_kill)->species;
//
// //if ((adjusted_fitness < min_fitness)&&
// // (((*curorg)->time_alive) >= NEAT::time_alive_minimum))
// //{
// // min_fitness=adjusted_fitness;
// // org_to_kill=(*curorg);
// // deadorg=curorg;
// // orgs_species=(*curorg)->species;
// //}
// //}
//
// curorg = organisms.begin();
// while (*curorg != org_to_kill) {
// ++curorg;
// }
// deadorg = curorg;
//
// //Con::printf("Org to kill time_alive = %d and fitness = %f",org_to_kill->time_alive,org_to_kill->fitness);
//
// if (org_to_kill) {
// //Remove the organism from its species and the population
// orgs_species->remove_org(org_to_kill); //Remove from species
// delete org_to_kill; //Delete the organism itself
// organisms.erase(deadorg); //Remove from population list
//
// //Did the species become empty?
// if ((orgs_species->organisms.size())==0) {
//
// remove_species(orgs_species);
// delete orgs_species;
// }
// //If not, re-estimate the species average after removing the organism
// else {
// orgs_species->estimate_average();
// }
// }
//
// return org_to_kill;
//}
////Print Population to a file specified by a string
//bool Population::print_to_file(char *filename){
//std::vector<Organism*>::iterator curorg;
//
//ofstream outFile(filename,ios::out);
//
////Make sure it worked
//if (!outFile) {
//cerr<<"Can't open "<<filename<<" for output"<<endl;
//return false;
//}
//
////Print all the Organisms' Genomes to the outFile
//for(curorg=organisms.begin();curorg!=organisms.end();++curorg) {
//((*curorg)->gnome)->print_to_file(outFile);
////We can confirm by writing the genome #'s to the screen
////cout<<((*curorg)->gnome)->genome_id<<endl;
//}
//
//outFile.close();
//
//return true;
//
//}