From 3649293133e1a99fa76a6ce193a4605044fcb5d1 Mon Sep 17 00:00:00 2001 From: RyleyGG Date: Tue, 6 Oct 2020 22:11:40 -0400 Subject: [PATCH] Updated GA attribute structure, separated selection file structure Updated GA attribute structure, separated selection file structure --- src/EasyGA.py | 19 ++-- src/crossover/crossover_methods.py | 4 +- src/fitness_function/fitness_examples.py | 4 +- src/initialization/initialization_methods.py | 2 +- src/initialization/methods.py | 33 ------- src/mutation/mutation_methods.py | 4 +- src/parent_selection/__init__.py | 2 +- src/parent_selection/methods.py | 37 -------- src/parent_selection/parent_selection.py | 59 ++++++++++++ src/selection/__init__.py | 2 - src/selection/selection_methods.py | 95 -------------------- src/selection/test_examples.py | 0 src/survivor_selection/__init__.py | 2 +- src/survivor_selection/methods.py | 8 -- src/survivor_selection/survivor_selection.py | 36 ++++++++ src/termination_point/termination_methods.py | 4 +- 16 files changed, 116 insertions(+), 195 deletions(-) delete mode 100644 src/initialization/methods.py delete mode 100644 src/parent_selection/methods.py create mode 100644 src/parent_selection/parent_selection.py delete mode 100644 src/selection/__init__.py delete mode 100644 src/selection/selection_methods.py delete mode 100644 src/selection/test_examples.py delete mode 100644 src/survivor_selection/methods.py create mode 100644 src/survivor_selection/survivor_selection.py diff --git a/src/EasyGA.py b/src/EasyGA.py index c7bfb9f..da37e8e 100644 --- a/src/EasyGA.py +++ b/src/EasyGA.py @@ -8,7 +8,8 @@ from fitness_function import Fitness_Examples from initialization import Initialization_Methods from termination_point import Termination_Methods # Population Methods -from selection import Selection_Methods +from survivor_selection import Survivor_Selection +from parent_selection import Parent_Selection # Manipulation Methods from mutation import Mutation_Methods from crossover import Crossover_Methods @@ -27,7 +28,7 @@ class GA: # Selection variables self.parent_ratio = 0.1 - self.selection_probablity = 0.95 + self.selection_probability = 0.95 # Termination variables self.current_generation = 0 @@ -41,16 +42,16 @@ class GA: self.mutation_rate = 0.10 # Default EasyGA implimentation structure - self.initialization_impl = Initialization_Methods().random_initialization - self.fitness_function_impl = Fitness_Examples().index_dependent_values + 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 = Selection_Methods().Survivor_Selection().remove_two_worst + self.survivor_selection_impl = Survivor_Selection.remove_two_worst # Methods for accomplishing parent-selection -> Crossover -> Mutation - self.parent_selection_impl = Selection_Methods().Parent_Selection().Roulette().roulette_selection - self.crossover_impl = Crossover_Methods().single_point_crossover - self.mutation_impl = Mutation_Methods().per_gene_mutation + self.parent_selection_impl = Parent_Selection.Tournament.with_replacement + self.crossover_impl = Crossover_Methods.single_point_crossover + self.mutation_impl = Mutation_Methods.per_gene_mutation # The type of termination to impliment - self.termination_impl = Termination_Methods().generation_based + self.termination_impl = Termination_Methods.generation_based def evolve_generation(self, number_of_generations = 1, consider_termination = True): diff --git a/src/crossover/crossover_methods.py b/src/crossover/crossover_methods.py index 48e55bf..c10c814 100644 --- a/src/crossover/crossover_methods.py +++ b/src/crossover/crossover_methods.py @@ -10,7 +10,7 @@ class Crossover_Methods: def __init__(self): pass - def single_point_crossover(self, ga): + 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""" @@ -31,7 +31,7 @@ class Crossover_Methods: return new_population - def multi_point_crossover(self, ga,number_of_points = 2): + def multi_point_crossover(ga, number_of_points = 2): """Multi point crossover is when a specific number (More then one) of "points" are created to merge the genetic makup of the chromosomes.""" pass diff --git a/src/fitness_function/fitness_examples.py b/src/fitness_function/fitness_examples.py index 1354e9e..e24015f 100644 --- a/src/fitness_function/fitness_examples.py +++ b/src/fitness_function/fitness_examples.py @@ -1,6 +1,6 @@ class Fitness_Examples: """Fitness function examples used""" - def is_it_5(self, chromosome): + def is_it_5(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 @@ -15,7 +15,7 @@ class Fitness_Examples: return fitness - def index_dependent_values(self, chromosome): + 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 diff --git a/src/initialization/initialization_methods.py b/src/initialization/initialization_methods.py index 5ede298..05768b6 100644 --- a/src/initialization/initialization_methods.py +++ b/src/initialization/initialization_methods.py @@ -6,7 +6,7 @@ from .gene_structure.gene import Gene as create_gene class Initialization_Methods: """Initialization examples that are used as defaults and examples""" - def random_initialization(self, ga): + def random_initialization(ga): """Takes the initialization inputs and choregraphs them to output the type of population with the given parameters.""" # Create the population object diff --git a/src/initialization/methods.py b/src/initialization/methods.py deleted file mode 100644 index 312580c..0000000 --- a/src/initialization/methods.py +++ /dev/null @@ -1,33 +0,0 @@ -# Import the data structure -from .population_structure.population import Population as create_population -from .chromosome_structure.chromosome import Chromosome as create_chromosome -from .gene_structure.gene import Gene as create_gene - -class Initialization_methods: - """Initialization examples that are used as defaults and examples""" - - def random_initialization(ga): - """Takes the initialization inputs and choregraphs them to output the type of population - with the given parameters.""" - # Create the population object - population = create_population() - - # Fill the population with chromosomes - for i in range(ga.population_size): - chromosome = create_chromosome() - #Fill the Chromosome with genes - for j in range(ga.chromosome_length): - # Using the chromosome_impl to set every index inside of the chromosome - if ga.chromosome_impl != None: - # Each chromosome location is specified with its own function - chromosome.add_gene(create_gene(ga.chromosome_impl(j))) - # Will break if chromosome_length != len(lists) in domain - elif ga.gene_impl != None: - # gene_impl = [range function,lowerbound,upperbound] - function = ga.gene_impl[0] - chromosome.add_gene(create_gene(function(*ga.gene_impl[1:]))) - else: - #Exit because either were not specified - print("Your domain or range were not specified") - population.add_chromosome(chromosome) - return population diff --git a/src/mutation/mutation_methods.py b/src/mutation/mutation_methods.py index 99181ed..353954d 100644 --- a/src/mutation/mutation_methods.py +++ b/src/mutation/mutation_methods.py @@ -5,7 +5,7 @@ class Mutation_Methods: def __init__(self): pass - def random_mutation(self, ga, chromosome_set = None): + def random_mutation(ga, chromosome_set = None): if chromosome_set == None: chromosome_set = ga.population.get_all_chromosomes() @@ -19,7 +19,7 @@ class Mutation_Methods: return chromosome_set - def per_gene_mutation(self, ga, chromosome_set = None, gene_mutate_count = 1): + def per_gene_mutation(ga, chromosome_set = None, gene_mutate_count = 1): gene_mutate_count_static = int(gene_mutate_count) diff --git a/src/parent_selection/__init__.py b/src/parent_selection/__init__.py index 8ecd200..24864cf 100644 --- a/src/parent_selection/__init__.py +++ b/src/parent_selection/__init__.py @@ -1,2 +1,2 @@ # FROM (. means local) file_name IMPORT function_name -from .methods import Parent_methods +from .parent_selection import Parent_Selection diff --git a/src/parent_selection/methods.py b/src/parent_selection/methods.py deleted file mode 100644 index b97811c..0000000 --- a/src/parent_selection/methods.py +++ /dev/null @@ -1,37 +0,0 @@ -class Parent_methods: - """Selection defintion here""" - - def tournament_selection(ga,matchs): - """Tournament selection involves running several "tournaments" among a - few individuals (or "chromosomes")chosen at random from the population. - The winner of each tournament (the one with the best fitness) is selected - for crossover. - Ex - Chromsome 1----1 wins ------ - Chromsome 2---- - --1 wins---- - - - - Chromsome 3----3 wins ------ -- 5 Wins --->Chromosome 5 becomes Parent - Chromsome 4---- - - - - Chromsome 5----5 wins ---------5 wins---- - Chromsome 6---- - ^--Matchs--^ - """ - - def small_tournament(ga): - """ Small tournament is only one round of tournament. Beat the other - randomly selected chromosome and your are selected as a parent. - Chromosome 1---- - -- 1 wins -> Becomes selected for crossover. - Chromosome 2---- - """ - pass - - def roulette_selection(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 - that it will be selected. Using the example of a casino roulette wheel. - Where the chromosomes are the numbers to be selected and the board size for - those numbers are directly proportional to the chromosome's current fitness. Where - the ball falls is a randomly generated number between 0 and 1""" - pass diff --git a/src/parent_selection/parent_selection.py b/src/parent_selection/parent_selection.py new file mode 100644 index 0000000..8f010d6 --- /dev/null +++ b/src/parent_selection/parent_selection.py @@ -0,0 +1,59 @@ +import random +from initialization.chromosome_structure.chromosome import Chromosome as create_chromosome +from initialization.gene_structure.gene import Gene as create_gene +from initialization.population_structure.population import Population +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/10) + if tournament_size < 3: + tournament_size = int(len(ga.population.get_all_chromosomes())*ga.parent_ratio/3) + + # Probability used for determining if a chromosome should enter the mating pool. + selection_probability = ga.selection_probability + + # Repeat tournaments until the mating pool is large enough. + while (len(ga.population.mating_pool) < len(ga.population.get_all_chromosomes())*ga.parent_ratio): + + # Generate a random tournament group and sort by fitness. + tournament_group = ga.sort_by_best_fitness([random.choice(ga.population.get_all_chromosomes()) for n in range(tournament_size)]) + + # For each chromosome, add it to the mating pool based on its rank in the tournament. + for index in range(tournament_size): + # Probability required is selection_probability * (1-selection_probability) ^ (tournament_size-index+1) + # e.g. top ranked fitness has probability: selection_probability + # second ranked fitness has probability: selection_probability * (1-selection_probability) + # third ranked fitness has probability: selection_probability * (1-selection_probability)^2 + # etc. + if random.uniform(0, 1) < selection_probability * pow(1-selection_probability, index+1): + ga.population.mating_pool.append(tournament_group[index]) + + class Roulette: + def roulette_selection(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 + that it will be selected. Using the example of a casino roulette wheel. + Where the chromosomes are the numbers to be selected and the board size for + those numbers are directly proportional to the chromosome's current fitness. Where + the ball falls is a randomly generated number between 0 and 1""" + total_fitness = sum(ga.population.chromosome_list[i].get_fitness() for i in range(len(ga.population.chromosome_list))) + rel_fitnesses = [] + + for chromosome in ga.population.chromosome_list: + if (total_fitness != 0): + rel_fitnesses.append(float(chromosome.fitness)/total_fitness) + + probability = [sum(rel_fitnesses[:i+1]) for i in range(len(rel_fitnesses))] + + while (len(ga.population.mating_pool) < len(ga.population.get_all_chromosomes())*ga.parent_ratio): + rand_number = random.random() + + # Loop through the list of probabilities + for i in range(len(probability)): + # If the probability is greater than the random_number, then select that chromosome + if (probability[i] >= rand_number): + ga.population.mating_pool.append(ga.population.chromosome_list[i]) + # print (f'Selected chromosome : {i}') + break \ No newline at end of file diff --git a/src/selection/__init__.py b/src/selection/__init__.py deleted file mode 100644 index 9c90847..0000000 --- a/src/selection/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# FROM (. means local) file_name IMPORT function_name -from .selection_methods import Selection_Methods diff --git a/src/selection/selection_methods.py b/src/selection/selection_methods.py deleted file mode 100644 index c8476a6..0000000 --- a/src/selection/selection_methods.py +++ /dev/null @@ -1,95 +0,0 @@ -import random -from initialization.chromosome_structure.chromosome import Chromosome as create_chromosome -from initialization.gene_structure.gene import Gene as create_gene -from initialization.population_structure.population import Population -from initialization.chromosome_structure.chromosome import Chromosome - -class Selection_Methods: - """Selection is the process by which chromosomes are selected for crossover and eventually, influence the next generation of chromosomes.""" - def __init__(self): - pass - - class Parent_Selection: - class Tournament: - def with_replacement(self, ga): - tournament_size = int(len(ga.population.get_all_chromosomes())*ga.parent_ratio/10) - if tournament_size < 3: - tournament_size = int(len(ga.population.get_all_chromosomes())*ga.parent_ratio/3) - - # Probability used for determining if a chromosome should enter the mating pool. - selection_probability = ga.selection_probability - - # Repeat tournaments until the mating pool is large enough. - while (len(ga.population.mating_pool) < len(ga.population.get_all_chromosomes())*ga.parent_ratio): - - # Generate a random tournament group and sort by fitness. - tournament_group = ga.sort_by_best_fitness([random.choice(ga.population.get_all_chromosomes()) for n in range(tournament_size)]) - - # For each chromosome, add it to the mating pool based on its rank in the tournament. - for index in range(tournament_size): - # Probability required is selection_probability * (1-selection_probability) ^ (tournament_size-index+1) - # e.g. top ranked fitness has probability: selection_probability - # second ranked fitness has probability: selection_probability * (1-selection_probability) - # third ranked fitness has probability: selection_probability * (1-selection_probability)^2 - # etc. - if random.uniform(0, 1) < selection_probability * pow(1-selection_probability, index+1): - ga.population.mating_pool.append(tournament_group[index]) - - class Roulette: - 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 - that it will be selected. Using the example of a casino roulette wheel. - Where the chromosomes are the numbers to be selected and the board size for - those numbers are directly proportional to the chromosome's current fitness. Where - the ball falls is a randomly generated number between 0 and 1""" - total_fitness = sum(ga.population.chromosome_list[i].get_fitness() for i in range(len(ga.population.chromosome_list))) - rel_fitnesses = [] - - for chromosome in ga.population.chromosome_list: - if (total_fitness != 0): - rel_fitnesses.append(float(chromosome.fitness)/total_fitness) - - probability = [sum(rel_fitnesses[:i+1]) for i in range(len(rel_fitnesses))] - - while (len(ga.population.mating_pool) < len(ga.population.get_all_chromosomes())*ga.parent_ratio): - rand_number = random.random() - - # Loop through the list of probabilities - for i in range(len(probability)): - # If the probability is greater than the random_number, then select that chromosome - if (probability[i] >= rand_number): - ga.population.mating_pool.append(ga.population.chromosome_list[i]) - # print (f'Selected chromosome : {i}') - break - - class Survivor_Selection: - def repeated_crossover(self, 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 - - 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(self, 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 diff --git a/src/selection/test_examples.py b/src/selection/test_examples.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/survivor_selection/__init__.py b/src/survivor_selection/__init__.py index ea2e686..9814ab0 100644 --- a/src/survivor_selection/__init__.py +++ b/src/survivor_selection/__init__.py @@ -1,2 +1,2 @@ # FROM (. means local) file_name IMPORT function_name -from .methods import Survivor_methods +from .survivor_selection import Survivor_Selection diff --git a/src/survivor_selection/methods.py b/src/survivor_selection/methods.py deleted file mode 100644 index ad287b8..0000000 --- a/src/survivor_selection/methods.py +++ /dev/null @@ -1,8 +0,0 @@ -class Survivor_methods: - """Survivor methods defintion here""" - - def elitism(): - pass - - def remove_two_worst(): - pass diff --git a/src/survivor_selection/survivor_selection.py b/src/survivor_selection/survivor_selection.py new file mode 100644 index 0000000..076c713 --- /dev/null +++ b/src/survivor_selection/survivor_selection.py @@ -0,0 +1,36 @@ +import random +from initialization.chromosome_structure.chromosome import Chromosome as create_chromosome +from initialization.gene_structure.gene import Gene as create_gene +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 + + 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 diff --git a/src/termination_point/termination_methods.py b/src/termination_point/termination_methods.py index 493f02d..8b3cd73 100644 --- a/src/termination_point/termination_methods.py +++ b/src/termination_point/termination_methods.py @@ -1,7 +1,7 @@ class Termination_Methods: """Example functions that can be used to terminate the the algorithms loop""" - def fitness_based(self, ga): + def fitness_based(ga): """Fitness based approach to terminate when the goal fitness has been reached""" status = True @@ -13,7 +13,7 @@ class Termination_Methods: break return status - def generation_based(self, ga): + def generation_based(ga): """Generation based approach to terminate when the goal generation has been reached""" status = True if(ga.current_generation > ga.generation_goal):