/// <summary> /// Constructs with the provided IActivationFunctionLibrary, NeatGenomeParameters and ID generators. /// </summary> public CppnGenomeFactory(int inputNeuronCount, int outputNeuronCount, IActivationFunctionLibrary activationFnLibrary, NeatGenomeParameters neatGenomeParams, UInt32IdGenerator genomeIdGenerator, UInt32IdGenerator innovationIdGenerator) : base(inputNeuronCount, outputNeuronCount, activationFnLibrary, neatGenomeParams, genomeIdGenerator, innovationIdGenerator) { }
public AutoencoderGenomeFactory(int inputNeuronCount, int outputNeuronCount, int hiddenNeuronCount, IActivationFunctionLibrary activationFnLibrary, NeatGenomeParameters neatGenomeParams, UInt32IdGenerator genomeIdGenerator, UInt32IdGenerator innovationIdGenerator) : base(inputNeuronCount, outputNeuronCount, activationFnLibrary, neatGenomeParams, genomeIdGenerator, innovationIdGenerator) { _hiddenNeuronCount = hiddenNeuronCount; }
/// <summary> /// Constructs with the provided input/output node count, activation function library, /// node and connection lists. /// </summary> public NetworkDefinition(int inputNodeCount, int outputNodeCount, IActivationFunctionLibrary activationFnLib, NodeList nodeList, ConnectionList connectionList) { _inputNodeCount = inputNodeCount; _outputNodeCount = outputNodeCount; _activationFnLib = activationFnLib; _nodeList = nodeList; _connectionList = connectionList; _isAcyclic = !CyclicNetworkTest.IsNetworkCyclic(this); }
/// <summary> /// Construct with the provided CPPN activation function library to draw CPPNs with (genome nodes contain an index into this library). /// </summary> /// <param name="actFnLib"></param> public CppnGenomeView(IActivationFunctionLibrary actFnLib) { InitializeComponent(); graphControl1.ViewportPainter = _viewportPainter = new IOGraphViewportPainter(new CppnGraphPainter(actFnLib)); }
/// <summary> /// Reads a network definition from XML. /// An activation function library is required to decode the function ID at each node, typically the /// library is stored alongside the network definition XML and will have already been read elsewhere and /// passed in here. /// </summary> /// <param name="xr">The XmlReader to read from.</param> /// <param name="activationFnLib">The activation function library used to decode node activation function IDs.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs should be read. They are required /// for HyperNEAT genomes but not NEAT</param> public static NetworkDefinition ReadNetworkDefinition(XmlReader xr, IActivationFunctionLibrary activationFnLib, bool nodeFnIds) { // Find <Network>. XmlIoUtils.MoveToElement(xr, false, __ElemNetwork); int initialDepth = xr.Depth; // Find <Nodes>. XmlIoUtils.MoveToElement(xr, true, __ElemNodes); // Create a reader over the <Nodes> sub-tree. int inputNodeCount = 0; int outputNodeCount = 0; NodeList nodeList = new NodeList(); 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 nodeType = ReadAttributeAsNodeType(xrSubtree, __AttrType); uint id = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrId); int fnId = 0; double[] auxState = null; if(nodeFnIds) { // Read activation fn ID. fnId = XmlIoUtils.ReadAttributeAsInt(xrSubtree, __AttrActivationFunctionId); // Read aux state as comma separated list of real values. auxState = XmlIoUtils.ReadAttributeAsDoubleArray(xrSubtree, __AttrAuxState); } // TODO: Read node aux state data. NetworkNode node = new NetworkNode(id, nodeType, fnId, auxState); nodeList.Add(node); // Track the number of input and output nodes. switch(nodeType) { 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. ConnectionList connList = new ConnectionList(); 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 srcId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrSourceId); uint tgtId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrTargetId); double weight = XmlIoUtils.ReadAttributeAsDouble(xrSubtree, __AttrWeight); NetworkConnection conn = new NetworkConnection(srcId, tgtId, weight); connList.Add(conn); } 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 network definition. return new NetworkDefinition(inputNodeCount, outputNodeCount, activationFnLib, nodeList, connList); }
/// <summary> /// Writes an activation function library to XML. This links activation function names to the /// integer IDs used by network nodes, which allows us emit just the ID for each node thus /// resulting in XML that is more compact compared to emitting the activation function name for /// each node. /// </summary> public static void Write(XmlWriter xw, IActivationFunctionLibrary activationFnLib) { xw.WriteStartElement(__ElemActivationFunctions); IList<ActivationFunctionInfo> fnList = activationFnLib.GetFunctionList(); foreach(ActivationFunctionInfo fnInfo in fnList) { xw.WriteStartElement(__ElemActivationFn); xw.WriteAttributeString(__AttrId, fnInfo.Id.ToString(NumberFormatInfo.InvariantInfo)); xw.WriteAttributeString(__AttrName, fnInfo.ActivationFunction.FunctionId); xw.WriteAttributeString(__AttrProbability, fnInfo.SelectionProbability.ToString("R", NumberFormatInfo.InvariantInfo)); xw.WriteEndElement(); } xw.WriteEndElement(); }
/// <summary> /// Reads a NetworkDefinition from XML. /// </summary> /// <param name="xmlNode">The XmlNode to read from. This can be an XmlDocument or XmlElement.</param> /// <param name="activationFnLib">The activation function library used to decode node activation function IDs.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs should be read. They are required /// for HyperNEAT genomes but not for NEAT. If false then all node activation function IDs default to 0.</param> public static NetworkDefinition ReadGenome(XmlNode xmlNode, IActivationFunctionLibrary activationFnLib, bool nodeFnIds) { using(XmlNodeReader xr = new XmlNodeReader(xmlNode)) { return ReadNetworkDefinition(xr, activationFnLib, nodeFnIds); } }
/// <summary> /// Construct with the provided activation function library. /// This must be the same library used by the genomes/graphs being painted. /// A legend of the activation functions is shown and the nodes are color coded to indicate the /// activation function at each node. /// </summary> public CppnGraphPainter(IActivationFunctionLibrary activationFnLibrary) { _activationFnLibrary = activationFnLibrary; }
/// <summary> /// Reads a network definition from XML. /// An activation function library is required to decode the function ID at each node, typically the /// library is stored alongside the network definition XML and will have already been read elsewhere and /// passed in here. /// </summary> /// <param name="xr">The XmlReader to read from.</param> /// <param name="activationFnLib">The activation function library used to decode node activation function IDs.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs should be read. They are required /// for HyperNEAT genomes but not NEAT</param> public static NetworkDefinition ReadNetworkDefinition(XmlReader xr, IActivationFunctionLibrary activationFnLib, bool nodeFnIds) { // Find <Network>. XmlIoUtils.MoveToElement(xr, false, __ElemNetwork); int initialDepth = xr.Depth; // Find <Nodes>. XmlIoUtils.MoveToElement(xr, true, __ElemNodes); // Create a reader over the <Nodes> sub-tree. int inputNodeCount = 0; int outputNodeCount = 0; NodeList nodeList = new NodeList(); 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 nodeType = ReadAttributeAsNodeType(xrSubtree, __AttrType); uint id = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrId); int fnId = 0; double[] auxState = null; if (nodeFnIds) { // Read activation fn ID. fnId = XmlIoUtils.ReadAttributeAsInt(xrSubtree, __AttrActivationFunctionId); // Read aux state as comma seperated list of real values. auxState = XmlIoUtils.ReadAttributeAsDoubleArray(xrSubtree, __AttrAuxState); } // TODO: Read node aux state data. NetworkNode node = new NetworkNode(id, nodeType, fnId, auxState); nodeList.Add(node); // Track the number of input and output nodes. switch (nodeType) { 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. ConnectionList connList = new ConnectionList(); 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 srcId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrSourceId); uint tgtId = XmlIoUtils.ReadAttributeAsUInt(xrSubtree, __AttrTargetId); double weight = XmlIoUtils.ReadAttributeAsDouble(xrSubtree, __AttrWeight); NetworkConnection conn = new NetworkConnection(srcId, tgtId, weight); connList.Add(conn); }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 network definition. return(new NetworkDefinition(inputNodeCount, outputNodeCount, activationFnLib, nodeList, connList)); }
/// <summary> /// Constructs with the provided substrate nodesets and mappings that describe how the nodesets are to be connected up. /// </summary> /// <param name="nodeSetList">Substrate nodes, represented as distinct sets of nodes. By convention the first and second /// sets in the list represent the input and output noes respectively. All other sets represent hidden nodes.</param> /// <param name="activationFnLibrary">The activation function library allocated to the networks that are 'grown' from the substrate.</param> /// <param name="activationFnId">The ID of an activation function function in activationFnLibrary. This is the activation function /// ID assigned to all nodes in networks that are 'grown' from the substrate. </param> /// <param name="weightThreshold">The weight threshold below which substrate connections are not created in grown networks.</param> /// <param name="maxWeight">Defines the weight range of grown connections (+-maxWeight).</param>/// /// <param name="nodeSetMappingList">A list of mappings between node sets that defines what connections to create between substrate nodes.</param> public Substrate(List<SubstrateNodeSet> nodeSetList, IActivationFunctionLibrary activationFnLibrary, int activationFnId, double weightThreshold, double maxWeight, List<NodeSetMapping> nodeSetMappingList) { VaildateSubstrateNodes(nodeSetList); _nodeSetList = nodeSetList; _inputNodeCount = _nodeSetList[0].NodeList.Count; _outputNodeCount = _nodeSetList[1].NodeList.Count; _dimensionality = _nodeSetList[0].NodeList[0]._position.GetUpperBound(0) + 1; _activationFnLibrary = activationFnLibrary; _activationFnId = activationFnId; _weightThreshold = weightThreshold; _maxWeight = maxWeight; _weightRescalingCoeff = _maxWeight / (1.0 - _weightThreshold); _nodeSetMappingList = nodeSetMappingList; // Get an estimate for the number of connections defined by mappings. int nonBiasConnectionCountHint = 0; foreach(NodeSetMapping mapping in nodeSetMappingList) { nonBiasConnectionCountHint += mapping.GetConnectionCountHint(nodeSetList); } if(nonBiasConnectionCountHint <= ConnectionCountCacheThreshold) { // Pre-generate the substrate connections and store them in a list. _connectionList = _connectionCountHint == 0 ? new List<SubstrateConnection>() : new List<SubstrateConnection>(nonBiasConnectionCountHint); foreach(NodeSetMapping mapping in nodeSetMappingList) { IEnumerable<SubstrateConnection> connectionSequence = mapping.GenerateConnections(nodeSetList); foreach(SubstrateConnection conn in connectionSequence) { _connectionList.Add(conn); } } _connectionList.TrimExcess(); } // Set total connection count hint value (includes additional connections to a bias node). _connectionCountHint = nonBiasConnectionCountHint + CalcBiasConnectionCountHint(nodeSetList); // Pre-create the network definition node list. This is re-used each time a network is created from the substrate. _netNodeList = CreateNetworkNodeList(); }
/// <summary> /// Construct a substrate with the provided node sets and a predetermined set of connections. /// </summary> /// <param name="nodeSetList">Substrate nodes, represented as distinct sets of nodes. By convention the first and second /// sets in the list represent the input and output noes respectively. All other sets represent hidden nodes.</param> /// <param name="activationFnLibrary">The activation function library allocated to the networks that are 'grown' from the substrate.</param> /// <param name="activationFnId">The ID of an activation function function in activationFnLibrary. This is the activation function /// ID assigned to all nodes in networks that are 'grown' from the substrate. </param> /// <param name="weightThreshold">The weight threshold below which substrate connections are not created in grown networks.</param> /// <param name="maxWeight">Defines the weight range of grown connections (+-maxWeight).</param> /// <param name="connectionList">A predetermined list of substrate connections.</param> public Substrate(List<SubstrateNodeSet> nodeSetList, IActivationFunctionLibrary activationFnLibrary, int activationFnId, double weightThreshold, double maxWeight, List<SubstrateConnection> connectionList) { VaildateSubstrateNodes(nodeSetList); _nodeSetList = nodeSetList; _inputNodeCount = _nodeSetList[0].NodeList.Count; _outputNodeCount = _nodeSetList[1].NodeList.Count; _dimensionality = _nodeSetList[0].NodeList[0]._position.GetUpperBound(0) + 1; _activationFnLibrary = activationFnLibrary; _activationFnId = activationFnId; _weightThreshold = weightThreshold; _maxWeight = maxWeight; _weightRescalingCoeff = _maxWeight / (1.0 - _weightThreshold); // Set total connection count hint value (includes additional connections to a bias node). _connectionList = connectionList; _connectionCountHint = connectionList.Count + CalcBiasConnectionCountHint(nodeSetList); // Pre-create the network definition node list. This is re-used each time a network is created from the substrate. _netNodeList = CreateNetworkNodeList(); }
public AutoencoderGenomeFactory(int inputNeuronCount, int outputNeuronCount, int hiddenNeuronCount, IActivationFunctionLibrary activationFnLibrary, NeatGenomeParameters neatGenomeParams) : base(inputNeuronCount, outputNeuronCount, activationFnLibrary, neatGenomeParams) { _hiddenNeuronCount = hiddenNeuronCount; }
public AutoencoderGenomeFactory(int inputNeuronCount, int outputNeuronCount, int hiddenNeuronCount, IActivationFunctionLibrary activationFnLibrary) : base(inputNeuronCount, outputNeuronCount, activationFnLibrary) { _hiddenNeuronCount = hiddenNeuronCount; }
/// <summary> /// Constructs with default NeatGenomeParameters, ID generators initialized to zero and the /// provided IActivationFunctionLibrary. /// </summary> public RbfGenomeFactory(int inputNeuronCount, int outputNeuronCount, IActivationFunctionLibrary activationFnLibrary) : base(inputNeuronCount, outputNeuronCount, activationFnLibrary) { }
/// <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 genomeFactory) { // 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.InputNeuronCount != inputCount || genome.OutputNeuronCount != outputCount) { throw new SharpNeatException(string.Format("Genome with wrong number of inputs and/or outputs, expected [{0}][{1}] got [{2}][{3}]", inputCount, outputCount, genome.InputNeuronCount, genome.OutputNeuronCount)); } // 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(maxGenomeId + 1); genomeFactory.InnovationIdGenerator.Reset(maxInnovationId + 1); // Retrospecitively assign the genome factory to the genomes. This is how we overcome the genome/genomeFactory // chicken and egg problem. foreach (NeatGenome genome in genomeList) { genome.GenomeFactory = genomeFactory; } return(genomeList); }
/// <summary> /// Constructs with default NeatGenomeParameters, ID generators initialized to zero and the /// provided IActivationFunctionLibrary. /// </summary> public CppnGenomeFactory(int inputNeuronCount, int outputNeuronCount, IActivationFunctionLibrary activationFnLibrary) : base(inputNeuronCount, outputNeuronCount, activationFnLibrary) { }
/// <summary> /// Constructs with the provided IActivationFunctionLibrary and NeatGenomeParameters. /// </summary> public CppnGenomeFactory(int inputNeuronCount, int outputNeuronCount, IActivationFunctionLibrary activationFnLibrary, NeatGenomeParameters neatGenomeParams) : base(inputNeuronCount,outputNeuronCount, activationFnLibrary, neatGenomeParams) { }
/// <summary> /// Initialize the experiment with some optional XML configutation data. /// </summary> public void Initialize(string name, XmlElement xmlConfig) { _name = name; _populationSize = XmlUtils.GetValueAsInt(xmlConfig, "PopulationSize"); _specieCount = XmlUtils.GetValueAsInt(xmlConfig, "SpecieCount"); _activationScheme = ExperimentUtils.CreateActivationScheme(xmlConfig, "Activation"); _complexityThreshold = XmlUtils.TryGetValueAsInt(xmlConfig, "ComplexityThreshold"); _description = XmlUtils.TryGetValueAsString(xmlConfig, "Description"); _parallelOptions = ExperimentUtils.ReadParallelOptions(xmlConfig); _guesses = XmlUtils.GetValueAsInt(xmlConfig, "Guesses"); Hashed = XmlUtils.TryGetValueAsBool(xmlConfig, "Hashed").HasValue ? XmlUtils.GetValueAsBool(xmlConfig, "Hashed") : false; ValidationGuesses = XmlUtils.GetValueAsInt(xmlConfig, "ValidationGuesses"); // Load the passwords from file string pwdfile = XmlUtils.TryGetValueAsString(xmlConfig, "ValidationPasswordFile"); if (pwdfile != null) { Console.Write("Loading passwords from [{0}]...", pwdfile); if (_passwords == null || _passwords.Count == 0) { int? pwLength = XmlUtils.TryGetValueAsInt(xmlConfig, "PasswordLength"); if (pwLength.HasValue) Console.Write("Filtering to {0}-character passwords...", pwLength.Value); _passwords = PasswordUtil.LoadPasswords(pwdfile, pwLength); } else Console.WriteLine("WARNING: Not loading passwords for experiment (already set)"); } else Console.WriteLine("WARNING: Not loading passwords for experiment (not provided in config file)"); _eaParams = new NeatEvolutionAlgorithmParameters(); _eaParams.SpecieCount = _specieCount; _neatGenomeParams = new NeatGenomeParameters(); _neatGenomeParams.FeedforwardOnly = false; _neatGenomeParams.AddNodeMutationProbability = 0.03; _neatGenomeParams.AddConnectionMutationProbability = 0.05; // TODO: Load states from XML config file // Generates all the valid states in the MC using all viable ASCII characters var stateList = new List<string>(); for (uint i = 32; i < 127; i++) stateList.Add(((char)i).ToString()); stateList.Add(null); _states = stateList.ToArray(); _activationFnLibrary = MarkovActivationFunctionLibrary.CreateLibraryMc(_states); }
public static NeatGenome ReadNeatGenome(string serializedGenomePath, int inputCount, int outputCount, IActivationFunctionLibrary actFuncLib) { // Create a new genome factory NeatGenomeFactory genomeFactory = new NeatGenomeFactory(inputCount, outputCount, actFuncLib); // Create a reader for the serialized genome XmlReader reader = XmlReader.Create(serializedGenomePath); // Create XML document and give it the reader reference XmlDocument document = new XmlDocument(); document.Load(reader); // Traverse down to the network definition XmlNodeList nodeList = document.GetElementsByTagName("Root"); // Read in the genome NeatGenome genome = NeatGenomeXmlIO.LoadCompleteGenomeList(nodeList[0], true, genomeFactory)[0]; return genome; }
/// <summary> /// Creates a AcyclicNetwork from an INetworkDefinition. /// </summary> public static FastAcyclicNetwork CreateFastAcyclicNetwork(INetworkDefinition networkDef, bool boundedOutput) { Debug.Assert(!CyclicNetworkTest.IsNetworkCyclic(networkDef), "Attempt to decode a cyclic network into a FastAcyclicNetwork."); // Determine the depth of each node in the network. // Node depths are used to separate the nodes into depth based layers, these layers can then be // used to determine the order in which signals are propagated through the network. AcyclicNetworkDepthAnalysis depthAnalysis = new AcyclicNetworkDepthAnalysis(); NetworkDepthInfo netDepthInfo = depthAnalysis.CalculateNodeDepths(networkDef); // Construct an array of NodeInfo, ordered by node depth. // Create/populate NodeInfo array. int[] nodeDepthArr = netDepthInfo._nodeDepthArr; INodeList nodeList = networkDef.NodeList; int nodeCount = nodeList.Count; NodeInfo[] nodeInfoByDepth = new NodeInfo[nodeCount]; for (int i = 0; i < nodeCount; i++) { nodeInfoByDepth[i]._nodeId = nodeList[i].Id; nodeInfoByDepth[i]._definitionIdx = i; nodeInfoByDepth[i]._nodeDepth = nodeDepthArr[i]; } // Sort NodeInfo array. // We use an IComparer here because an anonymous method is not accepted on the method overload that accepts // a sort range, which we use to avoid sorting the input and bias nodes. Sort() performs an unstable sort therefore // we must restrict the range of the sort to ensure the input and bias node indexes are unchanged. Restricting the // sort to the required range is also more efficient (less items to sort). int inputAndBiasCount = networkDef.InputNodeCount + 1; Array.Sort(nodeInfoByDepth, inputAndBiasCount, nodeCount - inputAndBiasCount, NodeDepthComparer.__NodeDepthComparer); // Array of live node indexes indexed by their index in the original network definition. This allows us to // locate the position of input and output nodes in their new positions in the live network data structures. int[] newIdxByDefinitionIdx = new int[nodeCount]; // Dictionary of live node indexes keyed by node ID. This allows us to convert the network definition connection // endpoints from node IDs to indexes into the live/runtime network data structures. Dictionary <uint, int> newIdxById = new Dictionary <uint, int>(nodeCount); // Populate both the lookup array and dictionary. for (int i = 0; i < nodeCount; i++) { NodeInfo nodeInfo = nodeInfoByDepth[i]; newIdxByDefinitionIdx[nodeInfo._definitionIdx] = i; newIdxById.Add(nodeInfo._nodeId, i); } // Make a copy of the sub-range of newIdxByDefinitionIdx that represents the output nodes. int outputCount = networkDef.OutputNodeCount; int[] outputNeuronIdxArr = new int[outputCount]; // Note. 'inputAndBiasCount' holds the index of the first output node. Array.Copy(newIdxByDefinitionIdx, inputAndBiasCount, outputNeuronIdxArr, 0, outputCount); // Construct arrays with additional 'per node' data/refs (activation functions, activation fn auxiliary data). IActivationFunctionLibrary activationFnLibrary = networkDef.ActivationFnLibrary; IActivationFunction[] nodeActivationFnArr = new IActivationFunction[nodeCount]; double[][] nodeAuxArgsArray = new double[nodeCount][]; for (int i = 0; i < nodeCount; i++) { int definitionIdx = nodeInfoByDepth[i]._definitionIdx; nodeActivationFnArr[i] = activationFnLibrary.GetFunction(nodeList[definitionIdx].ActivationFnId); nodeAuxArgsArray[i] = nodeList[definitionIdx].AuxState; } //=== Create array of FastConnection(s). // Loop the connections and lookup the node IDs for each connection's end points using newIdxById. IConnectionList connectionList = networkDef.ConnectionList; int connectionCount = connectionList.Count; FastConnection[] fastConnectionArray = new FastConnection[connectionCount]; for (int i = 0; i < connectionCount; i++) { INetworkConnection conn = connectionList[i]; fastConnectionArray[i]._srcNeuronIdx = newIdxById[conn.SourceNodeId]; fastConnectionArray[i]._tgtNeuronIdx = newIdxById[conn.TargetNodeId]; fastConnectionArray[i]._weight = conn.Weight; } // Sort fastConnectionArray by source node index. This allows us to activate the connections in the // order they are present within the network (by depth). We also secondary sort by target index to // improve CPU cache coherency of the data (in order accesses that are as close to each other as possible). Array.Sort(fastConnectionArray, delegate(FastConnection x, FastConnection y) { if (x._srcNeuronIdx < y._srcNeuronIdx) { return(-1); } if (x._srcNeuronIdx > y._srcNeuronIdx) { return(1); } // Secondary sort on target index. if (x._tgtNeuronIdx < y._tgtNeuronIdx) { return(-1); } if (x._tgtNeuronIdx > y._tgtNeuronIdx) { return(1); } // Connections are equal (this should not actually happen). return(0); }); // Create an array of LayerInfo(s). Each LayerInfo contains the index + 1 of both the last node and last // connection in that layer. // The array is in order of depth, from layer zero (bias and inputs nodes) to the last layer // (usually output nodes, but not necessarily if there is a dead end pathway with a high number of hops). // Note. There is guaranteed to be at least one connection with a source at a given depth level, this is // because for there to be a layer N there must necessarily be a connection from a node in layer N-1 // to a node in layer N. int netDepth = netDepthInfo._networkDepth; LayerInfo[] layerInfoArr = new LayerInfo[netDepth]; // Scanning over nodes can start at inputAndBiasCount instead of zero, // because we know that all nodes prior to that index are at depth zero. int nodeIdx = inputAndBiasCount; int connIdx = 0; for (int currDepth = 0; currDepth < netDepth; currDepth++) { // Scan for last node at the current depth. for (; nodeIdx < nodeCount && nodeInfoByDepth[nodeIdx]._nodeDepth == currDepth; nodeIdx++) { ; } // Scan for last connection at the current depth. for (; connIdx < fastConnectionArray.Length && nodeInfoByDepth[fastConnectionArray[connIdx]._srcNeuronIdx]._nodeDepth == currDepth; connIdx++) { ; } // Store node and connection end indexes for the layer. layerInfoArr[currDepth]._endNodeIdx = nodeIdx; layerInfoArr[currDepth]._endConnectionIdx = connIdx; } return(new FastAcyclicNetwork(nodeActivationFnArr, nodeAuxArgsArray, fastConnectionArray, layerInfoArr, outputNeuronIdxArr, nodeCount, networkDef.InputNodeCount, networkDef.OutputNodeCount, boundedOutput)); }