/// <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> /// Creates an initial population. /// </summary> /// <param name="nodes">A list of NodeType-ActivationStyle pairing. The node Id will be the position in the list.</param> /// <param name="connections">A list of InNodeId-OutNodeId-Weight tuples.</param> /// <param name="populationSize">The size of the population to test.</param> /// <param name="rando">A random number generator for perturbing the weights.</param> public Population(List <Tuple <NodeType, ActivationStyle> > nodes, List <Tuple <int, int, double> > connections, int populationSize, Random rando) { TargetPopulationSize = populationSize; ValidateConstructorParameters(nodes.Count, connections); for (int i = 0; i < nodes.Count; i++) { NodePool.Add(new NodeInformation(nodes[i].Item1, nodes[i].Item2)); } List <Gene> startGenes = new List <Gene>(); foreach (var tupe in connections) { ConnectionInformation ci = new ConnectionInformation(tupe.Item1, tupe.Item2); startGenes.Add(new Gene(ci, ConnectionPool.Size(), tupe.Item3, false)); InnovationInformation info = new InnovationInformation(ci); info.NewConnectionDetails.ConnectionId = ConnectionPool.Size(); GenerationalInnovations.Add(info); ConnectionPool.Add(ci); } Genome adam = new Genome(startGenes); List <Genome> firstGen = new List <Genome>() { adam }; for (int i = 1; i < TargetPopulationSize; i++) { Genome copy = new Genome(adam); Mutation.MutateTryAllNonStructural(copy, rando); firstGen.Add(copy); } SpeciateNewGeneration(firstGen); }