Beispiel #1
0
        /// <summary>
        /// Toggles the frozen status of a single gene if possible.
        /// </summary>
        /// <param name="genome">The genome to be mutated.</param>
        /// <param name="rando">A random number generator.</param>
        public static void MutateToggleFreeze(Genome genome, Random rando)
        {
            int  geneId = rando.Next(genome.Size());
            Gene gene   = genome.Genes[geneId];

            gene.Frozen = !gene.Frozen;

            if (!genome.Validate(true))
            {
                gene.Frozen = !gene.Frozen;
            }
        }
Beispiel #2
0
        /// <summary>
        /// Common genes are selected at random.
        /// </summary>
        /// <param name="child">The Gene object where information will be stored.</param>
        /// <param name="stronger">The stronger of the two parents.</param>
        /// <param name="weaker">The weaker of the two parents.</param>
        /// <param name="rando">A random number generator.</param>
        private static void MateMultiPointRegular(Gene child, Gene stronger, Gene weaker, Random rando)
        {
            if (rando.NextDouble() < SelectRandomlyTheStronger)
            {
                child.Copy(stronger);
            }
            else
            {
                child.Copy(weaker);
            }

            child.Frozen = false;
        }
Beispiel #3
0
        /// <summary>
        /// Get a unique innovation number based on the innovation numbers
        /// of the start and end node.
        /// This is used for historical marking.
        /// </summary>
        private static int GetInnovationNumber(Gene target)
        {
            Gene matchingGene = kindsOfGenes.FirstOrDefault(x =>
                                                            (x.StartNode.InnovationNb == target.StartNode.InnovationNb) &&
                                                            (x.EndNode.InnovationNb == target.EndNode.InnovationNb));

            if (matchingGene != null)
            {
                return(matchingGene.InnovationNb);
            }

            kindsOfGenes.Add(target);
            return(kindsOfGenes.Count - 1);
        }
Beispiel #4
0
        /// <summary>
        /// Mutates a genome by breaking a connection up into two separate connections.
        /// </summary>
        /// <param name="genome">The genome to be modified.</param>
        /// <param name="innovationsSeen">A list of previously seen innovations.</param>
        /// <param name="rando">A random number generator.</param>
        public static void MutateAddNode(Genome genome, InnovationPool innovationsSeen, Random rando)
        {
            List <Gene> possibleConnections = new List <Gene>(genome.Genes);

            possibleConnections.RemoveAll(x => x.Frozen || NodePool.FindById(x.link.InNode).Type == NodeType.BIAS);

            if (possibleConnections.Count == 0)
            {
                return;
            }

            //TODO: Note in original algorithm saying uniform distribution is not optimal here.
            Gene geneToSplit = possibleConnections[rando.Next(possibleConnections.Count)];

            geneToSplit.Frozen = true;

            ActivationStyle       style      = ActivationFunctions.ChooseActivationStyle(rando);
            InnovationInformation innovation = new InnovationInformation(geneToSplit.link, style);

            int firstConId  = -1;
            int secondConId = -1;

            int registeredInnovationId = innovationsSeen.FindByInnovation(innovation);

            if (registeredInnovationId == -1)
            {
                int newNodeId = NodePool.Add(new NodeInformation(NodeType.HIDDEN, style));

                ConnectionInformation firstConnect = new ConnectionInformation(geneToSplit.link.InNode, newNodeId);
                firstConId = ConnectionPool.Add(firstConnect);

                ConnectionInformation secondConnect = new ConnectionInformation(newNodeId, geneToSplit.link.OutNode);
                secondConId = ConnectionPool.Add(secondConnect);

                innovation.NewNodeDetails.NewNodeId          = newNodeId;
                innovation.NewNodeDetails.FirstConnectionId  = firstConId;
                innovation.NewNodeDetails.SecondConnectionId = secondConId;
                innovationsSeen.Add(innovation);
            }
            else
            {
                InnovationInformation registeredInnovation = innovationsSeen.FindById(registeredInnovationId);
                firstConId  = registeredInnovation.NewNodeDetails.FirstConnectionId;
                secondConId = registeredInnovation.NewNodeDetails.SecondConnectionId;
            }

            genome.Genes.Add(new Gene(ConnectionPool.FindById(firstConId), firstConId, 1.0, false));
            genome.Genes.Add(new Gene(ConnectionPool.FindById(secondConId), secondConId, geneToSplit.Weight, false));
        }
Beispiel #5
0
        /// <summary>
        /// Validates a single gene.
        /// </summary>
        /// <param name="gene">The gene to validate.</param>
        /// <param name="excludeFrozen">Exclude frozen genes from validation (true network).</param>
        /// <returns>True if the gene is connected to an input and output node.</returns>
        private bool ValidateGene(Gene gene, bool excludeFrozen)
        {
            List <Gene> checkedGenes = new List <Gene>();

            if (!ValidateGeneForward(gene, checkedGenes, excludeFrozen))
            {
                return(false);
            }
            checkedGenes = new List <Gene>();
            if (!ValidateGeneBackward(gene, checkedGenes, excludeFrozen))
            {
                return(false);
            }
            return(true);
        }
        public static void DisplayGene(Gene gene, EditorGeneData editorGeneData)
        {
            editorGeneData.isFoldedOut = EditorGUILayout.Foldout(editorGeneData.isFoldedOut, "Gene: " + gene.InnovationNb);
            if (!editorGeneData.isFoldedOut)
            {
                return;
            }

            EditorGUI.indentLevel++;

            EditorGUILayout.Toggle("Is enabled: ", gene.IsEnabled);
            EditorGUILayout.LabelField("InNode: " + gene.StartNode.InnovationNb);
            EditorGUILayout.LabelField("OutNode: " + gene.EndNode.InnovationNb);

            EditorGUI.indentLevel--;
        }
Beispiel #7
0
        /// <summary>
        /// Unfreezes a random frozen gene if possible.
        /// </summary>
        /// <param name="genome">The genome to be mutated.</param>
        /// <param name="rando">A random number generator.</param>
        public static void MutateUnFreeze(Genome genome, Random rando)
        {
            List <Gene> possibilities = genome.Genes.Where(x => x.Frozen).ToList();

            if (possibilities.Count == 0)
            {
                return;
            }

            int  possibleId = rando.Next(possibilities.Count);
            Gene gene       = possibilities[possibleId];

            gene.Frozen = false;

            if (!genome.Validate(true))
            {
                gene.Frozen = !gene.Frozen;
            }
        }
Beispiel #8
0
        /// <summary>
        /// Initialize the network with the starting genes.
        /// All inputs, but the bias, will be connected to output neurons.
        /// </summary>
        private void InitStartingGenes(float geneWeightRandomVal)
        {
            Genes = new List <Gene>(InputCount * OutputCount);
            var inputNeurons  = InputNeurons.ToArray();
            var outputNeurons = OutputNeurons.ToArray();

            for (int i = 0; i < InputCount; i++)
            {
                for (int j = 0; j < OutputCount; j++)
                {
                    Gene newGene = new Gene(
                        inputNeurons[i],
                        outputNeurons[j],
                        Random.Range(-geneWeightRandomVal, geneWeightRandomVal)
                        );

                    Genes.Add(newGene);
                    inputNeurons[i].OutGenes.Add(newGene);
                    outputNeurons[j].InGenes.Add(newGene);
                }
            }
        }
Beispiel #9
0
        /// <summary>
        /// Validates that a gene is connected to an output node.
        /// </summary>
        /// <param name="gene">The gene being validated.</param>
        /// <param name="pathsChecked">A list of genes that have been traversed in the recursion.</param>
        /// <param name="excludeFrozen">Exclude frozen genes from validation (true network).</param>
        /// <returns>True if the gene is connected to an output node.</returns>
        private bool ValidateGeneForward(Gene gene, List <Gene> pathsChecked, bool excludeFrozen)
        {
            pathsChecked.Add(gene);

            if (NodePool.FindById(gene.link.OutNode).Type == NodeType.OUTPUT)
            {
                return(true);
            }

            foreach (var path in Genes.Where(x => x.link.InNode == gene.link.OutNode && !pathsChecked.Contains(x)))
            {
                if (excludeFrozen && path.Frozen)
                {
                    continue;
                }
                if (ValidateGeneForward(path, pathsChecked, excludeFrozen))
                {
                    return(true);
                }
            }

            return(false);
        }
Beispiel #10
0
        /// <summary>
        /// Yarr... this be copied almost exactly as the original NEAT.
        /// In the original NEAT, none of the settings had chance this would happen.
        /// </summary>
        /// <param name="style">The specific type of multi-point mating style.</param>
        /// <param name="mother">A parent genome.</param>
        /// <param name="father">A parent genome.</param>
        /// <param name="rando">A random number generator.</param>
        /// <returns>A product of the given multi-point mating style.</returns>
        private static Genome MateSinglePoint(MatingStyle style, Genome mother, Genome father, Random rando)
        {
            mother.SortGenesByConnectionId();
            father.SortGenesByConnectionId();

            Genome largerParent  = mother;
            Genome smallerParent = father;

            if (father.Size() > mother.Size())
            {
                largerParent  = father;
                smallerParent = mother;
            }

            List <Gene> childGenes       = new List <Gene>();
            int         largerIndex      = 0;
            int         smallerIndex     = 0;
            int         crossoverPoint   = rando.Next(smallerParent.Size());
            int         crossoverCounter = 0;

            while (smallerParent.Size() != smallerIndex)
            {
                Gene chosenGene = new Gene();

                if (largerIndex == largerParent.Size())
                {
                    chosenGene.Copy(smallerParent.Genes[smallerIndex]);
                    smallerIndex++;
                }
                else if (smallerIndex == smallerParent.Size())
                {
                    //Should never happen
                    chosenGene.Copy(largerParent.Genes[largerIndex]);
                    largerIndex++;
                }
                else
                {
                    int largerGeneId  = largerParent.Genes[largerIndex].Id;
                    int smallerGeneId = smallerParent.Genes[smallerIndex].Id;

                    if (largerGeneId == smallerGeneId)
                    {
                        if (crossoverCounter < crossoverPoint)
                        {
                            chosenGene.Copy(largerParent.Genes[largerIndex]);
                        }
                        else if (crossoverCounter > crossoverPoint)
                        {
                            chosenGene.Copy(smallerParent.Genes[smallerIndex]);
                        }
                        else
                        {
                            switch (style)
                            {
                            case MatingStyle.SinglePoint:
                                //Function is equivalent to what is needed here despite the misleading name.
                                MateMultiPointAverage(chosenGene, largerParent.Genes[largerIndex], smallerParent.Genes[smallerIndex], rando);
                                break;

                            default:
                                throw new Exception("MateSinglePoint was not given a valid mating style.");
                            }
                        }


                        chosenGene.Frozen = false;
                        //The NEAT implementation did not call for a chance this would happen. It just did it. [ NEAT.1.2.1 => Genome::mate_singlepoint(...) ]
                        if (largerParent.Genes[largerIndex].Frozen || smallerParent.Genes[smallerIndex].Frozen)
                        {
                            chosenGene.Frozen = true;
                        }

                        largerIndex++;
                        smallerIndex++;
                        crossoverCounter++;
                    }
                    else if (largerGeneId < smallerGeneId)
                    {
                        if (crossoverCounter < crossoverPoint)
                        {
                            chosenGene.Copy(largerParent.Genes[largerIndex]);
                            largerIndex++;
                            crossoverCounter++;
                        }
                        else
                        {
                            chosenGene.Copy(smallerParent.Genes[smallerIndex]);
                            smallerIndex++;
                        }
                    }
                    else
                    {
                        smallerIndex++;
                        continue;
                    }
                }

                if (GeneIsUnique(chosenGene, childGenes))
                {
                    childGenes.Add(chosenGene);
                }
            }

            return(new Genome(childGenes));
        }
Beispiel #11
0
        /// <summary>
        /// Logic for disjoint/excess genes between mother/father in multi-point mating styles.
        /// Delegates common genes to a more specific function.
        /// </summary>
        /// <param name="style">The specific type of multi-point mating style.</param>
        /// <param name="mother">A parent genome.</param>
        /// <param name="father">A parent genome.</param>
        /// <param name="rando">A random number generator.</param>
        /// <returns>A product of the given multi-point mating style.</returns>
        private static Genome MateMultiPoint(MatingStyle style, Genome mother, Genome father, Random rando)
        {
            mother.SortGenesByConnectionId();
            father.SortGenesByConnectionId();

            Genome strongerParent = mother;
            Genome weakerParent   = father;

            if (father.Fitness.Score > mother.Fitness.Score)
            {
                strongerParent = father;
                weakerParent   = mother;
            }

            List <Gene> childGenes    = new List <Gene>();
            int         strongerIndex = 0;
            int         weakerIndex   = 0;

            while (strongerIndex < strongerParent.Size() || weakerIndex < weakerParent.Size())
            {
                Gene chosenGene = new Gene();

                if (strongerIndex == strongerParent.Size())
                {
                    break;
                }
                else if (weakerIndex == weakerParent.Size())
                {
                    chosenGene.Copy(strongerParent.Genes[strongerIndex]);
                    strongerIndex++;
                }
                else
                {
                    int strongerGeneId = strongerParent.Genes[strongerIndex].Id;
                    int weakerGeneId   = weakerParent.Genes[weakerIndex].Id;

                    if (strongerGeneId == weakerGeneId)
                    {
                        switch (style)
                        {
                        case MatingStyle.MultiPoint:
                            MateMultiPointRegular(chosenGene, strongerParent.Genes[strongerIndex], weakerParent.Genes[weakerIndex], rando);
                            break;

                        case MatingStyle.MultiPointAverage:
                            MateMultiPointAverage(chosenGene, strongerParent.Genes[strongerIndex], weakerParent.Genes[weakerIndex], rando);
                            break;

                        default:
                            throw new Exception("MateMultiPoint was not given a valid mating style.");
                        }

                        //TODO: If both parents freeze their kid... should it be automatically frozen?
                        chosenGene.Frozen = false;
                        if (strongerParent.Genes[strongerIndex].Frozen && weakerParent.Genes[weakerIndex].Frozen)
                        {
                            chosenGene.Frozen = true;
                        }
                        else if (strongerParent.Genes[strongerIndex].Frozen || weakerParent.Genes[weakerIndex].Frozen)
                        {
                            if (rando.NextDouble() < FreezeGeneOfFrozenParentProbability)
                            {
                                chosenGene.Frozen = true;
                            }
                        }

                        strongerIndex++;
                        weakerIndex++;
                    }
                    else if (strongerGeneId < weakerGeneId)
                    {
                        chosenGene.Copy(strongerParent.Genes[strongerIndex]);
                        strongerIndex++;
                    }
                    else
                    {
                        weakerIndex++;
                        continue;
                    }
                }

                if (GeneIsUnique(chosenGene, childGenes))
                {
                    childGenes.Add(chosenGene);
                }
            }

            return(new Genome(childGenes));
        }
        /// <summary>
        /// Classic compatibility function as implemented in the original NEAT.
        /// </summary>
        /// <param name="p1">The first genome.</param>
        /// <param name="p2">The second genome.</param>
        /// <returns>A measure of how far apart the two Genomes are.</returns>
        public static double Compatibility(Genome p1, Genome p2)
        {
            p1.SortGenesByConnectionId();
            p2.SortGenesByConnectionId();

            int p1Gene          = 0;
            int p2Gene          = 0;
            int p1End           = p1.Size();
            int p2End           = p2.Size();
            int disjointCount   = 0;
            int excessCount     = 0;
            int commonGeneCount = 0;

            double totalWeightDifferences = 0;

            while (p1Gene != p1End || p2Gene != p2End)
            {
                if (p1Gene == p1End)
                {
                    p2Gene++;
                    excessCount++;
                }
                else if (p2Gene == p2End)
                {
                    p1Gene++;
                    excessCount++;
                }
                else
                {
                    Gene g1 = p1.Genes[p1Gene];
                    Gene g2 = p2.Genes[p2Gene];

                    if (g1.Id == g2.Id)
                    {
                        commonGeneCount++;
                        totalWeightDifferences += Math.Abs(g1.Weight - g2.Weight);
                        p1Gene++;
                        p2Gene++;
                    }
                    else if (g1.Id < g2.Id)
                    {
                        disjointCount++;
                        p1Gene++;
                    }
                    else
                    {
                        disjointCount++;
                        p2Gene++;
                    }
                }
            }

            int N = 1;

            if (p1End >= GenomeSizeNormalizationThresh || p2End >= GenomeSizeNormalizationThresh)
            {
                N = Math.Max(p1End, p2End);
            }

            double ret =
                DisjointCoefficient * disjointCount / N +
                ExcessCoefficient * excessCount / N +
                AvgWeightDiffCoefficient * (totalWeightDifferences / commonGeneCount);

            return(ret);
        }
Beispiel #13
0
 public Gene(Gene other)
 {
     GeneInit(other.StartNode, other.EndNode, other.Weight, other.IsEnabled);
     InnovationNb = other.InnovationNb;
 }
Beispiel #14
0
 /// <summary>
 /// Remove gene from the network with all its dependencies.
 /// </summary>
 public Gene RemoveGene(Gene targetGene)
 {
     targetGene.Disconnect();
     Genes.Remove(targetGene);
     return(targetGene);
 }
Beispiel #15
0
        /// <summary>
        /// Constructs the network using the implied topography.
        /// </summary>
        /// <param name="allGenes">A list of all the genes in the genome.</param>
        /// <param name="sensorNodeIndices">An ordered list of sensor nodes needed.</param>
        /// <param name="outputNodeIndices">An ordered list of output nodes needed.</param>
        public Network(List <Gene> allGenes, Dictionary <int, NodeInformation> allNodes)
        {
            //TODO: Fix comments.
            foreach (var node in allNodes)
            {
                if (node.Value.IsInput())
                {
                    SensorNodes.Add(node.Key, new Node(node.Key, node.Value));
                }
                else if (node.Value.Type == NodeType.OUTPUT)
                {
                    OutputNodes.Add(node.Key, new Node(node.Key, node.Value));
                }
            }

            for (int i = 0; i < allGenes.Count; i++)
            {
                Gene gene = allGenes[i];

                if (gene.Frozen)
                {
                    continue;
                }

                ConnectionInformation link = gene.link;
                Node from = null;
                Node to   = null;

                foreach (var node in SensorNodes)
                {
                    if (node.Key == link.InNode)
                    {
                        from = node.Value;
                    }
                    if (node.Key == link.OutNode)
                    {
                        to = node.Value;
                    }
                }
                foreach (var node in HiddenNodes)
                {
                    if (node.Key == link.InNode)
                    {
                        from = node.Value;
                    }
                    if (node.Key == link.OutNode)
                    {
                        to = node.Value;
                    }
                }
                foreach (var node in OutputNodes)
                {
                    if (node.Key == link.InNode)
                    {
                        from = node.Value;
                    }
                    if (node.Key == link.OutNode)
                    {
                        to = node.Value;
                    }
                }

                if (from == null)
                {
                    from = new Node(link.InNode, allNodes[link.InNode]);
                    HiddenNodes.Add(from.Id, from);
                }
                if (to == null)
                {
                    to = new Node(link.OutNode, allNodes[link.OutNode]);

                    if (!HiddenNodes.Select(x => x.Key).ToList().Contains(to.Id))
                    {
                        HiddenNodes.Add(to.Id, to);
                    }
                }

                Connections.Add(new Connection(from, to, gene.Weight));
            }
        }