From c18a5310349d31ba06b5cbb90187d9e45bf59e22 Mon Sep 17 00:00:00 2001 From: RyleyGG Date: Sun, 4 Oct 2020 15:54:38 -0400 Subject: [PATCH] Updated selection implementation, added with/without replacement variation --- src/EasyGA.py | 6 +- src/crossover/crossover_types.py | 7 +- src/run_testing.py | 4 +- src/selection/selection_types.py | 134 +++++++++++++++++++++---------- 4 files changed, 101 insertions(+), 50 deletions(-) diff --git a/src/EasyGA.py b/src/EasyGA.py index bb66415..554c413 100644 --- a/src/EasyGA.py +++ b/src/EasyGA.py @@ -23,10 +23,10 @@ class GA: # Termination varibles self.current_generation = 0 self.current_fitness = 0 - self.generation_goal = 35 + self.generation_goal = 50 self.fitness_goal = 3 # Mutation variables - self.mutation_rate = 0.075 + self.mutation_rate = 0.05 # Rerun already computed fitness self.update_fitness = True @@ -35,7 +35,7 @@ class GA: self.initialization_impl = Initialization_Types().random_initialization self.fitness_function_impl = Fitness_Examples().is_it_5 self.mutation_impl = Mutation_Types().random_mutation - self.selection_impl = Selection_Types().tournament_selection + self.selection_impl = Selection_Types().Tournament().with_replacement self.crossover_impl = Crossover_Types().single_point_crossover self.termination_impl = Termination_Types().generation_based diff --git a/src/crossover/crossover_types.py b/src/crossover/crossover_types.py index 8463009..b8ee22b 100644 --- a/src/crossover/crossover_types.py +++ b/src/crossover/crossover_types.py @@ -25,9 +25,10 @@ class Crossover_Types: new_gene_set = [] parent_one = crossover_pool[i].get_genes() parent_two = crossover_pool[i+1].get_genes() - halfway_point = int(ga.chromosome_length/2) - new_gene_set.extend(parent_one[0:halfway_point]) - new_gene_set.extend(parent_two[halfway_point:]) + #halfway_point = int(ga.chromosome_length/2) + split_point = random.randint(0,ga.chromosome_length) + new_gene_set.extend(parent_one[0:split_point]) + new_gene_set.extend(parent_two[split_point:]) new_chromosome = Chromosome(new_gene_set) new_population.add_chromosome(new_chromosome) diff --git a/src/run_testing.py b/src/run_testing.py index 1576b3a..1568ac3 100644 --- a/src/run_testing.py +++ b/src/run_testing.py @@ -5,9 +5,9 @@ import random # Create the Genetic algorithm ga = EasyGA.GA() -ga.gene_impl = [random.randrange,1,10] +ga.gene_impl = [random.randrange,1,25] -# Run Everyhting +# Run Everything ga.evolve() # Print the current population diff --git a/src/selection/selection_types.py b/src/selection/selection_types.py index 4f45568..a0bde70 100644 --- a/src/selection/selection_types.py +++ b/src/selection/selection_types.py @@ -9,22 +9,69 @@ class Selection_Types: def __init__(self): pass - def tournament_selection(self, ga): - """This example currently uses a 'with replacement' approach (chromosomes are placed back into the pool after participating)""" - tournament_size = int(len(ga.population.get_all_chromosomes())/10) #currently hard-coded for purposes of the example. + class Tournament: + def with_replacement(self, ga): + tournament_size = int(len(ga.population.get_all_chromosomes())/10) #currently hard-coded for purposes of the example. + if tournament_size < 3: + tournament_size = int(len(ga.population.get_all_chromosomes())/3) - #selection_probability is the likelihood that a chromosome will be selected. - #best chromosome in a tournament is given a selection probablity of selection_probability - #2nd best is given probability of selection_probability*(1-selection_probability) - selection_probability = 0.95 - total_selected = 0 #Total Chromosomes selected + #selection_probability is the likelihood that a chromosome will be selected. + #best chromosome in a tournament is given a selection probablity of selection_probability + #2nd best is given probability of selection_probability*(1-selection_probability) + #3rd best is given probability of selection_probability*(1-selection_probability)**2 + selection_probability = 0.95 + total_selected = 0 #Total Chromosomes selected - while (total_selected <= ga.population_size*2): - #create & gather tournament group - tournament_group = [] - for i in range(tournament_size): - tournament_group.append(random.choice(ga.population.get_all_chromosomes())) + while (total_selected <= ga.population_size*2): + #create & gather tournament group + tournament_group = [] + for i in range(tournament_size): + tournament_group.append(random.choice(ga.population.get_all_chromosomes())) + total_selected = self.selection(tournament_group, tournament_size, total_selected, selection_probability)[0] + + new_population = self.create_new_population(ga) + return new_population + + def without_replacement(self, ga): + tournament_size = int(len(ga.population.get_all_chromosomes())/10) #currently hard-coded for purposes of the example. + if tournament_size < 3: + tournament_size = int(len(ga.population.get_all_chromosomes())/3) + + #selection_probability is the likelihood that a chromosome will be selected. + #best chromosome in a tournament is given a selection probablity of selection_probability + #2nd best is given probability of selection_probability*(1-selection_probability) + #3rd best is given probability of selection_probability*(1-selection_probability)**2 + selection_probability = 0.95 + total_selected = 0 #Total Chromosomes selected + available_chromosome_indices = [] + for i in range(len(ga.population.get_all_chromosomes())): + available_chromosome_indices.append(i) + + continue_selecting = True + + while (continue_selecting): + #create & gather tournament group + tournament_group = [] + + for i in range(tournament_size): + selected_chromosome_index = random.choice(available_chromosome_indices) + tournament_group.append(ga.population.get_all_chromosomes()[selected_chromosome_index]) + + winning_chromosome_index = self.selection(tournament_group, tournament_size, total_selected, selection_probability)[1] + for i in range(len(available_chromosome_indices)): + if tournament_group[winning_chromosome_index].selected: + del available_chromosome_indices[i] + break + #print(winning_chromosome_index) + #print(available_chromosome_indices) + if len(available_chromosome_indices) < 1: + continue_selecting = False + + new_population = self.create_new_population(ga) + return new_population + + def selection(self, tournament_group, tournament_size, total_selected, selection_probability): #Sort the tournament contenders based on their fitness #currently hard-coded to only consider higher fitness = better; can be changed once this impl is agreed on #also currently uses bubble sort because its easy @@ -43,6 +90,7 @@ class Selection_Types: tournament_group = tournament_group_temp #After sorting by fitness, randomly select a chromosome based on selection_probability + selected_chromosome_tournament_index = 0 for i in range(tournament_size): random_num = random.uniform(0,1) @@ -51,47 +99,49 @@ class Selection_Types: if random_num <= selection_probability: tournament_group[i].selected = True total_selected += 1 + selected_chromosome_tournament_index = i break else: if random_num <= selection_probability*((1-selection_probability)**(i-1)): tournament_group[i].selected = True total_selected += 1 + selected_chromosome_tournament_index = i break + return total_selected,selected_chromosome_tournament_index - new_population = ga.crossover_impl(ga) + def create_new_population(self, ga): + new_population = ga.crossover_impl(ga) - #If the crossover doesn't create enough chromosomes (ugly right now pls no judgerino, can be changed) - #Just does single-point crossover at random indices - while len(new_population.chromosomes) < ga.population_size: - crossover_pool = [] - for i in range(ga.population_size): - if ga.population.get_all_chromosomes()[i].selected: - crossover_pool.append(ga.population.get_all_chromosomes()[i]) + #If the crossover doesn't create enough chromosomes (ugly right now pls no judgerino, can be changed) + #Just does single-point crossover at random indices + while len(new_population.chromosomes) < ga.population_size: + crossover_pool = [] + for i in range(ga.population_size): + if ga.population.get_all_chromosomes()[i].selected: + crossover_pool.append(ga.population.get_all_chromosomes()[i]) - split_point = random.randint(0,ga.chromosome_length) - chromosome_list = [] - for i in range(len(crossover_pool)): - if i + 1 < len(crossover_pool): - new_gene_set = [] - parent_one = crossover_pool[i].get_genes() - parent_two = crossover_pool[i+1].get_genes() - new_gene_set.extend(parent_one[0:split_point]) - new_gene_set.extend(parent_two[split_point:]) - new_chromosome = Chromosome(new_gene_set) - chromosome_list.append(new_chromosome) - - for i in range(len(chromosome_list)): - new_population.add_chromosome(chromosome_list[i]) - if len(new_population.chromosomes) >= ga.population_size: - break - - new_chromosome_set = ga.mutation_impl(ga, new_population.get_all_chromosomes()) - new_population.set_all_chromosomes(new_chromosome_set) - - return new_population + split_point = random.randint(0,ga.chromosome_length) + chromosome_list = [] + for i in range(len(crossover_pool)): + if i + 1 < len(crossover_pool): + new_gene_set = [] + parent_one = crossover_pool[i].get_genes() + parent_two = crossover_pool[i+1].get_genes() + new_gene_set.extend(parent_one[0:split_point]) + new_gene_set.extend(parent_two[split_point:]) + new_chromosome = Chromosome(new_gene_set) + chromosome_list.append(new_chromosome) + for i in range(len(chromosome_list)): + new_population.add_chromosome(chromosome_list[i]) + if len(new_population.chromosomes) >= ga.population_size: + break + + new_chromosome_set = ga.mutation_impl(ga, new_population.get_all_chromosomes()) + new_population.set_all_chromosomes(new_chromosome_set) + return new_population def roulette_selection(self, ga): """Roulette selection works based off of how strong the fitness is of the