// Select a genome using tournament. protected virtual Genome TournamentSelect(List <Genome> population) { List <Genome> contestents = new List <Genome>(); // First, randomize number of contestents. for (int i = 0; i < tourneyContestents; i++) { int randomIndex = UnityEngine.Random.Range(0, population.Count); contestents.Add(population[randomIndex]); } Genome highestFit = contestents[0]; // Find highest fitness among contestents foreach (Genome g in contestents) { if (g.Fitness > highestFit.Fitness) { highestFit = g; } } // Create a copy of the best genome and return it. Genome copy = highestFit.GenomeCopy(); copy.RootNode = StaticMethods.DeepCopy <N_Root>(highestFit.RootNode); return(copy); }
// Assign fitness values according to their performance in the simulation step protected void Evaluate() { bestGenome = m_population[0]; // Assign fitness value for each genome based on its statistics. foreach (Genome g in m_population) { g.Fitness = 100; // If the genome won the match and it wasn't a draw (assuming 0 damage given is a draw) // set fitness to 150. if (g.WonLastMatch && g.DamageGiven > 0) { g.Fitness += 150; } g.Fitness += g.HealthRemaining; g.Fitness += g.DamageGiven; // Make the floor of all fitness values 0 to make the roulette selection valid. if (g.Fitness < 0) { g.Fitness = 0; } // Find the genome with highest fitness, making sure that the simulations are always // compared to the currently best genome. if (g.Fitness > bestGenome.Fitness) { bestGenome.RootNode = StaticMethods.DeepCopy <N_Root>(g.RootNode); bestGenome.Fitness = g.Fitness; } } }
private Genome BinaryCrowdedTournamentSelect(List <Genome> genomes) { List <Genome> contestents = new List <Genome>(); // First, randomize number of contestents. for (int i = 0; i < 2; i++) { int randomIndex = UnityEngine.Random.Range(0, genomes.Count); contestents.Add(genomes[randomIndex]); } Genome highestFit = contestents[0]; // Find highest fitness among contestents foreach (Genome g in contestents) { if (g.Fitness < highestFit.Fitness) // Lower rank/fitness is better in this case. { highestFit = g; } else if (g.Fitness == highestFit.Fitness) // If they belong to the same front { // In that case, choose based on crowding distance instead. if (g.CrowdingDistance > highestFit.CrowdingDistance) { highestFit = g; } } } // Create a copy of the best genome and return it. Genome copy = highestFit.GenomeCopy(); copy.RootNode = StaticMethods.DeepCopy <N_Root>(highestFit.RootNode); return(copy); }
// Combine parents and create two new children based on them protected void Combine(out Genome child0, out Genome child1, Genome parent0, Genome parent1) { Node subtree0, subtree1 = null; // First, check if there'll be any combination/crossover. float random = UnityEngine.Random.Range(0.0f, 1.0f); if (random < combinationRate) { List <Node> subtrees0 = TreeOperations.RetrieveSubtreeNodes(parent0.RootNode); List <Node> subtrees1 = TreeOperations.RetrieveSubtreeNodes(parent1.RootNode); // Select to random subtrees... int randomIndex = UnityEngine.Random.Range(0, subtrees0.Count); subtree0 = subtrees0[randomIndex]; randomIndex = UnityEngine.Random.Range(0, subtrees1.Count); subtree1 = subtrees1[randomIndex]; // Swap subtrees between 0 and 1. if (subtree0.Parent.GetType().IsSubclassOf(typeof(N_CompositionNode))) { N_CompositionNode comp0 = subtree0.Parent as N_CompositionNode; comp0.ReplaceChild(subtree0, StaticMethods.DeepCopy <Node>(subtree1)); } // Swap subtrees between 1 and 0. if (subtree1.Parent.GetType().IsSubclassOf(typeof(N_CompositionNode))) { N_CompositionNode comp1 = subtree1.Parent as N_CompositionNode; comp1.ReplaceChild(subtree1, StaticMethods.DeepCopy <Node>(subtree0)); } #region Old Combination //// Get initial comp of parent 0 //N_CompositionNode comp0 = parent0.RootNode.Child as N_CompositionNode; //// Fetch a random subtree from the parent's subtrees //int randomIndex = UnityEngine.Random.Range(0, comp0.GetChildren().Count); //subtree0 = comp0.GetChildren()[randomIndex]; //// Get initial comp of parent 1 //N_CompositionNode comp1 = parent1.RootNode.Child as N_CompositionNode; //// Fetch a random subtree from the parent's subtrees //randomIndex = UnityEngine.Random.Range(0, comp1.GetChildren().Count); //subtree1 = comp1.GetChildren()[randomIndex]; //// Swap subtrees between 0 and 1. //if (subtree0.Parent.GetType().IsSubclassOf(typeof(N_CompositionNode))) //{ // comp0.ReplaceChild(subtree0, StaticMethods.DeepCopy<Node>(subtree1)); //} //// Swap subtrees between 1 and 0. //if (subtree1.Parent.GetType().IsSubclassOf(typeof(N_CompositionNode))) //{ // comp1.ReplaceChild(subtree1, StaticMethods.DeepCopy<Node>(subtree0)); //} #endregion } // Regardless of combination, assign the children to be the parents. (combined or not) child0 = parent0; child1 = parent1; }
// Select two candidates for next generation based on their fitness values protected void RouletteSelect(out Genome parent0, out Genome parent1) { Dictionary <Genome, float> genomeToWeight = new Dictionary <Genome, float>(); parent0 = parent1 = null; // Sum up the total weight of all fitness values. float totalFitness = 0.0f; foreach (Genome g in m_population) { totalFitness += (float)g.Fitness; } // Calculate the relative fotness for each genome float fitnessSum = 0.0f; foreach (Genome g in m_population) { float weight = fitnessSum + ((float)g.Fitness / totalFitness); genomeToWeight.Add(g, weight); fitnessSum += (g.Fitness / totalFitness); } // Select two parents using semi-randomized roulette selection. for (int i = 0; i < 2; i++) { float random = UnityEngine.Random.Range(0.0f, 1.0f); Genome selectedTree = null; for (int j = 0; j < genomeToWeight.Count; j++) { var current = genomeToWeight.ElementAt(j); if (j == 0) { if (random < current.Value) { selectedTree = current.Key; } } else { var prev = genomeToWeight.ElementAt(j - 1); if (random < current.Value && random > prev.Value) { selectedTree = current.Key; } } } if (selectedTree == null) { Debug.LogError("Selected tree is null..."); } Genome parentG = selectedTree.GenomeCopy(); // Assign parents during different iterations. switch (i) // Make sure to also copy conditions and subroots { case 0: parent0 = parentG; parent0.RootNode = StaticMethods.DeepCopy <N_Root>(selectedTree.RootNode); break; case 1: parent1 = parentG; parent1.RootNode = StaticMethods.DeepCopy <N_Root>(selectedTree.RootNode); break; default: parent0 = new Genome(); parent1 = new Genome(); Debug.LogError("No parent was set in roulette"); break; } } if (parent0 == null || parent1 == null) { Debug.LogError("Parents weren't assigned during roulette selection!"); } }
// Update is called once per frame public override IEnumerator Evolve() { // Start by randomly generating the initial population. for (int i = 0; i < populationSize; i++) { m_population.Add(RandomGenome()); } // Randomly generate the first best genome. bestGenome = RandomGenome(); // First, process the initial population differently. feedbackText.SetText("Generation " + 0 + " out of " + generations + "..."); Debug.Log("Starting simulation step of initial population..."); // Start simulation and wait until done. simulationDone = false; StartCoroutine(Simulate(m_population)); yield return(new WaitUntil(() => simulationDone)); Debug.Log("Simulation step of initial population complete!"); // Assign fitness to each genome according to nondomination principle. SortFitnessByNondomination(m_population); CalculateCrowdingDistance(m_population); // Initial "next population" processing. while (m_childPop.Count < m_population.Count) { Genome parent0, parent1; parent0 = BinaryCrowdedTournamentSelect(m_population); parent1 = BinaryCrowdedTournamentSelect(m_population); // Combine parents to retrieve two children Genome child0, child1; Combine(out child0, out child1, parent0, parent1); Mutate(child0.RootNode); Mutate(child1.RootNode); m_childPop.Add(child0); m_childPop.Add(child1); } //Debug.Log("Evolving rest of generations..."); // Run general algorithm for remaining -th generations. (index 1 and forward for (int i = 1; i < generations; i++) { feedbackText.SetText("Generation " + i + " out of " + generations + "..."); //Debug.Log("Starting simulation step of generation " + i); // Start simulation and wait until done. simulationDone = false; StartCoroutine(Simulate(m_childPop)); yield return(new WaitUntil(() => simulationDone)); //Debug.Log("Simulation step complete!"); // First, create new generation as a combination of the last and the one before that. List <Genome> combinedGenomes = new List <Genome>(); combinedGenomes.AddRange(m_population); combinedGenomes.AddRange(m_childPop); List <Genome> nextPopCandidates = new List <Genome>(); // Sort according to nondomination and return list of fronts List <List <Genome> > fronts = GetFrontsByNondomination(combinedGenomes); int frontIndex = 0; // Create set of potential genome candidats while (nextPopCandidates.Count + fronts[frontIndex].Count <= populationSize) { // Calculate crowding distance for the front and add it to the population. CalculateCrowdingDistance(fronts[frontIndex]); nextPopCandidates.AddRange(fronts[frontIndex]); frontIndex++; } //Debug.Log(fronts[0].Count); // Retrieve the best genome from front 0, used for simulation Genome bestBack = BestFromFront(fronts[0]); bestGenome = bestBack.GenomeCopy(); bestGenome.RootNode = StaticMethods.DeepCopy <N_Root>(bestBack.RootNode); // Sort the front that didn't fit SortCrowdedComparison(fronts[frontIndex]); int fillingIndex = 0; // Complete the population by adding from the sorted front while (nextPopCandidates.Count < populationSize) { nextPopCandidates.Add(fronts[frontIndex][fillingIndex]); fillingIndex++; } // Push child back to pop and create new child using nextpop made from child and pop m_population = m_childPop; m_childPop = new List <Genome>(); // Finally, create the childpop for next generation. while (m_childPop.Count < populationSize) { Genome parent0, parent1; parent0 = BinaryCrowdedTournamentSelect(nextPopCandidates); parent1 = BinaryCrowdedTournamentSelect(nextPopCandidates); // Combine parents to retrieve two children Genome child0, child1; Combine(out child0, out child1, parent0, parent1); Mutate(child0.RootNode); Mutate(child1.RootNode); m_childPop.Add(child0); m_childPop.Add(child1); } } // Save the final best tree. FileSaver.GetInstance().SaveTree(bestGenome.RootNode, "multiEvolved"); feedbackText.SetText("Multi evolution complete!"); buttonCanvas.SetActive(true); yield return(null); }