/// <summary> /// Adds all innovations from another InnovationPool to end of Pool. /// </summary> /// <param name="pool">The other pool to add.</param> /// <returns>The last index of the new pool.</returns> public int AddRange(InnovationPool pool) { for (int i = 0; i < pool.Size(); i++) { Add(pool.FindById(i)); } return(Size() - 1); }
public List <Genome> Reproduce(int expectedChildren, InnovationPool seenInnovations, List <Species> interBredSpecies, Random rando) { List <Genome> children = new List <Genome>(); bool champDone = false; while (expectedChildren > 0) { if (!champDone && expectedChildren >= Const.ChampSurvivalThresh) { children.Add(Fitness.Champion); champDone = true; } else if (rando.NextDouble() < Const.MutateOnlyProbability || expectedChildren == 1) { Genome child = new Genome(Genomes[rando.Next(Size())]); Mutation.Mutate(child, seenInnovations, rando); children.Add(child); } else { Genome mother = Genomes[rando.Next(Size())]; Genome father = null; if (rando.NextDouble() < Const.InterSpeciesBreedingProbability) { Species spec = SelectOtherSpecies(interBredSpecies, rando); father = spec.Fitness.Champion; } else { father = Genomes[rando.Next(Size())]; } Genome child = Mating.Mate(mother, father, rando); //TODO: Add mother/father node checking criteria as well? if (rando.NextDouble() >= Const.MateOnlyProbability || GenomeCompatibility.Compatibility(mother, father) == 0.0) { Mutation.Mutate(child, seenInnovations, rando); } children.Add(child); } expectedChildren--; } return(children); }
/// <summary> /// Mutates a genome by breaking a connection up into two separate connections. /// </summary> /// <param name="genome">The genome to be modified.</param> /// <param name="innovationsSeen">A list of previously seen innovations.</param> /// <param name="rando">A random number generator.</param> public static void MutateAddNode(Genome genome, InnovationPool innovationsSeen, Random rando) { List <Gene> possibleConnections = new List <Gene>(genome.Genes); possibleConnections.RemoveAll(x => x.Frozen || NodePool.FindById(x.link.InNode).Type == NodeType.BIAS); if (possibleConnections.Count == 0) { return; } //TODO: Note in original algorithm saying uniform distribution is not optimal here. Gene geneToSplit = possibleConnections[rando.Next(possibleConnections.Count)]; geneToSplit.Frozen = true; ActivationStyle style = ActivationFunctions.ChooseActivationStyle(rando); InnovationInformation innovation = new InnovationInformation(geneToSplit.link, style); int firstConId = -1; int secondConId = -1; int registeredInnovationId = innovationsSeen.FindByInnovation(innovation); if (registeredInnovationId == -1) { int newNodeId = NodePool.Add(new NodeInformation(NodeType.HIDDEN, style)); ConnectionInformation firstConnect = new ConnectionInformation(geneToSplit.link.InNode, newNodeId); firstConId = ConnectionPool.Add(firstConnect); ConnectionInformation secondConnect = new ConnectionInformation(newNodeId, geneToSplit.link.OutNode); secondConId = ConnectionPool.Add(secondConnect); innovation.NewNodeDetails.NewNodeId = newNodeId; innovation.NewNodeDetails.FirstConnectionId = firstConId; innovation.NewNodeDetails.SecondConnectionId = secondConId; innovationsSeen.Add(innovation); } else { InnovationInformation registeredInnovation = innovationsSeen.FindById(registeredInnovationId); firstConId = registeredInnovation.NewNodeDetails.FirstConnectionId; secondConId = registeredInnovation.NewNodeDetails.SecondConnectionId; } genome.Genes.Add(new Gene(ConnectionPool.FindById(firstConId), firstConId, 1.0, false)); genome.Genes.Add(new Gene(ConnectionPool.FindById(secondConId), secondConId, geneToSplit.Weight, false)); }
/// <summary> /// Randomly mutates a genome. /// This is a conglomerate of mutations. /// </summary> /// <param name="genome">The genome to mutate.</param> /// <param name="innovationsSeen">A list of previously seen innovations.</param> /// <param name="rando">A random number generator.</param> public static void Mutate(Genome genome, InnovationPool innovationsSeen, Random rando) { MutationStyle style = ChooseMutationStyle(rando); Genome original = new Genome(genome); switch (style) { case MutationStyle.AddNode: MutateAddNode(genome, innovationsSeen, rando); break; case MutationStyle.AddConnection: MutateAddConnection(genome, innovationsSeen, rando); break; case MutationStyle.TryAllNonStructural: MutateTryAllNonStructural(genome, rando); break; default: throw new Exception("Mutate did not catch a valid MutationStyle."); } }
/// <summary> /// Mutates a given genome by adding a connection. /// Connection is guaranteed to not be 'into' a sensor or bias. /// </summary> /// <param name="genome">The genome to be modified.</param> /// <param name="innovationsSeen">A list of previously seen innovations.</param> /// <param name="rando">A random number generator.</param> public static void MutateAddConnection(Genome genome, InnovationPool innovationsSeen, Random rando) { //TODO: I'm getting the node information, but I only need that to construct allNodesNotInput... Dictionary <int, NodeInformation> allNodes = genome.GetAllNodeInformation(true); Dictionary <int, NodeInformation> allNodesNotInput = new Dictionary <int, NodeInformation>(allNodes); //TODO: Witnessed a bug where allNodes.Count == 0. // Could be a node where there are only frozen links connecting. foreach (var id in allNodesNotInput.Where(kvp => kvp.Value.IsInput()).ToList()) { allNodesNotInput.Remove(id.Key); } //TODO: Gotta be a better way than a tryCount... int tryCount = 0; int nodeFromId = -1; int nodeToId = -1; while (tryCount < 20) { nodeFromId = allNodes.Keys.ToList()[rando.Next(allNodes.Count)]; nodeToId = allNodesNotInput.Keys.ToList()[rando.Next(allNodesNotInput.Count)]; if (!genome.ContainsConnection(nodeFromId, nodeToId)) { break; } tryCount++; } if (tryCount == 20) { return; } ConnectionInformation connectInfo = new ConnectionInformation(nodeFromId, nodeToId); InnovationInformation innovation = new InnovationInformation(connectInfo); int connectId = -1; //TODO: Pull inital weight setting out of here. double weight = rando.NextDouble() * 2.0 - 1.0; int registeredInnovationId = innovationsSeen.FindByInnovation(innovation); if (registeredInnovationId == -1) { connectId = ConnectionPool.Add(connectInfo); innovation.NewConnectionDetails.ConnectionId = connectId; innovationsSeen.Add(innovation); } else { connectId = innovationsSeen.FindById(registeredInnovationId).NewConnectionDetails.ConnectionId; } genome.Genes.Add(new Gene(ConnectionPool.FindById(connectId), connectId, weight, false)); }