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.
|
# Otherwise evolve the population.
|
||||||
else:
|
else:
|
||||||
|
|
||||||
self.parent_selection_impl(self)
|
self.parent_selection_impl(self)
|
||||||
self.crossover_population_impl(self)
|
self.crossover_population_impl(self)
|
||||||
self.survivor_selection_impl(self)
|
self.survivor_selection_impl(self)
|
||||||
@ -79,13 +80,17 @@ class GA(Attributes):
|
|||||||
# Save the population to the database
|
# Save the population to the database
|
||||||
self.save_population()
|
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
|
number_of_generations -= 1
|
||||||
self.current_generation += 1
|
self.current_generation += 1
|
||||||
|
|
||||||
self.adapt()
|
|
||||||
|
|
||||||
|
def evolve(self, number_of_generations = 100, consider_termination = True):
|
||||||
def evolve(self, number_of_generations = 1, consider_termination = True):
|
|
||||||
"""Runs the ga until the termination point has been satisfied."""
|
"""Runs the ga until the termination point has been satisfied."""
|
||||||
|
|
||||||
while self.active():
|
while self.active():
|
||||||
@ -99,20 +104,21 @@ class GA(Attributes):
|
|||||||
|
|
||||||
|
|
||||||
def adapt(self):
|
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
|
"""Modifies the parent ratio and mutation rates
|
||||||
based on the adapt rate and percent converged.
|
based on the adapt rate and percent converged.
|
||||||
Attempts to balance out so that a portion of the
|
Attempts to balance out so that a portion of the
|
||||||
population gradually approaches the solution.
|
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
|
# 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
|
return
|
||||||
|
|
||||||
# Amount of the population desired to converge (default 50%)
|
# 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))
|
tol = lambda i: sqrt(abs(best_chromosome.fitness - self.population[i].fitness))
|
||||||
|
|
||||||
# Change rates with:
|
# Change rates with:
|
||||||
multiplier = 1 + self.adapt_rate
|
multiplier = 1 + self.adapt_probability_rate
|
||||||
|
|
||||||
self.parent_ratio = min(self.percent_converged, self.parent_ratio * multiplier)
|
|
||||||
|
|
||||||
# Too few converged: cross more and mutate less
|
# 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.selection_probability = min(
|
||||||
self.chromosome_mutation_rate = max(0.05, self.chromosome_mutation_rate / multiplier)
|
self.max_selection_probability,
|
||||||
self.gene_mutation_rate = max(0.01, self.gene_mutation_rate / multiplier)
|
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
|
# Too many converged: cross less and mutate more
|
||||||
else:
|
else:
|
||||||
|
|
||||||
self.selection_probability = max(0.01, self.selection_probability / multiplier)
|
self.selection_probability = max(
|
||||||
self.chromosome_mutation_rate = min(0.25, self.chromosome_mutation_rate * multiplier)
|
self.min_selection_probability,
|
||||||
self.gene_mutation_rate = min(0.99, self.gene_mutation_rate * multiplier)
|
self.selection_probability / multiplier
|
||||||
|
)
|
||||||
|
|
||||||
# First non-zero tolerance after amount_converged/8
|
self.chromosome_mutation_rate = min(
|
||||||
for i in range(amount_converged//8, len(self.population)):
|
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:
|
if tol(i) > 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
# First significantly different tolerance
|
# First significantly different tolerance
|
||||||
for j in range(i, len(self.population)):
|
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
|
break
|
||||||
|
|
||||||
# Strongly cross the best chromosome with the worst chromosomes
|
# Strongly cross the best chromosome with the worst chromosomes
|
||||||
@ -161,7 +212,7 @@ class GA(Attributes):
|
|||||||
self,
|
self,
|
||||||
self.population[n],
|
self.population[n],
|
||||||
best_chromosome,
|
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,
|
# If negative weights can't be used,
|
||||||
@ -170,7 +221,7 @@ class GA(Attributes):
|
|||||||
self.population[n] = self.crossover_individual_impl(
|
self.population[n] = self.crossover_individual_impl(
|
||||||
self,
|
self,
|
||||||
self.population[n],
|
self.population[n],
|
||||||
self.population[j],
|
self.population[k],
|
||||||
0.75
|
0.75
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -48,47 +48,55 @@ class Attributes:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
chromosome_length = 10,
|
chromosome_length = 10,
|
||||||
population_size = 10,
|
population_size = 10,
|
||||||
chromosome_impl = None,
|
chromosome_impl = None,
|
||||||
gene_impl = lambda: random.randint(1, 10),
|
gene_impl = lambda: random.randint(1, 10),
|
||||||
population = None,
|
population = None,
|
||||||
target_fitness_type = 'max',
|
target_fitness_type = 'max',
|
||||||
update_fitness = True,
|
update_fitness = True,
|
||||||
parent_ratio = 0.10,
|
parent_ratio = 0.10,
|
||||||
selection_probability = 0.50,
|
selection_probability = 0.50,
|
||||||
tournament_size_ratio = 0.10,
|
tournament_size_ratio = 0.10,
|
||||||
current_generation = 0,
|
current_generation = 0,
|
||||||
current_fitness = 0,
|
current_fitness = 0,
|
||||||
generation_goal = 15,
|
generation_goal = 15,
|
||||||
fitness_goal = None,
|
fitness_goal = None,
|
||||||
tolerance_goal = None,
|
tolerance_goal = None,
|
||||||
percent_converged = 0.50,
|
percent_converged = 0.50,
|
||||||
chromosome_mutation_rate = 0.15,
|
chromosome_mutation_rate = 0.15,
|
||||||
gene_mutation_rate = 0.05,
|
gene_mutation_rate = 0.05,
|
||||||
adapt_rate = 0.15,
|
adapt_rate = 0.20,
|
||||||
initialization_impl = Initialization_Methods.random_initialization,
|
adapt_probability_rate = 0.15,
|
||||||
fitness_function_impl = Fitness_Examples.is_it_5,
|
adapt_population_flag = True,
|
||||||
make_population = create_population,
|
max_selection_probability = 0.99,
|
||||||
make_chromosome = create_chromosome,
|
max_chromosome_mutation_rate = 0.20,
|
||||||
make_gene = create_gene,
|
max_gene_mutation_rate = None,
|
||||||
parent_selection_impl = Parent_Selection.Rank.tournament,
|
min_selection_probability = 0.01,
|
||||||
crossover_individual_impl = Crossover_Methods.Individual.single_point,
|
min_chromosome_mutation_rate = 0.01,
|
||||||
crossover_population_impl = Crossover_Methods.Population.sequential_selection,
|
min_gene_mutation_rate = None,
|
||||||
survivor_selection_impl = Survivor_Selection.fill_in_best,
|
initialization_impl = Initialization_Methods.random_initialization,
|
||||||
mutation_individual_impl = Mutation_Methods.Individual.individual_genes,
|
fitness_function_impl = Fitness_Examples.is_it_5,
|
||||||
mutation_population_impl = Mutation_Methods.Population.random_avoid_best,
|
make_population = create_population,
|
||||||
termination_impl = Termination_Methods.fitness_generation_tolerance,
|
make_chromosome = create_chromosome,
|
||||||
Database = sql_database.SQL_Database,
|
make_gene = create_gene,
|
||||||
database_name = 'database.db',
|
parent_selection_impl = Parent_Selection.Rank.tournament,
|
||||||
sql_create_data_structure = """CREATE TABLE IF NOT EXISTS data (
|
crossover_individual_impl = Crossover_Methods.Individual.single_point,
|
||||||
id INTEGER PRIMARY KEY,
|
crossover_population_impl = Crossover_Methods.Population.sequential_selection,
|
||||||
config_id INTEGER DEFAULT NULL,
|
survivor_selection_impl = Survivor_Selection.fill_in_best,
|
||||||
generation INTEGER NOT NULL,
|
mutation_individual_impl = Mutation_Methods.Individual.individual_genes,
|
||||||
fitness REAL,
|
mutation_population_impl = Mutation_Methods.Population.random_avoid_best,
|
||||||
chromosome TEXT
|
termination_impl = Termination_Methods.fitness_generation_tolerance,
|
||||||
); """,
|
Database = sql_database.SQL_Database,
|
||||||
Graph = matplotlib_graph.Matplotlib_Graph
|
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
|
# Initilization variables
|
||||||
@ -113,6 +121,16 @@ class Attributes:
|
|||||||
self.tolerance_goal = deepcopy(tolerance_goal)
|
self.tolerance_goal = deepcopy(tolerance_goal)
|
||||||
self.percent_converged = deepcopy(percent_converged)
|
self.percent_converged = deepcopy(percent_converged)
|
||||||
self.adapt_rate = deepcopy(adapt_rate)
|
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
|
# Mutation variables
|
||||||
self.chromosome_mutation_rate = deepcopy(chromosome_mutation_rate)
|
self.chromosome_mutation_rate = deepcopy(chromosome_mutation_rate)
|
||||||
|
|||||||
Reference in New Issue
Block a user