/// <summary> /// Writes a list of NeatGenome(s) to XML within a containing 'Root' /// element and the activation function library that the genomes are /// associated with. /// </summary> /// <param name="xw">XmlWriter to write XML to.</param> /// <param name="genomeList">List of genomes to write as XML.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs /// should be emitted. They are required /// for HyperNEAT genomes but not for NEAT.</param> public static void WriteComplete(XmlWriter xw, IList <NeatGenome> genomeList, bool nodeFnIds) { if (genomeList.Count == 0) { // Nothing to do. return; } // <Root> xw.WriteStartElement(__ElemRoot); // Write activation function library from the first genome. // (we expect all genomes to use the same library). IActivationFunctionLibrary activationFnLib = genomeList[0].ActivationFnLibrary; NetworkXmlIO.Write(xw, activationFnLib); // <Networks> xw.WriteStartElement(__ElemNetworks); // Write genomes. foreach (NeatGenome genome in genomeList) { Debug.Assert(genome.ActivationFnLibrary == activationFnLib); Write(xw, genome, nodeFnIds); } // </Networks> xw.WriteEndElement(); // </Root> xw.WriteEndElement(); }
/// <summary> /// Writes a NeatGenome to XML. /// </summary> /// <param name="xw">XmlWriter to write XML to.</param> /// <param name="genome">Genome to write as XML.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs /// should be emitted. They are required for HyperNEAT genomes but not /// for NEAT.</param> public static void Write(XmlWriter xw, NeatGenome genome, bool nodeFnIds) { xw.WriteStartElement(__ElemNetwork); xw.WriteAttributeString(__AttrId, genome.Id.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrBirthGeneration, genome.BirthGeneration.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrFitness, genome.EvaluationInfo.Fitness.ToString("R", NumberFormatInfo.InvariantInfo)); // Emit nodes. StringBuilder sb = new StringBuilder(); xw.WriteStartElement(__ElemNodes); foreach (NeuronGene nGene in genome.NeuronGeneList) { xw.WriteStartElement(__ElemNode); xw.WriteAttributeString(__AttrType, NetworkXmlIO.GetNodeTypeString(nGene.NodeType)); xw.WriteAttributeString(__AttrId, nGene.Id.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrModule, nGene.ModuleId.ToString(NumberFormatInfo.InvariantInfo)); // The pandemonium field is only needed for regulatory neurons. if (nGene.NodeType == NodeType.Regulatory) { xw.WriteAttributeString(__AttrPandemonium, nGene.Pandemonium.ToString(NumberFormatInfo.InvariantInfo)); } if (nodeFnIds) { // Write activation fn ID. xw.WriteAttributeString(__AttrActivationFunctionId, nGene.ActivationFnId.ToString(NumberFormatInfo.InvariantInfo)); // Write aux state as comma separated list of real values. XmlIoUtils.WriteAttributeString(xw, __AttrAuxState, nGene.AuxState); } xw.WriteEndElement(); } xw.WriteEndElement(); // Emit connections. xw.WriteStartElement(__ElemConnections); foreach (ConnectionGene cGene in genome.ConnectionList) { xw.WriteStartElement(__ElemConnection); xw.WriteAttributeString(__AttrId, cGene.InnovationId.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrSourceId, cGene.SourceNodeId.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrTargetId, cGene.TargetNodeId.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrWeight, cGene.Weight.ToString("R", NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrModule, cGene.ModuleId.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrProtected, cGene.Protected.ToString()); xw.WriteEndElement(); } xw.WriteEndElement(); // </Network> xw.WriteEndElement(); }
/// <summary> /// Writes a single NeatGenome to XML within a containing 'Root' element /// and the activation function library that the genome is associated with. /// </summary> /// <param name="xw">XmlWriter to write XML to.</param> /// <param name="genome">Genome to write as XML.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs /// should be emitted. They are required for HyperNEAT genomes but not /// for NEAT.</param> public static void WriteComplete(XmlWriter xw, NeatGenome genome, bool nodeFnIds) { // <Root> xw.WriteStartElement(__ElemRoot); // Write activation function library. NetworkXmlIO.Write(xw, genome.ActivationFnLibrary); // <Networks> xw.WriteStartElement(__ElemNetworks); // Write single genome. Write(xw, genome, nodeFnIds); // </Networks> xw.WriteEndElement(); // </Root> xw.WriteEndElement(); }
public static IActivationFunctionLibrary CreateLibraryMc(params string[] nodes) { List <ActivationFunctionInfo> fnList = new List <ActivationFunctionInfo>(nodes.Length); for (int i = 0; i < nodes.Length; i++) { var fn = new MarkovActivationFunction(nodes[i]); // TODO: Add ability to weight different nodes based on occurrence frequencies fnList.Add(new ActivationFunctionInfo(i, 1.0 / (double)nodes.Length, fn)); // Add the functionality to read/write XML files NetworkXmlIO.AddActivationFunction(fn.FunctionId, fn); } return(new DefaultActivationFunctionLibrary(fnList)); }
public static void Write(XmlWriter xw, INetworkDefinition networkDef, bool nodeFnIds) { xw.WriteStartElement(__ElemNetwork); xw.WriteStartElement(__ElemNodes); foreach (var node in networkDef.NodeList) { xw.WriteStartElement(__ElemNode); xw.WriteAttributeString(__AttrType, NetworkXmlIO.GetNodeTypeString(node.NodeType)); xw.WriteAttributeString(__AttrId, node.Id.ToString(NumberFormatInfo.InvariantInfo)); if (nodeFnIds) { // Write activation fn ID. xw.WriteAttributeString(__AttrActivationFunctionId, node.ActivationFnId.ToString(NumberFormatInfo.InvariantInfo)); // Write aux state as comma separated list of real values. XmlIoUtils.WriteAttributeString(xw, __AttrAuxState, node.AuxState); } xw.WriteEndElement(); } xw.WriteEndElement(); // Emit connections. xw.WriteStartElement(__ElemConnections); foreach (INetworkConnection connection in networkDef.ConnectionList) { xw.WriteStartElement(__ElemConnection); xw.WriteAttributeString(__AttrSourceId, connection.SourceNodeId.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrTargetId, connection.TargetNodeId.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrWeight, connection.Weight.ToString("R", NumberFormatInfo.InvariantInfo)); xw.WriteEndElement(); } xw.WriteEndElement(); // </Network> xw.WriteEndElement(); }
/// <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 list of NeatGenome(s) from XML that has a containing 'Root' /// element. The root element also contains the activation function /// library that the genomes are associated with. /// </summary> /// <param name="xr">The XmlReader to read from.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs /// should be read. If false then all node activation function IDs /// default to 0.</param> /// <param name="genomeFactory">A NeatGenomeFactory object to construct /// genomes against.</param> public static List <NeatGenome> ReadCompleteGenomeList(XmlReader xr, bool nodeFnIds, NeatGenomeFactory givenGenomeFactory) { genomeFactory = givenGenomeFactory; // Find <Root>. XmlIoUtils.MoveToElement(xr, false, __ElemRoot); // Read IActivationFunctionLibrary. This library is not used, it is // compared against the one already present in the genome factory to // confirm that the loaded genomes are compatible with the genome factory. XmlIoUtils.MoveToElement(xr, true, __ElemActivationFunctions); IActivationFunctionLibrary activationFnLib = NetworkXmlIO.ReadActivationFunctionLibrary(xr); XmlIoUtils.MoveToElement(xr, false, __ElemNetworks); // Read genomes. List <NeatGenome> genomeList = new List <NeatGenome>(); using (XmlReader xrSubtree = xr.ReadSubtree()) { // Re-scan for the root <Networks> element. XmlIoUtils.MoveToElement(xrSubtree, false); // Move to first Network elem. XmlIoUtils.MoveToElement(xrSubtree, true, __ElemNetwork); // Read Network elements. do { NeatGenome genome = ReadGenome(xrSubtree, nodeFnIds); genomeList.Add(genome); }while(xrSubtree.ReadToNextSibling(__ElemNetwork)); } // Check for empty list. if (genomeList.Count == 0) { return(genomeList); } // Get the number of inputs and outputs expected by the genome factory. int inputCount = genomeFactory.InputNeuronCount; int outputCount = genomeFactory.OutputNeuronCount; // Check all genomes have the same number of inputs & outputs. // Also track the highest genomeID and innovation ID values; // we need these to construct a new genome factory. uint maxGenomeId = 0; uint maxInnovationId = 0; foreach (NeatGenome genome in genomeList) { // Check number of inputs/outputs. if (genome.Input != inputCount || genome.Output != outputCount) { throw new SharpNeatException(string.Format( "Genome with wrong number of inputs and/or outputs," + " expected [{0}][{1}] got [{2}][{3}]", inputCount, outputCount, genome.Input, genome.Output)); } // Track max IDs. maxGenomeId = Math.Max(maxGenomeId, genome.Id); // Node and connection innovation IDs are in the same ID space. foreach (NeuronGene nGene in genome.NeuronGeneList) { maxInnovationId = Math.Max(maxInnovationId, nGene.InnovationId); } // Register connection IDs. foreach (ConnectionGene cGene in genome.ConnectionGeneList) { maxInnovationId = Math.Max(maxInnovationId, cGene.InnovationId); } } // Check that activation functions in XML match that in the genome factory. IList <ActivationFunctionInfo> loadedActivationFnList = activationFnLib.GetFunctionList(); IList <ActivationFunctionInfo> factoryActivationFnList = genomeFactory.ActivationFnLibrary.GetFunctionList(); if (loadedActivationFnList.Count != factoryActivationFnList.Count) { throw new SharpNeatException("The activation function library loaded" + "from XML does not match the genome" + "factory's activation function library."); } for (int i = 0; i < factoryActivationFnList.Count; i++) { if ((loadedActivationFnList[i].Id != factoryActivationFnList[i].Id) || (loadedActivationFnList[i].ActivationFunction.FunctionId != factoryActivationFnList[i].ActivationFunction.FunctionId)) { throw new SharpNeatException("The activation function library loaded" + "from XML does not match the genome" + "factory's activation function library."); } } // Initialise the genome factory's genome and innovation ID generators. genomeFactory.GenomeIdGenerator.Reset(Math.Max(genomeFactory.GenomeIdGenerator.Peek, maxGenomeId + 1)); genomeFactory.InnovationIdGenerator.Reset(Math.Max(genomeFactory.InnovationIdGenerator.Peek, maxInnovationId + 1)); // The active module in these loaded genomes corresponds to the last // module in the list: genomeFactory.CurrentModule = genomeList[0].FindActiveModule(); return(genomeList); }
/// <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)); }