/// <summary> /// Activate the network. Activation reads input signals from InputSignalArray and writes output signals /// to OutputSignalArray. /// </summary> public void Activate() { ReadOnlySpan <int> srcIds = _connIds.GetSourceIdSpan(); ReadOnlySpan <int> tgtIds = _connIds.GetTargetIdSpan(); ReadOnlySpan <double> weights = _weightArr.AsSpan(); Span <double> activations = _activationArr.AsSpan(); // Reset hidden and output node activation levels, ready for next activation. // Note. this reset is performed here instead of after the below loop because this resets the output // node values, which are the outputs of the network as a whole following activation; hence // they need to be remain unchanged until they have been read by the caller of Activate(). activations.Slice(_inputCount).Clear(); // Process all layers in turn. int conIdx = 0; int nodeIdx = _inputCount; // Loop through network layers. for (int layerIdx = 0; layerIdx < _layerInfoArr.Length - 1; layerIdx++) { LayerInfo layerInfo = _layerInfoArr[layerIdx]; // Push signals through the current layer's connections to the target nodes (that are all in 'downstream' layers). for (; conIdx < layerInfo.EndConnectionIdx; conIdx++) { // Get the connection source signal, multiply it by the connection weight, add the result // to the target node's current pre-activation level, and store the result. activations[tgtIds[conIdx]] = Math.FusedMultiplyAdd( activations[srcIds[conIdx]], weights[conIdx], activations[tgtIds[conIdx]]); } // Activate the next layer's nodes. This is possible because we know that all connections that // target these nodes have been processed, either during processing on the current layer's // connections, or earlier layers. This means that the final output value/signal (i.e post // activation function output) is available for all connections and nodes in the lower/downstream // layers. // // Pass the pre-activation levels through the activation function. // Note. The resulting post-activation levels are stored in _activationArr. layerInfo = _layerInfoArr[layerIdx + 1]; _activationFn( ref activations[nodeIdx], layerInfo.EndNodeIdx - nodeIdx); // Update nodeIdx to point at first node in the next layer. nodeIdx = layerInfo.EndNodeIdx; } // Copy the output signals from _activationArr into _outputArr. // These signals are scattered through _activationArr, and here we bring them together into a // contiguous segment of memory that is indexable by output index. for (int i = 0; i < _outputNodeIdxArr.Length; i++) { _outputArr[i] = _activationArr[_outputNodeIdxArr[i]]; } }
/// <summary> /// Activate the network. Activation reads input signals from InputSignalArray and writes output signals /// to OutputSignalArray. /// </summary> public void Activate() { ReadOnlySpan <int> srcIds = _connIds.GetSourceIdSpan(); ReadOnlySpan <int> tgtIds = _connIds.GetTargetIdSpan(); ReadOnlySpan <double> weights = _weightArr.AsSpan(); Span <double> activations = _activationMem.Span; Span <double> outputs = _outputMem.Span; ref int srcIdsRef = ref MemoryMarshal.GetReference(srcIds);
/// <summary> /// Activate the network for a fixed number of iterations defined by the 'maxIterations' parameter /// at construction time. Activation reads input signals from InputSignalArray and writes output signals /// to OutputSignalArray. /// </summary> public void Activate() { ReadOnlySpan <int> srcIds = _connIds.GetSourceIdSpan(); ReadOnlySpan <int> tgtIds = _connIds.GetTargetIdSpan(); ReadOnlySpan <double> weights = _weightArr.AsSpan(); Span <double> preActivations = _preActivationMem.Span; Span <double> postActivations = _postActivationMem.Span; // Note. Here we skip over the activations corresponding to the input neurons, as these have no // incoming connections, and therefore have fixed post-activation values and are never activated. int nonInputCount = _totalNodeCount - _inputCount; Span <double> preActivationsNonInputs = preActivations.Slice(_inputCount); Span <double> postActivationsNonInputs = postActivations.Slice(_inputCount); ref int srcIdsRef = ref MemoryMarshal.GetReference(srcIds);
private static ConnectionIds CopyAndMapIds( Span <DirectedConnection> connSpan, INodeIdMap nodeIdMap) { int count = connSpan.Length; var connIds = new ConnectionIds(count); var srcIds = connIds.GetSourceIdSpan(); var tgtIds = connIds.GetTargetIdSpan(); for (int i = 0; i < count; i++) { srcIds[i] = nodeIdMap.Map(connSpan[i].SourceId); tgtIds[i] = nodeIdMap.Map(connSpan[i].TargetId); } return(connIds); }
private static void CopyAndMapIds( DirectedConnection[] connArr, INodeIdMap nodeIdMap, out ConnectionIds connIds) { int count = connArr.Length; connIds = new ConnectionIds(count); var srcIds = connIds.GetSourceIdSpan(); var tgtIds = connIds.GetTargetIdSpan(); for (int i = 0; i < count; i++) { srcIds[i] = nodeIdMap.Map(connArr[i].SourceId); tgtIds[i] = nodeIdMap.Map(connArr[i].TargetId); } }
/// <summary> /// Split each IWeightedDirectedConnection in a list into an array of DirectedConnections(s), and an array of weights. /// Map the node IDs to indexes as we go. /// </summary> private static void CopyAndMapIds( Span <WeightedDirectedConnection <T> > connSpan, INodeIdMap nodeIdMap, out ConnectionIds connIds, out T[] weightArr) { int count = connSpan.Length; connIds = new ConnectionIds(count); var srcIds = connIds.GetSourceIdSpan(); var tgtIds = connIds.GetTargetIdSpan(); weightArr = new T[count]; for (int i = 0; i < count; i++) { srcIds[i] = nodeIdMap.Map(connSpan[i].SourceId); tgtIds[i] = nodeIdMap.Map(connSpan[i].TargetId); weightArr[i] = connSpan[i].Weight; } }
/// <summary> /// Activate the network for a fixed number of iterations defined by the 'maxIterations' parameter /// at construction time. Activation reads input signals from InputSignalArray and writes output signals /// to OutputSignalArray. /// </summary> public void Activate() { ReadOnlySpan <int> srcIds = _connIds.GetSourceIdSpan(); ReadOnlySpan <int> tgtIds = _connIds.GetTargetIdSpan(); ReadOnlySpan <double> weights = _weightArr.AsSpan(); Span <double> preActivations = _preActivationArr.AsSpan(); Span <double> postActivations = _postActivationArr.AsSpan(); // Note. Here we skip over the activations corresponding to the input neurons, as these have no // incoming connections, and therefore have fixed post-activation values and are never activated. int nonInputCount = _preActivationArr.Length - _inputCount; Span <double> preActivationsNonInputs = preActivations.Slice(_inputCount); Span <double> postActivationsNonInputs = postActivations.Slice(_inputCount); // Activate the network for a fixed number of timesteps. for (int i = 0; i < _cyclesPerActivation; i++) { // Loop connections. Get each connection's input signal, apply the weight and add the result to // the pre-activation signal of the target neuron. for (int j = 0; j < srcIds.Length; j++) { preActivations[tgtIds[j]] = Math.FusedMultiplyAdd( postActivations[srcIds[j]], weights[j], preActivations[tgtIds[j]]); } // Pass the pre-activation levels through the activation function, storing the results in the // post-activation span/array. _activationFn( ref preActivationsNonInputs[0], ref postActivationsNonInputs[0], nonInputCount); // Reset the elements of _preActivationArray that represent the output and hidden nodes. preActivationsNonInputs.Clear(); } }