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)); }
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); }
/// <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();
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); }
/// <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> /// 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); }
/// <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); }
/// <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); }
/// <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)); }
/// <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; } }
/// <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; } }
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]; } }
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 }); }