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:
@ -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)
|
||||||
|
|||||||
@ -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)):
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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()
|
||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user