Split adapting into parts

This commit is contained in:
SimpleArt
2020-12-03 17:10:39 -05:00
parent 6d0ec0c30e
commit 046592a3a8
2 changed files with 135 additions and 66 deletions

View File

@ -66,6 +66,7 @@ class GA(Attributes):
# Otherwise evolve the population.
else:
self.parent_selection_impl(self)
self.crossover_population_impl(self)
self.survivor_selection_impl(self)
@ -79,13 +80,17 @@ class GA(Attributes):
# Save the population to the database
self.save_population()
# Adapt the ga if the generation times the adapt rate
# passes through an integer value.
adapt_counter = self.adapt_rate*self.current_generation
if int(adapt_counter) > int(adapt_counter - self.adapt_rate):
self.adapt()
number_of_generations -= 1
self.current_generation += 1
self.adapt()
def evolve(self, number_of_generations = 1, consider_termination = True):
def evolve(self, number_of_generations = 100, consider_termination = True):
"""Runs the ga until the termination point has been satisfied."""
while self.active():
@ -99,20 +104,21 @@ class GA(Attributes):
def adapt(self):
"""Adapts the ga to hopefully get better results."""
self.adapt_probabilities()
self.adapt_population()
def adapt_probabilities(self):
"""Modifies the parent ratio and mutation rates
based on the adapt rate and percent converged.
Attempts to balance out so that a portion of the
population gradually approaches the solution.
Afterwards also performs weighted crossover between
the best chromosome and the rest of the chromosomes,
using negative weights to push away chromosomes that
are too similar and small positive weights to pull
in chromosomes that are too different.
"""
# Don't adapt
if self.adapt_rate is None or self.adapt_rate <= 0:
if self.adapt_probability_rate is None or self.adapt_probability_rate <= 0:
return
# Amount of the population desired to converge (default 50%)
@ -123,32 +129,77 @@ class GA(Attributes):
tol = lambda i: sqrt(abs(best_chromosome.fitness - self.population[i].fitness))
# Change rates with:
multiplier = 1 + self.adapt_rate
self.parent_ratio = min(self.percent_converged, self.parent_ratio * multiplier)
multiplier = 1 + self.adapt_probability_rate
# Too few converged: cross more and mutate less
if tol(amount_converged//2) > tol(amount_converged//2)*4:
if tol(amount_converged//2) > tol(amount_converged//4)*2:
self.selection_probability = min(0.99, self.selection_probability * multiplier)
self.chromosome_mutation_rate = max(0.05, self.chromosome_mutation_rate / multiplier)
self.gene_mutation_rate = max(0.01, self.gene_mutation_rate / multiplier)
self.selection_probability = min(
self.max_selection_probability,
self.selection_probability * multiplier
)
self.chromosome_mutation_rate = max(
self.min_chromosome_mutation_rate,
self.chromosome_mutation_rate / multiplier
)
self.gene_mutation_rate = max(
self.min_gene_mutation_rate,
self.gene_mutation_rate / multiplier
)
# Too many converged: cross less and mutate more
else:
self.selection_probability = max(0.01, self.selection_probability / multiplier)
self.chromosome_mutation_rate = min(0.25, self.chromosome_mutation_rate * multiplier)
self.gene_mutation_rate = min(0.99, self.gene_mutation_rate * multiplier)
self.selection_probability = max(
self.min_selection_probability,
self.selection_probability / multiplier
)
# First non-zero tolerance after amount_converged/8
for i in range(amount_converged//8, len(self.population)):
self.chromosome_mutation_rate = min(
self.max_chromosome_mutation_rate,
self.chromosome_mutation_rate * multiplier
)
self.gene_mutation_rate = min(
self.max_gene_mutation_rate,
self.gene_mutation_rate * multiplier
)
def adapt_population(self):
"""
Performs weighted crossover between the best chromosome and
the rest of the chromosomes, using negative weights to push
away chromosomes that are too similar and small positive
weights to pull in chromosomes that are too different.
"""
# Don't adapt the population.
if self.adapt_population_flag == False:
return
# Amount of the population desired to converge (default 50%)
amount_converged = round(self.percent_converged*len(self.population))
# Difference between best and i-th chromosomes
best_chromosome = self.population[0]
tol = lambda i: sqrt(abs(best_chromosome.fitness - self.population[i].fitness))
# First non-zero tolerance after amount_converged/4
for i in range(amount_converged//4, len(self.population)):
if tol(i) > 0:
break
# First significantly different tolerance
for j in range(i, len(self.population)):
if tol(j) > 4*tol(i):
if tol(j) > 2*tol(i):
break
# Second significantly different tolerance
for k in range(j, len(self.population)):
if tol(k) > 2*tol(j):
break
# Strongly cross the best chromosome with the worst chromosomes
@ -161,7 +212,7 @@ class GA(Attributes):
self,
self.population[n],
best_chromosome,
min(0.25, (4*tol(n) - tol(j)) / tol(n))
min(0.25, (2*tol(n) - tol(j)) / tol(n))
)
# If negative weights can't be used,
@ -170,7 +221,7 @@ class GA(Attributes):
self.population[n] = self.crossover_individual_impl(
self,
self.population[n],
self.population[j],
self.population[k],
0.75
)

View File

@ -48,47 +48,55 @@ class Attributes:
}
def __init__(self,
chromosome_length = 10,
population_size = 10,
chromosome_impl = None,
gene_impl = lambda: random.randint(1, 10),
population = None,
target_fitness_type = 'max',
update_fitness = True,
parent_ratio = 0.10,
selection_probability = 0.50,
tournament_size_ratio = 0.10,
current_generation = 0,
current_fitness = 0,
generation_goal = 15,
fitness_goal = None,
tolerance_goal = None,
percent_converged = 0.50,
chromosome_mutation_rate = 0.15,
gene_mutation_rate = 0.05,
adapt_rate = 0.15,
initialization_impl = Initialization_Methods.random_initialization,
fitness_function_impl = Fitness_Examples.is_it_5,
make_population = create_population,
make_chromosome = create_chromosome,
make_gene = create_gene,
parent_selection_impl = Parent_Selection.Rank.tournament,
crossover_individual_impl = Crossover_Methods.Individual.single_point,
crossover_population_impl = Crossover_Methods.Population.sequential_selection,
survivor_selection_impl = Survivor_Selection.fill_in_best,
mutation_individual_impl = Mutation_Methods.Individual.individual_genes,
mutation_population_impl = Mutation_Methods.Population.random_avoid_best,
termination_impl = Termination_Methods.fitness_generation_tolerance,
Database = sql_database.SQL_Database,
database_name = 'database.db',
sql_create_data_structure = """CREATE TABLE IF NOT EXISTS data (
id INTEGER PRIMARY KEY,
config_id INTEGER DEFAULT NULL,
generation INTEGER NOT NULL,
fitness REAL,
chromosome TEXT
); """,
Graph = matplotlib_graph.Matplotlib_Graph
chromosome_length = 10,
population_size = 10,
chromosome_impl = None,
gene_impl = lambda: random.randint(1, 10),
population = None,
target_fitness_type = 'max',
update_fitness = True,
parent_ratio = 0.10,
selection_probability = 0.50,
tournament_size_ratio = 0.10,
current_generation = 0,
current_fitness = 0,
generation_goal = 15,
fitness_goal = None,
tolerance_goal = None,
percent_converged = 0.50,
chromosome_mutation_rate = 0.15,
gene_mutation_rate = 0.05,
adapt_rate = 0.20,
adapt_probability_rate = 0.15,
adapt_population_flag = True,
max_selection_probability = 0.99,
max_chromosome_mutation_rate = 0.20,
max_gene_mutation_rate = None,
min_selection_probability = 0.01,
min_chromosome_mutation_rate = 0.01,
min_gene_mutation_rate = None,
initialization_impl = Initialization_Methods.random_initialization,
fitness_function_impl = Fitness_Examples.is_it_5,
make_population = create_population,
make_chromosome = create_chromosome,
make_gene = create_gene,
parent_selection_impl = Parent_Selection.Rank.tournament,
crossover_individual_impl = Crossover_Methods.Individual.single_point,
crossover_population_impl = Crossover_Methods.Population.sequential_selection,
survivor_selection_impl = Survivor_Selection.fill_in_best,
mutation_individual_impl = Mutation_Methods.Individual.individual_genes,
mutation_population_impl = Mutation_Methods.Population.random_avoid_best,
termination_impl = Termination_Methods.fitness_generation_tolerance,
Database = sql_database.SQL_Database,
database_name = 'database.db',
sql_create_data_structure = """CREATE TABLE IF NOT EXISTS data (
id INTEGER PRIMARY KEY,
config_id INTEGER DEFAULT NULL,
generation INTEGER NOT NULL,
fitness REAL,
chromosome TEXT
); """,
Graph = matplotlib_graph.Matplotlib_Graph
):
# Initilization variables
@ -113,6 +121,16 @@ class Attributes:
self.tolerance_goal = deepcopy(tolerance_goal)
self.percent_converged = deepcopy(percent_converged)
self.adapt_rate = deepcopy(adapt_rate)
self.adapt_probability_rate = deepcopy(adapt_probability_rate)
self.adapt_population_flag = deepcopy(adapt_population_flag)
# Bounds on probabilities when adapting
self.max_selection_probability = max_selection_probability
self.max_chromosome_mutation_rate = max_chromosome_mutation_rate
self.max_gene_mutation_rate = gene_mutation_rate if (max_gene_mutation_rate is None) else max_gene_mutation_rate
self.min_selection_probability = min_selection_probability
self.min_chromosome_mutation_rate = min_chromosome_mutation_rate
self.min_gene_mutation_rate = gene_mutation_rate if (min_gene_mutation_rate is None) else min_gene_mutation_rate
# Mutation variables
self.chromosome_mutation_rate = deepcopy(chromosome_mutation_rate)