Fixed method names and added some crossover methods and tests for floats
This commit is contained in:
@ -22,6 +22,7 @@ class GA:
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the GA."""
|
||||
|
||||
# Initilization variables
|
||||
self.chromosome_length = 10
|
||||
self.population_size = 10
|
||||
@ -33,7 +34,7 @@ class GA:
|
||||
|
||||
# Selection variables
|
||||
self.parent_ratio = 0.1
|
||||
self.selection_probability = 0.95
|
||||
self.selection_probability = 0.75
|
||||
self.tournament_size_ratio = 0.1
|
||||
|
||||
# Termination variables
|
||||
@ -54,7 +55,7 @@ class GA:
|
||||
|
||||
# Methods for accomplishing Parent-Selection -> Crossover -> Survivor_Selection -> Mutation
|
||||
self.parent_selection_impl = Parent_Selection.Tournament.with_replacement
|
||||
self.crossover_individual_impl = Crossover_Methods.Individual.single_point_crossover
|
||||
self.crossover_individual_impl = Crossover_Methods.Individual.single_point
|
||||
self.crossover_population_impl = Crossover_Methods.Population.random_selection
|
||||
self.survivor_selection_impl = Survivor_Selection.fill_in_best
|
||||
self.mutation_individual_impl = Mutation_Methods.Individual.single_gene
|
||||
@ -82,7 +83,7 @@ class GA:
|
||||
else:
|
||||
self.population.reset_mating_pool()
|
||||
self.set_all_fitness()
|
||||
self.population.set_all_chromosomes(self.sort_by_best_fitness(self.population.get_all_chromosomes()))
|
||||
self.population.sort_by_best_fitness(self)
|
||||
self.parent_selection_impl(self)
|
||||
next_population = self.crossover_population_impl(self)
|
||||
self.survivor_selection_impl(self, next_population)
|
||||
@ -119,7 +120,7 @@ class GA:
|
||||
"""
|
||||
|
||||
# Check each chromosome
|
||||
for chromosome in self.population.get_all_chromosomes():
|
||||
for chromosome in self.population.get_chromosome_list():
|
||||
|
||||
# Update fitness if needed or asked by the user
|
||||
if(chromosome.get_fitness() is None or self.update_fitness):
|
||||
|
||||
@ -27,13 +27,56 @@ class Crossover_Methods:
|
||||
class Individual:
|
||||
"""Methods for crossing parents"""
|
||||
|
||||
def single_point_crossover(ga, parent_one, parent_two):
|
||||
def single_point(ga, parent_one, parent_two):
|
||||
"""Cross two parents by swapping genes at one random point"""
|
||||
|
||||
index = random.randint(0, parent_one.size()-1)
|
||||
return ga.make_chromosome(parent_one.get_gene_list()[:index] + parent_two.get_gene_list()[index:])
|
||||
|
||||
|
||||
def multi_point_crossover(ga, parent_one, parent_two):
|
||||
def multi_point(ga, parent_one, parent_two):
|
||||
"""Cross two parents by swapping genes at multiple points"""
|
||||
pass
|
||||
|
||||
|
||||
def uniform(ga, parent_one, parent_two):
|
||||
"""Cross two parents by swapping all genes randomly"""
|
||||
return ga.make_chromosome([
|
||||
random.choice([parent_one.get_gene(i), parent_two.get_gene(i)])
|
||||
for i in range(parent_one.size())])
|
||||
|
||||
class Arithmetic:
|
||||
"""Crossover methods for numerical genes"""
|
||||
|
||||
def int_random(ga, parent_one, parent_two):
|
||||
"""Cross two parents by taking a random integer value between each of the genes"""
|
||||
return ga.make_chromosome([
|
||||
ga.make_gene(random.randint(*sorted([parent_one.get_gene(i).get_value(), parent_two.get_gene(i).get_value()])))
|
||||
for i in range(parent_one.size())])
|
||||
|
||||
|
||||
def int_weighted(ga, parent_one, parent_two):
|
||||
"""Cross two parents by taking a a weighted average of the genes"""
|
||||
|
||||
# the percentage of genes taken from the first gene
|
||||
weight = 0.25
|
||||
return ga.make_chromosome([
|
||||
ga.make_gene(int(weight*parent_one.get_gene(i).get_value()+(1-weight)*parent_two.get_gene(i).get_value()))
|
||||
for i in range(parent_one.size())])
|
||||
|
||||
|
||||
def float_random(ga, parent_one, parent_two):
|
||||
"""Cross two parents by taking a random numeric value between each of the genes"""
|
||||
return ga.make_chromosome([
|
||||
ga.make_gene(random.uniform(parent_one.get_gene(i).get_value(), parent_two.get_gene(i).get_value()))
|
||||
for i in range(parent_one.size())])
|
||||
|
||||
|
||||
def float_weighted(ga, parent_one, parent_two):
|
||||
"""Cross two parents by taking a a weighted average of the genes"""
|
||||
|
||||
# the percentage of genes taken from the first gene
|
||||
weight = 0.25
|
||||
return ga.make_chromosome([
|
||||
ga.make_gene(weight*parent_one.get_gene(i).get_value()+(1-weight)*parent_two.get_gene(i).get_value())
|
||||
for i in range(parent_one.size())])
|
||||
|
||||
@ -17,6 +17,13 @@ class Fitness_Examples:
|
||||
return fitness
|
||||
|
||||
|
||||
def near_5(chromosome):
|
||||
"""Test's the GA's ability to handle floats.
|
||||
Computes how close each gene is to 5.
|
||||
"""
|
||||
return sum([1-pow(1-gene.get_value()/5, 2) for gene in chromosome.get_gene_list()])
|
||||
|
||||
|
||||
def index_dependent_values(chromosome):
|
||||
"""Test of the GA's ability to improve fitness when the value is index-dependent.
|
||||
If a gene is equal to its index in the chromosome + 1, fitness is incremented.
|
||||
|
||||
@ -3,15 +3,19 @@ import EasyGA
|
||||
|
||||
# Create the Genetic algorithm
|
||||
ga = EasyGA.GA()
|
||||
ga.population_size = 100
|
||||
ga.generation_goal = 200
|
||||
ga.population_size = 25
|
||||
ga.generation_goal = 100
|
||||
ga.gene_impl = [random.randint,0,10]
|
||||
ga.selection_probability = 0.5
|
||||
ga.fitness_function_impl = EasyGA.Fitness_Examples.near_5
|
||||
ga.parent_selection_impl = EasyGA.Parent_Selection.Roulette.stochastic_selection
|
||||
ga.crossover_population_impl = EasyGA.Crossover_Methods.Population.sequential_selection
|
||||
ga.survivor_selection_impl = EasyGA.Survivor_Selection.fill_in_parents_then_random
|
||||
ga.crossover_individual_impl = EasyGA.Crossover_Methods.Individual.Arithmetic.int_random
|
||||
ga.survivor_selection_impl = EasyGA.Survivor_Selection.fill_in_best
|
||||
|
||||
ga.evolve()
|
||||
ga.set_all_fitness()
|
||||
ga.population.set_all_chromosomes(ga.sort_by_best_fitness(ga.population.get_all_chromosomes()))
|
||||
ga.population.sort_by_best_fitness(ga)
|
||||
|
||||
print(f"Current Generation: {ga.current_generation}")
|
||||
ga.population.print_all()
|
||||
|
||||
@ -7,8 +7,6 @@ class Chromosome:
|
||||
self.gene_list = gene_list
|
||||
|
||||
self.fitness = None
|
||||
# If the chromosome has been selected then the flag would switch to true
|
||||
self.selected = False
|
||||
|
||||
|
||||
def size(self):
|
||||
@ -16,10 +14,10 @@ class Chromosome:
|
||||
return len(self.gene_list)
|
||||
|
||||
|
||||
def add_gene(self, gene, index = -1):
|
||||
def add_gene(self, gene, index = None):
|
||||
"""Add a gene to the chromosome at the specified index, defaulted to end of the chromosome"""
|
||||
if index == -1:
|
||||
index = len(self.gene_list)
|
||||
if index is None:
|
||||
index = self.size()
|
||||
self.gene_list.insert(index, gene)
|
||||
|
||||
|
||||
@ -30,7 +28,7 @@ class Chromosome:
|
||||
|
||||
def get_gene(self, index):
|
||||
"""Returns the gene at the given index"""
|
||||
return gene_list[index]
|
||||
return self.gene_list[index]
|
||||
|
||||
|
||||
def get_gene_list(self):
|
||||
|
||||
@ -14,7 +14,7 @@ class Population:
|
||||
|
||||
def sort_by_best_fitness(self, ga):
|
||||
"""Sorts the population by fitness"""
|
||||
self.set_all_chromosomes(ga.sort_by_best_fitness(self.chromosome_list))
|
||||
self.set_chromosome_list(ga.sort_by_best_fitness(self.chromosome_list))
|
||||
|
||||
|
||||
def size(self):
|
||||
@ -27,10 +27,10 @@ class Population:
|
||||
pass
|
||||
|
||||
|
||||
def add_chromosome(self, chromosome, index = -1):
|
||||
def add_chromosome(self, chromosome, index = None):
|
||||
"""Adds a chromosome to the population at the input index, defaulted to the end of the chromosome set"""
|
||||
if index == -1:
|
||||
index = len(self.chromosome_list)
|
||||
if index is None:
|
||||
index = self.size()
|
||||
self.chromosome_list.insert(index, chromosome)
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ class Population:
|
||||
return self.mating_pool[index]
|
||||
|
||||
|
||||
def get_all_chromosomes(self):
|
||||
def get_chromosome_list(self):
|
||||
"""Returns all chromosomes in the population"""
|
||||
return self.chromosome_list
|
||||
|
||||
@ -80,11 +80,11 @@ class Population:
|
||||
|
||||
|
||||
def set_parent(self, index):
|
||||
"""Sets the index chromosome from the population as a parent"""
|
||||
"""Sets the indexed chromosome from the population as a parent"""
|
||||
self.add_parent(self.get_chromosome(index))
|
||||
|
||||
|
||||
def set_all_chromosomes(self, chromosome_list):
|
||||
def set_chromosome_list(self, chromosome_list):
|
||||
"""Sets the chromosome list"""
|
||||
self.chromosome_list = chromosome_list
|
||||
|
||||
@ -120,5 +120,5 @@ class Population:
|
||||
print("Current population:")
|
||||
|
||||
for index in range(self.size()):
|
||||
print(f'Chromosome - {index} {self.chromosome_list[index]}', end = "")
|
||||
print(f' / Fitness = {self.chromosome_list[index].get_fitness()}')
|
||||
print(f'Chromosome - {index} {self.get_chromosome(index)}', end = "")
|
||||
print(f' / Fitness = {self.get_chromosome(index).get_fitness()}')
|
||||
|
||||
@ -6,22 +6,22 @@ class Survivor_Selection:
|
||||
def fill_in_best(ga, next_population):
|
||||
"""Fills in the next population with the best chromosomes from the last population"""
|
||||
|
||||
ga.population.set_all_chromosomes(ga.population.get_all_chromosomes()[:ga.population.size()-next_population.size()] + next_population.get_all_chromosomes())
|
||||
ga.population.set_chromosome_list(ga.population.get_chromosome_list()[:ga.population.size()-next_population.size()] + next_population.get_chromosome_list())
|
||||
|
||||
|
||||
def fill_in_random(ga, next_population):
|
||||
"""Fills in the next population with random chromosomes from the last population"""
|
||||
|
||||
ga.population.set_all_chromosomes([
|
||||
random.choice(ga.population.get_all_chromosomes())
|
||||
ga.population.set_chromosome_list([
|
||||
random.choice(ga.population.get_chromosome_list())
|
||||
for n in range(ga.population.size()-next_population.size())]
|
||||
+ next_population.get_all_chromosomes())
|
||||
+ next_population.get_chromosome_list())
|
||||
|
||||
|
||||
def fill_in_parents_then_random(ga, next_population):
|
||||
"""Fills in the next population with all parents followed by random chromosomes from the last population"""
|
||||
|
||||
ga.population.set_all_chromosomes([
|
||||
random.choice(ga.population.get_all_chromosomes())
|
||||
ga.population.set_chromosome_list([
|
||||
random.choice(ga.population.get_chromosome_list())
|
||||
for n in range(ga.population.size()-len(ga.population.get_mating_pool())-next_population.size())]
|
||||
+ ga.population.get_mating_pool() + next_population.get_all_chromosomes())
|
||||
+ ga.population.get_mating_pool() + next_population.get_chromosome_list())
|
||||
|
||||
Reference in New Issue
Block a user