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 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 Crossover_Methods:
class Population: class Population:
"""Methods for selecting chromosomes to crossover.""" """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): 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.
@ -29,7 +37,7 @@ class Crossover_Methods:
) )
@__append_children_from_mating_pool @append_children_from_mating_pool
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.
@ -46,13 +54,6 @@ class Crossover_Methods:
class Individual: class Individual:
"""Methods for crossing parents.""" """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): def single_point(ga, parent_1, parent_2):
"""Cross two parents by swapping genes at one random point.""" """Cross two parents by swapping genes at one random point."""
@ -77,7 +78,7 @@ class Crossover_Methods:
class Arithmetic: class Arithmetic:
"""Crossover methods for numerical genes.""" """Crossover methods for numerical genes."""
@Individual._Individual__values_to_chromosome @values_to_chromosome
def int_random(ga, parent_1, parent_2): def int_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 value between each of the genes."""
@ -88,7 +89,7 @@ class Crossover_Methods:
yield random.randint(*sorted([value_1, value_2])) yield random.randint(*sorted([value_1, value_2]))
@Individual._Individual__values_to_chromosome @values_to_chromosome
def int_weighted(ga, parent_1, parent_2): def int_weighted(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."""
@ -102,7 +103,7 @@ class Crossover_Methods:
yield int(weight*value_1+(1-weight)*value_2) 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): def float_random(ga, parent_one, parent_two):
"""Cross two parents by taking a random numeric value between each of the genes.""" """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]) yield random.uniform([value_1, value_2])
@Individual._Individual__values_to_chromosome @values_to_chromosome
def float_weighted(ga, parent_one, parent_two): def float_weighted(ga, parent_one, parent_two):
"""Cross two parents by taking a a weighted average of the genes.""" """Cross two parents by taking a a weighted average of the genes."""

View File

@ -1,12 +1,7 @@
import random import random
from math import ceil from math import ceil
class Mutation_Methods: def loop_selections(selection_method):
class Population:
"""Methods for selecting chromosomes to mutate"""
def __loop_selections(selection_method):
def helper(ga): def helper(ga):
# Loop until enough mutations occur # Loop until enough mutations occur
for n in range(ceil(len(ga.population)*ga.chromosome_mutation_rate)): for n in range(ceil(len(ga.population)*ga.chromosome_mutation_rate)):
@ -14,27 +9,7 @@ class Mutation_Methods:
return helper return helper
@__loop_selections def loop_mutations(mutation_method):
def random_selection(ga):
"""Selects random chromosomes"""
index = random.randint(0, len(ga.population)-1)
ga.population[index] = ga.mutation_individual_impl(ga, ga.population[index])
@__loop_selections
def random_selection_then_cross(ga):
"""Selects random chromosomes and self-crosses with parent"""
index = random.randint(0, len(ga.population)-1)
chromosome = ga.population[index]
ga.population[index] = ga.crossover_individual_impl(ga, chromosome, ga.mutation_individual_impl(ga, chromosome))
class Individual:
"""Methods for mutating a single chromosome"""
def __loop_mutations(mutation_method):
def helper(ga, old_chromosome): def helper(ga, old_chromosome):
chromosome = ga.make_chromosome(list(old_chromosome)) chromosome = ga.make_chromosome(list(old_chromosome))
@ -46,7 +21,34 @@ class Mutation_Methods:
return helper return helper
@__loop_mutations class Mutation_Methods:
class Population:
"""Methods for selecting chromosomes to mutate"""
@loop_selections
def random_selection(ga):
"""Selects random chromosomes"""
index = random.randint(0, len(ga.population)-1)
ga.population[index] = ga.mutation_individual_impl(ga, ga.population[index])
@loop_selections
def random_selection_then_cross(ga):
"""Selects random chromosomes and self-crosses with parent"""
index = random.randint(0, len(ga.population)-1)
chromosome = ga.population[index]
ga.population[index] = ga.crossover_individual_impl(ga, chromosome, ga.mutation_individual_impl(ga, chromosome))
class Individual:
"""Methods for mutating a single chromosome"""
@loop_mutations
def individual_genes(ga, chromosome): def individual_genes(ga, chromosome):
"""Mutates a random gene in the chromosome and resets the fitness.""" """Mutates a random gene in the chromosome and resets the fitness."""
index = random.randint(0, len(chromosome)-1) index = random.randint(0, len(chromosome)-1)
@ -68,7 +70,7 @@ class Mutation_Methods:
"""Methods for mutating a chromosome """Methods for mutating a chromosome
by changing the order of the genes.""" by changing the order of the genes."""
@Individual._Individual__loop_mutations @loop_mutations
def swap_genes(ga, chromosome): def swap_genes(ga, chromosome):
"""Mutates a random gene in the chromosome and resets the fitness.""" """Mutates a random gene in the chromosome and resets the fitness."""

View File

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

View File

@ -1,13 +1,13 @@
import random import random
def append_to_next_population(survivor_method):
return lambda ga: ga.population.append_children(survivor_method(ga))
class Survivor_Selection: class Survivor_Selection:
"""Survivor selection determines which individuals should be brought to the next generation""" """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): def fill_in_best(ga):
"""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"""
@ -15,7 +15,7 @@ class Survivor_Selection:
return ga.population[:needed_amount] return ga.population[:needed_amount]
@__append_to_next_population @append_to_next_population
def fill_in_random(ga): def fill_in_random(ga):
"""Fills in the next population with random chromosomes from the last population""" """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)] 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): def fill_in_parents_then_random(ga):
"""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"""