/// <summary> /// Copy constructor. /// </summary> /// <param name="copyFrom">NeuronGene to copy from.</param> /// <param name="copyConnectivityData">Indicates whether or not top copy connectivity data for the neuron.</param> public NeuronGene(NeuronGene copyFrom, bool copyConnectivityData) { _innovationId = copyFrom._innovationId; _neuronType = copyFrom._neuronType; _activationFnId = copyFrom._activationFnId; if(null != copyFrom._auxState) { _auxState = (double[])copyFrom._auxState.Clone(); } if(copyConnectivityData) { _srcNeurons = new HashSet<uint>(copyFrom._srcNeurons); _tgtNeurons = new HashSet<uint>(copyFrom._tgtNeurons); } else { _srcNeurons = new HashSet<uint>(); _tgtNeurons = new HashSet<uint>(); } }
/// <summary> /// Copy constructor. /// </summary> /// <param name="copyFrom">NeuronGene to copy from.</param> /// <param name="copyConnectivityData">Indicates whether or not top copy connectivity data for the neuron.</param> public NeuronGene(NeuronGene copyFrom, bool copyConnectivityData) { _innovationId = copyFrom._innovationId; _neuronType = copyFrom._neuronType; _activationFnId = copyFrom._activationFnId; if (null != copyFrom._auxState) { _auxState = (double[])copyFrom._auxState.Clone(); } if (copyConnectivityData) { _srcNeurons = new HashSet <uint>(copyFrom._srcNeurons); _tgtNeurons = new HashSet <uint>(copyFrom._tgtNeurons); } else { _srcNeurons = new HashSet <uint>(); _tgtNeurons = new HashSet <uint>(); } }
/// <summary> /// Creates a single randomly initialised genome. /// A random set of connections are made form the input to the output neurons, the number of /// connections made is based on the NeatGenomeParameters.InitialInterconnectionsProportion /// which specifies the proportion of all possible input-output connections to be made in /// initial genomes. /// /// The connections that are made are allocated innovation IDs in a consistent manner across /// the initial population of genomes. To do this we allocate IDs sequentially to all possible /// interconnections and then randomly select some proportion of connections for inclusion in the /// genome. In addition, for this scheme to work the innovation ID generator must be reset to zero /// prior to each call to CreateGenome(), and a test is made to ensure this is the case. /// /// The consistent allocation of innovation IDs ensure that equivalent connections in different /// genomes have the same innovation ID, and although this isn't strictly necessary it is /// required for sexual reproduction to work effectively - like structures are detected by comparing /// innovation IDs only. /// </summary> /// <param name="birthGeneration">The current evolution algorithm generation. /// Assigned to the new genome as its birth generation.</param> public NeatGenome CreateGenome(uint birthGeneration) { NeuronGeneList neuronGeneList = new NeuronGeneList(_inputNeuronCount + _outputNeuronCount); NeuronGeneList inputNeuronGeneList = new NeuronGeneList(_inputNeuronCount); // includes single bias neuron. NeuronGeneList outputNeuronGeneList = new NeuronGeneList(_outputNeuronCount); // Create a single bias neuron. uint biasNeuronId = _innovationIdGenerator.NextId; if (0 != biasNeuronId) { // The ID generator must be reset before calling this method so that all generated genomes use the // same innovation ID for matching neurons and structures. throw new SharpNeatException("IdGenerator must be reset before calling CreateGenome(uint)"); } // Note. Genes within nGeneList must always be arranged according to the following layout plan. // Bias - single neuron. Innovation ID = 0 // Input neurons. // Output neurons. // Hidden neurons. NeuronGene neuronGene = CreateNeuronGene(biasNeuronId, NodeType.Bias); inputNeuronGeneList.Add(neuronGene); neuronGeneList.Add(neuronGene); // Create input neuron genes. for (int i = 0; i < _inputNeuronCount; i++) { neuronGene = CreateNeuronGene(_innovationIdGenerator.NextId, NodeType.Input); inputNeuronGeneList.Add(neuronGene); neuronGeneList.Add(neuronGene); } // Create output neuron genes. for (int i = 0; i < _outputNeuronCount; i++) { neuronGene = CreateNeuronGene(_innovationIdGenerator.NextId, NodeType.Output); outputNeuronGeneList.Add(neuronGene); neuronGeneList.Add(neuronGene); } // Define all possible connections between the input and output neurons (fully interconnected). int srcCount = inputNeuronGeneList.Count; int tgtCount = outputNeuronGeneList.Count; ConnectionDefinition[] connectionDefArr = new ConnectionDefinition[srcCount * tgtCount]; for (int srcIdx = 0, i = 0; srcIdx < srcCount; srcIdx++) { for (int tgtIdx = 0; tgtIdx < tgtCount; tgtIdx++) { connectionDefArr[i++] = new ConnectionDefinition(_innovationIdGenerator.NextId, srcIdx, tgtIdx); } } // Shuffle the array of possible connections. SortUtils.Shuffle(connectionDefArr, _rng); // Select connection definitions from the head of the list and convert them to real connections. // We want some proportion of all possible connections but at least one (Connectionless genomes are not allowed). int connectionCount = (int)NumericsUtils.ProbabilisticRound( (double)connectionDefArr.Length * _neatGenomeParamsComplexifying.InitialInterconnectionsProportion, _rng); connectionCount = Math.Max(1, connectionCount); // Create the connection gene list and populate it. ConnectionGeneList connectionGeneList = new ConnectionGeneList(connectionCount); for (int i = 0; i < connectionCount; i++) { ConnectionDefinition def = connectionDefArr[i]; NeuronGene srcNeuronGene = inputNeuronGeneList[def._sourceNeuronIdx]; NeuronGene tgtNeuronGene = outputNeuronGeneList[def._targetNeuronIdx]; ConnectionGene cGene = new ConnectionGene(def._innovationId, srcNeuronGene.InnovationId, tgtNeuronGene.InnovationId, GenerateRandomConnectionWeight()); connectionGeneList.Add(cGene); // Register connection with endpoint neurons. srcNeuronGene.TargetNeurons.Add(cGene.TargetNodeId); tgtNeuronGene.SourceNeurons.Add(cGene.SourceNodeId); } // Ensure connections are sorted. connectionGeneList.SortByInnovationId(); // Create and return the completed genome object. return(CreateGenome(_genomeIdGenerator.NextId, birthGeneration, neuronGeneList, connectionGeneList, _inputNeuronCount, _outputNeuronCount, false)); }
/// <summary> /// Tests if adding the specified connection would cause a cyclic pathway in the network connectivity. /// Returns true if the connection would form a cycle. /// Note. This same logic is implemented on NeatGenome.IsConnectionCyclic() but against slightly /// different data structures, hence the method is re-implemented here. /// </summary> public bool IsConnectionCyclic(uint srcNeuronId, uint tgtNeuronId) { // Quick test. Is connection connecting a neuron to itself. if (srcNeuronId == tgtNeuronId) { return(true); } // Quick test. If one of the neuron's is not yet registered with the builder then there can be no cyclic connection // (the connection is coming-from or going-to a dead end). NeuronGene srcNeuron; if (!_neuronDictionary.TryGetValue(srcNeuronId, out srcNeuron) || !_neuronDictionary.ContainsKey(tgtNeuronId)) { return(false); } // Trace backwards through sourceNeuron's source neurons. If targetNeuron is encountered then it feeds // signals into sourceNeuron already and therefore a new connection between sourceNeuron and targetNeuron // would create a cycle. // Maintain a set of neurons that have been visited. This allows us to avoid unnecessary re-traversal // of the network and detection of cyclic connections. HashSet <uint> visitedNeurons = new HashSet <uint>(); visitedNeurons.Add(srcNeuronId); // Search uses an explicitly created stack instead of function recursion, the logic here is that this // may be more efficient through avoidance of multiple function calls (but not sure). Stack <uint> workStack = new Stack <uint>(); // Push source neuron's sources onto the work stack. We could just push the source neuron but we choose // to cover that test above to avoid the one extra neuronID lookup that would require. foreach (uint neuronId in srcNeuron.SourceNeurons) { workStack.Push(neuronId); } // While there are neurons to check/traverse. while (0 != workStack.Count) { // Pop a neuron to check from the top of the stack, and then check it. uint currNeuronId = workStack.Pop(); if (visitedNeurons.Contains(currNeuronId)) { // Already visited (via a different route). continue; } if (currNeuronId == tgtNeuronId) { // Target neuron already feeds into the source neuron. return(true); } // Register visit of this node. visitedNeurons.Add(currNeuronId); // Push the current neuron's source neurons onto the work stack. NeuronGene currNeuron = _neuronDictionary[currNeuronId]; foreach (uint neuronId in currNeuron.SourceNeurons) { workStack.Push(neuronId); } } // Connection not cyclic. return(false); }
/// <summary> /// Reads a NeatGenome from XML. /// </summary> /// <param name="xr">The XmlReader to read from.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs /// should be read. They are required for HyperNEAT genomes but not for NEAT</param> public static NeatGenome ReadGenome(XmlReader xr, bool nodeFnIds) { // Find <Network>. XmlIoUtils.MoveToElement(xr, false, __ElemNetwork); int initialDepth = xr.Depth; // Read genome ID attribute if present. Otherwise default to zero; // it's the caller's responsibility to check IDs are unique and // in-line with the genome factory's ID generators. string genomeIdStr = xr.GetAttribute(__AttrId); uint genomeId; uint.TryParse(genomeIdStr, out genomeId); // Read birthGeneration attribute if present. Otherwise default to zero. string birthGenStr = xr.GetAttribute(__AttrBirthGeneration); uint birthGen; uint.TryParse(birthGenStr, out birthGen); // Read fitness attribute if present. Otherwise default to zero. string fitnessStr = xr.GetAttribute(__AttrFitness); double fitness; double.TryParse(fitnessStr, out fitness); // Find <Nodes>. XmlIoUtils.MoveToElement(xr, true, __ElemNodes); // Create a reader over the <Nodes> sub-tree. int inputNodeCount = 0; int outputNodeCount = 0; int regulatoryCount = 0; int localInCount = 0; int localOutCount = 0; // Used to count local input and output neurons (which are not // found in the base module = 0) int activeModule = 1; NeuronGeneList nGeneList = new NeuronGeneList(); using (XmlReader xrSubtree = xr.ReadSubtree()) { // Re-scan for the root <Nodes> element. XmlIoUtils.MoveToElement(xrSubtree, false); // Move to first node elem. XmlIoUtils.MoveToElement(xrSubtree, true, __ElemNode); // Read node elements. do { NodeType neuronType = NetworkXmlIO.ReadAttributeAsNodeType(xrSubtree, __AttrType); uint id = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrId); int functionId = GetFunctionId(neuronType); int module = XmlIoUtils.ReadAttributeAsInt(xrSubtree, __AttrModule); int pandemonium; // If we have a regulatory neuron, we read its pandemonium label if (neuronType == NodeType.Regulatory) { pandemonium = XmlIoUtils.ReadAttributeAsInt(xrSubtree, __AttrPandemonium); } // Otherwise it is simply -1 else { pandemonium = -1; } double[] auxState = null; if (nodeFnIds) { // Read activation fn ID. functionId = XmlIoUtils.ReadAttributeAsInt(xrSubtree, __AttrActivationFunctionId); // Read aux state as comma seperated list of real values. auxState = XmlIoUtils.ReadAttributeAsDoubleArray(xrSubtree, __AttrAuxState); } NeuronGene nGene = new NeuronGene(id, neuronType, functionId, module, pandemonium, auxState); nGeneList.Add(nGene); // Track the number of input and output nodes. switch (neuronType) { case NodeType.Input: ++inputNodeCount; break; case NodeType.Output: ++outputNodeCount; break; case NodeType.Regulatory: ++regulatoryCount; break; case NodeType.Local_Input: if (module == activeModule) { ++localInCount; } else { // Found a new module, discard previous count activeModule = module; localInCount = 1; localOutCount = 0; } break; case NodeType.Local_Output: // Here we do not care about the correct module, // because that has been considered in the local input // count (and local input always comes first). ++localOutCount; break; } }while(xrSubtree.ReadToNextSibling(__ElemNode)); } // Find <Connections>. XmlIoUtils.MoveToElement(xr, false, __ElemConnections); // Create a reader over the <Connections> sub-tree. ConnectionGeneList cGeneList = new ConnectionGeneList(); using (XmlReader xrSubtree = xr.ReadSubtree()) { // Re-scan for the root <Connections> element. XmlIoUtils.MoveToElement(xrSubtree, false); // Move to first connection elem. string localName = XmlIoUtils.MoveToElement(xrSubtree, true); if (localName == __ElemConnection) { // We have at least one connection. // Read connection elements. do { uint id = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrId); uint srcId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrSourceId); uint tgtId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrTargetId); double weight = XmlIoUtils.ReadAttributeAsDouble(xrSubtree, __AttrWeight); int module = XmlIoUtils.ReadAttributeAsInt(xrSubtree, __AttrModule); bool protect = XmlIoUtils.ReadAttributeAsBool(xrSubtree, __AttrProtected); ConnectionGene cGene = new ConnectionGene(id, srcId, tgtId, weight, module, protect); cGeneList.Add(cGene); }while(xrSubtree.ReadToNextSibling(__ElemConnection)); } } // Move the reader beyond the closing tags </Connections> and </Network>. do { if (xr.Depth <= initialDepth) { break; } }while(xr.Read()); // Construct and return loaded NeatGenome. We construct it first // so we can access its properties before leaving. bool rebuildConnectivity = true; // Integrity will fail if we attempt to create the genome before // updating some of the statistics (counts for each type of neurons, // etc). It can be done after that step! bool shouldAssertIntegrity = false; NeatGenome genome = new NeatGenome(null, genomeId, birthGen, nGeneList, cGeneList, rebuildConnectivity, shouldAssertIntegrity); if (genomeFactory != null) { // Note genomeFactory is not fully initialized yet, but it is needed to create // an EvaluationInfo structure. genome.GenomeFactory = genomeFactory; genome.EvaluationInfo.SetFitness(fitness); } // We update count variables. While it is true most are static // variables, loading genomes is done only once, so we can afford // to count them for each genome. genome.Input = inputNodeCount; genome.Output = outputNodeCount; genome.Regulatory = regulatoryCount; genome.LocalIn = localInCount; genome.LocalOut = localOutCount; // We use base for neurons InHiddenModules genome.NeuronGeneList.LocateLastBase(); genome.InHiddenModulesFromLoad(); genome.ActiveConnectionsFromLoad(); // Before this was done only once after creating all genomes. However, // we need these values if we want to perform an integrity check. // The performance overhead is negligible (specially in a one-time method) // and reliability is increased this way. genome.NeuronGeneList.LocateLastBase(); genome.NeuronGeneList.LocateFirstIndex(); genome.ConnectionGeneList.LocateFirstId(); Debug.Assert(genome.PerformIntegrityCheck()); return(genome); }
/// <summary> /// Reads a NeatGenome from XML. /// </summary> /// <param name="xr">The XmlReader to read from.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs should be read. They are required /// for HyperNEAT genomes but not for NEAT</param> public static NeatGenome ReadGenome(XmlReader xr, bool nodeFnIds) { // Find <Network>. XmlIoUtils.MoveToElement(xr, false, __ElemNetwork); int initialDepth = xr.Depth; // Read genome ID attribute if present. Otherwise default to zero; it's the caller's responsibility to // check IDs are unique and in-line with the genome factory's ID generators. string genomeIdStr = xr.GetAttribute(__AttrId); uint genomeId; uint.TryParse(genomeIdStr, out genomeId); // Read birthGeneration attribute if present. Otherwise default to zero. string birthGenStr = xr.GetAttribute(__AttrBirthGeneration); uint birthGen; uint.TryParse(birthGenStr, out birthGen); // Find <Nodes>. XmlIoUtils.MoveToElement(xr, true, __ElemNodes); // Create a reader over the <Nodes> sub-tree. int inputNodeCount = 0; int outputNodeCount = 0; NeuronGeneList nGeneList = new NeuronGeneList(); using (XmlReader xrSubtree = xr.ReadSubtree()) { // Re-scan for the root <Nodes> element. XmlIoUtils.MoveToElement(xrSubtree, false); // Move to first node elem. XmlIoUtils.MoveToElement(xrSubtree, true, __ElemNode); // Read node elements. do { NodeType neuronType = NetworkXmlIO.ReadAttributeAsNodeType(xrSubtree, __AttrType); uint id = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrId); int functionId = 0; double[] auxState = null; if (nodeFnIds) { // Read activation fn ID. functionId = XmlIoUtils.ReadAttributeAsInt(xrSubtree, __AttrActivationFunctionId); // Read aux state as comma separated list of real values. auxState = XmlIoUtils.ReadAttributeAsDoubleArray(xrSubtree, __AttrAuxState); } NeuronGene nGene = new NeuronGene(id, neuronType, functionId, auxState); nGeneList.Add(nGene); // Track the number of input and output nodes. switch (neuronType) { case NodeType.Input: inputNodeCount++; break; case NodeType.Output: outputNodeCount++; break; } }while(xrSubtree.ReadToNextSibling(__ElemNode)); } // Find <Connections>. XmlIoUtils.MoveToElement(xr, false, __ElemConnections); // Create a reader over the <Connections> sub-tree. ConnectionGeneList cGeneList = new ConnectionGeneList(); using (XmlReader xrSubtree = xr.ReadSubtree()) { // Re-scan for the root <Connections> element. XmlIoUtils.MoveToElement(xrSubtree, false); // Move to first connection elem. string localName = XmlIoUtils.MoveToElement(xrSubtree, true); if (localName == __ElemConnection) { // We have at least one connection. // Read connection elements. do { uint id = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrId); uint srcId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrSourceId); uint tgtId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrTargetId); double weight = XmlIoUtils.ReadAttributeAsDouble(xrSubtree, __AttrWeight); ConnectionGene cGene = new ConnectionGene(id, srcId, tgtId, weight); cGeneList.Add(cGene); }while(xrSubtree.ReadToNextSibling(__ElemConnection)); } } // Move the reader beyond the closing tags </Connections> and </Network>. do { if (xr.Depth <= initialDepth) { break; } }while(xr.Read()); // Construct and return loaded NeatGenome. return(new NeatGenome(null, genomeId, birthGen, nGeneList, cGeneList, inputNodeCount, outputNodeCount, 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; } }
/// <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. GetNeuronByIdAll(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. GetNeuronByIdAll(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; } }
/// <summary> /// Reads a NeatGenome from XML. /// </summary> /// <param name="xr">The XmlReader to read from.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs should be read. They are required /// for HyperNEAT genomes but not for NEAT</param> public static NeatGenome ReadGenome(XmlReader xr, bool nodeFnIds) { // Find <Network>. XmlIoUtils.MoveToElement(xr, false, __ElemNetwork); int initialDepth = xr.Depth; // Read genome ID attribute if present. Otherwise default to zero; it's the caller's responsibility to // check IDs are unique and in-line with the genome factory's ID generators. string genomeIdStr = xr.GetAttribute(__AttrId); uint genomeId; uint.TryParse(genomeIdStr, out genomeId); // Read birthGeneration attribute if present. Otherwise default to zero. string birthGenStr = xr.GetAttribute(__AttrBirthGeneration); uint birthGen; uint.TryParse(birthGenStr, out birthGen); // Find <Nodes>. XmlIoUtils.MoveToElement(xr, true, __ElemNodes); // Create a reader over the <Nodes> sub-tree. int inputNodeCount = 0; int outputNodeCount = 0; NeuronGeneList nGeneList = new NeuronGeneList(); using(XmlReader xrSubtree = xr.ReadSubtree()) { // Re-scan for the root <Nodes> element. XmlIoUtils.MoveToElement(xrSubtree, false); // Move to first node elem. XmlIoUtils.MoveToElement(xrSubtree, true, __ElemNode); // Read node elements. do { NodeType neuronType = NetworkXmlIO.ReadAttributeAsNodeType(xrSubtree, __AttrType); uint id = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrId); int functionId = 0; double[] auxState = null; if(nodeFnIds) { // Read activation fn ID. functionId = XmlIoUtils.ReadAttributeAsInt(xrSubtree, __AttrActivationFunctionId); // Read aux state as comma seperated list of real values. auxState = XmlIoUtils.ReadAttributeAsDoubleArray(xrSubtree, __AttrAuxState); } NeuronGene nGene = new NeuronGene(id, neuronType, functionId, auxState); nGeneList.Add(nGene); // Track the number of input and output nodes. switch(neuronType) { case NodeType.Input: inputNodeCount++; break; case NodeType.Output: outputNodeCount++; break; } } while(xrSubtree.ReadToNextSibling(__ElemNode)); } // Find <Connections>. XmlIoUtils.MoveToElement(xr, false, __ElemConnections); // Create a reader over the <Connections> sub-tree. ConnectionGeneList cGeneList = new ConnectionGeneList(); using(XmlReader xrSubtree = xr.ReadSubtree()) { // Re-scan for the root <Connections> element. XmlIoUtils.MoveToElement(xrSubtree, false); // Move to first connection elem. string localName = XmlIoUtils.MoveToElement(xrSubtree, true); if(localName == __ElemConnection) { // We have at least one connection. // Read connection elements. do { uint id = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrId); uint srcId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrSourceId); uint tgtId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrTargetId); double weight = XmlIoUtils.ReadAttributeAsDouble(xrSubtree, __AttrWeight); ConnectionGene cGene = new ConnectionGene(id, srcId, tgtId, weight); cGeneList.Add(cGene); } while(xrSubtree.ReadToNextSibling(__ElemConnection)); } } // Move the reader beyond the closing tags </Connections> and </Network>. do { if (xr.Depth <= initialDepth) { break; } } while(xr.Read()); // Construct and return loaded NeatGenome. return new NeatGenome(null, genomeId, birthGen, nGeneList, cGeneList, inputNodeCount, outputNodeCount, true, true); }