Fixed scope of function decorators

This commit is contained in:
SimpleArt
2020-11-19 22:28:04 -05:00
parent 46b4dc749d
commit 3d10adb2d9
4 changed files with 85 additions and 81 deletions

View File

@ -1,20 +1,28 @@
import random
def append_children_from_mating_pool(crossover_method):
def helper(ga):
mating_pool = ga.population.mating_pool
ga.population.append_children(
[chromosome for chromosome in crossover_method(ga, mating_pool)]
)
return helper
def values_to_chromosome(crossover_method):
def helper(ga, parent_1, parent_2):
return ga.make_chromosome([
ga.make_gene(value)
for value in crossover_method(ga, parent_1, parent_2)])
return helper
class Crossover_Methods:
class Population:
"""Methods for selecting chromosomes to crossover."""
def __append_children_from_mating_pool(crossover_method):
def helper(ga):
mating_pool = ga.population.mating_pool
ga.population.append_children([
[chromosome for cromosome in crossover_method(ga, mating_pool)]
)
return helper
@__append_children_from_mating_pool
@append_children_from_mating_pool
def sequential_selection(ga, mating_pool):
"""Select sequential pairs from the mating pool.
Every parent is paired with the previous parent.
@ -29,7 +37,7 @@ class Crossover_Methods:
)
@__append_children_from_mating_pool
@append_children_from_mating_pool
def random_selection(ga, mating_pool):
"""Select random pairs from the mating pool.
Every parent is paired with a random parent.
@ -46,13 +54,6 @@ class Crossover_Methods:
class Individual:
"""Methods for crossing parents."""
def __values_to_chromosome(crossover_method):
def helper(ga, parent_1, parent_2):
return ga.make_chromosome([
ga.make_gene(value)
for value in crossover_method(ga, parent_1, parent_2)])
return helper
def single_point(ga, parent_1, parent_2):
"""Cross two parents by swapping genes at one random point."""
@ -77,7 +78,7 @@ class Crossover_Methods:
class Arithmetic:
"""Crossover methods for numerical genes."""
@Individual._Individual__values_to_chromosome
@values_to_chromosome
def int_random(ga, parent_1, parent_2):
"""Cross two parents by taking a random integer value between each of the genes."""
@ -88,7 +89,7 @@ class Crossover_Methods:
yield random.randint(*sorted([value_1, value_2]))
@Individual._Individual__values_to_chromosome
@values_to_chromosome
def int_weighted(ga, parent_1, parent_2):
"""Cross two parents by taking a a weighted average of the genes."""
@ -102,7 +103,7 @@ class Crossover_Methods:
yield int(weight*value_1+(1-weight)*value_2)
@Individual._Individual__values_to_chromosome
@values_to_chromosome
def float_random(ga, parent_one, parent_two):
"""Cross two parents by taking a random numeric value between each of the genes."""
@ -113,7 +114,7 @@ class Crossover_Methods:
yield random.uniform([value_1, value_2])
@Individual._Individual__values_to_chromosome
@values_to_chromosome
def float_weighted(ga, parent_one, parent_two):
"""Cross two parents by taking a a weighted average of the genes."""

View File

@ -1,20 +1,33 @@
import random
from math import ceil
def loop_selections(selection_method):
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):
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)
return chromosome
return helper
class Mutation_Methods:
class Population:
"""Methods for selecting chromosomes to mutate"""
def __loop_selections(selection_method):
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
@__loop_selections
@loop_selections
def random_selection(ga):
"""Selects random chromosomes"""
@ -22,7 +35,7 @@ class Mutation_Methods:
ga.population[index] = ga.mutation_individual_impl(ga, ga.population[index])
@__loop_selections
@loop_selections
def random_selection_then_cross(ga):
"""Selects random chromosomes and self-crosses with parent"""
@ -34,19 +47,8 @@ class Mutation_Methods:
class Individual:
"""Methods for mutating a single chromosome"""
def __loop_mutations(mutation_method):
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)
return chromosome
return helper
@__loop_mutations
@loop_mutations
def individual_genes(ga, chromosome):
"""Mutates a random gene in the chromosome and resets the fitness."""
index = random.randint(0, len(chromosome)-1)
@ -68,7 +70,7 @@ class Mutation_Methods:
"""Methods for mutating a chromosome
by changing the order of the genes."""
@Individual._Individual__loop_mutations
@loop_mutations
def swap_genes(ga, chromosome):
"""Mutates a random gene in the chromosome and resets the fitness."""

View File

@ -1,36 +1,37 @@
import random
class Parent_Selection:
def __check_selection_probability(selection_method):
def helper(ga):
if 0 < ga.selection_probability < 1:
selection_method(ga)
else:
raise Exception("Selection probability must be greater than 0 to select parents.")
return helper
def __check_positive_fitness(selection_method):
def helper(ga):
if ga.get_chromosome_fitness(0) == 0 or ga.get_chromosome_fitness(-1) < 0:
raise Exception("Converted fitness values must be all positive. Consider using rank selection instead.")
else:
selection_method(ga)
return helper
def __ensure_sorted(selection_method):
def helper(ga):
ga.population.sort_by_best_fitness(ga)
def check_selection_probability(selection_method):
def helper(ga):
if 0 < ga.selection_probability < 1:
selection_method(ga)
return helper
else:
raise Exception("Selection probability must be greater than 0 to select parents.")
return helper
def check_positive_fitness(selection_method):
def helper(ga):
if ga.get_chromosome_fitness(0) == 0 or ga.get_chromosome_fitness(-1) < 0:
raise Exception("Converted fitness values must be all positive. Consider using rank selection instead.")
else:
selection_method(ga)
return helper
def ensure_sorted(selection_method):
def helper(ga):
ga.population.sort_by_best_fitness(ga)
selection_method(ga)
return helper
class Parent_Selection:
class Rank:
@Parent_Selection._Parent_Selection__check_selection_probability
@Parent_Selection._Parent_Selection__ensure_sorted
@check_selection_probability
@ensure_sorted
def tournament(ga):
"""
Will make tournaments of size tournament_size and choose the winner (best fitness)
@ -68,9 +69,9 @@ class Parent_Selection:
class Fitness:
@Parent_Selection._Parent_Selection__check_selection_probability
@Parent_Selection._Parent_Selection__check_positive_fitness
@Parent_Selection._Parent_Selection__ensure_sorted
@check_selection_probability
@check_positive_fitness
@ensure_sorted
def roulette(ga):
"""Roulette selection works based off of how strong the fitness is of the
chromosomes in the population. The stronger the fitness the higher the probability
@ -105,9 +106,9 @@ class Parent_Selection:
break
@Parent_Selection._Parent_Selection__check_selection_probability
@Parent_Selection._Parent_Selection__check_positive_fitness
@Parent_Selection._Parent_Selection__ensure_sorted
@check_selection_probability
@check_positive_fitness
@ensure_sorted
def stochastic(ga):
"""Stochastic roulette selection works based off of how strong the fitness is of the
chromosomes in the population. The stronger the fitness the higher the probability

View File

@ -1,13 +1,13 @@
import random
def append_to_next_population(survivor_method):
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"""
def __append_to_next_population(survivor_method):
return lambda ga: ga.population.append_children(survivor_method(ga))
@__append_to_next_population
@append_to_next_population
def fill_in_best(ga):
"""Fills in the next population with the best chromosomes from the last population"""
@ -15,7 +15,7 @@ class Survivor_Selection:
return ga.population[:needed_amount]
@__append_to_next_population
@append_to_next_population
def fill_in_random(ga):
"""Fills in the next population with random chromosomes from the last population"""
@ -23,7 +23,7 @@ class Survivor_Selection:
return [random.choice(ga.population) for n in range(needed_amount)]
@__append_to_next_population
@append_to_next_population
def fill_in_parents_then_random(ga):
"""Fills in the next population with all parents followed by random chromosomes from the last population"""