Fixed method names and added some crossover methods and tests for floats

This commit is contained in:
SimpleArt
2020-10-13 21:07:05 -04:00
parent b966b22b04
commit 0090db9dce
7 changed files with 89 additions and 36 deletions

View File

@ -6,22 +6,23 @@ from structure import Chromosome as create_chromosome
from structure import Gene as create_gene
# Structure Methods
from fitness_function import Fitness_Examples
from initialization import Initialization_Methods
from fitness_function import Fitness_Examples
from initialization import Initialization_Methods
from termination_point import Termination_Methods
# Parent/Survivor Selection Methods
from parent_selection import Parent_Selection
from parent_selection import Parent_Selection
from survivor_selection import Survivor_Selection
# Genetic Operator Methods
from mutation import Mutation_Methods
from mutation import Mutation_Methods
from crossover import Crossover_Methods
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):

View File

@ -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())])

View File

@ -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.

View File

@ -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()

View File

@ -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):

View File

@ -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()}')

View File

@ -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())