Comments updated

This commit is contained in:
SimpleArt
2020-11-20 19:06:55 -05:00
parent 68b10c3fde
commit 73461f7915
10 changed files with 159 additions and 52 deletions

View File

@ -47,7 +47,11 @@ class GA(Attributes):
while cond1() and cond3():
# If its the first generation
# Create the initial population if necessary.
if self.population is None:
self.initialize_population()
# If its the first generation, setup the database.
if self.current_generation == 0:
# Create the database here to allow the user to change the
@ -57,11 +61,7 @@ class GA(Attributes):
# Add the current configuration to the config table
self.database.insert_config(self)
# Create the initial population
if self.population is None:
self.initialize_population()
# Otherwise evolve the population
# Otherwise evolve the population.
else:
self.parent_selection_impl(self)
self.crossover_population_impl(self)
@ -107,7 +107,7 @@ class GA(Attributes):
"""
# Check each chromosome
for chromosome in self.population.get_chromosome_list():
for chromosome in self.population:
# Update fitness if needed or asked by the user
if chromosome.fitness is None or self.update_fitness:

View File

@ -1,6 +1,7 @@
import random
def append_children_from_mating_pool(crossover_method):
"""Appends the new chromosomes to the next population."""
return lambda ga:\
ga.population.append_children(
[chromosome for chromosome in crossover_method(ga, ga.population.mating_pool)]
@ -8,17 +9,20 @@ def append_children_from_mating_pool(crossover_method):
def genes_to_chromosome(crossover_method):
"""Converts a collection of genes into a chromosome."""
return lambda ga, parent_1, parent_2:\
return ga.make_chromosome(crossover_method(ga, parent_1, parent_2))
def values_to_genes(crossover_method):
"""Converts a collection of values into genes."""
return lambda ga, parent_1, parent_2:\
return (ga.make_gene(value) for value in crossover_method(ga, parent_1, parent_2))
class Crossover_Methods:
# Private method decorators, see above.
def __append_children_from_mating_pool(crossover_method):
return append_children_from_mating_pool(crossover_method)
def __genes_to_chromosome(crossover_method):

View File

@ -1,16 +1,20 @@
def chromosomes_to_population(initialize):
"""Makes a population from chromosomes."""
return lambda ga: ga.make_population([initialize(ga) for _ in range(ga.population_size)])
def genes_to_chromosome(initialize):
"""Converts a collection of genes to a chromosome."""
return lambda ga: ga.make_chromosome([genes for genes in initialize(ga)])
def value_to_gene(initialize):
"""Converts a collection of values to genes."""
return lambda ga: (ga.make_gene(value) for value in initialize(ga))
class Initialization_Methods:
"""Initialization examples that are used as defaults and examples"""
# Private method decorators, see above.
def __chromosomes_to_population(initialize):
return chromosomes_to_population(initialize)
def __genes_to_chromosome(initialize):
@ -23,10 +27,8 @@ class Initialization_Methods:
@genes_to_chromosome
@value_to_gene
def random_initialization(ga):
"""Takes the initialization inputs and
- return a new population
- filled with chromosomes
- filled with genes
"""Takes the initialization inputs and returns a collection of values.
Method decorators convert them to a GA population object.
"""
# Using the chromosome_impl to set every index inside of the chromosome

View File

@ -2,18 +2,18 @@ import random
from math import ceil
def loop_selections(selection_method):
"""Runs the selection method until enough chromosomes are mutated."""
def helper(ga):
# Loop until enough mutations occur
for n in range(ceil(len(ga.population)*ga.chromosome_mutation_rate)):
selection_method(ga)
return helper
def loop_mutations(mutation_method):
"""Runs the mutation method until enough genes are mutated."""
def helper(ga, old_chromosome):
chromosome = ga.make_chromosome(list(old_chromosome))
# Loops until enough mutations occur
for n in range(ceil(len(chromosome)*ga.gene_mutation_rate)):
mutation_method(ga, chromosome)
@ -23,6 +23,7 @@ def loop_mutations(mutation_method):
class Mutation_Methods:
# Private method decorators, see above.
def __loop_selections(selection_method):
return loop_selections(selection_method)
def __loop_mutations(mutation_method):
@ -32,10 +33,9 @@ class Mutation_Methods:
class Population:
"""Methods for selecting chromosomes to mutate"""
@loop_selections
def random_selection(ga):
"""Selects random chromosomes"""
"""Selects random chromosomes."""
index = random.randint(0, len(ga.population)-1)
ga.population[index] = ga.mutation_individual_impl(ga, ga.population[index])
@ -43,7 +43,7 @@ class Mutation_Methods:
@loop_selections
def random_selection_then_cross(ga):
"""Selects random chromosomes and self-crosses with parent"""
"""Selects random chromosomes and self-crosses with parent."""
index = random.randint(0, len(ga.population)-1)
chromosome = ga.population[index]
@ -51,12 +51,11 @@ class Mutation_Methods:
class Individual:
"""Methods for mutating a single chromosome"""
"""Methods for mutating a single chromosome."""
@loop_mutations
def individual_genes(ga, chromosome):
"""Mutates a random gene in the chromosome and resets the fitness."""
"""Mutates a random gene in the chromosome."""
index = random.randint(0, len(chromosome)-1)
# Using the chromosome_impl
@ -72,15 +71,15 @@ class Mutation_Methods:
raise Exception("Did not specify any initialization constraints.")
class Permutation:
"""Methods for mutating a chromosome
by changing the order of the genes."""
class Permutation:
"""Methods for mutating a chromosome
by changing the order of the genes."""
@loop_mutations
def swap_genes(ga, chromosome):
"""Mutates a random gene in the chromosome and resets the fitness."""
@loop_mutations
def swap_genes(ga, chromosome):
"""Swaps two random genes in the chromosome."""
index_one = random.randint(0, len(chromosome)-1)
index_two = random.randint(0, len(chromosome)-1)
index_one = random.randint(0, len(chromosome)-1)
index_two = random.randint(0, len(chromosome)-1)
chromosome[index_one], chromosome[index_two] = chromosome[index_two], chromosome[index_one]
chromosome[index_one], chromosome[index_two] = chromosome[index_two], chromosome[index_one]

View File

@ -1,6 +1,10 @@
import random
def check_selection_probability(selection_method):
"""Raises an exception if the selection_probability
is not between 0 and 1. Otherwise runs the selection
method.
"""
def helper(ga):
if 0 < ga.selection_probability < 1:
selection_method(ga)
@ -10,6 +14,10 @@ def check_selection_probability(selection_method):
def check_positive_fitness(selection_method):
"""Raises an exception if the population contains a
chromosome with negative fitness. Otherwise runs
the selection method.
"""
def helper(ga):
if ga.get_chromosome_fitness(0) > 0 and ga.get_chromosome_fitness(-1) >= 0:
selection_method(ga)
@ -19,6 +27,9 @@ def check_positive_fitness(selection_method):
def ensure_sorted(selection_method):
"""Sorts the population by fitness
and then runs the selection method.
"""
def helper(ga):
ga.population.sort_by_best_fitness(ga)
selection_method(ga)
@ -27,6 +38,7 @@ def ensure_sorted(selection_method):
class Parent_Selection:
# Private method decorators, see above.
def __check_selection_probability(selection_method):
return check_selection_probability(selection_method)
def __check_positive_fitness(selection_method):

View File

@ -56,43 +56,81 @@ class Chromosome:
def __iter__(self):
"""Returns an iterable of the gene list"""
"""
Allows the user to use
iter(chromosome)
list(chromosome) == chromosome.gene_list
tuple(chromosome)
for gene in chromosome
to loop through the chromosome.
"""
return iter(self.gene_list)
def __getitem__(self, index):
"""Returns the indexed gene"""
"""
Allows the user to use
gene = chromosome[index]
to get the indexed gene.
"""
return self.gene_list[index]
def __setitem__(self, index, gene):
"""Sets the indexed gene value"""
"""
Allows the user to use
chromosome[index] = gene
to set the indexed gene.
"""
self.gene_list[index] = gene
def __len__(self):
"""Returns the number of genes in the chromosome"""
"""
Allows the user to use
size = len(chromosome)
to get the length of the chromosome.
"""
return len(self.gene_list)
def __contains__(self, searched_gene):
"""Returns True if the chromosome contains the gene and False otherwise.
Ex. if chromosome in ga.population: ..."""
"""
Allows the user to use
if gene in chromosome
to check if a gene is in the chromosome.
"""
return (searched_gene in self.gene_list)
def index_of(self, searched_gene):
"""Returns the index of the gene in the current chromosome."""
"""
Allows the user to use
index = chromosome.index_of(gene)
to find the index of a gene in the chromosome.
Be sure to check if the chromosome contains the gene
first, or to catch an exception if the gene is not
in the chromosome.
"""
return self.gene_list.index(searched_gene)
def __repr__(self):
"""Create a backend string of the chromosome. Ex '1, 2, 3'."""
"""
Allows the user to use
repr(chromosome)
to get a backend representation of the chromosome.
"""
return ', '.join(repr(gene) for gene in self)
def __str__(self):
"""Create a printable string of the chromosome. Ex '[1][2][3]'."""
"""
Allows the user to use
str(chromosome)
print(chromosome)
to get a frontend representation of the chromosome.
"""
return ''.join(str(gene) for gene in self)

View File

@ -29,10 +29,19 @@ class Gene:
def __repr__(self):
"""Create a backend string of the chromosome. Ex '1'."""
"""
Allows the user to use
repr(gene)
to get a backend representation of the gene.
"""
return str(self.value)
def __str__(self):
"""Create a printable string of the chromosome. Ex '[1]'."""
"""
Allows the user to use
str(gene)
print(gene)
to get a frontend representation of the gene.
"""
return f'[{str(self.value)}]'

View File

@ -152,41 +152,75 @@ class Population:
def __iter__(self):
"""Returns an iterable of chromosomes"""
"""
Allows the user to use
iter(population)
list(population) == population.chromosome_list
tuple(population)
for chromosome in population
to loop through the population.
"""
return iter(self.chromosome_list)
def __getitem__(self, index):
"""Returns the indexed chromosome"""
"""
Allows the user to use
chromosome = population[index]
to get the indexed chromosome.
"""
return self.chromosome_list[index]
def __setitem__(self, index, chromosome):
"""Sets the indexed chromosome"""
"""
Allows the user to use
population[index] = chromosome
to set the indexed chromosome.
"""
self.chromosome_list[index] = chromosome
def __len__(self):
"""Returns the number of chromosomes in the current population"""
"""
Allows the user to use
size = len(population)
to get the length of the population.
"""
return len(self.chromosome_list)
def __contains__(self, searched_chromosome):
"""Returns True if the current population contains the chromosome and False otherwise.
Ex. if chromosome in ga.population: ..."""
"""
Allows the user to use
if chromosome in population
to check if a chromosome is in the population.
"""
return (searched_chromosome in self.chromosome_list)
def index_of(self, searched_chromosome):
"""Returns the index of the chromosome in the current population."""
"""
Allows the user to use
index = population.index_of(chromosome)
to find the index of a chromosome in the population.
Be sure to check if the population contains the chromosome
first, or to catch an exception if the chromosome is not
in the population.
"""
return self.chromosome_list.index(searched_chromosome)
def __repr__(self):
"""Returns a backend string representation of the entire population"""
"""
Allows the user to use
repr(population)
str(population)
print(population)
to get a backend representation of the population.
"""
return ''.join(
f'Chromosome - {index} {chromosome} ' +
f'/ Fitness = {chromosome.fitness}\n'

View File

@ -1,12 +1,14 @@
import random
def append_to_next_population(survivor_method):
"""Appends the selected chromosomes to the next population."""
return lambda ga: ga.population.append_children(survivor_method(ga))
class Survivor_Selection:
"""Survivor selection determines which individuals should be brought to the next generation"""
# Private method decorator, see above.
def __append_to_next_population(survivor_method):
return append_to_next_population(survivor_method)

View File

@ -1,4 +1,6 @@
def add_by_fitness_goal(termination_impl):
"""Adds termination by fitness goal to the method."""
def helper(ga):
# If fitness goal is set, check it.
@ -18,6 +20,8 @@ def add_by_fitness_goal(termination_impl):
def add_by_generation_goal(termination_impl):
"""Adds termination by generation goal to the method."""
def helper(ga):
# If generation goal is set, check it.
@ -30,6 +34,8 @@ def add_by_generation_goal(termination_impl):
def add_by_tolerance_goal(termination_impl):
"""Adds termination by tolerance goal to the method."""
def helper(ga):
# If tolerance is set, check it.
@ -50,6 +56,7 @@ def add_by_tolerance_goal(termination_impl):
class Termination_Methods:
"""Example functions that can be used to terminate the the algorithms loop"""
# Private method decorators, see above.
def __add_by_fitness_goal(termination_impl):
return add_by_fitness_goal(termination_impl)
def __add_by_generation_goal(termination_impl):