#include "Genotype.h" #include #include #include "IteratorHelper.h" #include "MathHelper.h" #include "NEAT.h" #include "Random.h" Genotype::Genotype(Innovator& innovator, int num_inputs, int num_outputs) { assert(num_inputs > 0 && num_inputs > 0); // create bias neuron Neurons.push_back( NeuronGene(innovator) ); // create input neurons for (int i=0;i::const_iterator, it_dad, dad.Connections ) { // see if mom has matching gene, via brute force search iterate_each( std::vector::iterator, it_mom, Connections ) { // does this one match? and, does the PRNG say we should do it? if ( RandomBool() && (*it_mom) == (*it_dad) ) { // yes, there's a match, make the swap (*it_mom) = (*it_dad); break; } } } // TODO - handle case where mom & dad have the same fitness } void Genotype::MutateConnectionWeights() { // mutate every connection iterate_each ( std::vector::iterator, it, Connections ) { (*it).Mutate(); } } bool Genotype::MutateAddNeuron(Innovator& innovator, int retries) { // pointers to the two neurons we're adding a neuron in between NeuronGene* input; NeuronGene* output; // figure out a connection to split size_t connection_id; int i; for (i=0;i= 0 && GetNeuronByInnovation(new_id) ) { // neuron already exists in this individual continue; } // make sure we're not splitting a recurrent link // TODO // make sure we're not splitting a looped link if ( (*input) == (*output) ) { // no sense in splitting a looped link continue; } // make sure we're not splitting a bias neuron if ( input->GetType() == NeuronGene::BIAS ) { // no sense in splitting bias neurons continue; } // if we get here, we have a good split break; } // if we found nothing, bail out if ( i >= retries ) { return false; } // disable it Connections[connection_id].Disable(); // create a nueron splitting these two nodes NeuronGene new_neuron = NeuronGene(innovator,*input,*output); // link the new node in with two new connections float orig_weight = Connections[connection_id].GetWeight(); Connections.push_back( ConnectionGene(innovator,(*input).GetInnovation(),new_neuron.GetInnovation(),1.0f) ); Connections.push_back( ConnectionGene(innovator,new_neuron.GetInnovation(),(*output).GetInnovation(),orig_weight) ); // finally, add neuron to our list // this invalidates the input, output pointers used above Neurons.push_back(new_neuron); return true; } bool Genotype::MutateAddConnection(Innovator& innovator, int retries) { // decide if this will be a looped, recurrent, or forward connection while (retries--) { // choose two random neurons int neuron_a_id = RandomRange(0,(int)Neurons.size()-1); int neuron_b_id = RandomRange(0,(int)Neurons.size()-1); // make sure target is not bias or input neuron if ( Neurons[neuron_b_id].GetType() == NeuronGene::BIAS || Neurons[neuron_b_id].GetType() == NeuronGene::INPUT ) { // TODO could skip this if we'd just select from (m_num_inputs+1,num_neurons) instead of (0,num_neurons) continue; } // check for loop if ( neuron_a_id == neuron_b_id ) { // TODO this could actually be allowed continue; } // check for recurrent if ( IsRecurrent(neuron_a_id,neuron_b_id) ) { // make forward std::swap(neuron_a_id,neuron_b_id); } // see if this connection exists if ( GetConnectionByNeuron(Neurons[neuron_a_id].GetInnovation(),Neurons[neuron_b_id].GetInnovation()) ) { // exists continue; } // we got a match, add the connection Connections.push_back( ConnectionGene(innovator,Neurons[neuron_a_id].GetInnovation(),Neurons[neuron_b_id].GetInnovation()) ); return true; } // nothing found return false; } NeuronGene* Genotype::GetNeuronByInnovation(int innovation) { // search every neuron for this innovation iterate_each ( std::vector::iterator, it, Neurons ) { if ( (*it).GetInnovation() == innovation ) { return &(*it); } } return 0; } ConnectionGene* Genotype::GetConnectionByNeuron(int from_neuron, int to_neuron) { // search for every connection that matches these neurons iterate_each ( std::vector::iterator, it, Connections ) { if ( (*it).GetInputNeuron() == from_neuron && (*it).GetOutputNeuron() == to_neuron ) { return &(*it); } } // nothing found return 0; } bool Genotype::IsRecurrent(int from_neuron, int to_neuron) { return (Neurons[to_neuron].GetY() < Neurons[from_neuron].GetY()) || (Neurons[to_neuron].GetY() == Neurons[from_neuron].GetY() && Neurons[to_neuron].GetY() < Neurons[from_neuron].GetY()); } void Genotype::Output() { static char* typestr[5] = { "unknown", "bias", "input", "output", "hidden" }; printf("\n"); for (size_t i=0;i::const_iterator, it_lhs, Connections ) { bool match = false; iterate_each ( std::vector::const_iterator, it_rhs, genome.Connections ) { if ( (*it_lhs) == (*it_rhs) ) { weight_diff += fabs((*it_lhs).GetWeight() - (*it_rhs).GetWeight()); match = true; break; } } if ( match ) { ++common; } else { ++disjoint_excess; } } size_t lhs_size = Connections.size() + Neurons.size(); size_t rhs_size = genome.Connections.size() + genome.Neurons.size(); size_t N = std::max(lhs_size, rhs_size); float weight_diff_avg = weight_diff / (float)common; return ((float)disjoint_excess/(float)N) + (0.4f * weight_diff_avg); } void Genotype::Mutate(Innovator& innovator) { switch ( Roulette(3,NEAT::pAddConnection,NEAT::pAddNeuron,NEAT::pWeightsMutated)) { case 0: MutateAddConnection(innovator); break; case 1: MutateAddNeuron(innovator); break; case 2: MutateConnectionWeights(); break; default: break; } }