From e05aa7f62b2c2478c63bee9ae517b348f38960c8 Mon Sep 17 00:00:00 2001 From: RyleyGG Date: Sun, 4 Oct 2020 17:59:59 -0400 Subject: [PATCH] Changed implementation framework Instead of a nested approach, selection/crossover/mutation are all called separately and directly by the GA. selection_impl was also separated into parent_selection_impl and survivor_selection_impl, as both are needed separately. --- src/EasyGA.py | 17 +- .../population_structure/population.py | 6 +- src/mutation/mutation_types.py | 23 ++- src/run_testing.py | 5 +- src/selection/selection_types.py | 171 +++++++----------- 5 files changed, 102 insertions(+), 120 deletions(-) diff --git a/src/EasyGA.py b/src/EasyGA.py index 554c413..9a08f05 100644 --- a/src/EasyGA.py +++ b/src/EasyGA.py @@ -16,17 +16,17 @@ class GA: """Initialize the GA.""" # Initilization variables self.chromosome_length = 10 - self.population_size = 100 + self.population_size = 150 self.chromosome_impl = None self.gene_impl = None self.population = None # Termination varibles self.current_generation = 0 self.current_fitness = 0 - self.generation_goal = 50 + self.generation_goal = 100 self.fitness_goal = 3 # Mutation variables - self.mutation_rate = 0.05 + self.mutation_rate = 0.10 # Rerun already computed fitness self.update_fitness = True @@ -34,8 +34,9 @@ class GA: # Defualt EastGA implimentation structure 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().with_replacement + self.mutation_impl = Mutation_Types().per_gene_mutation + self.parent_selection_impl = Selection_Types().Parent_Selection().Tournament().with_replacement + self.survivor_selection_impl = Selection_Types().Survivor_Selection().repeated_crossover self.crossover_impl = Crossover_Types().single_point_crossover self.termination_impl = Termination_Types().generation_based @@ -70,7 +71,11 @@ class GA: self.initialize_population() self.set_all_fitness(self.population.chromosomes) - next_population = self.selection_impl(self) + self.parent_selection_impl(self) + next_population = self.crossover_impl(self) + next_population = self.survivor_selection_impl(self, next_population) + next_population.set_all_chromosomes(self.mutation_impl(self, next_population.get_all_chromosomes())) + self.population = next_population self.set_all_fitness(self.population.chromosomes) diff --git a/src/initialization/population_structure/population.py b/src/initialization/population_structure/population.py index 44b9435..ae7670a 100644 --- a/src/initialization/population_structure/population.py +++ b/src/initialization/population_structure/population.py @@ -30,8 +30,10 @@ class Population: def set_all_chromosomes(self, chromosomes): self.chromosomes = chromosomes - def set_chromosome(self, chromosomes, index): - self.chromosome[index] = chromosome + def set_chromosome(self, chromosome, index = -1): + if index == -1: + index = len(self.chromosomes)-1 + self.chromosomes[index] = chromosome def set_fitness(self, fitness): self.fitness = fitness diff --git a/src/mutation/mutation_types.py b/src/mutation/mutation_types.py index 418abd3..12b4b6e 100644 --- a/src/mutation/mutation_types.py +++ b/src/mutation/mutation_types.py @@ -8,7 +8,7 @@ class Mutation_Types: def random_mutation(self, ga, chromosome_set = None): if chromosome_set == None: - chromosome_set = ga.population + chromosome_set = ga.population.get_all_chromosomes() chromosome_mutate_num = int(len(chromosome_set)*ga.mutation_rate) temp_population = ga.initialization_impl(ga) @@ -18,4 +18,25 @@ class Mutation_Types: chromosome_mutate_num -= 1 return chromosome_set + + def per_gene_mutation(self, ga, chromosome_set = None, gene_mutate_count = 1): + + gene_mutate_count_static = int(gene_mutate_count) + + if chromosome_set == None: + chromosome_set = ga.population.get_all_chromosomes() + + for i in range(len(chromosome_set)): + random_num = random.uniform(0,1) + + if (random_num <= ga.mutation_rate): + while gene_mutate_count > 0: + dummy_population = ga.initialization_impl(ga) #Really inefficient, but works for now + random_index = random.randint(0, ga.chromosome_length-1) + chromosome_set[i].get_genes()[random_index] = dummy_population.get_all_chromosomes()[random.randint(0,ga.population_size-1)].get_genes()[random_index] + gene_mutate_count -= 1 + gene_mutate_count = int(gene_mutate_count_static) + + return chromosome_set + diff --git a/src/run_testing.py b/src/run_testing.py index 1568ac3..cdc0a0f 100644 --- a/src/run_testing.py +++ b/src/run_testing.py @@ -5,7 +5,10 @@ import random # Create the Genetic algorithm ga = EasyGA.GA() -ga.gene_impl = [random.randrange,1,25] +#def random_parent_selection(population): + #while () + +ga.gene_impl = [random.randrange,1,100] # Run Everything ga.evolve() diff --git a/src/selection/selection_types.py b/src/selection/selection_types.py index a0bde70..b8aca55 100644 --- a/src/selection/selection_types.py +++ b/src/selection/selection_types.py @@ -9,113 +9,67 @@ class Selection_Types: def __init__(self): pass - 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) + class Parent_Selection: + 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) + parent_ratio = 0.25 - #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 + #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 = [] + while (total_selected < parent_ratio*ga.population_size): + #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] + for i in range(tournament_size): + tournament_group.append(random.choice(ga.population.get_all_chromosomes())) + + #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 + tournament_group_temp = tournament_group + not_sorted_check = 0 + while (not_sorted_check != len(tournament_group_temp)): + not_sorted_check = 0 + for i in range(len(tournament_group_temp)): + if ((i + 1 < len(tournament_group_temp)) and (tournament_group_temp[i + 1].fitness > tournament_group_temp[i].fitness)): + temp = tournament_group[i] + tournament_group_temp[i] = tournament_group[i + 1] + tournament_group_temp[i + 1] = temp + else: + not_sorted_check += 1 - new_population = self.create_new_population(ga) - return new_population + tournament_group = tournament_group_temp - 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) + #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) - #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) + #ugly implementation but its functional + if i == 0: + 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 - 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 - tournament_group_temp = tournament_group - not_sorted_check = 0 - while (not_sorted_check != len(tournament_group_temp)): - not_sorted_check = 0 - for i in range(len(tournament_group_temp)): - if ((i + 1 < len(tournament_group_temp)) and (tournament_group_temp[i + 1].fitness > tournament_group_temp[i].fitness)): - temp = tournament_group[i] - tournament_group_temp[i] = tournament_group[i + 1] - tournament_group_temp[i + 1] = temp - else: - not_sorted_check += 1 - - 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) - - #ugly implementation but its functional - if i == 0: - 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 - - 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: + class Survivor_Selection: + def repeated_crossover(self, ga, next_population): + while len(next_population.chromosomes) < ga.population_size: crossover_pool = [] for i in range(ga.population_size): if ga.population.get_all_chromosomes()[i].selected: @@ -130,19 +84,16 @@ class Selection_Types: 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) + new_chromosome = create_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: + next_population.add_chromosome(chromosome_list[i]) + if len(next_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 - + return next_population + def roulette_selection(self, ga): """Roulette selection works based off of how strong the fitness is of the chromosomes in the population. The stronger the fitness the higher the probability