Update ga.adapt()

Faster convergence by allowing more genetic variety using negative weights to push chromosomes too similar away from the best chromosome.
This commit is contained in:
SimpleArt
2020-12-01 10:36:17 -05:00
parent 4d273df5ae
commit 5b6d925088

View File

@ -1,3 +1,6 @@
# Import square root function for ga.adapt()
from math import sqrt
# Import all the data structure prebuilt modules # Import all the data structure prebuilt modules
from structure import Population as create_population from structure import Population as create_population
from structure import Chromosome as create_chromosome from structure import Chromosome as create_chromosome
@ -101,9 +104,11 @@ class GA(Attributes):
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 heavily crosses the worst chromosomes Afterwards also performs weighted crossover between
with the best chromosome, depending on how well the the best chromosome and the rest of the chromosomes,
overall population is doing. 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
@ -113,18 +118,9 @@ class GA(Attributes):
# Amount of the population desired to converge (default 50%) # Amount of the population desired to converge (default 50%)
amount_converged = round(self.percent_converged*len(self.population)) amount_converged = round(self.percent_converged*len(self.population))
# How much converged halfway # Difference between best and i-th chromosomes
best_fitness = self.population[0].fitness best_chromosome = self.population[0]
threshhold_fitness = self.population[amount_converged//2].fitness tol = lambda i: sqrt(abs(best_chromosome.fitness - self.population[i].fitness))
# Closeness required for convergence
tol_half = abs(best_fitness - threshhold_fitness)/2
# How much converged a quarter of the way
threshhold_fitness = self.population[amount_converged//4].fitness
# Tolerance result
tol_quar = abs(best_fitness - threshhold_fitness)
# Change rates with: # Change rates with:
multiplier = 1 + self.adapt_rate multiplier = 1 + self.adapt_rate
@ -133,12 +129,10 @@ class GA(Attributes):
min_rate = 0.05 min_rate = 0.05
max_rate = 0.25 max_rate = 0.25
# Adapt twice as fast if it's really bad self.parent_ratio = min(self.percent_converged, self.parent_ratio * multiplier)
if tol_quar < tol_half/2 or tol_quar > tol_half*2:
multiplier **= 2
# Too few converged: cross more and mutate less # Too few converged: cross more and mutate less
if tol_quar > tol_half: if tol(amount_converged//2) > tol(amount_converged//2)*4:
self.selection_probability = min(0.75 , self.selection_probability * multiplier) self.selection_probability = min(0.75 , self.selection_probability * multiplier)
self.chromosome_mutation_rate = max(min_rate, self.chromosome_mutation_rate / multiplier) self.chromosome_mutation_rate = max(min_rate, self.chromosome_mutation_rate / multiplier)
@ -151,15 +145,47 @@ class GA(Attributes):
self.chromosome_mutation_rate = min(max_rate, self.chromosome_mutation_rate * multiplier) self.chromosome_mutation_rate = min(max_rate, self.chromosome_mutation_rate * multiplier)
self.gene_mutation_rate = min(max_rate, self.gene_mutation_rate * multiplier) self.gene_mutation_rate = min(max_rate, self.gene_mutation_rate * multiplier)
# First non-zero tolerance after amount_converged/8
for i in range(amount_converged//8, 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):
break
# Strongly cross the best chromosome with the worst chromosomes # Strongly cross the best chromosome with the worst chromosomes
for n in range(1, amount_converged//4): for n in range(i, len(self.population)):
self.population[-n] = self.crossover_individual_impl(
# Strongly cross with the best chromosome
# May reject negative weight
try:
self.population[n] = self.crossover_individual_impl(
self, self,
self.population[-n], self.population[n],
self.population[0], best_chromosome,
min(0.5, tol_half) min(0.25, (tol(n) - tol(j)/4) / tol(n))
) )
self.population[-n].fitness = self.fitness_function_impl(self.population[-n])
# If negative weights can't be used,
# Cross with j-th chromosome instead
except:
self.population[n] = self.crossover_individual_impl(
self,
self.population[n],
self.population[j],
0.5
)
# Update fitnesses
self.population[n].fitness = self.fitness_function_impl(self.population[n])
if self.target_fitness_type == 'max' and self.population[n].fitness > best_chromosome.fitness:
best_chromosome = self.population[n]
elif self.target_fitness_type == 'min' and self.population[n].fitness < best_chromosome.fitness:
best_chromosome = self.population[n]
self.population.sort_by_best_fitness(self) self.population.sort_by_best_fitness(self)