/// <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)]); } }