Split adapting into parts
This commit is contained in:
101
src/EasyGA.py
101
src/EasyGA.py
@ -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
|
||||
)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user