public virtual void EvaluatePopulation(Population pop, EvolutionAlgorithm ea) { // Evaluate in single-file each genome within the population. // Only evaluate new genomes (those with EvaluationCount==0). int count = pop.GenomeList.Count; for(int i=0; i<count; i++) { IGenome g = pop.GenomeList[i]; if(g.EvaluationCount!=0) continue; INetwork network = g.Decode(activationFn); if(network==null) { // Future genomes may not decode - handle the possibility. g.Fitness = EvolutionAlgorithm.MIN_GENOME_FITNESS; } else { g.Fitness = Math.Max(networkEvaluator.EvaluateNetwork(network), EvolutionAlgorithm.MIN_GENOME_FITNESS); } // Reset these genome level statistics. g.TotalFitness = g.Fitness; g.EvaluationCount = 1; // Update master evaluation counter. evaluationCount++; } }
public void initializeEvolution(int populationSize, NeatGenome seedGenome) { if (seedGenome == null) { initializeEvolution(populationSize); return; } logOutput = new StreamWriter(outputFolder + "logfile.txt"); IdGenerator idgen = new IdGeneratorFactory().CreateIdGenerator(seedGenome); ea = new EvolutionAlgorithm(new Population(idgen, GenomeFactory.CreateGenomeList(seedGenome, populationSize, experiment.DefaultNeatParameters, idgen)), experiment.PopulationEvaluator, experiment.DefaultNeatParameters); }
public NoveltyThread(JSPopulationEvaluator jsPop, AssessGenotypeFunction assess, int popSize) { //save our objects for executing later! popEval = jsPop; populationSize = popSize; autoEvent = new AutoResetEvent(false); waitNextTime = true; novelThread = new Thread(delegate() { autoEvent.WaitOne(); //we'll start by testing with 0 parents, and popsize of 15 yay! noveltyRun = EvolutionManager.SharedEvolutionManager.initializeEvolutionAlgorithm(popEval, populationSize, assess); //let our algoirhtm know we want to do novelty gosh darnit if(noveltyRun.multiobjective != null) noveltyRun.multiobjective.doNovelty = true; //we make sure we don't wait in this loop, since we just got started! waitNextTime = false; while (true) { //this will cause us to pause! if (waitNextTime) { waitNextTime = false; autoEvent.WaitOne(); } // Start the stopwatch we'll use to measure eval performance Stopwatch sw = Stopwatch.StartNew(); //run the generation noveltyRun.PerformOneGeneration(); // Stop the stopwatch sw.Stop(); // Report the results Console.WriteLine("Time used per gen (float): {0} ms", sw.Elapsed.TotalMilliseconds); Console.WriteLine("Time used per gen (rounded): {0} ms", sw.ElapsedMilliseconds); } }); novelThread.Start(); }
//private static Random random; public static void Main(string[] args) { Util.Initialize(args[0]); var idgen = new IdGenerator(); IExperiment experiment = new LimitExperiment(); XmlSerializer ser = new XmlSerializer(typeof(Settings)); //Settings settings = new Settings() //{ // SmallBlind = 1, // BigBlind = 2, // GamesPerIndividual = 100, // LogFile = "mutlithreaded_log.txt", // MaxHandsPerTourney = 200, // PlayersPerGame = 6, // StackSize = 124, // Threads = 4 //}; //ser.Serialize(new StreamWriter("settings.xml"), settings); Settings settings = (Settings)ser.Deserialize(new StreamReader("settings.xml")); var eval = new PokerPopulationEvaluator<SimpleLimitNeuralNetPlayer2, RingGamePlayerEvaluator>(settings); var ea = new EvolutionAlgorithm( new Population(idgen, GenomeFactory.CreateGenomeList(experiment.DefaultNeatParameters, idgen, experiment.InputNeuronCount, experiment.OutputNeuronCount, experiment.DefaultNeatParameters.pInitialPopulationInterconnections, experiment.DefaultNeatParameters.populationSize)), eval, experiment.DefaultNeatParameters); Console.WriteLine("Starting real evolution"); for (int i = 0; true; i++) { Console.WriteLine("Generation {0}", i + 1); ea.PerformOneGeneration(); Console.WriteLine("Champion Fitness={0}", ea.BestGenome.Fitness); var doc = new XmlDocument(); XmlGenomeWriterStatic.Write(doc, (NeatGenome)ea.BestGenome); FileInfo oFileInfo = new FileInfo("genomes_simple\\" + "bestGenome" + i.ToString() + ".xml"); doc.Save(oFileInfo.FullName); } }
public void EvaluatePopulation(Population pop, EvolutionAlgorithm ea) { int count = pop.GenomeList.Count; evalPack e; IGenome g; int i; for (i = 0; i < count; i++) { //Console.WriteLine(i); sem.WaitOne(); g = pop.GenomeList[i]; e = new evalPack(networkEvaluator, activationFn, g, i % HyperNEATParameters.numThreads,(int)ea.Generation); ThreadPool.QueueUserWorkItem(new WaitCallback(evalNet), e); // Update master evaluation counter. evaluationCount++; /*if(printFinalPositions) file.WriteLine(g.Behavior.behaviorList[0].ToString() + ", " + g.Behavior.behaviorList[1].ToString());//*/ } //Console.WriteLine("waiting for last threads.."); for (int j = 0; j < HyperNEATParameters.numThreads; j++) { sem.WaitOne(); // Console.WriteLine("waiting"); } for (int j = 0; j < HyperNEATParameters.numThreads; j++) { //Console.WriteLine("releasing"); sem.Release(); } //Console.WriteLine("generation done..."); //calulate novelty scores... if(ea.NeatParameters.noveltySearch) { if(ea.NeatParameters.noveltySearch) { ea.CalculateNovelty(); } } }
public virtual void EvaluatePopulation(Population pop, EvolutionAlgorithm ea) { // Evaluate in single-file each genome within the population. // Only evaluate new genomes (those with EvaluationCount==0). int count = pop.GenomeList.Count; for(int i=0; i<count; i++) { IGenome g = pop.GenomeList[i]; if(g.EvaluationCount!=0) continue; INetwork network = g.Decode(activationFn); if(network==null) { // Future genomes may not decode - handle the possibility. g.Fitness = EvolutionAlgorithm.MIN_GENOME_FITNESS; } else { BehaviorType behavior; g.Fitness = Math.Max(networkEvaluator.EvaluateNetwork(network,out behavior), EvolutionAlgorithm.MIN_GENOME_FITNESS); g.RealFitness = g.Fitness; g.Behavior = behavior; } // Reset these genome level statistics. g.TotalFitness = g.Fitness; g.EvaluationCount = 1; // Update master evaluation counter. evaluationCount++; } if(ea.NeatParameters.noveltySearch) { if(ea.NeatParameters.noveltySearch && ea.noveltyInitialized) { ea.CalculateNovelty(); } } }
private void Mutate_AddConnection(EvolutionAlgorithm ea) { // We are always guaranteed to have enough neurons to form connections - because the input/output neurons are // fixed. Any domain that doesn't require input/outputs is a bit nonsensical! // Make a fixed number of attempts at finding a suitable connection to add. if(neuronGeneList.Count>1) { // At least 2 neurons, so we have a chance at creating a connection. for(int attempts=0; attempts<5; attempts++) { // Select candidate source and target neurons. Any neuron can be used as the source. Input neurons // should not be used as a target int srcNeuronIdx; int tgtNeuronIdx; /* Here's some code for adding connections that attempts to avoid any recursive conenctions * within a network by only linking to neurons with innovation id's greater than the source neuron. * Unfortunately this doesn't work because new neurons with large innovations ID's are inserted * randomly through a network's topology! Hence this code remains here in readyness to be resurrected * as part of some future work to support feedforward nets. // if(ea.NeatParameters.feedForwardOnly) // { // /* We can ensure that all networks are feedforward only by only adding feedforward connections here. // * Feed forward connections fall into one of the following categories. All references to indexes // * are indexes within this genome's neuronGeneList: // * 1) Source neuron is an input or hidden node, target is an output node. // * 2) Source is an input or hidden node, target is a hidden node with an index greater than the source node's index. // * 3) Source is an output node, target is an output node with an index greater than the source node's index. // * // * These rules are easier to understand if you understand how the different types if neuron are arranged within // * the neuronGeneList array. Neurons are arranged in the following order: // * // * 1) A single bias neuron is always first. // * 2) Experiment specific input neurons. // * 3) Output neurons. // * 4) Hidden neurons. // * // * The quantity and innovationID of all neurons within the first 3 categories remains fixed throughout the life // * of an experiment, hence we always know where to find the bias, input and output nodes. The number of hidden nodes // * can vary as ne nodes are created, pruned away or perhaps dropped during crossover, however they are always arranged // * newest to oldest, or in other words sorted by innovation idea, lowest ID first. // * // * If output neurons were at the end of the list with hidden nodes in the middle then generating feedforward // * connections would be as easy as selecting a target neuron with a higher index than the source neuron. However, that // * type of arrangement is not conducive to the operation of other routines, hence this routine is a little bit more // * complicated as a result. // */ // // // Ok, for a source neuron we can pick any neuron except the last output neuron. // int neuronIdxCount = neuronGeneList.Count; // int neuronIdxBound = neuronIdxCount-1; // // // Generate count-1 possibilities and avoid the last output neuron's idx. // srcNeuronIdx = (int)Math.Floor(Utilities.NextDouble() * neuronIdxBound); // if(srcNeuronIdx>inputBiasOutputNeuronCountMinus2) srcNeuronIdx++; // // // // Now generate a target idx depending on what type of neuron srcNeuronIdx is pointing to. // if(srcNeuronIdx<inputAndBiasNeuronCount) // { // Source is a bias or input neuron. Target can be any output or hidden neuron. // tgtNeuronIdx = inputAndBiasNeuronCount + (int)Math.Floor(Utilities.NextDouble() * (neuronIdxCount-inputAndBiasNeuronCount)); // } // else if(srcNeuronIdx<inputBiasOutputNeuronCount) // { // Source is an output neuron, but not the last output neuron. Target can be any output neuron with an index // // greater than srcNeuronIdx. // tgtNeuronIdx = (inputAndBiasNeuronCount+1) + (int)Math.Floor(Utilities.NextDouble() * ((inputBiasOutputNeuronCount-1)-srcNeuronIdx)); // } // else // { // Source is a hidden neuron. Target can be any hidden neuron after srcNeuronIdx or any output neuron. // tgtNeuronIdx = (int)Math.Floor(Utilities.NextDouble() * ((neuronIdxBound-srcNeuronIdx)+outputNeuronCount)); // // if(tgtNeuronIdx<outputNeuronCount) // { // Map to an output neuron idx. // tgtNeuronIdx += inputAndBiasNeuronCount; // } // else // { // // Map to one of the hidden neurons after srcNeuronIdx. // tgtNeuronIdx = (tgtNeuronIdx-outputNeuronCount)+srcNeuronIdx+1; // } // } // } // // Source neuron can by any neuron. Target neuron is any neuron except input neurons. // srcNeuronIdx = (int)Math.Floor(Utilities.NextDouble() * neuronGeneList.Count); // tgtNeuronIdx = inputAndBiasNeuronCount + (int)Math.Floor(Utilities.NextDouble() * (neuronGeneList.Count-inputAndBiasNeuronCount)); // // NeuronGene sourceNeuron = neuronGeneList[srcNeuronIdx]; // NeuronGene targetNeuron = neuronGeneList[tgtNeuronIdx]; // Find all potential inputs, or quit if there are not enough. // Neurons cannot be inputs if they are dummy input nodes of a module. NeuronGeneList potentialInputs = new NeuronGeneList(); foreach (NeuronGene n in neuronGeneList) { if (!(n.ActivationFunction is ModuleInputNeuron)) { potentialInputs.Add(n); } } if (potentialInputs.Count < 1) return; // Find all potential outputs, or quit if there are not enough. // Neurons cannot be outputs if they are dummy input or output nodes of a module, or network input or bias nodes. NeuronGeneList potentialOutputs = new NeuronGeneList(); foreach (NeuronGene n in neuronGeneList) { if (n.NeuronType != NeuronType.Bias && n.NeuronType != NeuronType.Input && !(n.ActivationFunction is ModuleInputNeuron) && !(n.ActivationFunction is ModuleOutputNeuron)) { potentialOutputs.Add(n); } } if (potentialOutputs.Count < 1) return; NeuronGene sourceNeuron = potentialInputs[Utilities.Next(potentialInputs.Count)]; NeuronGene targetNeuron = potentialOutputs[Utilities.Next(potentialOutputs.Count)]; // Check if a connection already exists between these two neurons. uint sourceId = sourceNeuron.InnovationId; uint targetId = targetNeuron.InnovationId; if(!TestForExistingConnection(sourceId, targetId)) { // Check if a matching mutation has already occured on another genome. // If so then re-use the connection ID. ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(sourceId, targetId); ConnectionGene existingConnection = (ConnectionGene)ea.NewConnectionGeneTable[connectionKey]; ConnectionGene newConnectionGene; if(existingConnection==null) { // Create a new connection with a new ID and add it to the Genome. newConnectionGene = new ConnectionGene(ea.NextInnovationId, sourceId, targetId, (Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange/4.0) - ea.NeatParameters.connectionWeightRange/8.0); // Register the new connection with NewConnectionGeneTable. ea.NewConnectionGeneTable.Add(connectionKey, newConnectionGene); // Add the new gene to this genome. We have a new ID so we can safely append the gene to the end // of the list without risk of breaking the innovation ID order. connectionGeneList.Add(newConnectionGene); } else { // Create a new connection, re-using the ID from existingConnection, and add it to the Genome. newConnectionGene = new ConnectionGene(existingConnection.InnovationId, sourceId, targetId, (Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange/4.0) - ea.NeatParameters.connectionWeightRange/8.0); // Add the new gene to this genome. We are re-using an ID so we must ensure the connection gene is // inserted into the correct position (sorted by innovation ID). connectionGeneList.InsertIntoPosition(newConnectionGene); } return; } } } // We couldn't find a valid connection to create. Instead of doing nothing lets perform connection // weight mutation. Mutate_ConnectionWeights(ea); }
private void Mutate_AddModule(EvolutionAlgorithm ea) { // Find all potential inputs, or quit if there are not enough. // Neurons cannot be inputs if they are dummy input nodes created for another module. NeuronGeneList potentialInputs = new NeuronGeneList(); foreach (NeuronGene n in neuronGeneList) { if (!(n.ActivationFunction is ModuleInputNeuron)) { potentialInputs.Add(n); } } if (potentialInputs.Count < 1) return; // Find all potential outputs, or quit if there are not enough. // Neurons cannot be outputs if they are dummy input or output nodes created for another module, or network input or bias nodes. NeuronGeneList potentialOutputs = new NeuronGeneList(); foreach (NeuronGene n in neuronGeneList) { if (n.NeuronType != NeuronType.Bias && n.NeuronType != NeuronType.Input && !(n.ActivationFunction is ModuleInputNeuron) && !(n.ActivationFunction is ModuleOutputNeuron)) { potentialOutputs.Add(n); } } if (potentialOutputs.Count < 1) return; // Pick a new function for the new module. IModule func = ModuleFactory.GetRandom(); // Choose inputs uniformly at random, with replacement. // Create dummy neurons to represent the module's inputs. // Create connections between the input nodes and the dummy neurons. IActivationFunction inputFunction = ActivationFunctionFactory.GetActivationFunction("ModuleInputNeuron"); List<uint> inputDummies = new List<uint>(func.InputCount); for (int i = 0; i < func.InputCount; i++) { NeuronGene newNeuronGene = new NeuronGene(ea.NextInnovationId, NeuronType.Hidden, inputFunction); neuronGeneList.Add(newNeuronGene); uint sourceId = potentialInputs[Utilities.Next(potentialInputs.Count)].InnovationId; uint targetId = newNeuronGene.InnovationId; inputDummies.Add(targetId); // Create a new connection with a new ID and add it to the Genome. ConnectionGene newConnectionGene = new ConnectionGene(ea.NextInnovationId, sourceId, targetId, (Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange) - ea.NeatParameters.connectionWeightRange / 2.0); // Register the new connection with NewConnectionGeneTable. ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(sourceId, targetId); ea.NewConnectionGeneTable.Add(connectionKey, newConnectionGene); // Add the new gene to this genome. We have a new ID so we can safely append the gene to the end // of the list without risk of breaking the innovation ID order. connectionGeneList.Add(newConnectionGene); } // Choose outputs uniformly at random, with replacement. // Create dummy neurons to represent the module's outputs. // Create connections between the output nodes and the dummy neurons. IActivationFunction outputFunction = ActivationFunctionFactory.GetActivationFunction("ModuleOutputNeuron"); List<uint> outputDummies = new List<uint>(func.OutputCount); for (int i = 0; i < func.OutputCount; i++) { NeuronGene newNeuronGene = new NeuronGene(ea.NextInnovationId, NeuronType.Hidden, outputFunction); neuronGeneList.Add(newNeuronGene); uint sourceId = newNeuronGene.InnovationId; uint targetId = potentialOutputs[Utilities.Next(potentialOutputs.Count)].InnovationId; outputDummies.Add(sourceId); // Create a new connection with a new ID and add it to the Genome. ConnectionGene newConnectionGene = new ConnectionGene(ea.NextInnovationId, sourceId, targetId, (Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange) - ea.NeatParameters.connectionWeightRange / 2.0); // Register the new connection with NewConnectionGeneTable. ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(sourceId, targetId); ea.NewConnectionGeneTable.Add(connectionKey, newConnectionGene); // Add the new gene to this genome. We have a new ID so we can safely append the gene to the end // of the list without risk of breaking the innovation ID order. connectionGeneList.Add(newConnectionGene); } // Pick a new ID for the new module and create it. // Modules do not participate in history comparisons, so we will always create a new innovation ID. // We can change this here if it becomes a problem. ModuleGene newModule = new ModuleGene(ea.NextInnovationId, func, inputDummies, outputDummies); moduleGeneList.Add(newModule); }
/// <summary> /// Initializes the EA using an initial population that has already been read into object format. /// </summary> /// <param name="pop"></param> public void initalizeEvolution(Population pop) { LogOutput = Logging ? new StreamWriter(Path.Combine(OutputFolder, "log.txt")) : null; FinalPositionOutput = FinalPositionLogging ? new StreamWriter(Path.Combine(OutputFolder, "final-position.txt")) : null; ArchiveModificationOutput = FinalPositionLogging ? new StreamWriter(Path.Combine(OutputFolder, "archive-mods.txt")) : null; ComplexityOutput = new StreamWriter(Path.Combine(OutputFolder, "complexity.txt")); ComplexityOutput.WriteLine("avg,stdev,min,max"); if (FinalPositionLogging) { FinalPositionOutput.WriteLine("ID,x,y"); ArchiveModificationOutput.WriteLine("ID,action,time,x,y"); } EA = new EvolutionAlgorithm(pop, experiment.PopulationEvaluator, experiment.DefaultNeatParameters); EA.outputFolder = OutputFolder; EA.neatBrain = NEATBrain; }
public void initializeEvolution(int populationSize, NeatGenome seedGenome) { if (seedGenome == null) { initializeEvolution(populationSize); return; } if (logOutput != null) logOutput.Close(); logOutput = new StreamWriter(outputFolder + "logfile.txt"); IdGenerator idgen = new IdGeneratorFactory().CreateIdGenerator(seedGenome); ea = new EvolutionAlgorithm(new Population(idgen, GenomeFactory.CreateGenomeList(seedGenome, populationSize, neatParams, idgen)), populationEval, neatParams); }
public void initalizeEvolution(Population pop) { if (logOutput != null) logOutput.Close(); logOutput = new StreamWriter(outputFolder + "logfile.txt"); //IdGenerator idgen = new IdGeneratorFactory().CreateIdGenerator(pop.GenomeList); ea = new EvolutionAlgorithm(pop, populationEval, neatParams); }
public override IGenome CreateOffspring_Sexual(EvolutionAlgorithm ea, IGenome parent) { NeatGenome otherParent = parent as NeatGenome; if (otherParent == null) return null; // Build a list of connections in either this genome or the other parent. CorrelationResults correlationResults = CorrelateConnectionGeneLists(connectionGeneList, otherParent.connectionGeneList); Debug.Assert(correlationResults.PerformIntegrityCheck(), "CorrelationResults failed integrity check."); //----- Connection Genes. // We will temporarily store the offspring's genes in newConnectionGeneList and keeping track of which genes // exist with newConnectionGeneTable. Here we ensure these objects are created, and if they already existed // then ensure they are cleared. Clearing existing objects is more efficient that creating new ones because // allocated memory can be re-used. // Key = connection key, value = index in newConnectionGeneList. if(newConnectionGeneTable==null) { // Provide a capacity figure to the new Hashtable. The offspring will be the same length (or thereabouts). newConnectionGeneTable = new Hashtable(connectionGeneList.Count); } else { newConnectionGeneTable.Clear(); } //TODO: No 'capacity' constructor on CollectionBase. Create modified/custom CollectionBase. // newConnectionGeneList must be constructed on each call because it is passed to a new NeatGenome // at construction time and a permanent reference to the list is kept. newConnectionGeneList = new ConnectionGeneList(ConnectionGeneList.Count); // A switch that stores which parent is fittest 1 or 2. Chooses randomly if both are equal. More efficient to calculate this just once. byte fitSwitch; if(Fitness > otherParent.Fitness) fitSwitch = 1; else if(Fitness < otherParent.Fitness) fitSwitch = 2; else { // Select one of the parents at random to be the 'master' genome during crossover. if(Utilities.NextDouble() < 0.5) fitSwitch = 1; else fitSwitch = 2; } bool combineDisjointExcessFlag = Utilities.NextDouble() < ea.NeatParameters.pDisjointExcessGenesRecombined; // Loop through the correlationResults, building a table of ConnectionGenes from the parents that will make it into our // new [single] offspring. We use a table keyed on connection end points to prevent passing connections to the offspring // that may have the same end points but a different innovation number - effectively we filter out duplicate connections. int idxBound = correlationResults.CorrelationItemList.Count; for(int i=0; i<idxBound; i++) { CreateOffspring_Sexual_ProcessCorrelationItem((CorrelationItem)correlationResults.CorrelationItemList[i], fitSwitch, combineDisjointExcessFlag, ea.NeatParameters); } //----- Neuron Genes. // Build a neuronGeneList by analysing each connection's neuron end-point IDs. // This strategy has the benefit of eliminating neurons that are no longer connected too. // Remember to always keep all input, output and bias neurons though! NeuronGeneList newNeuronGeneList = new NeuronGeneList(neuronGeneList.Count); // Keep a table of the NeuronGene ID's keyed by ID so that we can keep track of which ones have been added. // Key = innovation ID, value = null for some reason. if(newNeuronGeneTable==null) newNeuronGeneTable = new Hashtable(neuronGeneList.Count); else newNeuronGeneTable.Clear(); // Get the input/output neurons from this parent. All Genomes share these neurons, they do not change during a run. idxBound = neuronGeneList.Count; for(int i=0; i<idxBound; i++) { if(neuronGeneList[i].NeuronType != NeuronType.Hidden) { newNeuronGeneList.Add(new NeuronGene(neuronGeneList[i])); newNeuronGeneTable.Add(neuronGeneList[i].InnovationId, null); } else { // No more bias, input or output nodes. break the loop. break; } } // Now analyse the connections to determine which NeuronGenes are required in the offspring. // Loop through every connection in the child, and add to the child those hidden neurons that are sources or targets of the connection. idxBound = newConnectionGeneList.Count; for(int i=0; i<idxBound; i++) { NeuronGene neuronGene; ConnectionGene connectionGene = newConnectionGeneList[i]; if(!newNeuronGeneTable.ContainsKey(connectionGene.SourceNeuronId)) { //TODO: DAVID proper activation function // We can safely assume that any missing NeuronGenes at this point are hidden heurons. neuronGene = this.neuronGeneList.GetNeuronById(connectionGene.SourceNeuronId); if (neuronGene != null) newNeuronGeneList.Add(new NeuronGene(neuronGene)); else newNeuronGeneList.Add(new NeuronGene(otherParent.NeuronGeneList.GetNeuronById(connectionGene.SourceNeuronId))); //newNeuronGeneList.Add(new NeuronGene(connectionGene.SourceNeuronId, NeuronType.Hidden, ActivationFunctionFactory.GetActivationFunction("SteepenedSigmoid"))); newNeuronGeneTable.Add(connectionGene.SourceNeuronId, null); } if(!newNeuronGeneTable.ContainsKey(connectionGene.TargetNeuronId)) { //TODO: DAVID proper activation function // We can safely assume that any missing NeuronGenes at this point are hidden heurons. neuronGene = this.neuronGeneList.GetNeuronById(connectionGene.TargetNeuronId); if (neuronGene != null) newNeuronGeneList.Add(new NeuronGene(neuronGene)); else newNeuronGeneList.Add(new NeuronGene(otherParent.NeuronGeneList.GetNeuronById(connectionGene.TargetNeuronId))); //newNeuronGeneList.Add(new NeuronGene(connectionGene.TargetNeuronId, NeuronType.Hidden, ActivationFunctionFactory.GetActivationFunction("SteepenedSigmoid"))); newNeuronGeneTable.Add(connectionGene.TargetNeuronId, null); } } // Determine which modules to pass on to the child in the same way. // For each module in this genome or in the other parent, if it was referenced by even one connection add it and all its dummy neurons to the child. List<ModuleGene> newModuleGeneList = new List<ModuleGene>(); // Build a list of modules the child might have, which is a union of the parents' module lists, but they are all copies so we can't just do a union. List<ModuleGene> unionParentModules = new List<ModuleGene>(moduleGeneList); foreach (ModuleGene moduleGene in otherParent.moduleGeneList) { bool alreadySeen = false; foreach (ModuleGene match in unionParentModules) { if (moduleGene.InnovationId == match.InnovationId) { alreadySeen = true; break; } } if (!alreadySeen) { unionParentModules.Add(moduleGene); } } foreach (ModuleGene moduleGene in unionParentModules) { // Examine each neuron in the child to determine whether it is part of a module. foreach (List<uint> dummyNeuronList in new List<uint>[] { moduleGene.InputIds, moduleGene.OutputIds }) { foreach (uint dummyNeuronId in dummyNeuronList) { if (newNeuronGeneTable.ContainsKey(dummyNeuronId)) { goto childHasModule; } } } continue; // the child does not contain this module, so continue the loop and check for the next module. childHasModule: // the child does contain this module, so make sure the child gets all the nodes the module requires to work. // Make sure the child has all the neurons in the given module. newModuleGeneList.Add(new ModuleGene(moduleGene)); foreach (List<uint> dummyNeuronList in new List<uint>[] { moduleGene.InputIds, moduleGene.OutputIds }) { foreach (uint dummyNeuronId in dummyNeuronList) { if (!newNeuronGeneTable.ContainsKey(dummyNeuronId)) { newNeuronGeneTable.Add(dummyNeuronId, null); NeuronGene neuronGene = this.neuronGeneList.GetNeuronById(dummyNeuronId); if (neuronGene != null) { newNeuronGeneList.Add(new NeuronGene(neuronGene)); } else { newNeuronGeneList.Add(new NeuronGene(otherParent.NeuronGeneList.GetNeuronById(dummyNeuronId))); } } } } } // TODO: Inefficient code? newNeuronGeneList.SortByInnovationId(); // Schrum: Need to calculate this because of Module Mutation adding extra outputs int revisedOutputCount = 0; foreach(NeuronGene n in newNeuronGeneList) { if (n.NeuronType == NeuronType.Output) revisedOutputCount++; } // newConnectionGeneList is already sorted because it was generated by passing over the list returned by // CorrelateConnectionGeneLists() - which is always in order. // Schrum: Modified to add outputsPerPolicy as a parameter, and use revisedOutputCount return new NeatGenome(ea.NextGenomeId, newNeuronGeneList, newModuleGeneList, newConnectionGeneList, inputNeuronCount, revisedOutputCount, outputsPerPolicy); }
private void Mutate_ConnectionWeights(EvolutionAlgorithm ea) { // Determine the type of weight mutation to perform. int groupCount = ea.NeatParameters.ConnectionMutationParameterGroupList.Count; double[] probabilties = new double[groupCount]; for(int i=0; i<groupCount; i++) { probabilties[i] = ((ConnectionMutationParameterGroup)ea.NeatParameters.ConnectionMutationParameterGroupList[i]).ActivationProportion; } // Get a reference to the group we will be using. ConnectionMutationParameterGroup paramGroup = (ConnectionMutationParameterGroup)ea.NeatParameters.ConnectionMutationParameterGroupList[RouletteWheel.SingleThrow(probabilties)]; // Perform mutations of the required type. if(paramGroup.SelectionType==ConnectionSelectionType.Proportional) { bool mutationOccured=false; int connectionCount = connectionGeneList.Count; for(int i=0; i<connectionCount; i++) { if(Utilities.NextDouble() < paramGroup.Proportion) { MutateConnectionWeight(connectionGeneList[i], ea.NeatParameters, paramGroup); mutationOccured = true; } } if(!mutationOccured && connectionCount>0) { // Perform at least one mutation. Pick a gene at random. MutateConnectionWeight( connectionGeneList[(int)(Utilities.NextDouble() * connectionCount)], ea.NeatParameters, paramGroup); } } else // if(paramGroup.SelectionType==ConnectionSelectionType.FixedQuantity) { // Determine how many mutations to perform. At least one - if there are any genes. int connectionCount = connectionGeneList.Count; int mutations = Math.Min(connectionCount, Math.Max(1, paramGroup.Quantity)); if(mutations==0) return; // The mutation loop. Here we pick an index at random and scan forward from that point // for the first non-mutated gene. This prevents any gene from being mutated more than once without // too much overhead. In fact it's optimal for small numbers of mutations where clashes are unlikely // to occur. for(int i=0; i<mutations; i++) { // Pick an index at random. int index = (int)(Utilities.NextDouble()*connectionCount); ConnectionGene connectionGene = connectionGeneList[index]; // Scan forward and find the first non-mutated gene. while(connectionGeneList[index].IsMutated) { // Increment index. Wrap around back to the start if we go off the end. if(++index==connectionCount) index=0; } // Mutate the gene at 'index'. MutateConnectionWeight(connectionGeneList[index], ea.NeatParameters, paramGroup); connectionGeneList[index].IsMutated = true; } } }
/// <summary> /// We define a simple neuron structure as a neuron that has a single outgoing or single incoming connection. /// With such a structure we can easily eliminate the neuron and shift it's connections to an adjacent neuron. /// If the neuron's non-linearity was not being used then such a mutation is a simplification of the network /// structure that shouldn't adversly affect its functionality. /// </summary> private void Mutate_DeleteSimpleNeuronStructure(EvolutionAlgorithm ea) { // We will use the NeuronConnectionLookupTable to find the simple structures. EnsureNeuronConnectionLookupTable(); // Build a list of candidate simple neurons to choose from. ArrayList simpleNeuronIdList = new ArrayList(); foreach(NeuronConnectionLookup lookup in neuronConnectionLookupTable.Values) { // If we test the connection count with <=1 then we also pick up neurons that are in dead-end circuits, // RemoveSimpleNeuron is then able to delete these neurons from the network structure along with any // associated connections. // All neurons that are part of a module would appear to be dead-ended, but skip removing them anyway. if (lookup.neuronGene.NeuronType == NeuronType.Hidden && !(lookup.neuronGene.ActivationFunction is ModuleInputNeuron) && !(lookup.neuronGene.ActivationFunction is ModuleOutputNeuron) ) { if((lookup.incomingList.Count<=1) || (lookup.outgoingList.Count<=1)) simpleNeuronIdList.Add(lookup.neuronGene.InnovationId); } } // Are there any candiate simple neurons? if(simpleNeuronIdList.Count==0) { // No candidate neurons. As a fallback lets delete a connection. Mutate_DeleteConnection(); return; } // Pick a simple neuron at random. int idx = (int)Math.Floor(Utilities.NextDouble() * simpleNeuronIdList.Count); uint neuronId = (uint)simpleNeuronIdList[idx]; RemoveSimpleNeuron(neuronId, ea); }
public void initializeEvolution(int populationSize) { logOutput = new StreamWriter(outputFolder + "logfile.txt"); IdGenerator idgen = new IdGenerator(); ea = new EvolutionAlgorithm(new Population(idgen, GenomeFactory.CreateGenomeList(experiment.DefaultNeatParameters, idgen, experiment.InputNeuronCount, experiment.OutputNeuronCount, experiment.OutputsPerPolicy, experiment.DefaultNeatParameters.pInitialPopulationInterconnections, populationSize)), experiment.PopulationEvaluator, experiment.DefaultNeatParameters); }
static void Main(string[] args) { string folder = ""; NeatGenome seedGenome = null; string filename = null; string shape = "triangle"; bool isMulti = false; for (int j = 0; j < args.Length; j++) { if(j <= args.Length - 2) switch (args[j]) { case "-seed": filename = args[++j]; Console.WriteLine("Attempting to use seed from file " + filename); break; case "-folder": folder = args[++j]; Console.WriteLine("Attempting to output to folder " + folder); break; case "-shape": shape = args[++j]; Console.WriteLine("Attempting to do experiment with shape " + shape); break; case "-multi": isMulti = Boolean.Parse(args[++j]); Console.WriteLine("Experiment is heterogeneous? " + isMulti); break; } } if(filename!=null) { try { XmlDocument document = new XmlDocument(); document.Load(filename); seedGenome = XmlNeatGenomeReaderStatic.Read(document); } catch (Exception e) { System.Console.WriteLine("Problem loading genome. \n" + e.Message); } } double maxFitness = 0; int maxGenerations = 1000; int populationSize = 150; int inputs = 4; IExperiment exp = new SkirmishExperiment(inputs, 1, isMulti, shape); StreamWriter SW; SW = File.CreateText(folder + "logfile.txt"); XmlDocument doc; FileInfo oFileInfo; IdGenerator idgen; EvolutionAlgorithm ea; if (seedGenome == null) { idgen = new IdGenerator(); ea = new EvolutionAlgorithm(new Population(idgen, GenomeFactory.CreateGenomeList(exp.DefaultNeatParameters, idgen, exp.InputNeuronCount, exp.OutputNeuronCount, exp.DefaultNeatParameters.pInitialPopulationInterconnections, populationSize)), exp.PopulationEvaluator, exp.DefaultNeatParameters); } else { idgen = new IdGeneratorFactory().CreateIdGenerator(seedGenome); ea = new EvolutionAlgorithm(new Population(idgen, GenomeFactory.CreateGenomeList(seedGenome, populationSize, exp.DefaultNeatParameters, idgen)), exp.PopulationEvaluator, exp.DefaultNeatParameters); } for (int j = 0; j < maxGenerations; j++) { DateTime dt = DateTime.Now; ea.PerformOneGeneration(); if (ea.BestGenome.Fitness > maxFitness) { maxFitness = ea.BestGenome.Fitness; doc = new XmlDocument(); XmlGenomeWriterStatic.Write(doc, (NeatGenome)ea.BestGenome); oFileInfo = new FileInfo(folder + "bestGenome" + j.ToString() + ".xml"); doc.Save(oFileInfo.FullName); // This will output the substrate, uncomment if you want that /* doc = new XmlDocument(); XmlGenomeWriterStatic.Write(doc, (NeatGenome) SkirmishNetworkEvaluator.substrate.generateMultiGenomeModulus(ea.BestGenome.Decode(null),5)); oFileInfo = new FileInfo(folder + "bestNetwork" + j.ToString() + ".xml"); doc.Save(oFileInfo.FullName); */ } Console.WriteLine(ea.Generation.ToString() + " " + ea.BestGenome.Fitness + " " + (DateTime.Now.Subtract(dt))); //Do any post-hoc stuff here SW.WriteLine(ea.Generation.ToString() + " " + (maxFitness).ToString()); } SW.Close(); doc = new XmlDocument(); XmlGenomeWriterStatic.Write(doc, (NeatGenome)ea.BestGenome, ActivationFunctionFactory.GetActivationFunction("NullFn")); oFileInfo = new FileInfo(folder + "bestGenome.xml"); doc.Save(oFileInfo.FullName); }
/// <summary> /// Sexual reproduction. No mutation performed. /// </summary> /// <param name="parent"></param> /// <returns></returns> public abstract IGenome CreateOffspring_Sexual(EvolutionAlgorithm ea, IGenome parent);
/// <summary> /// Asexual reproduction with built in mutation. /// </summary> /// <returns></returns> public abstract IGenome CreateOffspring_Asexual(EvolutionAlgorithm ea);
private void RemoveSimpleNeuron(uint neuronId, EvolutionAlgorithm ea) { // Create new connections that connect all of the incoming and outgoing neurons // that currently exist for the simple neuron. NeuronConnectionLookup lookup = (NeuronConnectionLookup)neuronConnectionLookupTable[neuronId]; foreach(ConnectionGene incomingConnection in lookup.incomingList) { foreach(ConnectionGene outgoingConnection in lookup.outgoingList) { if(TestForExistingConnection(incomingConnection.SourceNeuronId, outgoingConnection.TargetNeuronId)) { // Connection already exists. continue; } // Test for matching connection within NewConnectionGeneTable. ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(incomingConnection.SourceNeuronId, outgoingConnection.TargetNeuronId); ConnectionGene existingConnection = (ConnectionGene)ea.NewConnectionGeneTable[connectionKey]; ConnectionGene newConnectionGene; if(existingConnection==null) { // No matching connection found. Create a connection with a new ID. newConnectionGene = new ConnectionGene(ea.NextInnovationId, incomingConnection.SourceNeuronId, outgoingConnection.TargetNeuronId, (Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange) - ea.NeatParameters.connectionWeightRange/2.0); // Register the new ID with NewConnectionGeneTable. ea.NewConnectionGeneTable.Add(connectionKey, newConnectionGene); // Add the new gene to the genome. connectionGeneList.Add(newConnectionGene); } else { // Matching connection found. Re-use its ID. newConnectionGene = new ConnectionGene(existingConnection.InnovationId, incomingConnection.SourceNeuronId, outgoingConnection.TargetNeuronId, (Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange) - ea.NeatParameters.connectionWeightRange/2.0); // Add the new gene to the genome. Use InsertIntoPosition() to ensure we don't break the sort // order of the connection genes. connectionGeneList.InsertIntoPosition(newConnectionGene); } } } // Delete the old connections. foreach(ConnectionGene incomingConnection in lookup.incomingList) connectionGeneList.Remove(incomingConnection); foreach(ConnectionGene outgoingConnection in lookup.outgoingList) { // Filter out recurrent connections - they will have already been // deleted in the loop through 'lookup.incomingList'. if(outgoingConnection.TargetNeuronId != neuronId) connectionGeneList.Remove(outgoingConnection); } // Delete the simple neuron - it no longer has any connections to or from it. neuronGeneList.Remove(neuronId); }
public void initalizeEvolution(Population pop) { logOutput = new StreamWriter(outputFolder + "logfile.txt"); //IdGenerator idgen = new IdGeneratorFactory().CreateIdGenerator(pop.GenomeList); ea = new EvolutionAlgorithm(pop, experiment.PopulationEvaluator, experiment.DefaultNeatParameters); }
/// <summary> /// Asexual reproduction with built in mutation. /// </summary> /// <returns></returns> public override IGenome CreateOffspring_Asexual(EvolutionAlgorithm ea) { // Make an exact copy this Genome. NeatGenome offspring = new NeatGenome(this, ea.NextGenomeId); // Mutate the new genome. offspring.Mutate(ea); return offspring; }
private void Mutate(EvolutionAlgorithm ea) { // Schrum: Only allow Module Mutation if there are // as many or fewer output neurons than hidden neurons. int hiddenNeuronCount = this.neuronGeneList.Count - (inputNeuronCount + outputNeuronCount); bool moduleMutationAllowed = hiddenNeuronCount >= outputNeuronCount; // Determine the type of mutation to perform. double[] probabilities = new double[] { ea.NeatParameters.pMutateAddNode, ea.NeatParameters.pMutateAddModule, ea.NeatParameters.pMutateAddConnection, ea.NeatParameters.pMutateDeleteConnection, ea.NeatParameters.pMutateDeleteSimpleNeuron, ea.NeatParameters.pMutateConnectionWeights, moduleMutationAllowed ? ea.NeatParameters.pMMP : 0, // Schrum: MM(Previous) moduleMutationAllowed ? ea.NeatParameters.pMMR : 0, // Schrum: MM(Random) moduleMutationAllowed ? ea.NeatParameters.pMMD : 0 // Schrum: MM(Duplicate) }; int outcome = RouletteWheel.SingleThrow(probabilities); switch(outcome) { case 0: Mutate_AddNode(ea); break; case 1: Mutate_AddModule(ea); break; case 2: Mutate_AddConnection(ea); break; case 3: Mutate_DeleteConnection(); break; case 4: Mutate_DeleteSimpleNeuronStructure(ea); break; case 5: Mutate_ConnectionWeights(ea); break; case 6: // Schrum: MM(P) Module_Mutation_Previous(ea); break; case 7: // Schrum: MM(R) Module_Mutation_Random(ea); break; case 8: // Schrum: MM(D) Module_Mutation_Duplicate(ea); break; } }
/// <summary> /// Clone this genome. /// </summary> /// <returns></returns> public override IGenome Clone(EvolutionAlgorithm ea) { // Utilise the copy constructor for cloning. return new NeatGenome(this, ea.NextGenomeId); }
// Schrum: Simple form of Module Mutation, MM(P) private void Module_Mutation_Previous(EvolutionAlgorithm ea) { // Push all output neurons together this.neuronGeneList.SortByNeuronOrder(); int numModules = this.outputNeuronCount / this.outputsPerPolicy; // Should evenly divide int randomModule = Utilities.Next(numModules); // Because outputs come after inputs. // Although list is 0-indexed, the +1 is needed because the bias does not count as an input double outputLayer = neuronGeneList[1 + inputNeuronCount].Layer; // Create the new module for (int i = 0; i < outputsPerPolicy; i++) { IActivationFunction outputActFunction = ActivationFunctionFactory.GetActivationFunction("BipolarSigmoid"); NeuronGene newNeuronGene = new NeuronGene(null, ea.NextInnovationId, outputLayer, NeuronType.Output, outputActFunction); neuronGeneList.Add(newNeuronGene); // Link to the new neuron: bias, then inputs, then appropriate module, then neuron within that module uint sourceNeuron = neuronGeneList[1 + inputNeuronCount + (randomModule * outputsPerPolicy) + i].InnovationId; ConnectionGene connection = new ConnectionGene(ea.NextInnovationId, sourceNeuron, newNeuronGene.InnovationId, 1.0); connectionGeneList.InsertIntoPosition(connection); this.outputNeuronCount++; // Increase number of outputs } // Schrum: Debugging //Console.WriteLine("MM(P): outputNeuronCount=" + outputNeuronCount); // Schrum: More debugging /* this.neuronGeneList.SortByInnovationId(); XmlDocument doc = new XmlDocument(); XmlGenomeWriterStatic.Write(doc, (NeatGenome)this); FileInfo oFileInfo = new FileInfo("MMPNet.xml"); doc.Save(oFileInfo.FullName); */ }
public void initializeEvolution(int populationSize) { if (logOutput != null) logOutput.Close(); logOutput = new StreamWriter(outputFolder + "logfile.txt"); IdGenerator idgen = new IdGenerator(); ea = new EvolutionAlgorithm(new Population(idgen, GenomeFactory.CreateGenomeList(neatParams, idgen, cppnInputs, cppnOutputs, neatParams.pInitialPopulationInterconnections, populationSize)), populationEval, neatParams); }
// Schrum: Module Mutation Random creates a new module with // completely random incoming links. private void Module_Mutation_Random(EvolutionAlgorithm ea) { // Push all output neurons together this.neuronGeneList.SortByNeuronOrder(); int numModules = this.outputNeuronCount / this.outputsPerPolicy; // Should evenly divide int randomModule = Utilities.Next(numModules); // Because outputs come after inputs. // Although list is 0-indexed, the +1 is needed because the bias does not count as an input double outputLayer = neuronGeneList[1 + inputNeuronCount].Layer; // Create the new module one neuron per loop iteration for (int i = 0; i < outputsPerPolicy; i++) { // The activation function for the output layer IActivationFunction outputActFunction = ActivationFunctionFactory.GetActivationFunction("BipolarSigmoid"); NeuronGene newNeuronGene = new NeuronGene(null, ea.NextInnovationId, outputLayer, NeuronType.Output, outputActFunction); neuronGeneList.Add(newNeuronGene); // Count links to random output neuron: bias, then inputs, then random module, then neuron within that module uint randomModuleInnovation = neuronGeneList[1 + inputNeuronCount + (randomModule * outputsPerPolicy) + i].InnovationId; int numIncoming = 0; foreach (ConnectionGene cg in this.ConnectionGeneList) { // Count the link if (cg.TargetNeuronId == randomModuleInnovation) numIncoming++; } // Give the new module (up to) the same number of links as some other module for (int j = 0; j < numIncoming; j++) // Will always create ay least one link { uint randomSource = NeuronGeneList[Utilities.Next(NeuronGeneList.Count)].InnovationId; // Magic equation stolen from Mutate_AddConnection below double randomWeight = (Utilities.NextDouble() * ea.NeatParameters.connectionWeightRange/4.0) - ea.NeatParameters.connectionWeightRange/8.0; if (!TestForExistingConnection(randomSource, newNeuronGene.InnovationId)) // Only create each connection once { ConnectionGene connection = new ConnectionGene(ea.NextInnovationId, randomSource, newNeuronGene.InnovationId, randomWeight); connectionGeneList.InsertIntoPosition(connection); } } this.outputNeuronCount++; // Increase number of outputs } }
/// <summary> /// Initializes the EA with an initial population generated from a single seed genome. /// </summary> public void initializeEvolution(int populationSize, NeatGenome seedGenome) { if (seedGenome == null) { initializeEvolution(populationSize); return; } LogOutput = Logging ? new StreamWriter(Path.Combine(OutputFolder, "log.txt")) : null; FinalPositionOutput = FinalPositionLogging ? new StreamWriter(Path.Combine(OutputFolder,"final-position.txt")) : null; ArchiveModificationOutput = FinalPositionLogging ? new StreamWriter(Path.Combine(OutputFolder, "archive-mods.txt")) : null; ComplexityOutput = new StreamWriter(Path.Combine(OutputFolder, "complexity.txt")); ComplexityOutput.WriteLine("avg,stdev,min,max"); if (FinalPositionLogging) { FinalPositionOutput.WriteLine("x,y"); ArchiveModificationOutput.WriteLine("ID,action,time,x,y"); } IdGenerator idgen = new IdGeneratorFactory().CreateIdGenerator(seedGenome); EA = new EvolutionAlgorithm(new Population(idgen, GenomeFactory.CreateGenomeList(seedGenome, populationSize, experiment.DefaultNeatParameters, idgen)), experiment.PopulationEvaluator, experiment.DefaultNeatParameters); EA.outputFolder = OutputFolder; EA.neatBrain = NEATBrain; }
// Schrum: Module Mutation Duplicate creates a new module with // links copying those of another module. private void Module_Mutation_Duplicate(EvolutionAlgorithm ea) { // Push all output neurons together this.neuronGeneList.SortByNeuronOrder(); int numModules = this.outputNeuronCount / this.outputsPerPolicy; // Should evenly divide int randomModule = Utilities.Next(numModules); // Duplicate this module // Because outputs come after inputs. // Although list is 0-indexed, the +1 is needed because the bias does not count as an input double outputLayer = neuronGeneList[1 + inputNeuronCount].Layer; // Create the new module one neuron per loop iteration for (int i = 0; i < outputsPerPolicy; i++) { // The activation function for the output layer IActivationFunction outputActFunction = ActivationFunctionFactory.GetActivationFunction("BipolarSigmoid"); NeuronGene newNeuronGene = new NeuronGene(null, ea.NextInnovationId, outputLayer, NeuronType.Output, outputActFunction); neuronGeneList.Add(newNeuronGene); uint randomModuleInnovation = neuronGeneList[1 + inputNeuronCount + (randomModule * outputsPerPolicy) + i].InnovationId; // Copy each connection to the new module neuron int originalLength = ConnectionGeneList.Count; // Don't need to check the newly added connections for (int j = 0; j < originalLength; j++) { ConnectionGene cg = ConnectionGeneList[j]; if (cg.TargetNeuronId == randomModuleInnovation) { // Copy the link ConnectionGene connection = new ConnectionGene(ea.NextInnovationId, cg.SourceNeuronId, newNeuronGene.InnovationId, cg.Weight); connectionGeneList.InsertIntoPosition(connection); } } this.outputNeuronCount++; // Increase number of outputs } }
/// <summary> /// Initializes the EA with a random intial population. /// </summary> public void initializeEvolution(int populationSize) { LogOutput = Logging ? new StreamWriter(Path.Combine(OutputFolder, "log.txt")) : null; FinalPositionOutput = FinalPositionLogging ? new StreamWriter(Path.Combine(OutputFolder,"final-position.txt")) : null; ArchiveModificationOutput = FinalPositionLogging ? new StreamWriter(Path.Combine(OutputFolder, "archive-mods.txt")) : null; ComplexityOutput = new StreamWriter(Path.Combine(OutputFolder, "complexity.txt")); ComplexityOutput.WriteLine("avg,stdev,min,max"); if (FinalPositionLogging) { FinalPositionOutput.WriteLine("ID,x,y"); ArchiveModificationOutput.WriteLine("ID,action,time,x,y"); } IdGenerator idgen = new IdGenerator(); EA = new EvolutionAlgorithm(new Population(idgen, GenomeFactory.CreateGenomeList(experiment.DefaultNeatParameters, idgen, experiment.InputNeuronCount, experiment.OutputNeuronCount, experiment.DefaultNeatParameters.pInitialPopulationInterconnections, populationSize, SimExperiment.neatBrain)), experiment.PopulationEvaluator, experiment.DefaultNeatParameters); EA.outputFolder = OutputFolder; EA.neatBrain = NEATBrain; }
/// <summary> /// Add a new node to the Genome. We do this by removing a connection at random and inserting /// a new node and two new connections that make the same circuit as the original connection. /// /// This way the new node is properly integrated into the network from the outset. /// </summary> /// <param name="ea"></param> private void Mutate_AddNode(EvolutionAlgorithm ea) { if(connectionGeneList.Count==0) return; // Select a connection at random. int connectionToReplaceIdx = (int)Math.Floor(Utilities.NextDouble() * connectionGeneList.Count); ConnectionGene connectionToReplace = connectionGeneList[connectionToReplaceIdx]; // Delete the existing connection. JOEL: Why delete old connection? //connectionGeneList.RemoveAt(connectionToReplaceIdx); // Check if this connection has already been split on another genome. If so then we should re-use the // neuron ID and two connection ID's so that matching structures within the population maintain the same ID. object existingNeuronGeneStruct = ea.NewNeuronGeneStructTable[connectionToReplace.InnovationId]; NeuronGene newNeuronGene; ConnectionGene newConnectionGene1; ConnectionGene newConnectionGene2; IActivationFunction actFunct; if(existingNeuronGeneStruct==null) { // No existing matching structure, so generate some new ID's. //TODO: DAVID proper random activation function // Replace connectionToReplace with two new connections and a neuron. actFunct=ActivationFunctionFactory.GetRandomActivationFunction(ea.NeatParameters); //newNeuronGene = new NeuronGene(ea.NextInnovationId, NeuronType.Hidden, actFunct); newNeuronGene = new NeuronGene(null, ea.NextInnovationId, (neuronGeneList.GetNeuronById(connectionToReplace.SourceNeuronId).Layer + neuronGeneList.GetNeuronById(connectionToReplace.TargetNeuronId).Layer) / 2.0, NeuronType.Hidden, actFunct); newConnectionGene1 = new ConnectionGene(ea.NextInnovationId, connectionToReplace.SourceNeuronId, newNeuronGene.InnovationId, 1.0); newConnectionGene2 = new ConnectionGene(ea.NextInnovationId, newNeuronGene.InnovationId, connectionToReplace.TargetNeuronId, connectionToReplace.Weight); // Register the new ID's with NewNeuronGeneStructTable. ea.NewNeuronGeneStructTable.Add(connectionToReplace.InnovationId, new NewNeuronGeneStruct(newNeuronGene, newConnectionGene1, newConnectionGene2)); } else { // An existing matching structure has been found. Re-use its ID's //TODO: DAVID proper random activation function // Replace connectionToReplace with two new connections and a neuron. actFunct = ActivationFunctionFactory.GetRandomActivationFunction(ea.NeatParameters); NewNeuronGeneStruct tmpStruct = (NewNeuronGeneStruct)existingNeuronGeneStruct; //newNeuronGene = new NeuronGene(tmpStruct.NewNeuronGene.InnovationId, NeuronType.Hidden, actFunct); newNeuronGene = new NeuronGene(null, tmpStruct.NewNeuronGene.InnovationId, tmpStruct.NewNeuronGene.Layer, NeuronType.Hidden, actFunct); newConnectionGene1 = new ConnectionGene(tmpStruct.NewConnectionGene_Input.InnovationId, connectionToReplace.SourceNeuronId, newNeuronGene.InnovationId, 1.0); newConnectionGene2 = new ConnectionGene(tmpStruct.NewConnectionGene_Output.InnovationId, newNeuronGene.InnovationId, connectionToReplace.TargetNeuronId, connectionToReplace.Weight); } // Add the new genes to the genome. neuronGeneList.Add(newNeuronGene); connectionGeneList.InsertIntoPosition(newConnectionGene1); connectionGeneList.InsertIntoPosition(newConnectionGene2); }