/// <summary> /// Crosses over this genome with the given genome, giving both the same score. Gives the created genome this genome's random. /// </summary> /// <param name="genome">The genome to cross over with.</param> /// <returns>The crossed-over genome.</returns> /// <exception cref="ArgumentNullException">When the given genome is null.</exception> public Genome Crossover(Genome genome) { return(Crossover(0, genome, 0, Random)); }
/// <summary> /// Gets the distance between this genome and the given genome. Higher = less compatible. /// </summary> /// <param name="genome">The genome to compare to.</param> /// <returns>The distance between this genome and the given genome.</returns> /// <remarks> /// The distance d can be measured by the following equation: /// <para/> /// d = c1(E / N) + c2(D / N) + c3 * W /// <para/> /// Where: /// d = distance | /// E = # excess genes | /// D = # of disjoint genes | /// W = weight difference of similar genes | /// N = # of genes in largest genome (this or them), 1 if #genes < 20 | /// c_ = constant for adjusting /// </remarks> /// <exception cref="ArgumentNullException">When the given genome is null.</exception> public double Distance(Genome genome) { Helpers.ThrowOnNull(genome, "genome"); int index_me = 0; //Leaving indexes from old implementation due to their mathematical usefulness later. int index_them = 0; int num_excess = 0; //The number of excess genes. int num_disjoint = 0; //The number of disjoint genes. double num_similar = 0; //The number of genes that are similar. double weight_diff = 0; //The weight difference between similar genes. SortedDictionary <int, ConnectionGene> .ValueCollection.Enumerator enumerator_me = ConnectionGenes.Values.GetEnumerator(); SortedDictionary <int, ConnectionGene> .ValueCollection.Enumerator enumerator_them = genome.ConnectionGenes.Values.GetEnumerator(); enumerator_me.MoveNext(); //Preps for first current. enumerator_them.MoveNext(); //Step through both genomes and find out how different they are. //This method is run a lot, so we want to do this by innovation number for efficiency. while (index_me < ConnectionGenes.Count && index_them < genome.ConnectionGenes.Count) { ConnectionGene connectionGene_me = enumerator_me.Current; ConnectionGene connectionGene_them = enumerator_them.Current; int inNum_me = connectionGene_me.ConnectionGenePattern.InnovationNumber; int inNum_them = connectionGene_them.ConnectionGenePattern.InnovationNumber; if (inNum_me == inNum_them) //Similar genes. { ++index_me; ++index_them; enumerator_me.MoveNext(); enumerator_them.MoveNext(); ++num_similar; weight_diff += Math.Abs(connectionGene_me.Weight - connectionGene_them.Weight); } else if (inNum_me > inNum_them) //Disjoint gene at them, increase them. { ++index_them; enumerator_them.MoveNext(); ++num_disjoint; } else //Disjoint gene at me, increase me. { ++index_me; enumerator_me.MoveNext(); ++num_disjoint; } } //Count excess genes. if (index_me < ConnectionGenes.Count) //We have leftover genes, use our count. { num_excess = ConnectionGenes.Count - index_me; } else if (index_them < genome.ConnectionGenes.Count) //They have leftover genes, use their count. { num_excess = genome.ConnectionGenes.Count - index_them; } //There is no else because if they have the same number of genes, there is no excess, so use the default 0. if (num_similar > 0) { //The weight_diff would be 0 if num_similar was too, so only do this if they aren't 0. weight_diff /= num_similar; } double N = Math.Max(ConnectionGenes.Count, genome.ConnectionGenes.Count); N = (N < 20) ? 1 : N; //Only needed for large networks. return(Pedigree.C1 * (num_excess / N) + Pedigree.C2 * (num_disjoint / N) + (Pedigree.C3 * weight_diff)); }
/// <summary> /// Crosses over this genome with the given genome. /// </summary> /// <param name="my_score">Score of this genome. See remarks.</param> /// <param name="genome">The genome to cross over with.</param> /// <param name="their_score">Score of the given genome. See remarks.</param> /// <param name="random">The random object for the created genome.</param> /// <returns>The crossed-over genome.</returns> /// <remarks> /// When the given scores are equal, use the following rules: /// <list type="bullet"> /// <item>On similar connection genes: See <see cref="NEAT.Genetic.Tracker.Pedigree.UniformCrossover"/>.</item> /// <item>On disjoint connection genes: Adds all to the created genome.</item> /// <item>On excess connection genes: Adds all to the created genome.</item> /// </list> /// /// When the given scores are not equal, use the following rules: /// <list type="bullet"> /// <item>On similar connection genes: See <see cref="NEAT.Genetic.Tracker.Pedigree.UniformCrossover"/>.</item> /// <item>On disjoint connection genes: Adds genes from parent with higher score.</item> /// <item>On excess connection genes: Adds genes from parent with higher score.</item> /// </list> /// </remarks> /// <exception cref="ArgumentNullException">When the genome or random is null.</exception> public Genome Crossover(double my_score, Genome genome, double their_score, Random random) { Helpers.ThrowOnNull(genome, "genome"); Helpers.ThrowOnNull(random, "random"); Genome created_genome = new Genome(Pedigree, random); #region ConnectionGenes SortedDictionary <int, ConnectionGene> .ValueCollection.Enumerator enumerator_me = ConnectionGenes.Values.GetEnumerator(); SortedDictionary <int, ConnectionGene> .ValueCollection.Enumerator enumerator_them = genome.ConnectionGenes.Values.GetEnumerator(); enumerator_me.MoveNext(); //Preps for first current. enumerator_them.MoveNext(); //Step through both genomes and cross them over randomly. while (enumerator_me.Current != null && enumerator_them.Current != null) { ConnectionGene connectionGene_me = enumerator_me.Current; ConnectionGene connectionGene_them = enumerator_them.Current; int inNum_me = connectionGene_me.ConnectionGenePattern.InnovationNumber; int inNum_them = connectionGene_them.ConnectionGenePattern.InnovationNumber; if (inNum_me == inNum_them) //Similar genes, choose either side at random. { enumerator_me.MoveNext(); enumerator_them.MoveNext(); if (Pedigree.UniformCrossover) { if (random.NextDouble() < .5) { created_genome.ConnectionGenes.Add(inNum_me, Pedigree.Copy_ConnectionGene(connectionGene_me)); } else { created_genome.ConnectionGenes.Add(inNum_them, Pedigree.Copy_ConnectionGene(connectionGene_them)); } } else { created_genome.ConnectionGenes.Add(inNum_me, Pedigree.Create_ConnectionGene(connectionGene_me.ConnectionGenePattern, (connectionGene_me.Weight + connectionGene_them.Weight) / 2, (random.NextDouble() < .5) ? connectionGene_me.Enabled : connectionGene_them.Enabled)); } } else if (inNum_me > inNum_them) //Disjoint gene at them, add this gene if allowed. { enumerator_them.MoveNext(); if (Math.Abs(my_score - their_score) < Pedigree.Crossover_ScoreDelta || their_score > my_score) { created_genome.ConnectionGenes.Add(inNum_them, Pedigree.Copy_ConnectionGene(connectionGene_them)); } } else //Disjoint gene at me, add this gene if allowed. { enumerator_me.MoveNext(); if (Math.Abs(my_score - their_score) < Pedigree.Crossover_ScoreDelta || my_score > their_score) { created_genome.ConnectionGenes.Add(inNum_me, Pedigree.Copy_ConnectionGene(connectionGene_me)); } } } //Run through the excess connections and add them all if allowed. if (enumerator_me.Current != null) //We have leftover genes, add ours if allowed. { if (Math.Abs(my_score - their_score) < Pedigree.Crossover_ScoreDelta || my_score > their_score) //Check legality. { do { created_genome.ConnectionGenes.Add(enumerator_me.Current.ConnectionGenePattern.InnovationNumber, Pedigree.Copy_ConnectionGene(enumerator_me.Current)); }while (enumerator_me.MoveNext()); } } else if (enumerator_them.Current != null) //They have leftover genes, add theirs if allowed. { if (Math.Abs(my_score - their_score) < Pedigree.Crossover_ScoreDelta || their_score > my_score) //Check legality. { do { created_genome.ConnectionGenes.Add(enumerator_them.Current.ConnectionGenePattern.InnovationNumber, Pedigree.Copy_ConnectionGene(enumerator_them.Current)); }while (enumerator_them.MoveNext()); } } //There is no else because if they have the same number of genes, there is no excess, so don't do anything. #endregion ConnectionGenes #region NodeGenes //Input/Output nodes and bias node. for (int i = 1; i <= Pedigree.Num_InputNodes + Pedigree.Num_OutputNodes + 1; ++i) //The +1 handles the bias node. { created_genome.NodeGenes.Add(i, NodeGenes[i]); } //Every other relavent node. foreach (ConnectionGene connectionGene in created_genome.ConnectionGenes.Values) { ConnectionGenePattern pattern = connectionGene.ConnectionGenePattern; if (!created_genome.NodeGenes.ContainsKey(pattern.From.InnovationNumber)) { NodeGene nodeGene_toCopy; try { nodeGene_toCopy = NodeGenes[pattern.From.InnovationNumber]; } catch (KeyNotFoundException) { nodeGene_toCopy = genome.NodeGenes[pattern.From.InnovationNumber]; } created_genome.NodeGenes.Add(pattern.From.InnovationNumber, Pedigree.Copy_NodeGene(nodeGene_toCopy)); } if (!created_genome.NodeGenes.ContainsKey(pattern.To.InnovationNumber)) { NodeGene nodeGene_toCopy; try { nodeGene_toCopy = NodeGenes[pattern.To.InnovationNumber]; } catch (KeyNotFoundException) { nodeGene_toCopy = genome.NodeGenes[pattern.To.InnovationNumber]; } created_genome.NodeGenes.Add(pattern.To.InnovationNumber, Pedigree.Copy_NodeGene(nodeGene_toCopy)); } } #endregion NodeGenes return(created_genome); }