Exemple #1
0
        /// <summary>
        /// Rebuild structure so we get layer information to make sure there are not circular dependencies.
        /// </summary>
        private void RebuildStructure()
        {
            // Clear the temporary node storage.
            TempNodes.Clear();

            // Load all nodes for each connection gene.
            ConnectionGenes.ForEach(LoadNodes);

            // Find all the node in the connection genes.
            TempNodes.AddRange(ConnectionGenes.SelectMany(p => new[] { p.InNode, p.OutNode }).Distinct());

            // Add all input nodes
            TempNodes.AddRange(Inputs.Select(p => p.InputNode));

            // Add all output nodes.
            TempNodes.AddRange(Outputs.Select(p => p.OutputNode));

            //Set all nodes to the lowest value so they don't keep older values.
            foreach (Node node in TempNodes)
            {
                if (!IsNodeAnOutputNode(node))
                {
                    SetLayer(node, uint.MinValue, true);
                }
            }

            foreach (Node node in TempNodes)
            {
                if (IsNodeAnOutputNode(node))
                {
                    // For each node of type OutputNode set the layer to 0
                    SetLayer(node, 0);
                }
                else if (IsNodeAnInputNode(node))
                {
                    // For each node of type InputNode set the layer to minimum value and force to true.
                    SetLayer(node, uint.MaxValue, true);
                }
            }
        }
        /// <summary>
        /// Rebuild structure so we get layer information to make sure there are not circular dependencies.
        /// </summary>
        private void RebuildStructure()
        {
            // Clear the temporary node storage.
            _tempNodes.Clear();

            // Load all nodes for each connection gene.
            ConnectionGenes.ForEach(LoadNodes);

            // Find all the node in the connection genes.
            _tempNodes.AddRange(ConnectionGenes.SelectMany(p => new[] { p.InNode, p.OutNode }).Distinct());

            // Add all input nodes
            _tempNodes.AddRange(Inputs.Select(p => p.InputNode));

            // Add all output nodes.
            _tempNodes.AddRange(Outputs.Select(p => p.OutputNode));

            // For each node of type OutputNode set the layer to 0, otherwise set the layer to the minimum value and force to true.
            foreach (Node node in _tempNodes)
            {
                if (node is OutputNode)
                {
                    SetLayer(node, 0);
                }
                else
                {
                    SetLayer(node, uint.MinValue, true);
                }
            }

            // For each node of type InputNode set the layer to minimum value and force to true.
            foreach (InputNode node in _tempNodes.OfType <InputNode>())
            {
                SetLayer(node, uint.MaxValue, true);
            }

            void SetLayer(Node node, uint layer, bool force = false)
            {
                // Set the layer depending on force and if the current layer is higher than the layer given.
                node.Layer = force ? layer : (layer > node.Layer ? layer : node.Layer);

                // If force is true, return early.
                if (force)
                {
                    return;
                }

                // For each connection gene where the out node identifier is the same as the given node identifier
                // set the layer to the node's layer plus one.
                foreach (ConnectionGene connectionGene in ConnectionGenes)
                {
                    if (connectionGene.OutNodeIdentifier.Equals(node.NodeIdentifier))
                    {
                        SetLayer(connectionGene.InNode, node.Layer + 1);
                    }
                }
            }

            void LoadNodes(ConnectionGene connectionGene)
            {
                // Get or create the In and Out nodes based on the given node identifiers.
                connectionGene.InNode  = GetOrCreateNodeForNodeId(connectionGene.InNodeIdentifier);
                connectionGene.OutNode = GetOrCreateNodeForNodeId(connectionGene.OutNodeIdentifier);
            }

            Node GetOrCreateNodeForNodeId(uint nodeIdentifier)
            {
                // Tries to get a node for the given id, if not found returns a default.
                Node node = GetNodeForId(nodeIdentifier);

                // If the node is not equal to default return the given node.
                if (node != default)
                {
                    return(node);
                }

                // Tries to find the node in the hidden nodes list, if not found returns a default.
                node = _hiddenNodes.FirstOrDefault(n => n.NodeIdentifier == nodeIdentifier);

                // If the node is not equal to default return the given node.
                if (node != default)
                {
                    return(node);
                }

                // If no node was found then create a hidden node.
                node = new HiddenNode(nodeIdentifier);

                // Add it to the hidden nodes list.
                _hiddenNodes.Add((HiddenNode)node);

                // Return the hidden node.
                return(node);
            }

            Node GetNodeForId(uint nodeIdentifier)
            {
                // Tries to find the first relation where the input node has the given identifier or return default.
                OrganismInputNode organismInputNode = Inputs.FirstOrDefault(n => n.InputNode.NodeIdentifier == nodeIdentifier);

                // if the organism input node is not default return the input node.
                if (organismInputNode != default)
                {
                    return(organismInputNode.InputNode);
                }

                // Tries to find the first relation where the output node has the given identifier, else return default.
                return(Outputs.FirstOrDefault(n => n.OutputNode.NodeIdentifier == nodeIdentifier)?.OutputNode);
            }
        }
        /// <summary>
        /// Adds a new connection between 2 randomly chosen nodes.
        /// The first node can not be an output.
        /// The second node cannot be an input node if the first node is an input node, and the layer cannot be the same as the first node.
        ///
        /// If the connection that is going to be created already exists, there will be a maximum of 5 attempts to create a new one.
        /// </summary>
        /// <param name="trainingRoomSettings">The training room settings.</param>
        /// <param name="innovationFunction">The innovation function.</param>
        private void AddConnectionMutation(TrainingRoomSettings trainingRoomSettings, Func <uint, uint, uint> innovationFunction)
        {
            // Set initial attempt counter to 0.
            int attemptsDone = 0;

            // Rebuild structure so we get layer information to make sure we don't get circular dependencies.
            RebuildStructure();

            while (true)
            {
                // Prepare start and end node.
                Node startNode;
                Node endNode;

                // Set start node to a random node while start note is of type OutputNode.
                do
                {
                    startNode = GetRandomNode();
                } while (startNode is OutputNode);


                // Set end node to a random node while the layer of the start and end node are the same or
                // start node is of type InputNode and end node is of type InputNode.
                do
                {
                    endNode = GetRandomNode();
                } while (endNode.Layer == startNode.Layer || (startNode is InputNode && endNode is InputNode));

                // If end node layer is higher than start node layer swap them.
                if (endNode.Layer > startNode.Layer)
                {
                    Node temp = endNode;
                    endNode   = startNode;
                    startNode = temp;
                }

                // If the connection between the selected start and end node already exists continue the loop, until 5 attempts are done,
                // then return early.
                if (ConnectionExists(startNode, endNode))
                {
                    if (attemptsDone >= 5)
                    {
                        return;
                    }
                    attemptsDone += 1;
                    continue;
                }

                // Create a new connection gene from the start and end nodes.
                ConnectionGene connection = new ConnectionGene(Id, innovationFunction(startNode.NodeIdentifier, endNode.NodeIdentifier), startNode.NodeIdentifier, endNode.NodeIdentifier, trainingRoomSettings.Random.NextDouble() * 2 - 1);

                // Add the connection gene.
                ConnectionGenes.Add(connection);

                // Update the max innovation.
                _maxInnovation = Math.Max(connection.InnovationNumber, _maxInnovation);

                // Break the loop.
                break;
            }

            Node GetRandomNode()
            {
                //TODO: Why not use the hidden nodes list instead of the connection genes?

                List <Node> nodes = ConnectionGenes.SelectMany(p => new[] { p.InNode, p.OutNode }).Distinct().ToList();

                nodes.AddRange(Inputs.Select(p => p.InputNode));
                nodes.AddRange(Outputs.Select(p => p.OutputNode));

                return(nodes[trainingRoomSettings.Random.Next(nodes.Count)]);
            }
        }