// Copies all Elements from population to oldPopulation public void clone() { for (int i = 0; i < populationSize; i++) { oldPopulation[i] = new Population(population[i].Path, population[i].Genome, population[i].Rating, population[i].Mutated, population[i].Selected); } }
// This function uses genomePart Uniform Crossover. It works like a crossover function but uses a significantly shorter mask genome // because it always chooses an entire 8bit block public void eightBitCrossover() { // Size of current new generation is 40% of old generation (before crossover) int selectionSize = Convert.ToInt32(0.4 * populationSize); int r; Genome parent1 = new Genome(); Genome parent2 = new Genome(); bool[] mask; Genome child1 = new Genome(); Genome child2 = new Genome(); // Iterates from selection Size (current maximum of the new population) to population size, the wanted maximum. That means 60% come from crossover for (int i = selectionSize; i < populationSize; i++) { // Randomly selects first parent r = Variables.getRandomInt(0, selectionSize); parent1 = oldPopulation[r].Genome; // Randomly selects second parent r = Variables.getRandomInt(0, selectionSize); parent2 = oldPopulation[r].Genome; // Crates a random mask genome in form of a char[]. char[] is much faster than bool[], genome or bitarray and for our purpose the format doesn't matter mask = GenomePart.getRandomGenome(); for (int j = 0; j < 20; j++) { // Checks the mask if (mask[j]) { // If mask == 1 then the i'th 8bit block will be selected from parent1 for child1 and from parent2 for child2 // k = j*8 since every position in the mask genome covers 8 positions in the actual genome for (int k = j * 8; k < j * 8 + 8; k++) { child1.Genome1.Set(k, parent1.Genome1.Get(k)); child2.Genome1.Set(k, parent2.Genome1.Get(k)); } } else { // If mask == 0 then the i'th 8bit block will be selected from parent1 for child2 and from parent2 for child1 // k = j*8 since every position in the mask genome covers 8 positions in the actual genome for (int k = j * 8; k < j * 8 + 8; k++) { child1.Genome1.Set(k, parent2.Genome1.Get(k)); child2.Genome1.Set(k, parent1.Genome1.Get(k)); } } } Genome.genomeToPath(child1); population[i] = new Population(Variables.path, child1, 0, false, false); // Counts up since we are adding 2 children per iteration, not just one i++; // If we are not yet at maximum (which could happen due to double->int conversion) we add the second child too if (i < populationSize) { Genome.genomeToPath(child2); population[i] = new Population(Variables.path, child2, 0, false, false); } } }
// This function selects via tournament selection. Selected paths are not removed from the population, so the same path can be chosen several times public void tournamentSelection() { int highestFitness = 0, r = 0, tournamentSize = 2; // New Population consists of 40% Selection int selectionSize = Convert.ToInt32(0.4 * populationSize); // Iterates over the new population for (int j = 0; j < selectionSize; j++) { // Chooses tournamentSize members from the old population and saves the best one to highestFitness for (int i = 0; i < tournamentSize; i++) { r = Variables.getRandomInt(0, selectionSize); if (i == 0 || oldPopulation[r].Rating > oldPopulation[highestFitness].Rating) highestFitness = r; } // Adds the winner of the tournament (highestFitness) to the new population population[j] = new Population(oldPopulation[highestFitness].Path, oldPopulation[highestFitness].Genome, oldPopulation[highestFitness].Rating, false, true); } }
// This function uses two fixed points for two-point crossover. For better performance these two points may be made random and/or adjusted over time // For example, in higher generations they could be moved further to the back public void twoFixedPointCrossover() { int selectionSize = Convert.ToInt32(0.4 * populationSize); Genome parent1 = new Genome(); Genome parent2 = new Genome(); Genome child1 = new Genome(); Genome child2 = new Genome(); int r; int fixpoint1 = 4; int fixpoint2 = 15; for (int i = selectionSize; i < populationSize; i++) { // Randomly selects first parent r = Variables.getRandomInt(0, selectionSize); parent1 = oldPopulation[r].Genome; // Randomly selects second parent r = Variables.getRandomInt(0, selectionSize); parent2 = oldPopulation[r].Genome; for (int j = 0; j < 20; j++) { // Case 1: fixpoint 1 to fixpoint 2, fixpoints exclusive if (j > fixpoint1 && j < fixpoint2) { // Iterates over the eight-bit-blocks and adds all bits to the respective parent for (int k = j * 8; k < j * 8 + 8; k++) { child1.Genome1.Set(k, parent1.Genome1.Get(k)); child2.Genome1.Set(k, parent2.Genome1.Get(k)); } } // Case 2: Before fixpoint 1 and after fixpoint 2, fixpoints inclusive else { // Iterates over the eight-bit-blocks and adds all bits to the respective parent for (int k = j * 8; k < j * 8 + 8; k++) { child1.Genome1.Set(k, parent2.Genome1.Get(k)); child2.Genome1.Set(k, parent1.Genome1.Get(k)); } } } Genome.genomeToPath(child1); population[i] = new Population(Variables.path, child1, 0, false, false); // Counts up since we are adding 2 children per iteration, not just one i++; // If we are not yet at maximum (which could happen due to double->int conversion) we add the second child too if (i < populationSize) { Genome.genomeToPath(child2); population[i] = new Population(Variables.path, child2, 0, false, false); } } }
// This function uses single-bit Uniform Crossover. public void singleBitCrossover() { // Size of current new generation is 40% of old generation (before crossover) int selectionSize = Convert.ToInt32(0.4 * populationSize); int r; Genome parent1 = new Genome(); Genome parent2 = new Genome(); Genome mask = new Genome(); Genome child1 = new Genome(); Genome child2 = new Genome(); // Iterates from selection Size (current maximum of the new population) to population size, the wanted maximum. That means 60% come from crossover for (int i = selectionSize; i < populationSize; i ++) { // Randomly selects first parent r = Variables.getRandomInt(0,selectionSize); parent1 = oldPopulation[r].Genome; // Randomly selects second parent r = Variables.getRandomInt(0, selectionSize); parent2 = oldPopulation[r].Genome; // Crates a random mask genome. This function sucks. mask = new Genome(true); // Generates children for (int j = 0; j < 160; j++) { if (mask.Genome1.Get(j)) { // If mask(i) is 1 then child1 gets the value at that point from parent1, child2 from parent2 child1.Genome1.Set(j, parent1.Genome1.Get(j)); child2.Genome1.Set(j, parent2.Genome1.Get(j)); } else { // If mask(i) is 0 then child1 gets the value at that point from parent2, child2 from parent1 child1.Genome1.Set(j, parent2.Genome1.Get(j)); child2.Genome1.Set(j, parent1.Genome1.Get(j)); } } // Adds the genome and its corresponding path to the population at position i. Also sets Selected to false. Genome.genomeToPath(child1); population[i] = new Population(Variables.path, child1, 0, false, false); // Counts up since we are adding 2 children per iteration, not just one i++; // If we are not yet at maximum (which could happen due to double->int conversion) we add the second child too if (i < populationSize) { Genome.genomeToPath(child2); population[i] = new Population(Variables.path, child2, 0, false, false); } } }
// This function is a straight selection which simply seletc the x best members of the population. Members cannot be chosen several times public void selection() { // New Population consists of 40% Selection int selectionSize = Convert.ToInt32(0.4 * populationSize); int d; // Iterates over the new population until selectionSize is met for (int i = 0; i < selectionSize; i++) { // Stores the highest rating d = 0; // Iterates over the old population, looking for the highest rating for (int j = 0; j < populationSize; j++) { if (oldPopulation[j].Rating > oldPopulation[d].Rating) d = j; } // Takes the path with the highest rating from old population and sets Selected/Mutated population[i] = new Population(oldPopulation[d].Path, oldPopulation[d].Genome, oldPopulation[d].Rating, false, true); // Sets the Rating of the selected path to 0 so it is not selected again oldPopulation[d].Rating = 0; } }
// This selection function uses a wighted (roulette wheel) selection process where members with a high fitness have a higher chance to be chosen. // Members can be chosen repeatedly // This function is equivalent to rouletteWheelSelection in functionality but not in implementation. The other function should be prefered unless it produces problems. public void rouletteWheelSelection2() { double totalFitness = 0; // New Population consists of 40% Selection int selectionSize = Convert.ToInt32(0.4 * populationSize); // Calculates toal Fitness of the old population for (int i = 0; i < populationSize; i++) { totalFitness += oldPopulation[i].Rating; } // Normalizes all fitness values for (int i = 0; i < populationSize; i++) { oldPopulation[i].Rating = oldPopulation[i].Rating / totalFitness; } Population[] tempPopulation = new Population[populationSize]; double j; int d; // Sorts oldPopulation into temppOpulation by descending Fitness Rating for (int l = 0; l < populationSize; l++) { j = 0; d = 0; // Finds the member in oldPopulation with the highest rating for (int i = 0; i < populationSize; i++) { if (oldPopulation[i].Rating > j) { j = oldPopulation[i].Rating; d = i; } } // oldPopulation[d], which is the member with the highest rating, goes to position i tempPopulation[l] = new Population(oldPopulation[d].Path, oldPopulation[d].Genome, oldPopulation[d].Rating, false, true); // Sets the current oldPopulation Rating to 0 so it is not selected again oldPopulation[d].Rating = 0; } double acc = 0; // Calculates the accumulated rating for every position so every member has it's own Rating + all previous ones as it's rating for (int i = 0; i < populationSize; i++) { acc += tempPopulation[i].Rating; tempPopulation[i].Rating = acc; } double r; for (int i = 0; i < selectionSize; i++) { r = Variables.getRandomNumber(0, 1); d = 0; while (tempPopulation[d].Rating <= r) d++; population[i] = new Population(tempPopulation[d].Path, tempPopulation[d].Genome, tempPopulation[d].Rating, false, true); } }
// This selection function uses a weighted (roulette wheel) selection process where members with a high fitness have a higher chance to be chosen. // Members can be chosen repeatedly public void rouletteWheelSelection() { // Sums up all ratings to a total double totalFitness = 0; double r; double c; int j; // Calculates toal Fitness of the old population for (int i = 0; i < populationSize; i++) { totalFitness += oldPopulation[i].Rating; } // New Population consists of 40% Selection int selectionSize = Convert.ToInt32(0.4 * populationSize); // Creates <selectionSize> new members for the new population for (int i = 0; i < selectionSize; i++) { // Random Number r = Variables.getRandomNumber(0, totalFitness); // Current Sum and iterator are 0 c = 0; j = 0; // Sums over oldPopulation until the sum is bigger than the random number do { c += oldPopulation[j].Rating; j++; } while (c < r); // Selects j - 1 (since we did j++ after fulfilling the condition) from the oldPopulation and adds it to the new one at i population[i] = new Population(oldPopulation[j - 1].Path, oldPopulation[j - 1].Genome, oldPopulation[j - 1].Rating, false, true); } }
// This function is only used once (Generation 0) and populates the algorithm with randomly generated paths. public void initialize() { double rating; // Generates an initial population of size <PopulationSize> along with their fitness for (int i = 0; i < populationSize; i++) { // Generates a random path of length 20 and saves it to the global path EZPathFollowing.PathPrimitives.generatePath(); // Creates a Simulation for this path Variables.simulation = new Simulation(Variables.vehicle, Variables.path); Variables.simulation.run(); // Rates the Path and adds the rating to the ratings List rating = FitnessFunction.fitness(Variables.simulation.getPath()); // Adds the path, genome and rating to the population population[i] = new Population(Variables.path, Variables.genome, rating); // Draws. glcontrol.Refresh(); } }