Optimizations/updates

1. Deleted duplicate functions in EasyGA
2. Added new index-dependent fitness example
3. GA now auto-sorts by best fitness immediately after the fitness is calculated across the board
4. Removed 'selected' status flag from the Chromosome flag
5. Added mating_pool attribute to the population
6. Changed other code to be in line with 4 and 5
7. Optimized tournament selection method
This commit is contained in:
RyleyGG
2020-10-06 17:55:17 -04:00
parent 3bfa962194
commit e7ac0e23f4
7 changed files with 61 additions and 104 deletions

View File

@ -18,17 +18,20 @@ class GA:
"""Initialize the GA.""" """Initialize the GA."""
# Initilization variables # Initilization variables
self.chromosome_length = 10 self.chromosome_length = 10
self.population_size = 100 self.population_size = 150
self.chromosome_impl = None self.chromosome_impl = None
self.gene_impl = None self.gene_impl = None
self.population = None self.population = None
self.target_fitness_type = 'maximum'
self.parent_ratio = 0.1
# Termination varibles # Termination varibles
self.current_generation = 0 self.current_generation = 0
self.generation_goal = 50 self.generation_goal = 50
self.current_fitness = 0 self.current_fitness = 0
self.generation_goal = 100 self.generation_goal = 250
self.fitness_goal = 3 self.fitness_goal = 9
# Mutation variables # Mutation variables
self.mutation_rate = 0.10 self.mutation_rate = 0.10
@ -37,7 +40,7 @@ class GA:
# Defualt EastGA implimentation structure # Defualt EastGA implimentation structure
self.initialization_impl = Initialization_Methods().random_initialization self.initialization_impl = Initialization_Methods().random_initialization
self.fitness_function_impl = Fitness_Examples().is_it_5 self.fitness_function_impl = Fitness_Examples().index_dependent_values
# Selects which chromosomes should be automaticly moved to the next population # 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 = Selection_Methods().Survivor_Selection().remove_two_worst
# Methods for accomplishing parent-selection -> Crossover -> Mutation # Methods for accomplishing parent-selection -> Crossover -> Mutation
@ -54,6 +57,7 @@ class GA:
if self.current_generation == 0: if self.current_generation == 0:
self.initialize_population() self.initialize_population()
self.set_all_fitness(self.population.chromosome_list) self.set_all_fitness(self.population.chromosome_list)
self.population.set_all_chromosomes(self.sort_by_best_fitness())
self.parent_selection_impl(self) self.parent_selection_impl(self)
next_population = self.crossover_impl(self) next_population = self.crossover_impl(self)
@ -62,6 +66,7 @@ class GA:
self.population = next_population self.population = next_population
self.set_all_fitness(self.population.chromosome_list) self.set_all_fitness(self.population.chromosome_list)
self.population.set_all_chromosomes(self.sort_by_best_fitness())
number_of_generations -= 1 number_of_generations -= 1
self.current_generation += 1 self.current_generation += 1
@ -94,34 +99,26 @@ class GA:
# Set the chromosomes fitness using the fitness function # Set the chromosomes fitness using the fitness function
chromosome.set_fitness(self.fitness_function_impl(chromosome)) chromosome.set_fitness(self.fitness_function_impl(chromosome))
def evolve(self): def sort_by_best_fitness(self, chromosome_set = None):
"""Runs the ga until the termination point has been satisfied."""
# While the termination point hasnt been reached keep running
while(self.active()):
self.evolve_generation()
def evolve_generation(self, number_of_generations = 1, consider_termination = True): if chromosome_set == None:
"""Evolves the ga the specified number of generations.""" chromosome_set = self.population.get_all_chromosomes()
while(number_of_generations > 0 and (consider_termination == False or self.termination_impl(self))):
# If its the first generation then initialize the population
if self.current_generation == 0:
self.initialize_population()
self.set_all_fitness(self.population.chromosome_list)
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 chromosome_set_temp = chromosome_set
self.set_all_fitness(self.population.chromosome_list) not_sorted_check = 0
while (not_sorted_check != len(chromosome_set_temp)):
not_sorted_check = 0
for i in range(len(chromosome_set_temp)):
if ((i + 1 < len(chromosome_set_temp)) and (chromosome_set_temp[i + 1].fitness > chromosome_set_temp[i].fitness)):
temp = chromosome_set[i]
chromosome_set_temp[i] = chromosome_set[i + 1]
chromosome_set_temp[i + 1] = temp
else:
not_sorted_check += 1
number_of_generations -= 1 chromosome_set = chromosome_set_temp
self.current_generation += 1
def active(self): return chromosome_set
"""Returns if the ga should terminate base on the termination implimented"""
return self.termination_impl(self)
def make_gene(self,value): def make_gene(self,value):
return create_gene(value) return create_gene(value)

View File

@ -14,10 +14,7 @@ class Crossover_Methods:
"""Single point crossover is when a "point" is selected and the genetic """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""" make up of the two parent chromosomes are "Crossed" or better known as swapped"""
crossover_pool = [] crossover_pool = ga.population.mating_pool
for i in range(ga.population_size):
if ga.population.get_all_chromosomes()[i].selected:
crossover_pool.append(ga.population.get_all_chromosomes()[i])
new_population = Population() new_population = Population()
for i in range(len(crossover_pool)): for i in range(len(crossover_pool)):

View File

@ -14,3 +14,15 @@ class Fitness_Examples:
fitness += 1 fitness += 1
return fitness return fitness
def index_dependent_values(self, 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
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
return fitness

View File

@ -6,7 +6,6 @@ class Chromosome:
else: else:
self.gene_list = genes self.gene_list = genes
self.fitness = None self.fitness = None
self.selected = False
def add_gene(self, gene, index = -1): def add_gene(self, gene, index = -1):
if index == -1: if index == -1:

View File

@ -7,6 +7,7 @@ class Population:
else: else:
self.chromosome_list = chromosomes self.chromosome_list = chromosomes
self.fitness = None self.fitness = None
self.mating_pool = []
def get_closet_fitness(self,value): def get_closet_fitness(self,value):
# Get the chomosome that has the closets fitness to the value defined # Get the chomosome that has the closets fitness to the value defined

View File

@ -13,4 +13,4 @@ ga.gene_impl = [random.randrange,1,100]
ga.evolve() ga.evolve()
# Print the current population # Print the current population
ga.population.print_all() ga.population.print_all()

View File

@ -12,68 +12,33 @@ class Selection_Methods:
class Parent_Selection: class Parent_Selection:
class Tournament: class Tournament:
def with_replacement(self, ga): def with_replacement(self, ga):
tournament_size = int(len(ga.population.get_all_chromosomes())/10) #currently hard-coded for purposes of the example. tournament_size = int(len(ga.population.get_all_chromosomes())*ga.parent_ratio/10)
if tournament_size < 3: if tournament_size < 3:
tournament_size = int(len(ga.population.get_all_chromosomes())/3) tournament_size = int(len(ga.population.get_all_chromosomes())*ga.parent_ratio/3)
parent_ratio = 0.25
# Probability used for determining if a chromosome should enter the mating pool.
#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 selection_probability = 0.95
total_selected = 0 #Total Chromosomes selected
# Repeat tournaments until the mating pool is large enough.
while (total_selected < parent_ratio*ga.population_size): while (len(ga.population.mating_pool) < len(ga.population.get_all_chromosomes())*ga.parent_ratio):
#create & gather tournament group
tournament_group = []
for i in range(tournament_size):
tournament_group.append(random.choice(ga.population.get_all_chromosomes()))
#Sort the tournament contenders based on their fitness # Generate a random tournament group and sort by fitness.
#currently hard-coded to only consider higher fitness = better; can be changed once this impl is agreed on tournament_group = ga.sort_by_best_fitness([random.choice(ga.population.get_all_chromosomes()) for n in range(tournament_size)])
#also currently uses bubble sort because its easy
tournament_group_temp = tournament_group # For each chromosome, add it to the mating pool based on its rank in the tournament.
not_sorted_check = 0 for index in range(tournament_size):
while (not_sorted_check != len(tournament_group_temp)): # Probability required is selection_probability * (1-selection_probability) ^ (tournament_size-index+1)
not_sorted_check = 0 # e.g. top ranked fitness has probability: selection_probability
for i in range(len(tournament_group_temp)): # second ranked fitness has probability: selection_probability * (1-selection_probability)
if ((i + 1 < len(tournament_group_temp)) and (tournament_group_temp[i + 1].fitness > tournament_group_temp[i].fitness)): # third ranked fitness has probability: selection_probability * (1-selection_probability)^2
temp = tournament_group[i] # etc.
tournament_group_temp[i] = tournament_group[i + 1] if random.uniform(0, 1) < selection_probability * pow(1-selection_probability, index+1):
tournament_group_temp[i + 1] = temp ga.population.mating_pool.append(tournament_group[index])
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
class Survivor_Selection: class Survivor_Selection:
def repeated_crossover(self, ga, next_population): #Might be cheating? I don't know honestly - RG 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: while len(next_population.get_all_chromosomes()) < ga.population_size:
crossover_pool = [] crossover_pool = ga.population.mating_pool
for i in range(ga.population_size):
if ga.population.get_all_chromosomes()[i].selected:
crossover_pool.append(ga.population.get_all_chromosomes()[i])
split_point = random.randint(0,ga.chromosome_length) split_point = random.randint(0,ga.chromosome_length)
chromosome_list = [] chromosome_list = []
@ -95,23 +60,9 @@ class Selection_Methods:
return next_population return next_population
def remove_two_worst(self, ga, next_population): def remove_two_worst(self, ga, next_population):
#Bubble sorting by highest fitness
temp_population = ga.population
not_sorted_check = 0
while (not_sorted_check != len(temp_population.get_all_chromosomes())):
not_sorted_check = 0
for i in range(len(temp_population.get_all_chromosomes())):
if ((i + 1 < len(temp_population.get_all_chromosomes())) and (temp_population.get_all_chromosomes()[i + 1].fitness > temp_population.get_all_chromosomes()[i].fitness)):
temp = temp_population.get_all_chromosomes()[i]
temp_population.get_all_chromosomes()[i] = ga.population.get_all_chromosomes()[i + 1]
temp_population.get_all_chromosomes()[i + 1] = temp
else:
not_sorted_check += 1
iterator = 0 iterator = 0
while len(next_population.get_all_chromosomes()) < ga.population_size: while len(next_population.get_all_chromosomes()) < ga.population_size:
next_population.add_chromosome(temp_population.get_all_chromosomes()[iterator]) next_population.add_chromosome(ga.population.get_all_chromosomes()[iterator])
iterator += 1 iterator += 1
return next_population return next_population