/// <summary> /// Adds a node by selecting a random connection, disabling it and replacing it by 2 genes. /// The first gene goes from the original input node, to the new node with a weight of 1. /// The second gene goes from the the new node to the old output node with the same weight as the original connection. /// </summary> /// <param name="trainingRoomSettings">The training room settings.</param> /// <param name="getAndIncreaseNodeIdFunction">The get and increase node id function.</param> /// <param name="innovationFunction">The innovation function.</param> private void AddNodeMutation(TrainingRoomSettings trainingRoomSettings, Func <uint> getAndIncreaseNodeIdFunction, Func <uint, uint, uint> innovationFunction) { // If the connection genes count is 0, then there are not connection genes to mutate. if (ConnectionGenes.Count == 0) { return; } // Find a random connection. ConnectionGene theChosenOne = ConnectionGenes.ElementAt(trainingRoomSettings.Random.Next(ConnectionGenes.Count)); // Disable the connection gene. theChosenOne.Enabled = false; // Generate the new node's id. uint newNodeId = getAndIncreaseNodeIdFunction(); // Get the old in and out node identifiers. uint oldInId = theChosenOne.InNodeIdentifier; uint oldOutId = theChosenOne.OutNodeIdentifier; // Create a connectionGene from oldIn (a) to new (c) and from new (c) to oldOut (b) ConnectionGene aToC = new ConnectionGene(Id, innovationFunction(oldInId, newNodeId), oldInId, newNodeId, 1); ConnectionGene cToB = new ConnectionGene(Id, innovationFunction(newNodeId, oldOutId), newNodeId, oldOutId, theChosenOne.Weight); // Add new connection genes. ConnectionGenes.Add(aToC); ConnectionGenes.Add(cToB); // Update the max innovation. _maxInnovation = Math.Max(Math.Max(cToB.InnovationNumber, aToC.InnovationNumber), _maxInnovation); }
/// <summary> /// Adds a node by selecting a random connection, disabling it and replacing it by 2 genes. /// The first gene goes from the original input node, to the new node with a weight of 1. /// The second gene goes from the the new node to the old output node with the same weight as the original connection. /// </summary> private void AddNodeMutation() { if (ConnectionGenes.Count == 0) { return; } // Choose a random connection to add a node into ConnectionGene theChosenOne = ConnectionGenes.ElementAt(TrainingRoom.Random.Next(ConnectionGenes.Count)); // Disable it theChosenOne.Enabled = false; // Generate the new node's id uint newNodeId = TrainingRoom.GetAndIncreaseNodeId(); uint oldInId = theChosenOne.InId; uint oldOutId = theChosenOne.OutId; // Create a connectionGene from oldIn (a) to new (c) and from new (c) to oldOut (b) ConnectionGene aToC = new ConnectionGene(Id, oldInId, newNodeId, 1, TrainingRoom.GetInnovationNumber(oldInId, newNodeId)); ConnectionGene cToB = new ConnectionGene(Id, newNodeId, oldOutId, theChosenOne.Weight, TrainingRoom.GetInnovationNumber(newNodeId, oldOutId)); ConnectionGenes.Add(aToC); ConnectionGenes.Add(cToB); _maxInnovation = Math.Max(Math.Max(cToB.InnovationNumber, aToC.InnovationNumber), _maxInnovation); }
/// <summary> /// Mutates the the organism based on the training room settings. /// </summary> /// <param name="trainingRoomSettings">The training room settings.</param> /// <param name="getAndIncreaseNodeIdFunction">The get and increase node id function.</param> /// <param name="innovationFunction">The innovation function.</param> public void Mutate(TrainingRoomSettings trainingRoomSettings, Func <uint> getAndIncreaseNodeIdFunction, Func <uint, uint, uint> innovationFunction) { // If the random value is lower than the add connection chance add a connection mutation. if (trainingRoomSettings.Random.NextDouble() < trainingRoomSettings.AddConnectionChance) { AddConnectionMutation(trainingRoomSettings, innovationFunction); } // If the random value is lower than the add node chance add a node mutation. if (trainingRoomSettings.Random.NextDouble() < trainingRoomSettings.AddNodeChance) { AddNodeMutation(trainingRoomSettings, getAndIncreaseNodeIdFunction, innovationFunction); } // If the random value is higher than the mutate weight chance or the connection gene count is lower than 0 return early. if (trainingRoomSettings.Random.NextDouble() > trainingRoomSettings.MutateWeightChance || ConnectionGenes.Count <= 0) { return; } // Find a random connection gene. ConnectionGene connectionGene = ConnectionGenes.ElementAt(trainingRoomSettings.Random.Next(ConnectionGenes.Count)); // If the random value is lower than the weight reassign chance set a new weight else add weight. if (trainingRoomSettings.Random.NextDouble() < trainingRoomSettings.WeightReassignChance) { connectionGene.Weight = trainingRoomSettings.Random.NextDouble() * 2 - 1; } else { connectionGene.Weight += (trainingRoomSettings.Random.NextDouble() * 2 - 1) * 0.1; } //TODO: Inputs mutate? }
/// <summary> /// Mutates the brain, each time this is called there is a chance to: /// - Add a connection /// - Add a node /// - Change the weight of an existing gene, which can either randomly be reset to a random value or slightly change /// These changes can all happen in one call, but the chance that this happens depends on the training room settings. /// </summary> public void Mutate() { if (TrainingRoom.Random.NextDouble() < TrainingRoom.TrainingRoomSettings.AddConnectionChance) { AddConnectionMutation(); } if (TrainingRoom.Random.NextDouble() < TrainingRoom.TrainingRoomSettings.AddNodeChance) { AddNodeMutation(); } if ((TrainingRoom.Random.NextDouble() < TrainingRoom.TrainingRoomSettings.MutateWeightChance) && ConnectionGenes.Count > 0) { ConnectionGene connectionGene = ConnectionGenes.ElementAt(TrainingRoom.Random.Next(ConnectionGenes.Count)); if (TrainingRoom.Random.NextDouble() < TrainingRoom.TrainingRoomSettings.WeightReassignChance) { connectionGene.Weight = TrainingRoom.Random.NextDouble() * 2 - 1; } else { connectionGene.Weight += (TrainingRoom.Random.NextDouble() * 2 - 1) * 0.1; } } }