/// <summary> /// Add a ConnectionGene to the builder, but only if the connection is not already present (as determined by it's neuron ID endpoints). /// </summary> /// <param name="connectionGene">The connection to add.</param> /// <param name="parentGenome">The connection's parent genome. This is used to obtain NeuronGene(s) for the connection endpoints.</param> /// <param name="overwriteExisting">A flag that indicates if this connection should take precedence over an existing connection with /// the same endpoints.</param> public void TryAddGene(ConnectionGene connectionGene, NeatGenome parentGenome, bool overwriteExisting) { // Check if a matching gene has already been added. ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(connectionGene.SourceNodeId, connectionGene.TargetNodeId); ConnectionGene existingConnectionGene; if (!_connectionGeneDictionary.TryGetValue(connectionKey, out existingConnectionGene)) { // Add new connection gene. ConnectionGene connectionGeneCopy = new ConnectionGene(connectionGene); _connectionGeneDictionary.Add(connectionKey, connectionGeneCopy); // Insert connection gene into a list. Use more efficient approach (append to end) if we know the gene belongs at the end. if (connectionGeneCopy.InnovationId > _highestConnectionGeneId) { _connectionGeneList.Add(connectionGeneCopy); _highestConnectionGeneId = connectionGeneCopy.InnovationId; } else { _connectionGeneList.InsertIntoPosition(connectionGeneCopy); } // Add neuron genes (if not already added). // Source neuron. NeuronGene srcNeuronGene; if (!_neuronDictionary.TryGetValue(connectionGene.SourceNodeId, out srcNeuronGene)) { srcNeuronGene = parentGenome.NeuronGeneList.GetNeuronById(connectionGene.SourceNodeId); srcNeuronGene = new NeuronGene(srcNeuronGene, false); // Make a copy. _neuronDictionary.Add(srcNeuronGene.Id, srcNeuronGene); } // Target neuron. NeuronGene tgtNeuronGene; if (!_neuronDictionary.TryGetValue(connectionGene.TargetNodeId, out tgtNeuronGene)) { tgtNeuronGene = parentGenome.NeuronGeneList.GetNeuronById(connectionGene.TargetNodeId); tgtNeuronGene = new NeuronGene(tgtNeuronGene, false); // Make a copy. _neuronDictionary.Add(tgtNeuronGene.Id, tgtNeuronGene); } // Register connectivity with each neuron. srcNeuronGene.TargetNeurons.Add(tgtNeuronGene.Id); tgtNeuronGene.SourceNeurons.Add(srcNeuronGene.Id); } else if (overwriteExisting) { // The genome we are building already has a connection with the same neuron endpoints as the one we are // trying to add. It didn't match up during correlation because it has a different innovation number, this // is possible because the innovation history buffers throw away old innovations in a FIFO manner in order // to prevent them from bloating. // Here the 'overwriteExisting' flag is set so the gene we are currently trying to add is probably from the // fitter parent, and therefore we want to use its connection weight in place of the existing gene's weight. existingConnectionGene.Weight = connectionGene.Weight; } }
public void ConectionBufferTest() { NeatGenomeFactory genomeFactory = new NeatGenomeFactory(3, 1); for (uint i = 0; i < 200000; i++) { ConnectionEndpointsStruct endpointsStruct = new ConnectionEndpointsStruct(i, i+1); AddedNeuronGeneStruct addedNeuronGeneStruct = new AddedNeuronGeneStruct(genomeFactory.InnovationIdGenerator); genomeFactory.AddedConnectionBuffer.Enqueue(endpointsStruct, i); genomeFactory.AddedNeuronBuffer.Enqueue(i, addedNeuronGeneStruct); } //Assert.Fail(); }
/// <summary> /// Performs an integrity check on the network definition. /// Returns true if OK. /// </summary> public bool PerformIntegrityCheck() { // We will always have at least a bias and an output. int count = _nodeList.Count; if(count < 2) { Debug.WriteLine(string.Format("Node list has less than the minimum number of neuron genes [{0}]", count)); return false; } // Check bias neuron. if(NodeType.Bias != _nodeList[0].NodeType) { Debug.WriteLine("Missing bias gene"); return false; } if(0u != _nodeList[0].Id) { Debug.WriteLine(string.Format("Bias neuron ID != 0. [{0}]", _nodeList[0].Id)); return false; } // Check input nodes. uint prevId = 0u; int idx = 1; for(int i=0; i<_inputNodeCount; i++, idx++) { if(NodeType.Input != _nodeList[idx].NodeType) { Debug.WriteLine(string.Format("Invalid node type. Expected Input, got [{0}]", _nodeList[idx].NodeType)); return false; } if(_nodeList[idx].Id <= prevId) { Debug.WriteLine("Input node is out of order and/or a duplicate."); return false; } prevId = _nodeList[idx].Id; } // Check output neurons. for(int i=0; i<_outputNodeCount; i++, idx++) { if(NodeType.Output != _nodeList[idx].NodeType) { Debug.WriteLine(string.Format("Invalid node type. Expected Output, got [{0}]", _nodeList[idx].NodeType)); return false; } if(_nodeList[idx].Id <= prevId) { Debug.WriteLine("Output node is out of order and/or a duplicate."); return false; } prevId = _nodeList[idx].Id; } // Check hidden neurons. // All remaining neurons should be hidden neurons. for(; idx<count; idx++) { if(NodeType.Hidden != _nodeList[idx].NodeType) { Debug.WriteLine(string.Format("Invalid node type. Expected Hidden, got [{0}]", _nodeList[idx].NodeType)); return false; } if(_nodeList[idx].Id <= prevId) { Debug.WriteLine("Hidden node is out of order and/or a duplicate."); return false; } prevId = _nodeList[idx].Id; } // Check connections. count = _connectionList.Count; if(0 == count) { // At least one connection is required, connectionless genomes are pointless. Debug.WriteLine("Zero connections."); return false; } // Check for multiple connections between nodes. // Build a dictionary of connection endpoints. Dictionary<ConnectionEndpointsStruct, object> endpointDict = new Dictionary<ConnectionEndpointsStruct,object>(count); foreach(NetworkConnection conn in _connectionList) { ConnectionEndpointsStruct key = new ConnectionEndpointsStruct(conn.SourceNodeId, conn.TargetNodeId); if(endpointDict.ContainsKey(key)) { Debug.WriteLine("Connection error. A connection between the specified endpoints already exists."); return false; } endpointDict.Add(key, null); } // Integrity check OK. return true; }
/// <summary> /// Add a ConnectionGene to the builder, but only if the connection is not already present (as determined by it's neuron ID endpoints). /// </summary> /// <param name="connectionGene">The connection to add.</param> /// <param name="parentGenome">The conenction's parent genome. This is used to obtain NeuronGene(s) for the connection endpoints.</param> /// <param name="overwriteExisting">A flag that indicates if this connection should take precedence oevr an existing connection with /// the same endpoints.</param> public void TryAddGene(ConnectionGene connectionGene, NeatGenome parentGenome, bool overwriteExisting) { // Check if a matching gene has already been added. ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(connectionGene.SourceNodeId, connectionGene.TargetNodeId); ConnectionGene existingConnectionGene; if(!_connectionGeneDictionary.TryGetValue(connectionKey, out existingConnectionGene)) { // Add new connection gene. ConnectionGene connectionGeneCopy = new ConnectionGene(connectionGene); _connectionGeneDictionary.Add(connectionKey, connectionGeneCopy); // Insert connection gene into a list. Use more efficient approach (append to end) if we know the gene belongs at the end. if(connectionGeneCopy.InnovationId > _highestConnectionGeneId) { _connectionGeneList.Add(connectionGeneCopy); _highestConnectionGeneId = connectionGeneCopy.InnovationId; } else { _connectionGeneList.InsertIntoPosition(connectionGeneCopy); } // Add neuron genes (if not already added). // Source neuron. NeuronGene srcNeuronGene; if(!_neuronDictionary.TryGetValue(connectionGene.SourceNodeId, out srcNeuronGene)) { srcNeuronGene = parentGenome.NeuronGeneList.GetNeuronById(connectionGene.SourceNodeId); srcNeuronGene = new NeuronGene(srcNeuronGene, false); // Make a copy. _neuronDictionary.Add(srcNeuronGene.Id, srcNeuronGene); } // Target neuron. NeuronGene tgtNeuronGene; if(!_neuronDictionary.TryGetValue(connectionGene.TargetNodeId, out tgtNeuronGene)) { tgtNeuronGene = parentGenome.NeuronGeneList.GetNeuronById(connectionGene.TargetNodeId); tgtNeuronGene = new NeuronGene(tgtNeuronGene, false); // Make a copy. _neuronDictionary.Add(tgtNeuronGene.Id, tgtNeuronGene); } // Register connectivity with each neuron. srcNeuronGene.TargetNeurons.Add(tgtNeuronGene.Id); tgtNeuronGene.SourceNeurons.Add(srcNeuronGene.Id); } else if(overwriteExisting) { // The genome we are building already has a connection with the same neuron endpoints as the one we are // trying to add. It didn't match up during correlation because it has a different innovation number, this // is possible because the innovation history buffers throw away old innovations in a FIFO manner in order // to prevent them from bloating. // Here the 'overwriteExisting' flag is set so the gene we are currently trying to add is probably from the // fitter parent, and therefore we want to use its connection weight in place of the existing gene's weight. existingConnectionGene.Weight = connectionGene.Weight; } }