/// <summary> /// Create an array of FastConnection(s) representing the connectivity of the provided INetworkDefinition. /// </summary> private static FastConnection[] CreateFastConnectionArray(INetworkDefinition networkDef) { // We vary the decode logic depending on the size of the genome. The most CPU intensive aspect of // decoding is the conversion of the neuron IDs at connection endpoints into neuron indexes. For small // genomes we simply use the BinarySearch() method on NeuronGeneList for each lookup; Each lookup is // an operation with O(log n) time complexity. Thus for C connections and N neurons the number of operations // to perform all lookups is approximately = 2*C*Log(N) // // For larger genomes we invest time in building a Dictionary that maps neuron IDs to their indexes, this on the // basis that the time invested will be more than recovered in time saved performing lookups; The time complexity // of a dictionary lookup is near constant O(1). Thus number of operations is approximately = O(2*C*1) + the time // required to build the dictionary which is approximately O(N). // // Therefore the choice of lookup type is based on which of these two expressions gives the lowest value. // // Binary search. LookupOps = 2 * C * Log2(N) * x // Dictionary Search. LookupOps = (N * y) + (2 * C * z) // // Where x, y and z are constants that adjust for the relative speeds of the lookup and dictionary building operations. // Note that the actual time required to perform these separate algorithms is actually a far more complex problem, and // for modern CPUs exact times cannot be calculated because of large memory caches and superscalar architecture that // makes execution times in a real environment effectively non-deterministic. Thus these calculations are a rough // guide/heuristic that estimate which algorithm will perform best. The constants can be found experimentally but will // tend to vary depending on factors such as CPU and memory architecture, .Net framework version and what other tasks the // CPU is currently doing which may affect our utilisation of memory caches. // TODO: Experimentally determine reasonably good values for constants x,y and z in some common real world runtime platform. IConnectionList connectionList = networkDef.ConnectionList; INodeList nodeList = networkDef.NodeList; int connectionCount = connectionList.Count; int nodeCount = nodeList.Count; FastConnection[] fastConnectionArray = new FastConnection[connectionCount]; if ((2.0 * connectionCount * Math.Log(nodeCount, 2.0)) < ((2.0 * connectionCount) + nodeCount)) { // Binary search requires items to be sorted. Debug.Assert(nodeList.IsSorted()); // Loop the connections and lookup the neuron IDs for each connection's end points using a binary search // on nGeneList. This is probably the quickest approach for small numbers of lookups. for (int i = 0; i < connectionCount; i++) { INetworkConnection conn = connectionList[i]; fastConnectionArray[i]._srcNeuronIdx = nodeList.BinarySearch(conn.SourceNodeId); fastConnectionArray[i]._tgtNeuronIdx = nodeList.BinarySearch(conn.TargetNodeId); fastConnectionArray[i]._weight = conn.Weight; // Check that the correct neuron indexes were found. Debug.Assert( nodeList[fastConnectionArray[i]._srcNeuronIdx].Id == conn.SourceNodeId && nodeList[fastConnectionArray[i]._tgtNeuronIdx].Id == conn.TargetNodeId); } } else { // Build dictionary of neuron indexes keyed on neuron innovation ID. Dictionary <uint, int> neuronIndexDictionary = new Dictionary <uint, int>(nodeCount); for (int i = 0; i < nodeCount; i++) { // ENHANCEMENT: Check if neuron innovation ID requires further manipulation to make a good hash code. neuronIndexDictionary.Add(nodeList[i].Id, i); } // Loop the connections and lookup the neuron IDs for each connection's end points using neuronIndexDictionary. // This is probably the quickest approach for large numbers of lookups. for (int i = 0; i < connectionCount; i++) { INetworkConnection conn = connectionList[i]; fastConnectionArray[i]._srcNeuronIdx = neuronIndexDictionary[conn.SourceNodeId]; fastConnectionArray[i]._tgtNeuronIdx = neuronIndexDictionary[conn.TargetNodeId]; fastConnectionArray[i]._weight = conn.Weight; // Check that the correct neuron indexes were found. Debug.Assert( nodeList[fastConnectionArray[i]._srcNeuronIdx].Id == conn.SourceNodeId && nodeList[fastConnectionArray[i]._tgtNeuronIdx].Id == conn.TargetNodeId); } } return(fastConnectionArray); }