public void ConnectFast() { if (!useOnlyWebSockets) { Log("IRTSession", GameSparksRT.LogLevel.DEBUG, PeerId + ": Creating new fastConnection to " + FastPort); //if (fastConnection != null) { fastConnection = new FastConnection(hostName, FastPort, this); //} } }
/// <summary> /// Takes a connection and a counter and updates the corresponding /// FastConnection element in a phenomeVariables array. /// </summary> private static void AddConnection(ConnectionGene connection, out FastConnection fastConnectionElement) { // We need the NEW index for sources and targets, not the // original index in genome. fastConnectionElement._srcNeuronIdx = idToNewIndex[connection.SourceNodeId]; fastConnectionElement._tgtNeuronIdx = idToNewIndex[connection.TargetNodeId]; fastConnectionElement._weight = connection.Weight; }
/// <summary> /// Creates a AcyclicNetwork from an INetworkDefinition. /// </summary> public static FastAcyclicNetwork CreateFastAcyclicNetwork(INetworkDefinition networkDef) { 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)); }
/// <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); }
private void CheckConnection() { try { if (DateTime.Now > mustConnectBy) { if (ConnectState == GameSparksRT.ConnectState.Disconnected) { Log("IRTSession", GameSparksRT.LogLevel.INFO, "Disconnected, trying to connect"); ConnectState = GameSparksRT.ConnectState.Connecting; #if !__WINDOWS__ if (useOnlyWebSockets) { ConnectWSReliable(); } else #endif { ConnectReliable(); } connectionAttempts++; } else if (ConnectState == GameSparksRT.ConnectState.Connecting) { ConnectState = GameSparksRT.ConnectState.Disconnected; Log("IRTSession", GameSparksRT.LogLevel.INFO, "Not connected in time, retrying"); #if !__WINDOWS__ if (useOnlyWebSockets) { if (reliableWSConnection != null) { reliableWSConnection.StopInternal(); reliableWSConnection = null; } } else #endif { if (reliableConnection != null) { reliableConnection.StopInternal(); reliableConnection = null; } if (fastConnection != null) { fastConnection.StopInternal(); fastConnection = null; } } } } #if __WINDOWS__ #else } catch (ThreadAbortException e) { Log("IRTSession", GameSparksRT.LogLevel.INFO, e.StackTrace); return; #endif } catch (Exception e) { //General exception, ignore it System.Diagnostics.Debug.WriteLine(e); Log("IRTSession", GameSparksRT.LogLevel.ERROR, e.StackTrace); } }