Genome Species::ReproduceOne(Population& a_Pop, Parameters& a_Parameters, RNG& a_RNG) { Genome t_baby; // for storing the result ////////////////////////// // Reproduction // Spawn only one baby // this tells us if the baby is a result of mating bool t_mated = false; // There must be individuals there.. ASSERT(NumIndividuals() > 0); // for a species of size 1 we can only mutate // NOTE: but does it make sense since we know this is the champ? if (NumIndividuals() == 1) { t_baby = GetIndividual(a_Parameters, a_RNG); t_mated = false; } // else we can mate else { Genome t_mom = GetIndividual(a_Parameters, a_RNG); // choose whether to mate at all // Do not allow crossover when in simplifying phase if ((a_RNG.RandFloat() < a_Parameters.CrossoverRate) && (a_Pop.GetSearchMode() != SIMPLIFYING)) { // get the father Genome t_dad; bool t_interspecies = false; // There is a probability that the father may come from another species if ((a_RNG.RandFloat() < a_Parameters.InterspeciesCrossoverRate) && (a_Pop.m_Species.size()>1)) { // Find different species (random one) // !!!!!!!!!!!!!!!!! // But the different species must have at least one evaluated individual int t_diffspec = 0; int t_giveup = 64; do { t_diffspec = a_RNG.RandInt(0, static_cast<int>(a_Pop.m_Species.size()-1)); } while ((a_Pop.m_Species[t_diffspec].m_AverageFitness == 0) && (t_giveup--)); if (a_Pop.m_Species[t_diffspec].m_AverageFitness == 0) t_dad = GetIndividual(a_Parameters, a_RNG); else t_dad = a_Pop.m_Species[t_diffspec].GetIndividual(a_Parameters, a_RNG); t_interspecies = true; } else { // Mate within species t_dad = GetIndividual(a_Parameters, a_RNG); // The other parent should be a different one // number of tries to find different parent int t_tries = 32; while(((t_mom.GetID() == t_dad.GetID()) || ((!a_Parameters.AllowClones) && (t_mom.CompatibilityDistance(t_dad, a_Parameters) <= 0.00001)) ) && (t_tries--)) { t_dad = GetIndividual(a_Parameters, a_RNG); } t_interspecies = false; } // OK we have both mom and dad so mate them // Choose randomly one of two types of crossover if (a_RNG.RandFloat() < a_Parameters.MultipointCrossoverRate) { t_baby = t_mom.Mate( t_dad, false, t_interspecies, a_RNG); } else { t_baby = t_mom.Mate( t_dad, true, t_interspecies, a_RNG); } t_mated = true; } // don't mate - reproduce the mother asexually else { t_baby = t_mom; t_mated = false; } } /* if (t_baby.HasDeadEnds()) { std::cout << "Dead ends in baby after crossover" << std::endl; // int p; // std::cin >> p; }*/ // OK we have the baby, so let's mutate it. bool t_baby_is_clone = false; if ((!t_mated) || (a_RNG.RandFloat() < a_Parameters.OverallMutationRate)) MutateGenome(t_baby_is_clone, a_Pop, t_baby, a_Parameters, a_RNG); // We have a new offspring now // give the offspring a new ID t_baby.SetID(a_Pop.GetNextGenomeID()); a_Pop.IncrementNextGenomeID(); // sort the baby's genes t_baby.SortGenes(); // clear the baby's fitness t_baby.SetFitness(0); t_baby.SetAdjFitness(0); t_baby.SetOffspringAmount(0); t_baby.ResetEvaluated(); // debug trap /* if (t_baby.NumLinks() == 0) { std::cout << "No links in baby after reproduction" << std::endl; // int p; // std::cin >> p; } if (t_baby.HasDeadEnds()) { std::cout << "Dead ends in baby after reproduction" << std::endl; // int p; // std::cin >> p; } */ return t_baby; }
// Mutates a genome void Species::MutateGenome( bool t_baby_is_clone, Population &a_Pop, Genome &t_baby, Parameters& a_Parameters, RNG& a_RNG ) { #if 1 // NEW version: // All mutations are mutually exclusive - can't have 2 mutations at once // for example a weight mutation and time constants mutation // or add link and add node and then weight mutation // We will perform roulette wheel selection to choose the type of mutation and will mutate the baby // This method guarantees that the baby will be mutated at least with one mutation enum MutationTypes {ADD_NODE = 0, ADD_LINK, REMOVE_NODE, REMOVE_LINK, CHANGE_ACTIVATION_FUNCTION, MUTATE_WEIGHTS, MUTATE_ACTIVATION_A, MUTATE_ACTIVATION_B, MUTATE_TIMECONSTS, MUTATE_BIASES }; std::vector<int> t_muts; std::vector<double> t_mut_probs; // ADD_NODE; t_mut_probs.push_back( a_Parameters.MutateAddNeuronProb ); // ADD_LINK; t_mut_probs.push_back( a_Parameters.MutateAddLinkProb ); // REMOVE_NODE; t_mut_probs.push_back( a_Parameters.MutateRemSimpleNeuronProb ); // REMOVE_LINK; t_mut_probs.push_back( a_Parameters.MutateRemLinkProb ); // CHANGE_ACTIVATION_FUNCTION; t_mut_probs.push_back( a_Parameters.MutateNeuronActivationTypeProb ); // MUTATE_WEIGHTS; t_mut_probs.push_back( a_Parameters.MutateWeightsProb ); // MUTATE_ACTIVATION_A; t_mut_probs.push_back( a_Parameters.MutateActivationAProb ); // MUTATE_ACTIVATION_B; t_mut_probs.push_back( a_Parameters.MutateActivationBProb ); // MUTATE_TIMECONSTS; t_mut_probs.push_back( a_Parameters.MutateNeuronTimeConstantsProb ); // MUTATE_BIASES; t_mut_probs.push_back( a_Parameters.MutateNeuronBiasesProb ); // Special consideration for phased searching - do not allow certain mutations depending on the search mode // also don't use additive mutations if we just want to get rid of the clones if ((a_Pop.GetSearchMode() == SIMPLIFYING) || t_baby_is_clone) { t_mut_probs[ADD_NODE] = 0; // add node t_mut_probs[ADD_LINK] = 0; // add link } if ((a_Pop.GetSearchMode() == COMPLEXIFYING) || t_baby_is_clone) { t_mut_probs[REMOVE_NODE] = 0; // rem node t_mut_probs[REMOVE_LINK] = 0; // rem link } bool t_mutation_success = false; // repeat until successful while (t_mutation_success == false) { int ChosenMutation = a_RNG.Roulette(t_mut_probs); // Now mutate based on the choice switch(ChosenMutation) { case ADD_NODE: t_mutation_success = t_baby.Mutate_AddNeuron(a_Pop.AccessInnovationDatabase(), a_Parameters, a_RNG); break; case ADD_LINK: t_mutation_success = t_baby.Mutate_AddLink(a_Pop.AccessInnovationDatabase(), a_Parameters, a_RNG); break; case REMOVE_NODE: t_mutation_success = t_baby.Mutate_RemoveSimpleNeuron(a_Pop.AccessInnovationDatabase(), a_RNG); break; case REMOVE_LINK: { // Keep doing this mutation until it is sure that the baby will not // end up having dead ends or no links Genome t_saved_baby = t_baby; bool t_no_links = false, t_has_dead_ends = false; int t_tries = 128; do { t_tries--; if (t_tries <= 0) { t_saved_baby = t_baby; break; // give up } t_saved_baby = t_baby; t_mutation_success = t_saved_baby.Mutate_RemoveLink(a_RNG); t_no_links = t_has_dead_ends = false; if (t_saved_baby.NumLinks() == 0) t_no_links = true; t_has_dead_ends = t_saved_baby.HasDeadEnds(); } while(t_no_links || t_has_dead_ends); t_baby = t_saved_baby; // debugger trap if (t_baby.NumLinks() == 0) { std::cerr << "No links in baby after mutation" << std::endl; } if (t_baby.HasDeadEnds()) { std::cerr << "Dead ends in baby after mutation" << std::endl; } } break; case CHANGE_ACTIVATION_FUNCTION: t_baby.Mutate_NeuronActivation_Type(a_Parameters, a_RNG); t_mutation_success = true; break; case MUTATE_WEIGHTS: t_baby.Mutate_LinkWeights(a_Parameters, a_RNG); t_mutation_success = true; break; case MUTATE_ACTIVATION_A: t_baby.Mutate_NeuronActivations_A(a_Parameters, a_RNG); t_mutation_success = true; break; case MUTATE_ACTIVATION_B: t_baby.Mutate_NeuronActivations_B(a_Parameters, a_RNG); t_mutation_success = true; break; case MUTATE_TIMECONSTS: t_baby.Mutate_NeuronTimeConstants(a_Parameters, a_RNG); t_mutation_success = true; break; case MUTATE_BIASES: t_baby.Mutate_NeuronBiases(a_Parameters, a_RNG); t_mutation_success = true; break; default: t_mutation_success = false; break; } } #else // Old version of the function - added just to test various ways to do mutation bool t_mutation_success = false; // repeat until successful while (t_mutation_success == false) { if (a_RNG.RandFloat() < a_Parameters.MutateAddNeuronProb) t_mutation_success = t_baby.Mutate_AddNeuron(a_Pop.AccessInnovationDatabase(), a_Parameters, a_RNG); else if (a_RNG.RandFloat() < a_Parameters.MutateAddLinkProb) t_mutation_success = t_baby.Mutate_AddLink(a_Pop.AccessInnovationDatabase(), a_Parameters, a_RNG); else { /*if (a_RNG.RandFloat() < a_Parameters.MutateNeuronActivationTypeProb) { t_baby.Mutate_NeuronActivation_Type(a_Parameters, a_RNG); t_mutation_success = true; }*/ if (a_RNG.RandFloat() < a_Parameters.MutateWeightsProb) { t_baby.Mutate_LinkWeights(a_Parameters, a_RNG); t_mutation_success = true; break; } /*case MUTATE_ACTIVATION_A: t_baby.Mutate_NeuronActivations_A(a_Parameters, a_RNG); t_mutation_success = true; break; case MUTATE_ACTIVATION_B: t_baby.Mutate_NeuronActivations_B(a_Parameters, a_RNG); t_mutation_success = true; break; case MUTATE_TIMECONSTS: t_baby.Mutate_NeuronTimeConstants(a_Parameters, a_RNG); t_mutation_success = true; break; case MUTATE_BIASES: t_baby.Mutate_NeuronBiases(a_Parameters, a_RNG); t_mutation_success = true; break;*/ } } #endif }
// Reproduce mates & mutates the individuals of the species // It may access the global species list in the population // because some babies may turn out to belong in another species // that have to be created. // Also calls Birth() for every new baby void Species::Reproduce(Population &a_Pop, Parameters& a_Parameters, RNG& a_RNG) { Genome t_baby; // temp genome for reproduction int t_offspring_count = Rounded(GetOffspringRqd()); // no offspring?! yikes.. dead species! if (t_offspring_count == 0) { // maybe do something else? return; } ////////////////////////// // Reproduction // Spawn t_offspring_count babies bool t_champ_chosen = false; bool t_baby_exists_in_pop = false; while(t_offspring_count--) { bool t_new_individual = true; // if the champ was not chosen, do it now.. if (!t_champ_chosen) { t_baby = m_Individuals[0]; t_champ_chosen = true; t_new_individual = false; } // or if it was, then proceed with the others else { do // - while the baby already exists somewhere in the new population { // this tells us if the baby is a result of mating bool t_mated = false; // There must be individuals there.. ASSERT(NumIndividuals() > 0); // for a species of size 1 we can only mutate // NOTE: but does it make sense since we know this is the champ? if (NumIndividuals() == 1) { t_baby = GetIndividual(a_Parameters, a_RNG); t_mated = false; } // else we can mate else { do // keep trying to mate until a good offspring is produced { Genome t_mom = GetIndividual(a_Parameters, a_RNG); // choose whether to mate at all // Do not allow crossover when in simplifying phase if ((a_RNG.RandFloat() < a_Parameters.CrossoverRate) && (a_Pop.GetSearchMode() != SIMPLIFYING)) { // get the father Genome t_dad; bool t_interspecies = false; // There is a probability that the father may come from another species if ((a_RNG.RandFloat() < a_Parameters.InterspeciesCrossoverRate) && (a_Pop.m_Species.size()>1)) { // Find different species (random one) // !!!!!!!!!!!!!!!!! int t_diffspec = a_RNG.RandInt(0, static_cast<int>(a_Pop.m_Species.size()-1)); t_dad = a_Pop.m_Species[t_diffspec].GetIndividual(a_Parameters, a_RNG); t_interspecies = true; } else { // Mate within species t_dad = GetIndividual(a_Parameters, a_RNG); // The other parent should be a different one // number of tries to find different parent int t_tries = 32; if (!a_Parameters.AllowClones) while(((t_mom.GetID() == t_dad.GetID()) || (t_mom.CompatibilityDistance(t_dad, a_Parameters) < 0.00001) ) && (t_tries--)) { t_dad = GetIndividual(a_Parameters, a_RNG); } else while(((t_mom.GetID() == t_dad.GetID()) ) && (t_tries--)) { t_dad = GetIndividual(a_Parameters, a_RNG); } t_interspecies = false; } // OK we have both mom and dad so mate them // Choose randomly one of two types of crossover if (a_RNG.RandFloat() < a_Parameters.MultipointCrossoverRate) { t_baby = t_mom.Mate( t_dad, false, t_interspecies, a_RNG); } else { t_baby = t_mom.Mate( t_dad, true, t_interspecies, a_RNG); } t_mated = true; } // don't mate - reproduce the mother asexually else { t_baby = t_mom; t_mated = false; } } while (t_baby.HasDeadEnds() || (t_baby.NumLinks() == 0)); // in case of dead ends after crossover we will repeat crossover // until it works } // Mutate the baby if ((!t_mated) || (a_RNG.RandFloat() < a_Parameters.OverallMutationRate)) MutateGenome(t_baby_exists_in_pop, a_Pop, t_baby, a_Parameters, a_RNG); // Check if this baby is already present somewhere in the offspring // we don't want that t_baby_exists_in_pop = false; // Unless of course, we want if (!a_Parameters.AllowClones) { for(unsigned int i=0; i<a_Pop.m_TempSpecies.size(); i++) { for(unsigned int j=0; j<a_Pop.m_TempSpecies[i].m_Individuals.size(); j++) { if ( (t_baby.CompatibilityDistance(a_Pop.m_TempSpecies[i].m_Individuals[j], a_Parameters) < 0.00001) // identical genome? ) { t_baby_exists_in_pop = true; break; } } } } } while (t_baby_exists_in_pop); // end do } // Final place to test for problems // If there is anything wrong here, we will just // pick a random individual and leave him unchanged if ((t_baby.NumLinks() == 0) || t_baby.HasDeadEnds()) { t_baby = GetIndividual(a_Parameters, a_RNG); t_new_individual = false; } if (t_new_individual) { // We have a new offspring now // give the offspring a new ID t_baby.SetID(a_Pop.GetNextGenomeID()); a_Pop.IncrementNextGenomeID(); // sort the baby's genes t_baby.SortGenes(); // clear the baby's fitness t_baby.SetFitness(0); t_baby.SetAdjFitness(0); t_baby.SetOffspringAmount(0); t_baby.ResetEvaluated(); } ////////////////////////////////// // put the baby to its species // ////////////////////////////////// // before Reproduce() is invoked, it is assumed that a // clone of the population exists with the name of m_TempSpecies // we will store results there. // after all reproduction completes, the original species will be replaced back bool t_found = false; std::vector<Species>::iterator t_cur_species = a_Pop.m_TempSpecies.begin(); // No species yet? if (t_cur_species == a_Pop.m_TempSpecies.end()) { // create the first species and place the baby there a_Pop.m_TempSpecies.push_back( Species(t_baby, a_Pop.GetNextSpeciesID())); a_Pop.IncrementNextSpeciesID(); } else { // try to find a compatible species Genome t_to_compare = t_cur_species->GetRepresentative(); t_found = false; while((t_cur_species != a_Pop.m_TempSpecies.end()) && (!t_found)) { if (t_baby.IsCompatibleWith( t_to_compare, a_Parameters)) { // found a compatible species t_cur_species->AddIndividual(t_baby); t_found = true; // the search is over } else { // keep searching for a matching species t_cur_species++; if (t_cur_species != a_Pop.m_TempSpecies.end()) { t_to_compare = t_cur_species->GetRepresentative(); } } } // if couldn't find a match, make a new species if (!t_found) { a_Pop.m_TempSpecies.push_back( Species(t_baby, a_Pop.GetNextSpeciesID())); a_Pop.IncrementNextSpeciesID(); } } } }