Changed implementation framework

Instead of a nested approach, selection/crossover/mutation are all called separately and directly by the GA. selection_impl was also separated into parent_selection_impl and survivor_selection_impl, as both are needed separately.
This commit is contained in:
RyleyGG
2020-10-04 17:59:59 -04:00
parent c18a531034
commit e05aa7f62b
5 changed files with 102 additions and 120 deletions

View File

@ -16,17 +16,17 @@ 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
# Termination varibles # Termination varibles
self.current_generation = 0 self.current_generation = 0
self.current_fitness = 0 self.current_fitness = 0
self.generation_goal = 50 self.generation_goal = 100
self.fitness_goal = 3 self.fitness_goal = 3
# Mutation variables # Mutation variables
self.mutation_rate = 0.05 self.mutation_rate = 0.10
# Rerun already computed fitness # Rerun already computed fitness
self.update_fitness = True self.update_fitness = True
@ -34,8 +34,9 @@ class GA:
# Defualt EastGA implimentation structure # Defualt EastGA implimentation structure
self.initialization_impl = Initialization_Types().random_initialization self.initialization_impl = Initialization_Types().random_initialization
self.fitness_function_impl = Fitness_Examples().is_it_5 self.fitness_function_impl = Fitness_Examples().is_it_5
self.mutation_impl = Mutation_Types().random_mutation self.mutation_impl = Mutation_Types().per_gene_mutation
self.selection_impl = Selection_Types().Tournament().with_replacement self.parent_selection_impl = Selection_Types().Parent_Selection().Tournament().with_replacement
self.survivor_selection_impl = Selection_Types().Survivor_Selection().repeated_crossover
self.crossover_impl = Crossover_Types().single_point_crossover self.crossover_impl = Crossover_Types().single_point_crossover
self.termination_impl = Termination_Types().generation_based self.termination_impl = Termination_Types().generation_based
@ -70,7 +71,11 @@ class GA:
self.initialize_population() self.initialize_population()
self.set_all_fitness(self.population.chromosomes) self.set_all_fitness(self.population.chromosomes)
next_population = self.selection_impl(self) 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 self.population = next_population
self.set_all_fitness(self.population.chromosomes) self.set_all_fitness(self.population.chromosomes)

View File

@ -30,8 +30,10 @@ class Population:
def set_all_chromosomes(self, chromosomes): def set_all_chromosomes(self, chromosomes):
self.chromosomes = chromosomes self.chromosomes = chromosomes
def set_chromosome(self, chromosomes, index): def set_chromosome(self, chromosome, index = -1):
self.chromosome[index] = chromosome if index == -1:
index = len(self.chromosomes)-1
self.chromosomes[index] = chromosome
def set_fitness(self, fitness): def set_fitness(self, fitness):
self.fitness = fitness self.fitness = fitness

View File

@ -8,7 +8,7 @@ class Mutation_Types:
def random_mutation(self, ga, chromosome_set = None): def random_mutation(self, ga, chromosome_set = None):
if chromosome_set == None: if chromosome_set == None:
chromosome_set = ga.population chromosome_set = ga.population.get_all_chromosomes()
chromosome_mutate_num = int(len(chromosome_set)*ga.mutation_rate) chromosome_mutate_num = int(len(chromosome_set)*ga.mutation_rate)
temp_population = ga.initialization_impl(ga) temp_population = ga.initialization_impl(ga)
@ -18,4 +18,25 @@ class Mutation_Types:
chromosome_mutate_num -= 1 chromosome_mutate_num -= 1
return chromosome_set return chromosome_set
def per_gene_mutation(self, ga, chromosome_set = None, gene_mutate_count = 1):
gene_mutate_count_static = int(gene_mutate_count)
if chromosome_set == None:
chromosome_set = ga.population.get_all_chromosomes()
for i in range(len(chromosome_set)):
random_num = random.uniform(0,1)
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)
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)
return chromosome_set

View File

@ -5,7 +5,10 @@ import random
# Create the Genetic algorithm # Create the Genetic algorithm
ga = EasyGA.GA() ga = EasyGA.GA()
ga.gene_impl = [random.randrange,1,25] #def random_parent_selection(population):
#while ()
ga.gene_impl = [random.randrange,1,100]
# Run Everything # Run Everything
ga.evolve() ga.evolve()

View File

@ -9,113 +9,67 @@ class Selection_Types:
def __init__(self): def __init__(self):
pass pass
class Tournament: class Parent_Selection:
def with_replacement(self, ga): class Tournament:
tournament_size = int(len(ga.population.get_all_chromosomes())/10) #currently hard-coded for purposes of the example. def with_replacement(self, ga):
if tournament_size < 3: 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())/3) if tournament_size < 3:
tournament_size = int(len(ga.population.get_all_chromosomes())/3)
parent_ratio = 0.25
#selection_probability is the likelihood that a chromosome will be selected. #selection_probability is the likelihood that a chromosome will be selected.
#best chromosome in a tournament is given a selection probablity of selection_probability #best chromosome in a tournament is given a selection probablity of selection_probability
#2nd best is given probability of selection_probability*(1-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 #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 total_selected = 0 #Total Chromosomes selected
while (total_selected <= ga.population_size*2): while (total_selected < parent_ratio*ga.population_size):
#create & gather tournament group #create & gather tournament group
tournament_group = [] tournament_group = []
for i in range(tournament_size): for i in range(tournament_size):
tournament_group.append(random.choice(ga.population.get_all_chromosomes())) tournament_group.append(random.choice(ga.population.get_all_chromosomes()))
total_selected = self.selection(tournament_group, tournament_size, total_selected, selection_probability)[0]
#Sort the tournament contenders based on their fitness
#currently hard-coded to only consider higher fitness = better; can be changed once this impl is agreed on
#also currently uses bubble sort because its easy
tournament_group_temp = tournament_group
not_sorted_check = 0
while (not_sorted_check != len(tournament_group_temp)):
not_sorted_check = 0
for i in range(len(tournament_group_temp)):
if ((i + 1 < len(tournament_group_temp)) and (tournament_group_temp[i + 1].fitness > tournament_group_temp[i].fitness)):
temp = tournament_group[i]
tournament_group_temp[i] = tournament_group[i + 1]
tournament_group_temp[i + 1] = temp
else:
not_sorted_check += 1
new_population = self.create_new_population(ga) tournament_group = tournament_group_temp
return new_population
def without_replacement(self, ga): #After sorting by fitness, randomly select a chromosome based on selection_probability
tournament_size = int(len(ga.population.get_all_chromosomes())/10) #currently hard-coded for purposes of the example. selected_chromosome_tournament_index = 0
if tournament_size < 3: for i in range(tournament_size):
tournament_size = int(len(ga.population.get_all_chromosomes())/3) random_num = random.uniform(0,1)
#selection_probability is the likelihood that a chromosome will be selected. #ugly implementation but its functional
#best chromosome in a tournament is given a selection probablity of selection_probability if i == 0:
#2nd best is given probability of selection_probability*(1-selection_probability) if random_num <= selection_probability:
#3rd best is given probability of selection_probability*(1-selection_probability)**2 tournament_group[i].selected = True
selection_probability = 0.95 total_selected += 1
total_selected = 0 #Total Chromosomes selected selected_chromosome_tournament_index = i
available_chromosome_indices = [] break
for i in range(len(ga.population.get_all_chromosomes())): else:
available_chromosome_indices.append(i) if random_num <= selection_probability*((1-selection_probability)**(i-1)):
tournament_group[i].selected = True
total_selected += 1
selected_chromosome_tournament_index = i
break
continue_selecting = True class Survivor_Selection:
def repeated_crossover(self, ga, next_population):
while (continue_selecting): while len(next_population.chromosomes) < ga.population_size:
#create & gather tournament group
tournament_group = []
for i in range(tournament_size):
selected_chromosome_index = random.choice(available_chromosome_indices)
tournament_group.append(ga.population.get_all_chromosomes()[selected_chromosome_index])
winning_chromosome_index = self.selection(tournament_group, tournament_size, total_selected, selection_probability)[1]
for i in range(len(available_chromosome_indices)):
if tournament_group[winning_chromosome_index].selected:
del available_chromosome_indices[i]
break
#print(winning_chromosome_index)
#print(available_chromosome_indices)
if len(available_chromosome_indices) < 1:
continue_selecting = False
new_population = self.create_new_population(ga)
return new_population
def selection(self, tournament_group, tournament_size, total_selected, selection_probability):
#Sort the tournament contenders based on their fitness
#currently hard-coded to only consider higher fitness = better; can be changed once this impl is agreed on
#also currently uses bubble sort because its easy
tournament_group_temp = tournament_group
not_sorted_check = 0
while (not_sorted_check != len(tournament_group_temp)):
not_sorted_check = 0
for i in range(len(tournament_group_temp)):
if ((i + 1 < len(tournament_group_temp)) and (tournament_group_temp[i + 1].fitness > tournament_group_temp[i].fitness)):
temp = tournament_group[i]
tournament_group_temp[i] = tournament_group[i + 1]
tournament_group_temp[i + 1] = temp
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
return total_selected,selected_chromosome_tournament_index
def create_new_population(self, ga):
new_population = ga.crossover_impl(ga)
#If the crossover doesn't create enough chromosomes (ugly right now pls no judgerino, can be changed)
#Just does single-point crossover at random indices
while len(new_population.chromosomes) < ga.population_size:
crossover_pool = [] crossover_pool = []
for i in range(ga.population_size): for i in range(ga.population_size):
if ga.population.get_all_chromosomes()[i].selected: if ga.population.get_all_chromosomes()[i].selected:
@ -130,19 +84,16 @@ class Selection_Types:
parent_two = crossover_pool[i+1].get_genes() parent_two = crossover_pool[i+1].get_genes()
new_gene_set.extend(parent_one[0:split_point]) new_gene_set.extend(parent_one[0:split_point])
new_gene_set.extend(parent_two[split_point:]) new_gene_set.extend(parent_two[split_point:])
new_chromosome = Chromosome(new_gene_set) new_chromosome = create_chromosome(new_gene_set)
chromosome_list.append(new_chromosome) chromosome_list.append(new_chromosome)
for i in range(len(chromosome_list)): for i in range(len(chromosome_list)):
new_population.add_chromosome(chromosome_list[i]) next_population.add_chromosome(chromosome_list[i])
if len(new_population.chromosomes) >= ga.population_size: if len(next_population.chromosomes) >= ga.population_size:
break break
return next_population
new_chromosome_set = ga.mutation_impl(ga, new_population.get_all_chromosomes())
new_population.set_all_chromosomes(new_chromosome_set)
return new_population
def roulette_selection(self, ga): def roulette_selection(self, ga):
"""Roulette selection works based off of how strong the fitness is of the """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 chromosomes in the population. The stronger the fitness the higher the probability