Exemple #1
0
        /// <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);
            }
        }
Exemple #3
0
    /// <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));
    }