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 from structure import Gene as create_gene
# Structure Methods # Structure Methods
from fitness_function import Fitness_Examples from fitness_function import Fitness_Examples
from initialization import Initialization_Methods from initialization import Initialization_Methods
from termination_point import Termination_Methods from termination_point import Termination_Methods
# Parent/Survivor Selection Methods # Parent/Survivor Selection Methods
from parent_selection import Parent_Selection from parent_selection import Parent_Selection
from survivor_selection import Survivor_Selection from survivor_selection import Survivor_Selection
# Genetic Operator Methods # Genetic Operator Methods
from mutation import Mutation_Methods from mutation import Mutation_Methods
from crossover import Crossover_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 = 10 self.population_size = 10
@ -33,7 +34,7 @@ class GA:
# Selection variables # Selection variables
self.parent_ratio = 0.1 self.parent_ratio = 0.1
self.selection_probability = 0.95 self.selection_probability = 0.75
self.tournament_size_ratio = 0.1 self.tournament_size_ratio = 0.1
# Termination variables # Termination variables
@ -54,7 +55,7 @@ class GA:
# Methods for accomplishing Parent-Selection -> Crossover -> Survivor_Selection -> Mutation # Methods for accomplishing Parent-Selection -> Crossover -> Survivor_Selection -> Mutation
self.parent_selection_impl = Parent_Selection.Tournament.with_replacement 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.crossover_population_impl = Crossover_Methods.Population.random_selection
self.survivor_selection_impl = Survivor_Selection.fill_in_best self.survivor_selection_impl = Survivor_Selection.fill_in_best
self.mutation_individual_impl = Mutation_Methods.Individual.single_gene self.mutation_individual_impl = Mutation_Methods.Individual.single_gene
@ -82,7 +83,7 @@ class GA:
else: else:
self.population.reset_mating_pool() self.population.reset_mating_pool()
self.set_all_fitness() 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) self.parent_selection_impl(self)
next_population = self.crossover_population_impl(self) next_population = self.crossover_population_impl(self)
self.survivor_selection_impl(self, next_population) self.survivor_selection_impl(self, next_population)
@ -119,7 +120,7 @@ class GA:
""" """
# Check each chromosome # 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 # Update fitness if needed or asked by the user
if(chromosome.get_fitness() is None or self.update_fitness): if(chromosome.get_fitness() is None or self.update_fitness):

View File

@ -27,13 +27,56 @@ class Crossover_Methods:
class Individual: class Individual:
"""Methods for crossing parents""" """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""" """Cross two parents by swapping genes at one random point"""
index = random.randint(0, parent_one.size()-1) index = random.randint(0, parent_one.size()-1)
return ga.make_chromosome(parent_one.get_gene_list()[:index] + parent_two.get_gene_list()[index:]) 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""" """Cross two parents by swapping genes at multiple points"""
pass 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 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): def index_dependent_values(chromosome):
"""Test of the GA's ability to improve fitness when the value is index-dependent. """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. 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 # Create the Genetic algorithm
ga = EasyGA.GA() ga = EasyGA.GA()
ga.population_size = 100 ga.population_size = 25
ga.generation_goal = 200 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.parent_selection_impl = EasyGA.Parent_Selection.Roulette.stochastic_selection
ga.crossover_population_impl = EasyGA.Crossover_Methods.Population.sequential_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.evolve()
ga.set_all_fitness() 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}") print(f"Current Generation: {ga.current_generation}")
ga.population.print_all() ga.population.print_all()

View File

@ -7,8 +7,6 @@ class Chromosome:
self.gene_list = gene_list self.gene_list = gene_list
self.fitness = None self.fitness = None
# If the chromosome has been selected then the flag would switch to true
self.selected = False
def size(self): def size(self):
@ -16,10 +14,10 @@ class Chromosome:
return len(self.gene_list) 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""" """Add a gene to the chromosome at the specified index, defaulted to end of the chromosome"""
if index == -1: if index is None:
index = len(self.gene_list) index = self.size()
self.gene_list.insert(index, gene) self.gene_list.insert(index, gene)
@ -30,7 +28,7 @@ class Chromosome:
def get_gene(self, index): def get_gene(self, index):
"""Returns the gene at the given index""" """Returns the gene at the given index"""
return gene_list[index] return self.gene_list[index]
def get_gene_list(self): def get_gene_list(self):

View File

@ -14,7 +14,7 @@ class Population:
def sort_by_best_fitness(self, ga): def sort_by_best_fitness(self, ga):
"""Sorts the population by fitness""" """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): def size(self):
@ -27,10 +27,10 @@ class Population:
pass 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""" """Adds a chromosome to the population at the input index, defaulted to the end of the chromosome set"""
if index == -1: if index is None:
index = len(self.chromosome_list) index = self.size()
self.chromosome_list.insert(index, chromosome) self.chromosome_list.insert(index, chromosome)
@ -64,7 +64,7 @@ class Population:
return self.mating_pool[index] return self.mating_pool[index]
def get_all_chromosomes(self): def get_chromosome_list(self):
"""Returns all chromosomes in the population""" """Returns all chromosomes in the population"""
return self.chromosome_list return self.chromosome_list
@ -80,11 +80,11 @@ class Population:
def set_parent(self, index): 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)) 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""" """Sets the chromosome list"""
self.chromosome_list = chromosome_list self.chromosome_list = chromosome_list
@ -120,5 +120,5 @@ class Population:
print("Current population:") print("Current population:")
for index in range(self.size()): for index in range(self.size()):
print(f'Chromosome - {index} {self.chromosome_list[index]}', end = "") print(f'Chromosome - {index} {self.get_chromosome(index)}', end = "")
print(f' / Fitness = {self.chromosome_list[index].get_fitness()}') 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): def fill_in_best(ga, next_population):
"""Fills in the next population with the best chromosomes from the last 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): def fill_in_random(ga, next_population):
"""Fills in the next population with random chromosomes from the last population""" """Fills in the next population with random chromosomes from the last population"""
ga.population.set_all_chromosomes([ ga.population.set_chromosome_list([
random.choice(ga.population.get_all_chromosomes()) random.choice(ga.population.get_chromosome_list())
for n in range(ga.population.size()-next_population.size())] 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): 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""" """Fills in the next population with all parents followed by random chromosomes from the last population"""
ga.population.set_all_chromosomes([ ga.population.set_chromosome_list([
random.choice(ga.population.get_all_chromosomes()) random.choice(ga.population.get_chromosome_list())
for n in range(ga.population.size()-len(ga.population.get_mating_pool())-next_population.size())] 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())