Beispiel #1
0
        /// <summary>
        /// Mutates a random connection by radomizing its weight. Creates connection if there is none.
        /// </summary>
        public void Mutate_WeightRandom()
        {
            if (ConnectionGenes.Count == 0)
            {
                Mutate_Link();
            }
            else
            {
                ConnectionGene connectionGene = ConnectionGenes.RandomValue(Random).Take(1).ElementAt(0);

                if (connectionGene != null)
                {
                    connectionGene.Weight = Pedigree.Mutation_WeightRandom * (Random.NextDouble() * 2 - 1);
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Mutates a random connection splitting it with a node.
        /// </summary>
        public void Mutate_Node()
        {
            if (NodeGenes.Count >= Pedigree.MaxNodes)
            {
                return; //Do nothing if we have max nodes.
            }
            else if (ConnectionGenes.Count == 0)
            {
                return; //Literally cannot make a node.
            }


            ConnectionGene connectionGene = ConnectionGenes.RandomValue(Random).Take(1).ElementAt(0);

            if (connectionGene == null)
            {
                return;
            }


            NodeGene from = NodeGenes[connectionGene.ConnectionGenePattern.From.InnovationNumber];
            NodeGene to   = NodeGenes[connectionGene.ConnectionGenePattern.To.InnovationNumber];

            NodeGene created = Pedigree.Create_NodeGene(connectionGene);

            if (NodeGenes.ContainsKey(created.NodeGenePattern.InnovationNumber))
            {
                return; //TODO maybe retry here as well?
            }

            NodeGenes.Add(created.NodeGenePattern.InnovationNumber, created);


            ConnectionGene created_connectionGene_1 = Pedigree.Create_ConnectionGene(from, created, 1, true); //Default weight of 1.
            ConnectionGene created_connectionGene_2 = Pedigree.Create_ConnectionGene(created, to, connectionGene.Weight, connectionGene.Enabled);

            ConnectionGenes.Remove(connectionGene.ConnectionGenePattern.InnovationNumber);

            ConnectionGenes.Add(created_connectionGene_1.ConnectionGenePattern.InnovationNumber, created_connectionGene_1);
            ConnectionGenes.Add(created_connectionGene_2.ConnectionGenePattern.InnovationNumber, created_connectionGene_2);
        }
Beispiel #3
0
        /// <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 &lt; 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));
        }
Beispiel #4
0
        /// <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);
        }