From 1197447d7ecad3ef557dd665acb7f3e0da7fc91c Mon Sep 17 00:00:00 2001 From: SimpleArt <71458112+SimpleArt@users.noreply.github.com> Date: Fri, 27 Nov 2020 19:12:40 -0500 Subject: [PATCH] Added ga.adapt() --- src/EasyGA.py | 79 ++++++++++++++++++++++++++++++++++--- src/attributes.py | 2 + src/structure/population.py | 9 +++-- 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/EasyGA.py b/src/EasyGA.py index 82a84f3..f32bddc 100644 --- a/src/EasyGA.py +++ b/src/EasyGA.py @@ -79,6 +79,8 @@ class GA(Attributes): number_of_generations -= 1 self.current_generation += 1 + self.adapt() + def evolve(self, number_of_generations = 1, consider_termination = True): """Runs the ga until the termination point has been satisfied.""" @@ -93,6 +95,64 @@ class GA(Attributes): return self.termination_impl(self) + def adapt(self): + """Modifies the parent ratio and mutation rates + based on the adapt rate and percent converged. + """ + + # Don't adapt + if self.adapt_rate is None or self.adapt_rate <= 0: + return + + # How much converged + best_fitness = self.population[0].fitness + threshhold_fitness = self.population[round(self.percent_converged*len(self.population)/4)].fitness + + # Closeness required for convergence + tol = 0.01 if self.tolerance_goal is None else self.tolerance_goal + tol *= 1 + abs(best_fitness) + + # Change rates with: + multiplier = 1 + self.adapt_rate + + # Minimum and maximum rates allowed + min_val = 0.05 + max_val = 0.75 + limit = max_val / multiplier + + # Too few converged: cross more and mutate less + if abs(best_fitness - threshhold_fitness) > tol: + + threshhold_fitness = self.population[round(self.percent_converged*len(self.population)/8)].fitness + + # Way too few converged + if abs(best_fitness - threshhold_fitness) > tol: + multiplier **= 2 + limit = max_val / multiplier + + self.parent_ratio = min(max_val, self.parent_ratio*multiplier) + self.selection_probability = min(max_val, self.selection_probability*multiplier) + self.chromosome_mutation_rate = max(min_val, self.chromosome_mutation_rate/multiplier) + self.gene_mutation_rate = max(min_val, self.gene_mutation_rate/multiplier) + + # Too many converged: cross less and mutate more + else: + + threshhold_fitness = self.population[round(self.percent_converged*len(self.population)/2)].fitness + + # Way too many converged + if abs(best_fitness - threshhold_fitness) > tol: + multiplier **= 2 + limit = max_val / multiplier + + self.parent_ratio = max(min_val, self.parent_ratio/multiplier) + self.selection_probability = max(min_val, self.selection_probability/multiplier) + self.chromosome_mutation_rate = min(max_val, self.chromosome_mutation_rate*multiplier) + self.gene_mutation_rate = min(max_val, self.gene_mutation_rate*multiplier) + + + + def initialize_population(self): """Initialize the population using the initialization implimentation @@ -117,18 +177,25 @@ class GA(Attributes): chromosome.fitness = self.fitness_function_impl(chromosome) - def sort_by_best_fitness(self, chromosome_list): + def sort_by_best_fitness(self, chromosome_list, in_place = False): """Sorts the chromosome list by fitness based on fitness type. 1st element has best fitness. 2nd element has second best fitness. etc. """ - return sorted( - chromosome_list, # list to be sorted - key = lambda chromosome: chromosome.fitness, # by fitness - reverse = (self.target_fitness_type == 'max') # ordered by fitness type - ) + if in_place: + return chromosome_list.sort( # list to be sorted + key = lambda chromosome: chromosome.fitness, # by fitness + reverse = (self.target_fitness_type == 'max') # ordered by fitness type + ) + + else: + return sorted( + chromosome_list, # list to be sorted + key = lambda chromosome: chromosome.fitness, # by fitness + reverse = (self.target_fitness_type == 'max') # ordered by fitness type + ) def get_chromosome_fitness(self, index): diff --git a/src/attributes.py b/src/attributes.py index fabcefd..dd62d35 100644 --- a/src/attributes.py +++ b/src/attributes.py @@ -66,6 +66,7 @@ class Attributes: percent_converged = 0.50, chromosome_mutation_rate = 0.15, gene_mutation_rate = 0.05, + adapt_rate = 0.05, initialization_impl = Initialization_Methods.random_initialization, fitness_function_impl = Fitness_Examples.is_it_5, make_population = create_population, @@ -111,6 +112,7 @@ class Attributes: self.fitness_goal = deepcopy(fitness_goal) self.tolerance_goal = deepcopy(tolerance_goal) self.percent_converged = deepcopy(percent_converged) + self.adapt_rate = deepcopy(adapt_rate) # Mutation variables self.chromosome_mutation_rate = deepcopy(chromosome_mutation_rate) diff --git a/src/structure/population.py b/src/structure/population.py index 7948ddf..6ab2155 100644 --- a/src/structure/population.py +++ b/src/structure/population.py @@ -46,13 +46,16 @@ class Population: def append_children(self, chromosome_list): - """Appends a list of chromosomes to the next population""" - self.next_population += chromosome_list + """Appends a list of chromosomes to the next population. + Appends to the front so that chromosomes with fitness + values already will stay sorted. + """ + self.next_population = chromosome_list + self.next_population def sort_by_best_fitness(self, ga): """Sorts the population by fitness""" - self.chromosome_list = ga.sort_by_best_fitness(self.chromosome_list) + ga.sort_by_best_fitness(self.chromosome_list, in_place = True) def add_chromosome(self, chromosome, index = None):