From b6ae77c7eae82fff07a1ad5096696e4cdf4cc3f6 Mon Sep 17 00:00:00 2001 From: SimpleArt <71458112+SimpleArt@users.noreply.github.com> Date: Mon, 12 Oct 2020 21:39:17 -0400 Subject: [PATCH] Several Changes Crossover/Mutation: - Split into individual and population subclasses. - Added sequential population crossover selection. - Renamed and reimplemented mutation methods. EasyGA: - Improved make_obj methods for the chromosomes and populations to take arguments. Initialization: - Improved to shorter code. - Fixed repeated error messages Chromosome: - Changed get/set_genes to get/set_gene_list. --- src/EasyGA.py | 36 +++----- src/crossover/crossover_methods.py | 49 ++++++----- .../chromosome_structure/chromosome.py | 4 +- src/initialization/initialization_methods.py | 43 +++++----- src/mutation/mutation_methods.py | 85 +++++++++++-------- 5 files changed, 114 insertions(+), 103 deletions(-) diff --git a/src/EasyGA.py b/src/EasyGA.py index af45f78..52ea96a 100644 --- a/src/EasyGA.py +++ b/src/EasyGA.py @@ -47,16 +47,21 @@ class GA: self.mutation_rate = 0.10 # Default EasyGA implimentation structure - self.initialization_impl = Initialization_Methods.random_initialization + self.initialization_impl = Initialization_Methods.random_initialization self.fitness_function_impl = Fitness_Examples.index_dependent_values + self.make_population = create_population + self.make_chromosome = create_chromosome + self.make_gene = create_gene # Selects which chromosomes should be automaticly moved to the next population self.survivor_selection_impl = Survivor_Selection.fill_in_best # Methods for accomplishing parent-selection -> Crossover -> 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 + self.parent_selection_impl = Parent_Selection.Tournament.with_replacement + self.crossover_individual_impl = Crossover_Methods.Individual.single_point_crossover + self.crossover_population_impl = Crossover_Methods.Population.random_selection + self.mutation_individual_impl = Mutation_Methods.Individual.single_gene + self.mutation_population_impl = Mutation_Methods.Population.random_selection # The type of termination to impliment self.termination_impl = Termination_Methods.generation_based @@ -72,11 +77,10 @@ class GA: self.population.set_all_chromosomes(self.sort_by_best_fitness(self.population.get_all_chromosomes())) else: self.parent_selection_impl(self) - next_population = self.crossover_impl(self) + next_population = self.crossover_population_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.mutation_population_impl(self) self.set_all_fitness(self.population.chromosome_list) self.population.set_all_chromosomes(self.sort_by_best_fitness(self.population.get_all_chromosomes())) @@ -100,7 +104,8 @@ class GA: def initialize_population(self): """Initialize the population using the initialization - implimentation that is currently set""" + implimentation that is currently set + """ self.population = self.initialization_impl(self) @@ -132,18 +137,3 @@ class GA: chromosome_set = chromosome_set_temp return chromosome_set - - - def make_gene(self,value): - """Let's the user create a gene.""" - return create_gene(value) - - - def make_chromosome(self): - """Let's the user create a chromosome.""" - return create_chromosome() - - - def make_population(self): - """Let's the user create a population.""" - return create_population() diff --git a/src/crossover/crossover_methods.py b/src/crossover/crossover_methods.py index b81442f..d7f4175 100644 --- a/src/crossover/crossover_methods.py +++ b/src/crossover/crossover_methods.py @@ -3,29 +3,34 @@ from initialization.chromosome_structure.chromosome import Chromosome from initialization.population_structure.population import Population class Crossover_Methods: - 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 swapped at that point""" - crossover_pool = ga.population.mating_pool + class Population: + """Methods for selecting chromosomes to crossover""" - """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): - 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) - 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) + def sequential_selection(ga): + """Select sequential pairs from the mating pool""" - return new_population + mating_pool = ga.population.mating_pool + return Population([ga.crossover_individual_impl(mating_pool[index], mating_pool[index+1]) for index in range(len(mating_pool)-1)]) - 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 + + def random_selection(ga): + """Select random pairs from the mating pool""" + + mating_pool = ga.population.mating_pool + return Population([ga.crossover_individual_impl(random.choice(mating_pool), random.choice(mating_pool)) for n in mating_pool]) + + + class Individual: + """Methods for crossing parents""" + + def single_point_crossover(parent_one, parent_two): + """Cross two parents by swapping genes at one random point""" + + index = random.randint(0, parent_one.size()-1) + return Chromosome(parent_one.get_gene_list()[:index] + parent_two.get_gene_list()[index:]) + + + def multi_point_crossover(parent_one, parent_two): + """Cross two parents by swapping genes at multiple points""" + pass diff --git a/src/initialization/chromosome_structure/chromosome.py b/src/initialization/chromosome_structure/chromosome.py index 2f20935..2b36311 100644 --- a/src/initialization/chromosome_structure/chromosome.py +++ b/src/initialization/chromosome_structure/chromosome.py @@ -27,7 +27,7 @@ class Chromosome: del self.gene_list[index] - def get_genes(self): + def get_gene_list(self): return self.gene_list @@ -40,7 +40,7 @@ class Chromosome: self.gene_list[index] = gene - def set_genes(self, genes): + def set_gene_list(self, genes): self.gene_list = genes diff --git a/src/initialization/initialization_methods.py b/src/initialization/initialization_methods.py index 3cf3e35..6b73a40 100644 --- a/src/initialization/initialization_methods.py +++ b/src/initialization/initialization_methods.py @@ -7,27 +7,26 @@ 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.""" + """Takes the initialization inputs and returns a population with the given parameters.""" - # Create the population object - population = create_population() + # Using the chromosome_impl to set every index inside of the chromosome + if ga.chromosome_impl != None: + return create_population([ + create_chromosome([ + create_gene(ga.chromosome_impl(j)) + for j in range(ga.chromosome_length)]) + for i in range(ga.population_size)]) - # 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: - function = ga.gene_impl[0] - chromosome.add_gene(create_gene(function(*ga.gene_impl[1:]))) - else: - #Exit because either were not specified - print("You did not specify any initialization constraints.") - break - population.add_chromosome(chromosome) - return population + # Using the gene_impl to set every gene to be the same + elif ga.gene_impl != None: + function = ga.gene_impl[0] + return create_population([ + create_chromosome([ + create_gene(function(*ga.gene_impl[1:])) + for j in range(ga.chromosome_length)]) + for i in range(ga.population_size)]) + + # Exit because no gene creation method specified + else: + print("You did not specify any initialization constraints.") + return None diff --git a/src/mutation/mutation_methods.py b/src/mutation/mutation_methods.py index e1e3166..4bfe9e9 100644 --- a/src/mutation/mutation_methods.py +++ b/src/mutation/mutation_methods.py @@ -1,46 +1,63 @@ import random class Mutation_Methods: - - def __init__(self): - 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""" + class Population: + """Methods for selecting chromosomes to mutate""" - """Defaulting to the GA's current population if no input is explicitly given""" - if chromosome_set == None: - chromosome_set = ga.population.get_all_chromosomes() + def random_selection(ga): + """Selects random chromosomes""" - chromosome_mutate_num = int(len(chromosome_set)*ga.mutation_rate) - temp_population = ga.initialization_impl(ga) + # Loop through the population + for index in range(ga.population.size()): - """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 - - 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""" + # Randomly apply mutations + if random.uniform(0, 1) < ga.mutation_rate: + ga.population.set_chromosome(ga.mutation_individual_impl(ga, ga.population.get_all_chromosomes()[index]), index) - gene_mutate_count_static = int(gene_mutate_count) - if chromosome_set == None: - chromosome_set = ga.population.get_all_chromosomes() + class Individual: + """Methods for mutating a single chromosome""" - for i in range(len(chromosome_set)): - random_num = random.uniform(0,1) + def whole_chromosome(ga, chromosome): + """Makes a completely random chromosome""" - """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) + # Using the chromosome_impl to set every index inside of the chromosome + if ga.chromosome_impl != None: + return ga.make_chromosome([ + ga.make_gene(ga.chromosome_impl(j)) + for j in range(chromosome.size())]) - return chromosome_set + # Using the gene_impl + elif ga.gene_impl != None: + function = ga.gene_impl[0] + return ga.make_chromosome([ + ga.make_gene(function(*ga.gene_impl[1:])) + for j in range(chromosome.size())]) + + # Exit because no gene creation method specified + else: + print("You did not specify any initialization constraints.") + return None + + + def single_gene(ga, chromosome): + """Makes a completely random chromosome""" + chromosome.set_fitness(None) + + # Using the chromosome_impl + if ga.chromosome_impl != None: + index = random.randint(0, chromosome.size()-1) + chromosome.set_gene(ga.make_gene(ga.chromosome_impl(index)), index) + + # Using the gene_impl + elif ga.gene_impl != None: + function = ga.gene_impl[0] + index = random.randint(0, chromosome.size()-1) + chromosome.set_gene(ga.make_gene(function(*ga.gene_impl[1:])), index) + + # Exit because no gene creation method specified + else: + print("You did not specify any initialization constraints.") + + return chromosome