/// <summary> /// Constructs a AcyclicNeuralNet with the provided neural net definition parameters. /// </summary> /// <param name="digraph">Network structure definition</param> /// <param name="weightArr">Connection weights array.</param> /// <param name="activationFn">Node activation function.</param> public NeuralNetAcyclic( DirectedGraphAcyclic digraph, double[] weightArr, VecFnSegment <double> activationFn) { // Store refs to network structure data. _srcIdArr = digraph.ConnectionIdArrays._sourceIdArr; _tgtIdArr = digraph.ConnectionIdArrays._targetIdArr; _weightArr = weightArr; _layerInfoArr = digraph.LayerArray; // Store network activation function. _activationFn = activationFn; // Store input/output node counts. _inputCount = digraph.InputCount; _outputCount = digraph.OutputCount; // Create working array for node activation signals. _activationArr = new double[digraph.TotalNodeCount]; // Wrap a sub-range of the _activationArr that holds the activation values for the input nodes. this.InputVector = new VectorSegment <double>(_activationArr, 0, _inputCount); // Wrap the output nodes. Nodes have been sorted by depth within the network therefore the output // nodes can no longer be guaranteed to be in a contiguous segment at a fixed location. As such their // positions are indicated by outputNodeIdxArr, and so we package up this array with the node signal // array to abstract away the indirection described by outputNodeIdxArr. this.OutputVector = new MappingVector <double>(_activationArr, digraph.OutputNodeIdxArr); }
/// <summary> /// Constructs a AcyclicNeuralNet with the provided neural net definition parameters. /// </summary> /// <param name="digraph">Network structure definition.</param> /// <param name="weightArr">Connection weights array.</param> /// <param name="activationFn">Node activation function.</param> public NeuralNetAcyclicSafe( DirectedGraphAcyclic digraph, double[] weightArr, VecFn <double> activationFn) { Debug.Assert(digraph.ConnectionIds.GetSourceIdSpan().Length == weightArr.Length); // Store refs to network structure data. _connIds = digraph.ConnectionIds; _weightArr = weightArr; _layerInfoArr = digraph.LayerArray; // Store network activation function. _activationFn = activationFn; // Store input/output node counts. _inputCount = digraph.InputCount; _outputCount = digraph.OutputCount; // Create working array for node activation signals. _activationArr = new double[digraph.TotalNodeCount]; // Map the inputs vector to the corresponding segment of node activation values. this.Inputs = new Memory <double>(_activationArr, 0, _inputCount); // Get an array to act a as a contiguous run of output signals. _outputArr = new double[_outputCount]; this.Outputs = _outputArr; // Store the indexes into _activationArr that give the output signals. _outputNodeIdxArr = digraph.OutputNodeIdxArr; }
/// <summary> /// Create a NeatGenome with the given meta data, connection genes and supplementary data. /// </summary> /// <param name="id">Genome ID.</param> /// <param name="birthGeneration">Birth generation.</param> /// <param name="connGenes">Connection genes.</param> /// <param name="hiddenNodeIdArr">An array of the hidden node IDs in the genome, in ascending order.</param> /// <returns>A new NeatGenome instance.</returns> public NeatGenome <T> Create( int id, int birthGeneration, ConnectionGenes <T> connGenes, int[] hiddenNodeIdArr) { int inputCount = _metaNeatGenome.InputNodeCount; // Create a mapping from node IDs to node indexes. Dictionary <int, int> nodeIdxById = BuildNodeIndexById(hiddenNodeIdArr); // Create a DictionaryNodeIdMap. DictionaryNodeIdMap nodeIndexByIdMap = new DictionaryNodeIdMap(inputCount, nodeIdxById); // Create a digraph from the genome. DirectedGraph digraph = NeatGenomeBuilderUtils.CreateDirectedGraph( _metaNeatGenome, connGenes, nodeIndexByIdMap); // Calc the depth of each node in the digraph. GraphDepthInfo depthInfo = _graphDepthAnalysis.CalculateNodeDepths(digraph); // Create a weighted acyclic digraph. // Note. This also outputs connectionIndexMap. For each connection in the acyclic graph this gives // the index of the same connection in the genome; this is because connections are re-ordered based // on node depth in the acyclic graph. DirectedGraphAcyclic acyclicDigraph = DirectedGraphAcyclicBuilderUtils.CreateDirectedGraphAcyclic( digraph, depthInfo, out int[] newIdByOldId, out int[] connectionIndexMap, ref _timesortWorkArr, ref _timesortWorkVArr); // TODO: Write unit tests to cover this! // Update nodeIdxById with the new depth based node index allocations. // Notes. // The current nodeIndexByIdMap maps node IDs (also know as innovation IDs in NEAT) to a compact // ID space in which any gaps have been removed, i.e. a compacted set of IDs that can be used as indexes, // i.e. if there are N nodes in total then the highest node ID will be N-1. // // Here we map the new compact IDs to an alternative ID space that is also compact, but ensures that nodeIDs // reflect the depth of a node in the acyclic graph. UpdateNodeIndexById(nodeIdxById, hiddenNodeIdArr, newIdByOldId); // Create the neat genome. return(new NeatGenome <T>( _metaNeatGenome, id, birthGeneration, connGenes, hiddenNodeIdArr, nodeIndexByIdMap, acyclicDigraph, connectionIndexMap)); }
/// <summary> /// Create an array that gives the node layer for each node, keyed by node index. /// </summary> /// <param name="digraphAcyclic">The directed acyclic graph.</param> /// <returns>A new integer array.</returns> private static int[] BuildNodeLayerByIdx_Acyclic(DirectedGraphAcyclic digraphAcyclic) { // Alloc the array, and populate with the already determined node depth values as represented // by the layer info provided by DirectedGraphAcyclic. int nodeCount = digraphAcyclic.TotalNodeCount; int[] nodeLayerByIdx = new int[nodeCount]; // Use a restricted scope for the loop variables. { // The first layer is layer 1; layer zero will be used later to represent input nodes only. int layerIdx = 1; int nodeIdx = 0; foreach (LayerInfo layerInfo in digraphAcyclic.LayerArray) { for (; nodeIdx < layerInfo.EndNodeIdx; nodeIdx++) { nodeLayerByIdx[nodeIdx] = layerIdx; } layerIdx++; } } // Assign input nodes to their own layer (layer zero). for (int i = 0; i < digraphAcyclic.InputCount; i++) { nodeLayerByIdx[i] = 0; } // Assign output nodes to their own layer. int outputLayerIdx = digraphAcyclic.LayerArray.Length + 1; foreach (int outputNodeIdx in digraphAcyclic.OutputNodeIdxArr) { nodeLayerByIdx[outputNodeIdx] = outputLayerIdx; } // Remove empty layers (if any), by adjusting the depth values in nodeLayerByIdx. RemoveEmptyLayers(nodeLayerByIdx, outputLayerIdx + 1); // Return the constructed node layer lookup table. return(nodeLayerByIdx); }
/// <summary> /// Constructs a AcyclicNeuralNet with the provided neural net definition parameters. /// </summary> /// <param name="digraph">Network structure definition.</param> /// <param name="weightArr">Connection weights array.</param> /// <param name="activationFn">Node activation function.</param> public NeuralNetAcyclic( DirectedGraphAcyclic digraph, double[] weightArr, VecFn <double> activationFn) { Debug.Assert(digraph.ConnectionIds.GetSourceIdSpan().Length == weightArr.Length); // Store refs to network structure data. _connIds = digraph.ConnectionIds; _weightArr = weightArr; _layerInfoArr = digraph.LayerArray; // Store network activation function. _activationFn = activationFn; // Store input/output node counts. _inputCount = digraph.InputCount; _outputCount = digraph.OutputCount; _totalNodeCount = digraph.TotalNodeCount; // Get a working array for node activations signals and a separate output signal segment on the end. // And map the memory segments onto the array. _workingArr = ArrayPool <double> .Shared.Rent(_totalNodeCount + _outputCount); _activationMem = _workingArr.AsMemory(0, _totalNodeCount); _outputMem = _workingArr.AsMemory(_totalNodeCount, _outputCount); // Map the inputs vector to the corresponding segment of node activation values. this.Inputs = _activationMem.Slice(0, _inputCount); // Use the already defined outputs memory segment. this.Outputs = _outputMem; // Store the indexes into _activationArr that give the output signals. _outputNodeIdxArr = digraph.OutputNodeIdxArr; }