Updated Implementation Framework

Updated to cover changes made by Dan to Master regarding general design changes

Also added remove_two_worst survivor selection method
This commit is contained in:
RyleyGG
2020-10-05 20:46:25 -04:00
parent e05aa7f62b
commit 665062fdf1
24 changed files with 133 additions and 126 deletions

View File

@ -3,25 +3,29 @@ import random
from initialization import Population as create_population from initialization import Population as create_population
from initialization import Chromosome as create_chromosome from initialization import Chromosome as create_chromosome
from initialization import Gene as create_gene from initialization import Gene as create_gene
# Import example classes # Structure Methods
from fitness_function import Fitness_Examples from fitness_function import Fitness_Examples
from initialization import Initialization_Types from initialization import Initialization_Methods
from termination_point import Termination_Types from termination_point import Termination_Methods
from selection import Selection_Types # Population Methods
from crossover import Crossover_Types from selection import Selection_Methods
from mutation import Mutation_Types # Manipulation Methods
from mutation import Mutation_Methods
from crossover import Crossover_Methods
class GA: class GA:
def __init__(self): def __init__(self):
"""Initialize the GA.""" """Initialize the GA."""
# Initilization variables # Initilization variables
self.chromosome_length = 10 self.chromosome_length = 10
self.population_size = 150 self.population_size = 100
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.generation_goal = 50
self.current_fitness = 0 self.current_fitness = 0
self.generation_goal = 100 self.generation_goal = 100
self.fitness_goal = 3 self.fitness_goal = 3
@ -32,13 +36,46 @@ class GA:
self.update_fitness = True self.update_fitness = True
# Defualt EastGA implimentation structure # Defualt EastGA implimentation structure
self.initialization_impl = Initialization_Types().random_initialization self.initialization_impl = Initialization_Methods().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().per_gene_mutation # Selects which chromosomes should be automaticly moved to the next population
self.parent_selection_impl = Selection_Types().Parent_Selection().Tournament().with_replacement self.survivor_selection_impl = Selection_Methods().Survivor_Selection().remove_two_worst
self.survivor_selection_impl = Selection_Types().Survivor_Selection().repeated_crossover # Methods for accomplishing parent-selection -> Crossover -> Mutation
self.crossover_impl = Crossover_Types().single_point_crossover self.parent_selection_impl = Selection_Methods().Parent_Selection().Tournament().with_replacement
self.termination_impl = Termination_Types().generation_based self.crossover_impl = Crossover_Methods().single_point_crossover
self.mutation_impl = Mutation_Methods().per_gene_mutation
# The type of termination to impliment
self.termination_impl = Termination_Methods().generation_based
def evolve_generation(self, number_of_generations = 1, consider_termination = True):
"""Evolves the ga the specified number of generations."""
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
self.set_all_fitness(self.population.chromosome_list)
number_of_generations -= 1
self.current_generation += 1
def evolve(self):
"""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 active(self):
"""Returns if the ga should terminate base on the termination implimented"""
# Send termination_impl the whole ga class
return self.termination_impl(self)
def initialize_population(self): def initialize_population(self):
"""Initialize the population using the initialization """Initialize the population using the initialization
@ -69,7 +106,7 @@ class GA:
# If its the first generation then initialize the population # If its the first generation then initialize the population
if self.current_generation == 0: if self.current_generation == 0:
self.initialize_population() self.initialize_population()
self.set_all_fitness(self.population.chromosomes) self.set_all_fitness(self.population.chromosome_list)
self.parent_selection_impl(self) self.parent_selection_impl(self)
next_population = self.crossover_impl(self) next_population = self.crossover_impl(self)
@ -77,7 +114,7 @@ class GA:
next_population.set_all_chromosomes(self.mutation_impl(self, next_population.get_all_chromosomes())) 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.chromosome_list)
number_of_generations -= 1 number_of_generations -= 1
self.current_generation += 1 self.current_generation += 1

View File

@ -1,2 +1,2 @@
# FROM (. means local) file_name IMPORT function_name # FROM (. means local) file_name IMPORT function_name
from .crossover_types import Crossover_Types from .crossover_methods import Crossover_Methods

View File

@ -2,7 +2,7 @@ import random
from initialization.chromosome_structure.chromosome import Chromosome from initialization.chromosome_structure.chromosome import Chromosome
from initialization.population_structure.population import Population from initialization.population_structure.population import Population
class Crossover_Types: class Crossover_Methods:
""" Crossover explination goes here. """ Crossover explination goes here.
Points - Defined as sections between the chromosomes genetic makeup Points - Defined as sections between the chromosomes genetic makeup

View File

View File

@ -1,2 +1,2 @@
# FROM (. means local) file_name IMPORT class name # FROM (. means local) file_name IMPORT class name
from .examples import Fitness_Examples from .fitness_examples import Fitness_Examples

View File

@ -1,13 +1,12 @@
class Fitness_Examples: class Fitness_Examples:
"""Fitness function examples used""" """Fitness function examples used"""
def is_it_5(self, chromosome): def is_it_5(self, chromosome):
"""A very simple case test function - If the chromosomes gene value is a 5 add one """A very simple case test function - If the chromosomes gene value is a 5 add one
to the chromosomes overall fitness value.""" to the chromosomes overall fitness value."""
# Overall fitness value # Overall fitness value
fitness = 0 fitness = 0
# For each gene in the chromosome # For each gene in the chromosome
for gene in chromosome.genes: for gene in chromosome.gene_list:
# Check if its value = 5 # Check if its value = 5
if(gene.value == 5): if(gene.value == 5):
# If its value is 5 then add one to # If its value is 5 then add one to

View File

@ -0,0 +1,12 @@
class test_fitness_funciton:
def get_fitness(self, chromosome):
# For every gene in chromosome
for i in range(len(chromosome.genes)):
# If the gene has a five then add one to the fitness
# Example -> Chromosome = [5],[2],[2],[5],[5] then fitness = 3
if (chromosome.genes[i].get_value == 5):
# Add to the genes fitness
chromosome.genes[i].fitness += 1
# Add to the chromosomes fitness
chromosome.fitness += 1
return chromosome.fitness

View File

@ -1,5 +1,5 @@
# FROM (. means local) file_name IMPORT function_name # FROM (. means local) file_name IMPORT function_name
from .initialization_types import Initialization_Types from .initialization_methods import Initialization_Methods
from .population_structure.population import Population from .population_structure.population import Population
from .chromosome_structure.chromosome import Chromosome from .chromosome_structure.chromosome import Chromosome
from .gene_structure.gene import Gene from .gene_structure.gene import Gene

View File

@ -2,37 +2,37 @@ class Chromosome:
def __init__(self, genes = None): def __init__(self, genes = None):
if genes is None: if genes is None:
self.genes = [] self.gene_list = []
else: else:
self.genes = genes self.gene_list = genes
self.fitness = None self.fitness = None
self.selected = False self.selected = False
def add_gene(self, gene, index = -1): def add_gene(self, gene, index = -1):
if index == -1: if index == -1:
index = len(self.genes) index = len(self.gene_list)
self.genes.insert(index, gene) self.gene_list.insert(index, gene)
def remove_gene(self, index): def remove_gene(self, index):
del self.genes[index] del self.gene_list[index]
def get_genes(self): def get_genes(self):
return self.genes return self.gene_list
def get_fitness(self): def get_fitness(self):
return self.fitness return self.fitness
def set_gene(self, gene, index): def set_gene(self, gene, index):
self.genes[index] = gene self.gene_list[index] = gene
def set_genes(self, genes): def set_genes(self, genes):
self.genes = genes self.gene_list = genes
def set_fitness(self, fitness): def set_fitness(self, fitness):
self.fitness = fitness self.fitness = fitness
def __repr__(self): def __repr__(self):
output_str = '' output_str = ''
for gene in self.genes: for gene in self.gene_list:
output_str += gene.__repr__() output_str += gene.__repr__()
return output_str return output_str

View File

@ -1,22 +0,0 @@
# Imported library
import random
def check_values(low,high):
#Check to make sure its not less then zero
assert low > 0 , "The random gene low can not be less then zero"
# Check to make sure the high value is not
# lower than or equal to low and not 0.
assert high > low, "High value can not be smaller then low value"
assert high != 0, "High value can not be zero"
def random_gene(gene_input, gene_input_type, gene_index):
created_gene = None
if gene_input_type[gene_index] == "range":
created_gene = random.randint(gene_input[gene_index][0], gene_input[gene_index][1])
elif gene_input_type[gene_index] == "domain":
created_gene = random.choice(gene_input[gene_index])
elif gene_input_type[gene_index] == "float-range":
created_gene = random.uniform(gene_input[gene_index][0], gene_input[gene_index][1])
return created_gene

View File

@ -3,12 +3,12 @@ from .population_structure.population import Population as create_population
from .chromosome_structure.chromosome import Chromosome as create_chromosome from .chromosome_structure.chromosome import Chromosome as create_chromosome
from .gene_structure.gene import Gene as create_gene from .gene_structure.gene import Gene as create_gene
class Initialization_Types: class Initialization_Methods:
"""Initialization examples that are used as defaults and examples""" """Initialization examples that are used as defaults and examples"""
def random_initialization(self, ga): def random_initialization(self, ga):
"""Takes the initialization inputs and choregraphs them to output the type of population """Takes the initialization inputs and choregraphs them to output the type of population with the given parameters."""
with the given parameters."""
# Create the population object # Create the population object
population = create_population() population = create_population()

View File

@ -3,9 +3,9 @@ class Population:
# fitness = Empty; population = [chromosome, chromosome, etc.] # fitness = Empty; population = [chromosome, chromosome, etc.]
def __init__(self, chromosomes = None): def __init__(self, chromosomes = None):
if chromosomes is None: if chromosomes is None:
self.chromosomes = [] self.chromosome_list = []
else: else:
self.chromosomes = chromosomes self.chromosome_list = chromosomes
self.fitness = None self.fitness = None
def get_closet_fitness(self,value): def get_closet_fitness(self,value):
@ -14,46 +14,38 @@ class Population:
def add_chromosome(self, chromosome, index = -1): def add_chromosome(self, chromosome, index = -1):
if index == -1: if index == -1:
index = len(self.chromosomes) index = len(self.chromosome_list)
self.chromosomes.insert(index, chromosome) self.chromosome_list.insert(index, chromosome)
def remove_chromosome(self, index): def remove_chromosome(self, index):
del self.chromosomes[index] del self.chromosome_list[index]
def get_all_chromosomes(self): def get_all_chromosomes(self):
"""returns all chromosomes in the population""" """returns all chromosomes in the population"""
return self.chromosomes return self.chromosome_list
def get_fitness(self): def get_fitness(self):
return self.fitness return self.fitness
def set_all_chromosomes(self, chromosomes): def set_all_chromosomes(self, chromosomes):
self.chromosomes = chromosomes self.chromosome_list = chromosomes
def set_chromosome(self, chromosome, index = -1): def set_chromosome(self, chromosome, index = -1):
if index == -1: if index == -1:
index = len(self.chromosomes)-1 index = len(self.chromosomes)-1
self.chromosomes[index] = chromosome self.chromosome_list[index] = chromosome
def set_fitness(self, fitness): def set_fitness(self, fitness):
self.fitness = fitness self.fitness = fitness
def __repr__(self): def __repr__(self):
for index in range(len(self.chromosomes)): for index in range(len(self.chromosomes)):
return f'{self.chromosomes[index]}' return f'{self.chromosome_list[index]}'
def print_all(self): def print_all(self):
# Ex .Current population # Ex .Current population
# Chromosome 1 - [gene][gene][gene][.etc] / Chromosome fitness - # # Chromosome 1 - [gene][gene][gene][.etc] / Chromosome fitness - #
print("Current population:") print("Current population:")
for index in range(len(self.chromosomes)): for index in range(len(self.chromosome_list)):
print(f'Chromosome - {index} {self.chromosomes[index]}', end = "") print(f'Chromosome - {index} {self.chromosome_list[index]}', end = "")
print(f' / Fitness = {self.chromosomes[index].fitness}') print(f' / Fitness = {self.chromosome_list[index].fitness}')
def generate_first_chromosomes(self, chromosome_count, chromosome_length, gene_lower_bound, gene_upper_bound):
#Creating the chromosomes with Genes of random size
for x in range(chromosome_count):
chromosome = Chromosome(chromosome_length)
for y in range(chromosome_length):
chromosome.gene_set[y] = Gene(random.randint(gene_lower_bound[y], gene_upper_bound[y]))
self.chromosome_set.append(chromosome)

View File

@ -1,31 +0,0 @@
# Import the data structure
from .population_structure.population import population as create_population
from .chromosome_structure.chromosome import chromosome as create_chromosome
from .gene_structure.gene import gene as create_gene
from .gene_function.gene_random import random_gene as random_gene
def random_initialization(chromosome_length,population_size,gene_function,gene_input,gene_input_type):
if gene_function == random_gene:
# Create the population object
population = create_population()
# Fill the population with chromosomes
for i in range(population_size):
chromosome = create_chromosome()
#Fill the Chromosome with genes
for j in range(chromosome_length):
chromosome.add_gene(create_gene(gene_function(gene_input, gene_input_type, j)))
population.add_chromosome(chromosome)
return population
else: #For user input gene-function, don't do anything with gene_input parameter
# Create the population object
population = create_population()
# Fill the population with chromosomes
for i in range(population_size):
chromosome = create_chromosome()
#Fill the Chromosome with genes
for j in range(chromosome_length):
chromosome.add_gene(create_gene(gene_function()))
population.add_chromosome(chromosome)
return population

View File

View File

@ -1,2 +1,2 @@
# FROM (. means local) file_name IMPORT function_name # FROM (. means local) file_name IMPORT function_name
from .mutation_types import Mutation_Types from .mutation_methods import Mutation_Methods

View File

@ -1,6 +1,6 @@
import random import random
class Mutation_Types: class Mutation_Methods:
def __init__(self): def __init__(self):
pass pass

View File

View File

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

View File

@ -1,2 +1,2 @@
# FROM (. means local) file_name IMPORT function_name # FROM (. means local) file_name IMPORT function_name
from .selection_types import Selection_Types from .selection_methods import Selection_Methods

View File

@ -4,7 +4,7 @@ from initialization.gene_structure.gene import Gene as create_gene
from initialization.population_structure.population import Population from initialization.population_structure.population import Population
from initialization.chromosome_structure.chromosome import Chromosome from initialization.chromosome_structure.chromosome import Chromosome
class Selection_Types: class Selection_Methods:
"""Selection is the process by which chromosomes are selected for crossover and eventually, influence the next generation of chromosomes.""" """Selection is the process by which chromosomes are selected for crossover and eventually, influence the next generation of chromosomes."""
def __init__(self): def __init__(self):
pass pass
@ -68,8 +68,8 @@ class Selection_Types:
break break
class Survivor_Selection: class Survivor_Selection:
def repeated_crossover(self, ga, next_population): def repeated_crossover(self, ga, next_population): #Might be cheating? I don't know honestly - RG
while len(next_population.chromosomes) < ga.population_size: while len(next_population.get_all_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:
@ -90,10 +90,31 @@ class Selection_Types:
for i in range(len(chromosome_list)): for i in range(len(chromosome_list)):
next_population.add_chromosome(chromosome_list[i]) next_population.add_chromosome(chromosome_list[i])
if len(next_population.chromosomes) >= ga.population_size: if len(next_population.get_all_chromosomes()) >= ga.population_size:
break break
return next_population return 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
while len(next_population.get_all_chromosomes()) < ga.population_size:
next_population.add_chromosome(temp_population.get_all_chromosomes()[iterator])
iterator += 1
return next_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

View File

View File

@ -1,2 +1,2 @@
# FROM (. means local) file_name IMPORT class name # FROM (. means local) file_name IMPORT class name
from .termination_types import Termination_Types from .termination_methods import Termination_Methods

View File

@ -1,16 +1,16 @@
class Termination_Types: class Termination_Methods:
"""Example functions that can be used to terminate the the algorithms loop""" """Example functions that can be used to terminate the the algorithms loop"""
def fitness_based(self, ga): def fitness_based(self, ga):
"""Fitness based approach to terminate when the goal fitness has been reached""" """Fitness based approach to terminate when the goal fitness has been reached"""
continue_status = True status = True
if(ga.current_fitness > ga.fitness_goal): if(ga.current_fitness > ga.fitness_goal):
continue_status = False status = False
return continue_status return status
def generation_based(self, ga): def generation_based(self, ga):
"""Generation based approach to terminate when the goal generation has been reached""" """Generation based approach to terminate when the goal generation has been reached"""
continue_status = True status = True
if(ga.current_generation > ga.generation_goal-1): if(ga.current_generation > ga.generation_goal):
continue_status = False status = False
return continue_status return status

View File