Beispiel #1
0
        public static NeatGenome <double> CreateNeatGenome2(
            INeatGenomeBuilder <double> genomeBuilder)
        {
            var connGenes = new ConnectionGenes <double>(12);

            connGenes[0] = (0, 3, 0.3);
            connGenes[1] = (0, 4, 0.4);
            connGenes[2] = (0, 5, 0.5);

            connGenes[3] = (3, 6, 3.6);
            connGenes[4] = (4, 7, 4.7);
            connGenes[5] = (5, 8, 5.8);

            connGenes[6] = (6, 9, 6.9);
            connGenes[7] = (7, 10, 7.1);
            connGenes[8] = (8, 11, 8.11);

            connGenes[9]  = (9, 1, 9.1);
            connGenes[10] = (10, 1, 10.1);
            connGenes[11] = (11, 1, 11.1);

            var genome = genomeBuilder.Create(1, 0, connGenes);

            return(genome);
        }
Beispiel #2
0
        private NeatGenome <T> LoadInner(StreamReader sr)
        {
            // Reset line counter.
            _lineIdx = 0;

            // Read node counts.
            int inputCount  = ReadInt32Line();
            int outputCount = ReadInt32Line();

            ReadEndOfSection();
            ValidateNodeCounts(inputCount, outputCount);

            // Read connections.
            ReadConnections(out DirectedConnection[] connArr, out T[] weightArr);

            // Read activation function(s).
            // Note. For a NeatGenome we expect a single line specifying a single activation function with ID 0,
            // and a function code that specifies the function. Additional lines in the section are ignored.
            ReadActivationFunctions();

            // Read activation function(s).
            ValidateActivationFunctions();

            // Create a genome object and return.
            var connGenes = new ConnectionGenes <T>(connArr, weightArr);

            return(_genomeBuilder.Create(_genomeId++, 0, connGenes));
        }
Beispiel #3
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)
        {
            // Clone the parent's connection weight array.
            var weightArr = (T[])parent.ConnectionGenes._weightArr.Clone();

            // Apply mutation to the connection weights.
            _weightMutationScheme.MutateWeights(weightArr, rng);

            // Create the child genome's ConnectionGenes object.
            // Note. The parent genome's connection arrays are re-used; these remain unchanged because we are mutating
            // connection *weights* only, so we can avoid the cost of cloning these arrays.
            var connGenes = new ConnectionGenes <T>(
                parent.ConnectionGenes._connArr,
                weightArr);

            // Create and return a new genome.
            // Note. The parent's ConnectionIndexArray and HiddenNodeIdArray can be re-used here because the new genome
            // has the same set of connections (same neural net structure).
            return(_genomeBuilder.Create(
                       _genomeIdSeq.Next(),
                       _generationSeq.Peek,
                       connGenes,
                       parent.HiddenNodeIdArray,
                       parent.NodeIndexByIdMap,
                       parent.DirectedGraph,
                       parent.ConnectionIndexMap));
        }
Beispiel #4
0
        private NeatGenome <T> CreateGenomeInner(NeatGenome <T> parent1, NeatGenome <T> parent2)
        {
            // Randomly select one parent as being the primary parent.
            if (_rng.NextBool())
            {
                VariableUtils.Swap(ref parent1, ref parent2);
            }

            // Enumerate over the connection genes in both parents.
            foreach (var geneIndexPair in EnumerateParentGenes(parent1.ConnectionGenes, parent2.ConnectionGenes))
            {
                // Create a connection gene based on the current position in both parents.
                ConnectionGene <T>?connGene = CreateConnectionGene(
                    parent1.ConnectionGenes, parent2.ConnectionGenes,
                    geneIndexPair.Item1, geneIndexPair.Item2,
                    out bool isSecondaryGene);

                if (connGene.HasValue)
                {
                    // Attempt to add the gene to the child genome we are building.
                    _builder.TryAddGene(connGene.Value, isSecondaryGene);
                }
            }

            // Convert the genes to the structure required by NeatGenome.
            var connGenes = _builder.ToConnectionGenes();

            // Create and return a new genome.
            return(_genomeBuilder.Create(
                       _genomeIdSeq.Next(),
                       _generationSeq.Peek,
                       connGenes));
        }
Beispiel #5
0
    private NeatGenome <T> CreateGenomeInner(
        NeatGenome <T> parent1,
        NeatGenome <T> parent2,
        IRandomSource rng)
    {
        // Resolve a flag that determines if *all* disjoint genes from the secondary parent will be included in the child genome, or not.
        // This approach is from SharpNEAT v2.x and is preserved to act as baseline in v4.x, but better strategies may exist.
        bool includeSecondaryParentGene = DiscreteDistribution.SampleBernoulli(rng, _secondaryParentGeneProbability);

        // Enumerate over the connection genes in both parents.
        foreach (var geneIndexPair in EnumerateParentGenes(parent1.ConnectionGenes, parent2.ConnectionGenes))
        {
            // Create a connection gene based on the current position in both parents.
            ConnectionGene <T>?connGene = CreateConnectionGene(
                parent1.ConnectionGenes, parent2.ConnectionGenes,
                geneIndexPair.Item1, geneIndexPair.Item2,
                includeSecondaryParentGene, rng);

            if (connGene.HasValue)
            {   // Attempt to add the gene to the child genome we are building.
                _connGeneListBuilder.TryAddGene(connGene.Value);
            }
        }

        // Convert the genes to the structure required by NeatGenome.
        ConnectionGenes <T> connGenes = _connGeneListBuilder.ToConnectionGenes();

        // Create and return a new genome.
        return(_genomeBuilder.Create(
                   _genomeIdSeq.Next(),
                   _generationSeq.Peek,
                   connGenes));
    }
Beispiel #6
0
        public static NeatGenome <double> CreateNeatGenome(
            MetaNeatGenome <double> metaNeatGenome,
            INeatGenomeBuilder <double> genomeBuilder)
        {
            var connGenes = new ConnectionGenes <double>(12);

            connGenes[0] = (0, 3, 0.1);
            connGenes[1] = (0, 4, 0.2);
            connGenes[2] = (0, 5, 0.3);

            connGenes[3] = (3, 6, 0.4);
            connGenes[4] = (4, 7, 0.5);
            connGenes[5] = (5, 8, 0.6);

            connGenes[6] = (6, 9, 0.7);
            connGenes[7] = (7, 10, 0.8);
            connGenes[8] = (8, 11, 0.9);

            connGenes[9]  = (9, 1, 1.0);
            connGenes[10] = (10, 1, 1.1);
            connGenes[11] = (11, 1, 1.2);

            var genome = genomeBuilder.Create(0, 0, connGenes);

            return(genome);
        }
Beispiel #7
0
        /// <summary>
        /// Create a new child genome from a given parent genome.
        /// </summary>
        /// <param name="parent">The parent genome.</param>
        /// <returns>A new child genome.</returns>
        public NeatGenome <T> CreateChildGenome(NeatGenome <T> parent)
        {
            Debug.Assert(_metaNeatGenome == parent.MetaNeatGenome, "Parent genome has unexpected MetaNeatGenome.");

            // Attempt to find a new connection that we can add to the genome.
            DirectedConnection directedConn;

            if (!TryGetConnection(parent, out 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() ? _weightDistB.Sample() : _weightDistA.Sample();

            // 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>
    /// 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)
    {
        // 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.
        // Note. The set of hidden node IDs remains unchanged from the parent, therefore we are able to re-use
        // both parent.HiddenNodeIdArray and NodeIndexByIdMap.
        return(_genomeBuilder.Create(
                   _genomeIdSeq.Next(),
                   _generationSeq.Peek,
                   connGenes,
                   parent.HiddenNodeIdArray,
                   parent.NodeIndexByIdMap));
    }
        /// <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)
        {
            // We require at least two connections in the parent, i.e. we avoid creating genomes with
            // no connections, which would be pointless.
            if (parent.ConnectionGenes.Length < 2)
            {
                return(null);
            }

            // Select a gene at random to delete.
            var parentConnArr   = parent.ConnectionGenes._connArr;
            var parentWeightArr = parent.ConnectionGenes._weightArr;
            int parentLen       = parentConnArr.Length;
            int deleteIdx       = rng.Next(parentLen);

            // 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 deleteIdx.
            Array.Copy(parentConnArr, connArr, deleteIdx);
            Array.Copy(parentWeightArr, weightArr, deleteIdx);

            // Copy remaining genes (if any).
            Array.Copy(parentConnArr, deleteIdx + 1, connArr, deleteIdx, childLen - deleteIdx);
            Array.Copy(parentWeightArr, deleteIdx + 1, weightArr, deleteIdx, childLen - deleteIdx);

            // Get an array of hidden node IDs.
            var hiddenNodeIdArr = GetHiddenNodeIdArray(parent, deleteIdx, connArr);

            // Create and return a new genome.
            return(_genomeBuilder.Create(
                       _genomeIdSeq.Next(),
                       _generationSeq.Peek,
                       connGenes,
                       hiddenNodeIdArr));
        }
        /// <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.ProbabilisticRound(_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];
            DiscreteDistributionUtils.SampleUniformWithoutReplacement(
                _connectionDefArr.Length, sampleArr, _rng);

            // 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(_metaNeatGenome.ConnectionWeightRange, true);
            }

            // Get create a new genome with a new ID, birth generation of zero.
            int id = _genomeIdSeq.Next();

            return(_genomeBuilder.Create(id, 0, connGenes));
        }