Example #1
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));
        }
Example #2
0
    private static ConnectionGene <T>?CreateConnectionGene(
        ConnectionGenes <T> connGenes1,
        ConnectionGenes <T> connGenes2,
        int idx1, int idx2,
        bool includeSecondaryParentGene,
        IRandomSource rng)
    {
        // Select gene at random if it is present on both parents.
        if (idx1 != -1 && idx2 != -1)
        {
            if (rng.NextBool())
            {
                return(CreateConnectionGene(connGenes1, idx1));
            }
            else
            {
                return(CreateConnectionGene(connGenes2, idx2));
            }
        }

        // Use the primary parent's gene if it has one.
        if (idx1 != -1)
        {
            return(CreateConnectionGene(connGenes1, idx1));
        }

        // Otherwise use the secondary parent's gene if the 'includeSecondaryParentGene' flag is set.
        if (includeSecondaryParentGene)
        {
            return(CreateConnectionGene(connGenes2, idx2));
        }

        return(null);
    }
Example #3
0
        /// <summary>
        /// Initialise a span with 'naturally' ordered random integers.
        /// The initialised span will contain sub-spans of sorted integers, in both ascending and descending order.
        /// </summary>
        /// <param name="keys">The span to initialise.</param>
        /// <param name="rng">Random number generator.</param>
        public static void InitNatural(Span <int> keys, IRandomSource rng)
        {
            // Init with an incrementing sequence.
            for (int i = 0; i < keys.Length; i++)
            {
                keys[i] = i;
            }

            // Reverse multiple random sub-ranges.
            int reverseCount = (int)(Math.Sqrt(keys.Length) * 2.0);
            int len          = keys.Length;

            for (int i = 0; i < reverseCount; i++)
            {
                int idx  = rng.Next(keys.Length);
                int idx2 = rng.Next(keys.Length);

                if (idx > idx2)
                {
                    VariableUtils.Swap(ref idx, ref idx2);
                }

                if (rng.NextBool())
                {
                    keys[0..idx].Reverse();
Example #4
0
        private ConnectionGene <T>?CreateConnectionGene(
            ConnectionGenes <T> connGenes1,
            ConnectionGenes <T> connGenes2,
            int idx1, int idx2,
            IRandomSource rng)
        {
            // Select gene at random if it is present on both parents.
            if (-1 != idx1 && -1 != idx2)
            {
                if (rng.NextBool())
                {
                    return(CreateConnectionGene(connGenes1, idx1));
                }
                else
                {
                    return(CreateConnectionGene(connGenes2, idx2));
                }
            }

            // Use the primary parent's gene if it has one.
            if (-1 != idx1)
            {
                return(CreateConnectionGene(connGenes1, idx1));
            }

            // Otherwise use the secondary parent's gene stochastically.
            if (DiscreteDistribution.SampleBernoulli(rng, _secondaryParentGeneProbability))
            {
                return(CreateConnectionGene(connGenes2, idx2));
            }

            return(null);
        }
Example #5
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));
        }
Example #6
0
        /// <summary>
        /// Take a sample from the uniform distribution with interval (-1, 1).
        /// </summary>
        /// <param name="rng">Random source.</param>
        /// <returns>A random sample.</returns>
        public static float SampleSigned(IRandomSource rng)
        {
            float sample = rng.NextFloat();

            if (rng.NextBool())
            {
                sample *= -1.0f;
            }
            return(sample);
        }
Example #7
0
        /// <summary>
        /// Take a sample from the uniform distribution with interval (-1, 1).
        /// </summary>
        /// <param name="rng">Random source.</param>
        /// <returns>A random sample.</returns>
        public static double SampleSigned(IRandomSource rng)
        {
            double sample = rng.NextDouble();

            if (rng.NextBool())
            {
                sample *= -1.0f;
            }
            return(sample);
        }
Example #8
0
        /// <summary>
        /// Take a sample from the uniform distribution with interval (-max, max).
        /// </summary>
        /// <param name="rng">Random source.</param>
        /// <param name="max">Maximum absolute value (exclusive).</param>
        /// <returns>A random sample.</returns>
        public static float SampleSigned(IRandomSource rng, float max)
        {
            Debug.Assert(max >= 0.0);

            float sample = rng.NextFloat() * max;

            if (rng.NextBool())
            {
                sample *= -1.0f;
            }
            return(sample);
        }
Example #9
0
        /// <summary>
        /// Take a sample from the uniform distribution with interval (-max, max).
        /// </summary>
        /// <param name="rng">Random source.</param>
        /// <param name="max">Maximum absolute value (exclusive).</param>
        /// <returns>A random sample.</returns>
        public static double SampleSigned(IRandomSource rng, double max)
        {
            Debug.Assert(max >= 0.0);

            double sample = rng.NextDouble() * max;

            if (rng.NextBool())
            {
                sample *= -1.0;
            }
            return(sample);
        }
    /// <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));
    }
Example #11
0
        /// <summary>
        /// Fill an array with samples from the uniform distribution with interval (-max, max).
        /// </summary>
        /// <param name="rng">Random source.</param>
        /// <param name="max">Maximum absolute value (exclusive).</param>
        /// <param name="buf">The array to fill with samples.</param>
        public static void SampleSigned(IRandomSource rng, float max, float[] buf)
        {
            Debug.Assert(max >= 0.0);

            for (int i = 0; i < buf.Length; i++)
            {
                float sample = rng.NextFloat() * max;
                if (rng.NextBool())
                {
                    sample *= -1.0f;
                }
                buf[i] = sample;
            }
        }
Example #12
0
        /// <summary>
        /// Fill an array with samples from the uniform distribution with interval (-max, max).
        /// </summary>
        /// <param name="rng">Random source.</param>
        /// <param name="max">Maximum absolute value (exclusive).</param>
        /// <param name="buf">The array to fill with samples.</param>
        public static void SampleSigned(IRandomSource rng, double max, double[] buf)
        {
            Debug.Assert(max >= 0.0);

            for (int i = 0; i < buf.Length; i++)
            {
                double sample = rng.NextDouble() * max;
                if (rng.NextBool())
                {
                    sample *= -1.0;
                }
                buf[i] = sample;
            }
        }
Example #13
0
        public void IterationSetup_NaturallyOrderedKeys()
        {
            // Init with an incrementing sequence.
            for (int i = 0; i < _keys.Length; i++)
            {
                _keys[i] = i;
            }

            // Reverse multiple random sub-ranges.
            int reverseCount = (int)(Math.Sqrt(_keys.Length) * 2.0);
            int len          = _keys.Length;

            for (int i = 0; i < reverseCount; i++)
            {
                int idx  = _rng.Next(len);
                int idx2 = _rng.Next(len);

                if (idx > idx2)
                {
                    VariableUtils.Swap(ref idx, ref idx2);
                }

                if (_rng.NextBool())
                {
                    Array.Reverse(_keys, 0, idx + 1);
                    Array.Reverse(_keys, idx2, len - idx2);
                }
                else
                {
                    Array.Reverse(_keys, idx, idx2 - idx);
                }
            }

            // Init value arrays (just copy key value into these).
            for (int i = 0; i < _keys.Length; i++)
            {
                _values[i]  = _keys[i];
                _values2[i] = _keys[i];
            }
        }
Example #14
0
        private IntPoint[] GenerateRandomTestCase(int largeBoxRelativePos)
        {
            // Randomly select a position for the small box (the box is a single pixel in size).
            IntPoint smallBoxPos = new IntPoint(_rng.Next(TestFieldResolution), _rng.Next(TestFieldResolution));

            // Large box center is 5 pixels to the right, down or diagonally from the small box.
            IntPoint largeBoxPos = smallBoxPos;

            switch (largeBoxRelativePos)
            {
            case 0:     // Right
                largeBoxPos._x += 5;
                break;

            case 1:     // Down
                largeBoxPos._y += 5;
                break;

            case 2:     // Diagonal
                // Two alternate position get us to exactly 5 pixels distant from the small box.
                if (_rng.NextBool())
                {
                    largeBoxPos._x += 3;
                    largeBoxPos._y += 4;
                }
                else
                {
                    largeBoxPos._x += 4;
                    largeBoxPos._y += 3;
                }
                break;
            }

            // Handle cases where the large box is outside the visual field or overlapping the edge.
            if (largeBoxPos._x > CoordBoundIdx)
            {   // Wrap around.
                largeBoxPos._x -= TestFieldResolution;

                if (0 == largeBoxPos._x)
                {   // Move box fully into the visual field.
                    largeBoxPos._x++;
                }
            }
            else if (CoordBoundIdx == largeBoxPos._x)
            {   // Move box fully into the visual field.
                largeBoxPos._x--;
            }
            else if (0 == largeBoxPos._x)
            {   // Move box fully into the visual field.
                largeBoxPos._x++;
            }


            if (largeBoxPos._y > CoordBoundIdx)
            {   // Wrap around.
                largeBoxPos._y -= TestFieldResolution;

                if (0 == largeBoxPos._y)
                {   // Move box fully into the visual field.
                    largeBoxPos._y++;
                }
            }
            else if (CoordBoundIdx == largeBoxPos._y)
            {   // Move box fully into the visual field.
                largeBoxPos._y--;
            }
            else if (0 == largeBoxPos._y)
            {   // Move box fully into the visual field.
                largeBoxPos._y++;
            }
            return(new IntPoint[] { smallBoxPos, largeBoxPos });
        }