From 94d7c52666caa7850c4853c91dda0ca090bd71ac Mon Sep 17 00:00:00 2001 From: RyleyGG Date: Mon, 12 Oct 2020 09:23:41 -0400 Subject: [PATCH] Added comments & fixed small bug Mostly added comments, but also fixed a small bug in parent selection where the tournament size would be much smaller than it should be. --- src/EasyGA.py | 2 +- src/crossover/crossover_methods.py | 12 +--- src/fitness_function/fitness_examples.py | 6 +- src/mutation/mutation_methods.py | 10 ++- .../parent_selection_methods.py | 2 +- src/run_testing.py | 2 +- .../survivor_selection_methods.py | 62 ++++++++++--------- 7 files changed, 50 insertions(+), 46 deletions(-) diff --git a/src/EasyGA.py b/src/EasyGA.py index 9e6ee4c..382334d 100644 --- a/src/EasyGA.py +++ b/src/EasyGA.py @@ -45,7 +45,7 @@ class GA: self.initialization_impl = Initialization_Methods.random_initialization self.fitness_function_impl = Fitness_Examples.index_dependent_values # Selects which chromosomes should be automaticly moved to the next population - self.survivor_selection_impl = Survivor_Selection.remove_two_worst + self.survivor_selection_impl = Survivor_Selection.remove_worst # Methods for accomplishing parent-selection -> Crossover -> Mutation self.parent_selection_impl = Parent_Selection.Tournament.with_replacement self.crossover_impl = Crossover_Methods.single_point_crossover diff --git a/src/crossover/crossover_methods.py b/src/crossover/crossover_methods.py index fb2c83d..b81442f 100644 --- a/src/crossover/crossover_methods.py +++ b/src/crossover/crossover_methods.py @@ -3,19 +3,13 @@ from initialization.chromosome_structure.chromosome import Chromosome from initialization.population_structure.population import Population class Crossover_Methods: - """ Crossover explination goes here. - - Points - Defined as sections between the chromosomes genetic makeup - """ - def __init__(self): - pass - def single_point_crossover(ga): - """Single point crossover is when a "point" is selected and the genetic - make up of the two parent chromosomes are "Crossed" or better known as swapped""" + """Single point crossover is when a "point" is selected and the genetic + make up of the two parent chromosomes are swapped at that point""" crossover_pool = ga.population.mating_pool + """The structure of GA requires that the crossover method return a population strictly with offspring chromosomes""" new_population = Population() for i in range(len(crossover_pool)): if i + 1 < len(crossover_pool): diff --git a/src/fitness_function/fitness_examples.py b/src/fitness_function/fitness_examples.py index e24015f..ccac36c 100644 --- a/src/fitness_function/fitness_examples.py +++ b/src/fitness_function/fitness_examples.py @@ -16,11 +16,9 @@ class Fitness_Examples: return fitness def index_dependent_values(chromosome): - """A very simple case test function - If the chromosomes gene value is a 5 add one - to the chromosomes overall fitness value.""" - # Overall fitness value + """Test of the GA's ability to improve fitness when the value is index-dependent""" + """If a gene is equal to its index in the chromosome + 1, fitness is incremented""" fitness = 0 - # For each gene in the chromosome for i in range(len(chromosome.gene_list)): if (chromosome.gene_list[i].value == i+1): fitness += 1 diff --git a/src/mutation/mutation_methods.py b/src/mutation/mutation_methods.py index 353954d..7515b4d 100644 --- a/src/mutation/mutation_methods.py +++ b/src/mutation/mutation_methods.py @@ -6,13 +6,16 @@ class Mutation_Methods: pass def random_mutation(ga, chromosome_set = None): - + """Will take the input population and randomly reset entire chromosomes based on the GA's mutation rate""" + + """Defaulting to the GA's current population if no input is explicitly given""" if chromosome_set == None: chromosome_set = ga.population.get_all_chromosomes() chromosome_mutate_num = int(len(chromosome_set)*ga.mutation_rate) temp_population = ga.initialization_impl(ga) + """While more chromosomes need to be mutated, grab a random chromosome and re-initialize it entirely""" while chromosome_mutate_num > 0: chromosome_set[random.randint(0,ga.population_size-1)] = temp_population.get_all_chromosomes()[chromosome_mutate_num] chromosome_mutate_num -= 1 @@ -20,7 +23,8 @@ class Mutation_Methods: return chromosome_set def per_gene_mutation(ga, chromosome_set = None, gene_mutate_count = 1): - + """Will iterate through all chromosomes, and if its selected, will randomly replace one of its genes based on initialization values""" + gene_mutate_count_static = int(gene_mutate_count) if chromosome_set == None: @@ -29,10 +33,12 @@ class Mutation_Methods: for i in range(len(chromosome_set)): random_num = random.uniform(0,1) + """If a chromosome was selected to be mutated""" 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) + """Replaces a random gene in the actual chromosome with a gene from a newly initialized chromosome""" 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) diff --git a/src/parent_selection/parent_selection_methods.py b/src/parent_selection/parent_selection_methods.py index 3df7b76..d331f2f 100644 --- a/src/parent_selection/parent_selection_methods.py +++ b/src/parent_selection/parent_selection_methods.py @@ -7,7 +7,7 @@ from initialization.chromosome_structure.chromosome import Chromosome class Parent_Selection: class Tournament: def with_replacement(ga): - tournament_size = int(len(ga.population.get_all_chromosomes())*ga.parent_ratio*ga.tournament_size_ratio) + tournament_size = int(len(ga.population.get_all_chromosomes())*ga.tournament_size_ratio) if tournament_size < 5: tournament_size = 5 # Probability used for determining if a chromosome should enter the mating pool. diff --git a/src/run_testing.py b/src/run_testing.py index 95252c7..79b6599 100644 --- a/src/run_testing.py +++ b/src/run_testing.py @@ -4,7 +4,7 @@ import random # Create the Genetic algorithm ga = EasyGA.GA() -ga.population_size = 15 +ga.population_size = 100 ga.chromosome_length = 10 ga.generation_goal = 100 ga.gene_impl = [random.randint,1,10] diff --git a/src/survivor_selection/survivor_selection_methods.py b/src/survivor_selection/survivor_selection_methods.py index 076c713..d872862 100644 --- a/src/survivor_selection/survivor_selection_methods.py +++ b/src/survivor_selection/survivor_selection_methods.py @@ -5,32 +5,38 @@ from initialization.population_structure.population import Population from initialization.chromosome_structure.chromosome import Chromosome class Survivor_Selection: - def repeated_crossover(ga, next_population): #Might be cheating? I don't know honestly - RG - while len(next_population.get_all_chromosomes()) < ga.population_size: - crossover_pool = ga.population.mating_pool + """Survivor selection determines which individuals should be brought to the next generation""" - 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 = create_chromosome(new_gene_set) - chromosome_list.append(new_chromosome) - - - for i in range(len(chromosome_list)): - next_population.add_chromosome(chromosome_list[i]) - if len(next_population.get_all_chromosomes()) >= ga.population_size: - break - return next_population - - def remove_two_worst(ga, next_population): - iterator = 0 - while len(next_population.get_all_chromosomes()) < ga.population_size: - next_population.add_chromosome(ga.population.get_all_chromosomes()[iterator]) - iterator += 1 - return next_population \ No newline at end of file + """ Pretty sure this isn't actually survivor selection - seems like its 'cheating' + def repeated_crossover(ga, next_population): + while len(next_population.get_all_chromosomes()) < ga.population_size: + crossover_pool = ga.population.mating_pool + + 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 = create_chromosome(new_gene_set) + chromosome_list.append(new_chromosome) + + + for i in range(len(chromosome_list)): + next_population.add_chromosome(chromosome_list[i]) + if len(next_population.get_all_chromosomes()) >= ga.population_size: + break + return next_population + """ + + """Will bring all but the worst-performing chromosomes from the current generation""" + """The exact number of chromosomes removed depends on how many offspring were generated by parent selection""" + def remove_worst(ga, next_population): + iterator = 0 + while len(next_population.get_all_chromosomes()) < ga.population_size: + next_population.add_chromosome(ga.population.get_all_chromosomes()[iterator]) + iterator += 1 + return next_population \ No newline at end of file