/// <summary> /// Mutates the genome by creating a new connection. /// </summary> public void Mutate_Link() { for (int i = 0; i < NEAT.LINK_ATTEMPTS; ++i) { NodeGene nodeGene_a = Nodes.GetRandomElement(); NodeGene nodeGene_b = Nodes.GetRandomElement(); if (nodeGene_a.X == nodeGene_b.X) { continue; } ConnectionGene connectionGene; if (nodeGene_a.X < nodeGene_b.X) { connectionGene = new ConnectionGene(nodeGene_a, nodeGene_b, 0); //Temp innovation number. } else { connectionGene = new ConnectionGene(nodeGene_b, nodeGene_a, 0); //Temp innovation number. } if (Connections.Contains(connectionGene)) { continue; } connectionGene = NEAT.CreateConnection(connectionGene.From, connectionGene.To); connectionGene.Weight = NEAT.WEIGHT_RANDOM + (random.NextDouble() * 2 - 1); Connections.Add_Sorted_Gene(connectionGene); //This needs to be sorted otherwise something breaks. return; } }
/// <summary> /// Crosses over the two given Genomes. /// </summary> /// <param name="genome_1">The first Genome.</param> /// <param name="genome_2">The second Genome.</param> /// <param name="random">The random object to use when deciding genes.</param> /// <returns>The crossed-over Genome.</returns> public static Genome CrossOver(Genome genome_1, Genome genome_2, Random random) { if (genome_1.Connections.Size != 0 && genome_2.Connections.Size != 0) //Fixes IndexOutOfRangeException. { if (genome_1.Connections[genome_1.Connections.Size - 1].InnovationNumber < genome_2.Connections[genome_2.Connections.Size - 1].InnovationNumber) { return(CrossOver(genome_2, genome_1, random)); //Handles excess properly. } } else { int first_size = genome_1.Connections.Size; int second_size = genome_2.Connections.Size; if (first_size < second_size) { return(CrossOver(genome_2, genome_1, random)); //Handles excess properly. } } Genome created_genome = genome_1.NEAT.CreateGenome(); #region Connections int index_me = 0; int index_them = 0; while (index_me < genome_1.Connections.Size && index_them < genome_2.Connections.Size) { ConnectionGene connectionGene_me = genome_1.Connections[index_me]; ConnectionGene connectionGene_them = genome_2.Connections[index_them]; int inNum_me = connectionGene_me.InnovationNumber; int inNum_them = connectionGene_them.InnovationNumber; if (inNum_me == inNum_them) //Similar genes, choose either side at random. { index_me++; index_them++; if (random.NextDouble() < .5) { created_genome.Connections.Add(NEAT.CopyConnectionGene(connectionGene_me)); } else { created_genome.Connections.Add(NEAT.CopyConnectionGene(connectionGene_them)); } } else if (inNum_me > inNum_them) //Disjoint gene at them, increase them. { index_them++; //This is hotly debated, but will still work without this. //created_genome.Connections.Add(NEAT.CopyConnectionGene(connectionGene_them)); } else //Disjoint gene at me, increase me. { index_me++; created_genome.Connections.Add(NEAT.CopyConnectionGene(connectionGene_me)); } } //This is why this genome must have the higher innovation number. while (index_me < genome_1.Connections.Size) //Run through the excess connections and add them all. { created_genome.Connections.Add(NEAT.CopyConnectionGene(genome_1.Connections[index_me++])); } #endregion Connections #region Nodes //foreach (ConnectionGene connectionGene in created_genome.Connections) //TODO implement foreach functionality into RandomHashSet for (int i = 0; i < created_genome.Connections.Size; ++i) { ConnectionGene connectionGene = created_genome.Connections[i]; created_genome.Nodes.Add(connectionGene.From); //Finally making use of the uniqness in RandomHashSet. created_genome.Nodes.Add(connectionGene.To); } #endregion Nodes return(created_genome); }
/// <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> public double Distance(Genome genome) { if (Connections.Size != 0 && genome.Connections.Size != 0) //Fixes IndexOutOfRangeException. { if (Connections[Connections.Size - 1].InnovationNumber < genome.Connections[genome.Connections.Size - 1].InnovationNumber) { return(genome.Distance(this)); //Handles excess properly. } } else { int my_size = Connections.Size; int their_size = genome.Connections.Size; if (my_size < their_size) { return(genome.Distance(this)); //Handles excess properly. } } int index_me = 0; int index_them = 0; int num_excess = 0; //The number of excess genes. int num_disjoint = 0; //The number of disjoint genes. double weight_diff = 0; //The weight difference between similar genes. double num_similar = 0; //The number of genes that are similar. bool was_skipped = true; //The band-aid. while (index_me < Connections.Size && index_them < genome.Connections.Size) { was_skipped = false; ConnectionGene connectionGene_me = Connections[index_me]; ConnectionGene connectionGene_them = genome.Connections[index_them]; int inNum_me = connectionGene_me.InnovationNumber; int inNum_them = connectionGene_them.InnovationNumber; if (inNum_me == inNum_them) //Similar genes. { index_me++; index_them++; 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++; num_disjoint++; } else //Disjoint gene at me, increase me. { index_me++; num_disjoint++; } } //This is why this genome must have the higher innovation number. num_excess = Connections.Size - index_me; if (was_skipped) { return(0); } weight_diff /= num_similar; double N = Math.Max(Connections.Size, genome.Connections.Size); N = (N < 20) ? 1 : N; return(NEAT.C1 * (num_excess / N) + NEAT.C2 * (num_disjoint / N) + (NEAT.C3 * weight_diff)); }