Cleaned up spacing and added exception checking
This commit is contained in:
@ -1,40 +1,91 @@
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
def append_children_to_next_population(crossover_method):
|
def append_children_to_next_population(population_method):
|
||||||
"""Appends the new chromosomes to the next population."""
|
"""Appends the new chromosomes to the next population.
|
||||||
|
Also modifies the input to include the mating pool.
|
||||||
|
"""
|
||||||
|
|
||||||
return lambda ga:\
|
return lambda ga:\
|
||||||
ga.population.append_children(
|
ga.population.append_children(
|
||||||
list(crossover_method(ga, ga.population.mating_pool))
|
list(population_method(ga, ga.population.mating_pool))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def genes_to_chromosome(crossover_method):
|
def weak_check_exceptions(population_method):
|
||||||
|
"""Checks if the first two chromosomes can be crossed."""
|
||||||
|
|
||||||
|
def new_method(ga, mating_pool):
|
||||||
|
|
||||||
|
# check if any genes are an Exception from
|
||||||
|
# crossing just the first two parents.
|
||||||
|
for gene in ga.crossover_individual_impl(ga, mating_pool[0], mating_pool[1]):
|
||||||
|
if isinstance(gene.value, Exception):
|
||||||
|
raise gene.value
|
||||||
|
|
||||||
|
# continue if no Exceptions found.
|
||||||
|
else:
|
||||||
|
return population_method(ga, ga.population.mating_pool)
|
||||||
|
|
||||||
|
return new_method
|
||||||
|
|
||||||
|
|
||||||
|
def strong_check_exceptions(population_method):
|
||||||
|
"""Checks if every pair of selected chromosomes can be crossed.
|
||||||
|
Warning: Very slow, consider comparing the types of genes
|
||||||
|
allowed to the method used beforehand instead.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def new_method(ga, mating_pool):
|
||||||
|
|
||||||
|
next_population = list(population_method(ga, ga.population.mating_pool))
|
||||||
|
|
||||||
|
# check if any genes are an Exception.
|
||||||
|
for chromosome in next_population:
|
||||||
|
for gene in chromosome:
|
||||||
|
if isinstance(gene.value, Exception):
|
||||||
|
raise gene.value
|
||||||
|
|
||||||
|
# continue if no Exceptions found.
|
||||||
|
else:
|
||||||
|
return next_population
|
||||||
|
|
||||||
|
return new_method
|
||||||
|
|
||||||
|
|
||||||
|
def genes_to_chromosome(individual_method):
|
||||||
"""Converts a collection of genes into a chromosome.
|
"""Converts a collection of genes into a chromosome.
|
||||||
Note: Will recreate the gene list if given gene list.
|
Note: Will recreate the gene list if given gene list.
|
||||||
Methods used here do not construct gene lists
|
Built-in methods do not construct gene lists
|
||||||
and use yield for efficiency.
|
and use yield for efficiency.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return lambda ga, parent_1, parent_2:\
|
return lambda ga, parent_1, parent_2:\
|
||||||
ga.make_chromosome(
|
ga.make_chromosome(
|
||||||
list(crossover_method(ga, parent_1, parent_2))
|
list(individual_method(ga, parent_1, parent_2))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def values_to_genes(crossover_method):
|
def values_to_genes(individual_method):
|
||||||
"""Converts a collection of values into genes."""
|
"""Converts a collection of values into genes.
|
||||||
|
Returns a generator of genes to avoid storing a new list.
|
||||||
|
"""
|
||||||
|
|
||||||
return lambda ga, parent_1, parent_2:\
|
return lambda ga, parent_1, parent_2:\
|
||||||
(ga.make_gene(value) for value in crossover_method(ga, parent_1, parent_2))
|
(
|
||||||
|
ga.make_gene(value)
|
||||||
|
for value
|
||||||
|
in individual_method(ga, parent_1, parent_2)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Crossover_Methods:
|
class Crossover_Methods:
|
||||||
|
|
||||||
# Private method decorators, see above.
|
# Private method decorators, see above.
|
||||||
def _append_children_to_next_population(crossover_method):
|
_append_children_to_next_population = append_children_to_next_population
|
||||||
return append_children_to_next_population(crossover_method)
|
_weak_check_exceptions = weak_check_exceptions
|
||||||
def _genes_to_chromosome(crossover_method):
|
_strong_check_exceptions = strong_check_exceptions
|
||||||
return values_to_chromosome(crossover_method)
|
_genes_to_chromosome = genes_to_chromosome
|
||||||
def _values_to_genes(crossover_method):
|
_values_to_genes = values_to_genes
|
||||||
return values_to_genes(crossover_method)
|
|
||||||
|
|
||||||
|
|
||||||
class Population:
|
class Population:
|
||||||
@ -42,6 +93,7 @@ class Crossover_Methods:
|
|||||||
|
|
||||||
|
|
||||||
@append_children_to_next_population
|
@append_children_to_next_population
|
||||||
|
@weak_check_exceptions
|
||||||
def sequential_selection(ga, mating_pool):
|
def sequential_selection(ga, mating_pool):
|
||||||
"""Select sequential pairs from the mating pool.
|
"""Select sequential pairs from the mating pool.
|
||||||
Every parent is paired with the previous parent.
|
Every parent is paired with the previous parent.
|
||||||
@ -50,24 +102,25 @@ class Crossover_Methods:
|
|||||||
|
|
||||||
for index in range(len(mating_pool)): # for each parent in the mating pool
|
for index in range(len(mating_pool)): # for each parent in the mating pool
|
||||||
yield ga.crossover_individual_impl( # apply crossover to
|
yield ga.crossover_individual_impl( # apply crossover to
|
||||||
ga, #
|
ga, #
|
||||||
mating_pool[index], # the parent and
|
mating_pool[index], # the parent and
|
||||||
mating_pool[index-1] # the previous parent
|
mating_pool[index-1] # the previous parent
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@append_children_to_next_population
|
@append_children_to_next_population
|
||||||
|
@weak_check_exceptions
|
||||||
def random_selection(ga, mating_pool):
|
def random_selection(ga, mating_pool):
|
||||||
"""Select random pairs from the mating pool.
|
"""Select random pairs from the mating pool.
|
||||||
Every parent is paired with a random parent.
|
Every parent is paired with a random parent.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for parent in mating_pool: # for each parent in the mating pool
|
for parent in mating_pool: # for each parent in the mating pool
|
||||||
yield ga.crossover_individual_impl( # apply crossover to
|
yield ga.crossover_individual_impl( # apply crossover to
|
||||||
ga, #
|
ga, #
|
||||||
parent, # the parent and
|
parent, # the parent and
|
||||||
random.choice(mating_pool) # a random parent
|
random.choice(mating_pool) # a random parent
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Individual:
|
class Individual:
|
||||||
@ -101,53 +154,35 @@ class Crossover_Methods:
|
|||||||
|
|
||||||
@genes_to_chromosome
|
@genes_to_chromosome
|
||||||
@values_to_genes
|
@values_to_genes
|
||||||
def int_random(ga, parent_1, parent_2):
|
def random(ga, parent_1, parent_2):
|
||||||
"""Cross two parents by taking a random integer value between each of the genes."""
|
"""Cross two parents by taking a random integer or float value between each of the genes."""
|
||||||
|
|
||||||
value_list_1 = parent_1.gene_value_list
|
value_iter_1 = parent_1.gene_value_iter
|
||||||
value_list_2 = parent_2.gene_value_list
|
value_iter_2 = parent_2.gene_value_iter
|
||||||
|
|
||||||
for value_1, value_2 in zip(value_list_1, value_list_2):
|
for value_1, value_2 in zip(value_iter_1, value_iter_2):
|
||||||
yield random.randint(*sorted([value_1, value_2]))
|
if type(value_1) == type(value_2) == int:
|
||||||
|
yield random.randint(*sorted([value_1, value_2]))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
yield random.uniform([value_1, value_2])
|
||||||
|
except:
|
||||||
|
yield ValueError("Unhandled gene type found. Use integer or float genes.")
|
||||||
|
|
||||||
|
|
||||||
@genes_to_chromosome
|
@genes_to_chromosome
|
||||||
@values_to_genes
|
@values_to_genes
|
||||||
def int_weighted(ga, parent_1, parent_2):
|
def average(ga, parent_1, parent_2):
|
||||||
"""Cross two parents by taking a a weighted average of the genes."""
|
"""Cross two parents by taking a a weighted average of the genes."""
|
||||||
|
|
||||||
# the percentage of genes taken from the first gene
|
value_iter_1 = parent_1.gene_value_iter
|
||||||
weight = 0.25
|
value_iter_2 = parent_2.gene_value_iter
|
||||||
|
|
||||||
value_list_1 = parent_1.gene_value_list
|
for value_1, value_2 in zip(value_iter_1, value_iter_2):
|
||||||
value_list_2 = parent_2.gene_value_list
|
if type(value_1) == type(value_2) == int:
|
||||||
|
yield (value_1+value_2)//2
|
||||||
for value_1, value_2 in zip(value_list_1, value_list_2):
|
else:
|
||||||
yield int(weight*value_1+(1-weight)*value_2)
|
try:
|
||||||
|
yield (value_1+value_2)/2
|
||||||
|
except:
|
||||||
@genes_to_chromosome
|
raise ValueError("Could not take the average of the gene values. Use integer or float genes.")
|
||||||
@values_to_genes
|
|
||||||
def float_random(ga, parent_one, parent_two):
|
|
||||||
"""Cross two parents by taking a random numeric value between each of the genes."""
|
|
||||||
|
|
||||||
value_list_1 = parent_1.gene_value_list
|
|
||||||
value_list_2 = parent_2.gene_value_list
|
|
||||||
|
|
||||||
for value_1, value_2 in zip(value_list_1, value_list_2):
|
|
||||||
yield random.uniform([value_1, value_2])
|
|
||||||
|
|
||||||
|
|
||||||
@genes_to_chromosome
|
|
||||||
@values_to_genes
|
|
||||||
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
|
|
||||||
|
|
||||||
value_list_1 = parent_1.gene_value_list
|
|
||||||
value_list_2 = parent_2.gene_value_list
|
|
||||||
|
|
||||||
for value_1, value_2 in zip(value_list_1, value_list_2):
|
|
||||||
yield weight*value_1+(1-weight)*value_2
|
|
||||||
|
|||||||
Reference in New Issue
Block a user