XOR experiment
test.ne
trait_param_mut_prob 0.5
trait_mutation_power 1.0
linktrait_mut_sig 1.0
nodetrait_mut_sig 0.5
weigh_mut_power 2.5
recur_prob 0.00
disjoint_coeff 1.0
excess_coeff 1.0
mutdiff_coeff 0.4
compat_thresh 3.0
age_significance 1.0
survival_thresh 0.20
mutate_only_prob 0.25
mutate_random_trait_prob 0.1
mutate_link_trait_prob 0.1
mutate_node_trait_prob 0.1
mutate_link_weights_prob 0.9
mutate_toggle_enable_prob 0.00
mutate_gene_reenable_prob 0.000
mutate_add_node_prob 0.03
mutate_add_link_prob 0.05
interspecies_mate_rate 0.001
mate_multipoint_prob 0.6
mate_multipoint_avg_prob 0.4
mate_singlepoint_prob 0.0
mate_only_prob 0.2
recur_only_prob 0.0
pop_size 150
dropoff_age 15
newlink_tries 20
print_every 30
babies_stolen 0
num_runs 100
xorstartgenes
genomestart 1
trait 1 0.1 0 0 0 0 0 0 0
trait 2 0.2 0 0 0 0 0 0 0
trait 3 0.3 0 0 0 0 0 0 0
node 1 0 1 3
node 2 0 1 1
node 3 0 1 1
node 4 0 0 2
gene 1 1 4 0.0 0 1 0 1
gene 2 2 4 0.0 0 2 0 1
gene 3 3 4 0.0 0 3 0 1
genomeend 1
////////////////////////////////////////////////////////////////////////////////////////
//The XOR evolution routines *****************************************
Population *xor_test(int gens);
bool xor_evaluate(Organism *org);
int xor_epoch(Population *pop,int generation,char *filename, int &winnernum, int &winnergenes,int &winnernodes);
////////////////////////////////////////////////////////////////////////////////////////
//Perform evolution on XOR, for gens generations
Population *xor_test(int gens) {
Population *pop;
Genome *start_genome;
char curword[20];
int id;
ostringstream *fnamebuf;
int gen;
int evals[NEAT::num_runs]; //Hold records for each run
int genes[NEAT::num_runs];
int nodes[NEAT::num_runs];
int winnernum;
int winnergenes;
int winnernodes;
//For averaging
int totalevals=0;
int totalgenes=0;
int totalnodes=0;
int expcount;
ifstream iFile("xorstartgenes",ios::in);
cout<<"START XOR TEST"<<endl;
cout<<"Reading in the start genome"<<endl;
//Read in the start Genome
iFile>>curword;
iFile>>id;
cout<<"Reading in Genome id "<<id<<endl;
start_genome=new Genome(id,iFile);
iFile.close();
for(expcount=0;expcount<NEAT::num_runs;expcount++) {
//Spawn the Population
cout<<"Spawning Population off Genome2"<<endl;
pop=new Population(start_genome,NEAT::pop_size);
cout<<"Verifying Spawned Pop"<<endl;
pop->verify();
for (gen=1;gen<=gens;gen++) {
cout<<"Epoch "<<gen<<endl;
//This is how to make a custom filename
fnamebuf=new ostringstream();
(*fnamebuf)<<"gen_"<<gen<<ends; //needs end marker
#ifndef NO_SCREEN_OUT
cout<<"name of fname: "<<fnamebuf->str()<<endl;
#endif
char temp[50];
sprintf (temp, "gen_%d", gen);
//Check for success
if (xor_epoch(pop,gen,temp,winnernum,winnergenes,winnernodes)) {
{
//Collect Stats on end of experiment
evals[expcount]=NEAT::pop_size*(gen-1)+winnernum;
genes[expcount]=winnergenes;
nodes[expcount]=winnernodes;
gen=gens;
}
//Clear output filename
fnamebuf->clear();
delete fnamebuf;
}
if (expcount<NEAT::num_runs-1) delete pop;
}
//Average and print stats
cout<<"Nodes: "<<endl;
for(expcount=0;expcount<NEAT::num_runs;expcount++) {
cout<<nodes[expcount]<<endl;
totalnodes+=nodes[expcount];
}
cout<<"Genes: "<<endl;
for(expcount=0;expcount<NEAT::num_runs;expcount++) {
cout<<genes[expcount]<<endl;
totalgenes+=genes[expcount];
}
cout<<"Evals "<<endl;
for(expcount=0;expcount<NEAT::num_runs;expcount++) {
cout<<evals[expcount]<<endl;
totalevals+=evals[expcount];
}
cout<<"Average Nodes: "<<((double) totalnodes/NEAT::num_runs)<<endl;
cout<<"Average Genes: "<<((double) totalgenes/NEAT::num_runs)<<endl;
cout<<"Average Evals: "<<((double) totalevals/NEAT::num_runs)<<endl;
return pop;
}
bool xor_evaluate(Organism *org) {
Network *net;
double out[4]; //The four outputs
double this_out; //The current output
int count;
double errorsum;
bool success; //Check for successful activation
int numnodes; /* Used to figure out how many nodes
should be visited during activation */
int net_depth; //The max depth of the network to be activated
int relax; //Activates until relaxation
//The four possible input combinations to xor
//The first number is for biasing
double in[4][3]={{1.0,0.0,0.0},
{1.0,0.0,1.0},
{1.0,1.0,0.0},
{1.0,1.0,1.0}};
net=org->net;
numnodes=((org->gnome)->nodes).size();
net_depth=net->max_depth();
//TEST CODE: REMOVE
//cout<<"ACTIVATING: "<<org->gnome<<endl;
//cout<<"DEPTH: "<<net_depth<<endl;
//Load and activate the network on each input
for(count=0;count<=3;count++) {
net->load_sensors(in[count]);
//Relax net and get output
success=net->activate();
//use depth to ensure relaxation
for (relax=0;relax<=net_depth;relax++) {
success=net->activate();
this_out=(*(net->outputs.begin()))->activation;
}
out[count]=(*(net->outputs.begin()))->activation;
net->flush();
}
if (success) {
errorsum=(fabs(out[0])+fabs(1.0-out[1])+fabs(1.0-out[2])+fabs(out[3]));
org->fitness=pow((4.0-errorsum),2);
org->error=errorsum;
}
else {
//The network is flawed (shouldnt happen)
errorsum=999.0;
org->fitness=0.001;
}
#ifndef NO_SCREEN_OUT
cout<<"Org "<<(org->gnome)->genome_id<<" error: "<<errorsum<<" ["<<out[0]<<" "<<out[1]<<" "<<out[2]<<" "<<out[3]<<"]"<<endl;
cout<<"Org "<<(org->gnome)->genome_id<<" fitness: "<<org->fitness<<endl;
#endif
// if (errorsum<0.05) {
//if (errorsum<0.2) {
if ((out[0]<0.5)&&(out[1]>=0.5)&&(out[2]>=0.5)&&(out[3]<0.5)) {
org->winner=true;
return true;
}
else {
org->winner=false;
return false;
}
}
int xor_epoch(Population *pop,int generation,char *filename,int &winnernum,int &winnergenes,int &winnernodes) {
vector<Organism*>::iterator curorg;
vector<Species*>::iterator curspecies;
//char cfilename[100];
//strncpy( cfilename, filename.c_str(), 100 );
//ofstream cfilename(filename.c_str());
bool win=false;
//Evaluate each organism on a test
for(curorg=(pop->organisms).begin();curorg!=(pop->organisms).end();++curorg) {
if (xor_evaluate(*curorg)) {
win=true;
winnernum=(*curorg)->gnome->genome_id;
winnergenes=(*curorg)->gnome->extrons();
winnernodes=((*curorg)->gnome->nodes).size();
if (winnernodes==5) {
//You could dump out optimal genomes here if desired
//(*curorg)->gnome->print_to_filename("xor_optimal");
//cout<<"DUMPED OPTIMAL"<<endl;
}
}
}
//Average and max their fitnesses for dumping to file and snapshot
for(curspecies=(pop->species).begin();curspecies!=(pop->species).end();++curspecies) {
//This experiment control routine issues commands to collect ave
//and max fitness, as opposed to having the snapshot do it,
//because this allows flexibility in terms of what time
//to observe fitnesses at
(*curspecies)->compute_average_fitness();
(*curspecies)->compute_max_fitness();
}
//Take a snapshot of the population, so that it can be
//visualized later on
//if ((generation%1)==0)
// pop->snapshot();
//Only print to file every print_every generations
if (win||
((generation%(NEAT::print_every))==0))
pop->print_to_file_by_species(filename);
if (win) {
for(curorg=(pop->organisms).begin();curorg!=(pop->organisms).end();++curorg) {
if ((*curorg)->winner) {
cout<<"WINNER IS #"<<((*curorg)->gnome)->genome_id<<endl;
//Prints the winner to file
//IMPORTANT: This causes generational file output!
print_Genome_tofile((*curorg)->gnome,"xor_winner");
}
}
}
pop->epoch(generation);
if (win) return 1;
else return 0;
}