/// <summary> /// Create a new child genome from a given parent genome. /// </summary> /// <param name="parent">The parent genome.</param> /// <param name="rng">Random source.</param> /// <returns>A new child genome.</returns> public NeatGenome <T>?CreateChildGenome(NeatGenome <T> parent, IRandomSource rng) { Debug.Assert(_metaNeatGenome == parent.MetaNeatGenome, "Parent genome has unexpected MetaNeatGenome."); // Attempt to find a new connection that we can add to the genome. if (!TryGetConnection(parent, rng, out DirectedConnection directedConn, out int insertIdx)) { // Failed to find a new connection. return(null); } // Determine the connection weight. // 50% of the time use weights very close to zero. // Note. this recreates the strategy used in SharpNEAT 2.x. // ENHANCEMENT: Reconsider the distribution of new weights and if there are better approaches (distributions) we could use. T weight = rng.NextBool() ? _weightSamplerB.Sample(rng) : _weightSamplerA.Sample(rng); // Create a new connection gene array that consists of the parent connection genes plus the new gene // inserted at the correct (sorted) position. var parentConnArr = parent.ConnectionGenes._connArr; var parentWeightArr = parent.ConnectionGenes._weightArr; int parentLen = parentConnArr.Length; // Create the child genome's ConnectionGenes object. int childLen = parentLen + 1; var connGenes = new ConnectionGenes <T>(childLen); var connArr = connGenes._connArr; var weightArr = connGenes._weightArr; // Copy genes up to insertIdx. Array.Copy(parentConnArr, connArr, insertIdx); Array.Copy(parentWeightArr, weightArr, insertIdx); // Copy the new genome into its insertion point. connArr[insertIdx] = new DirectedConnection( directedConn.SourceId, directedConn.TargetId); weightArr[insertIdx] = weight; // Copy remaining genes (if any). Array.Copy(parentConnArr, insertIdx, connArr, insertIdx + 1, parentLen - insertIdx); Array.Copy(parentWeightArr, insertIdx, weightArr, insertIdx + 1, parentLen - insertIdx); // Create and return a new genome. // Notes. // The set of hidden node IDs remains unchanged from the parent, therefore we are able to re-use parent.HiddenNodeIdArray. // However, the presence of a new connection invalidates parent.NodeIndexByIdMap for use in the new genome, because the allocated // node indexes are dependent on node depth in the acyclic graph, which in turn can be modified by the presence of a new connection. return(_genomeBuilder.Create( _genomeIdSeq.Next(), _generationSeq.Peek, connGenes, parent.HiddenNodeIdArray)); }
/// <summary> /// Invoke the strategy. /// </summary> /// <param name="weightArr">The connection weight array to apply mutations to.</param> /// <param name="rng">Random source.</param> public void Invoke(double[] weightArr, IRandomSource rng) { // Select a subset of connection genes to mutate. int[] selectedIdxArr = _selectionStrategy.SelectSubset(weightArr.Length, rng); // Loop over the connection genes to be mutated, and mutate them. for (int i = 0; i < selectedIdxArr.Length; i++) { weightArr[selectedIdxArr[i]] += _weightDeltaSampler.Sample(rng); } }
/// <summary> /// Creates a single randomly initialised genome. /// </summary> private NeatGenome <T> CreateGenome() { // Determine how many connections to create in the new genome, as a proportion of all possible connections // between the input and output nodes. int connectionCount = (int)NumericsUtils.StochasticRound(_connectionDefArr.Length * _connectionsProportion, _rng); // Ensure there is at least one connection. connectionCount = Math.Max(1, connectionCount); // Select a random subset of all possible connections between the input and output nodes. int[] sampleArr = new int[connectionCount]; DiscreteDistribution.SampleUniformWithoutReplacement( _rng, _connectionDefArr.Length, sampleArr); // Sort the samples. // Note. This results in the neural net connections being sorted by sourceID then targetID. Array.Sort(sampleArr); // Create the connection gene arrays and populate them. var connGenes = new ConnectionGenes <T>(connectionCount); var connArr = connGenes._connArr; var weightArr = connGenes._weightArr; for (int i = 0; i < sampleArr.Length; i++) { DirectedConnection cdef = _connectionDefArr[sampleArr[i]]; connArr[i] = new DirectedConnection( cdef.SourceId, cdef.TargetId); weightArr[i] = _connWeightDist.Sample(_rng); } // Get create a new genome with a new ID, birth generation of zero. int id = _genomeIdSeq.Next(); return(_genomeBuilder.Create(id, 0, connGenes)); }