Added more structure methods and some quality of life changes
Overall cleaned up a lot of comments. EasyGA: - Code cleanup. Population: - Added sort_by_best_fitness - Added parent/mating pool methods. - Renamed some methods for consistency. Chromosome: - Added get_gene(index). Parent Selection: - Improved selection methods to use the ga.selection_probability so that the roulette selection actually works well. - Added stochastic selection. Survivor Selection: - Added fill_in_random and fill_in_parents_then_random. Crossover/Mutation: - Cleaned up code.
This commit is contained in:
@ -10,26 +10,40 @@ class Parent_Selection:
|
||||
The total number of parents selected is determined by parent_ratio, an attribute to the GA object.
|
||||
"""
|
||||
|
||||
# Error if can't select parents
|
||||
if ga.selection_probability <= 0:
|
||||
print("Selection probability must be greater than 0 to select parents.")
|
||||
return
|
||||
|
||||
# Make sure the population is sorted by fitness
|
||||
ga.population.sort_by_best_fitness(ga)
|
||||
|
||||
# Choose the tournament size.
|
||||
# Use no less than 5 chromosomes per tournament.
|
||||
tournament_size = int(ga.population.size()*ga.tournament_size_ratio)
|
||||
if tournament_size < 5:
|
||||
tournament_size = 5
|
||||
# Probability used for determining if a chromosome should enter the mating pool.
|
||||
selection_probability = ga.selection_probability
|
||||
|
||||
# Repeat tournaments until the mating pool is large enough.
|
||||
while (len(ga.population.mating_pool) < ga.population.size()*ga.parent_ratio):
|
||||
while (len(ga.population.get_mating_pool()) < ga.population.size()*ga.parent_ratio):
|
||||
|
||||
# Generate a random tournament group and sort by fitness.
|
||||
tournament_group = ga.sort_by_best_fitness([random.choice(ga.population.get_all_chromosomes()) for n in range(tournament_size)])
|
||||
tournament_group = sorted([random.randint(0, ga.population.size()-1) for n in range(tournament_size)])
|
||||
|
||||
# For each chromosome, add it to the mating pool based on its rank in the tournament.
|
||||
for index in range(tournament_size):
|
||||
# Probability required is selection_probability * (1-selection_probability) ^ (tournament_size-index+1)
|
||||
|
||||
# Probability required is selection_probability * (1-selection_probability) ^ index
|
||||
# e.g. top ranked fitness has probability: selection_probability
|
||||
# second ranked fitness has probability: selection_probability * (1-selection_probability)
|
||||
# third ranked fitness has probability: selection_probability * (1-selection_probability)^2
|
||||
# etc.
|
||||
if random.uniform(0, 1) < selection_probability * pow(1-selection_probability, index):
|
||||
ga.population.mating_pool.append(tournament_group[index])
|
||||
if random.uniform(0, 1) < ga.selection_probability * pow(1-ga.selection_probability, index):
|
||||
ga.population.set_parent(tournament_group[index])
|
||||
|
||||
# Stop if parent ratio reached
|
||||
if len(ga.population.get_mating_pool()) >= ga.population.size()*ga.parent_ratio:
|
||||
break
|
||||
|
||||
|
||||
class Roulette:
|
||||
@ -40,27 +54,74 @@ class Parent_Selection:
|
||||
that it will be selected. Using the example of a casino roulette wheel.
|
||||
Where the chromosomes are the numbers to be selected and the board size for
|
||||
those numbers are directly proportional to the chromosome's current fitness. Where
|
||||
the ball falls is a randomly generated number between 0 and 1"""
|
||||
the ball falls is a randomly generated number between 0 and 1."""
|
||||
|
||||
# Make sure the population is sorted by fitness
|
||||
ga.population.sort_by_best_fitness(ga)
|
||||
|
||||
# Error if can't select parents
|
||||
if ga.selection_probability <= 0:
|
||||
print("Selection probability must be greater than 0 to select parents.")
|
||||
return
|
||||
|
||||
# Error if not all chromosomes has positive fitness
|
||||
if (ga.population.get_chromosome(0).get_fitness() == 0 or ga.population.get_chromosome(-1).get_fitness() < 0):
|
||||
print("Error using roulette selection, all fitnesses must be positive.")
|
||||
print("Consider using stockastic roulette selection or tournament selection.")
|
||||
return
|
||||
|
||||
# The sum of all the fitnessess in a population
|
||||
total_fitness = sum(ga.population.chromosome_list[i].get_fitness() for i in range(len(ga.population.chromosome_list)))
|
||||
rel_fitnesses = []
|
||||
|
||||
# A list of each chromosome's relative chance of getting chosen
|
||||
for chromosome in ga.population.chromosome_list:
|
||||
if (total_fitness != 0):
|
||||
rel_fitnesses.append(float(chromosome.fitness)/total_fitness)
|
||||
fitness_sum = sum(chromosome.get_fitness() for chromosome in ga.population.get_all_chromosomes())
|
||||
|
||||
# A list of ranges that represent the probability of a chromosome getting chosen
|
||||
probability = [sum(rel_fitnesses[:i+1]) for i in range(len(rel_fitnesses))]
|
||||
probability = [ga.selection_probability]
|
||||
|
||||
# The chance of being selected increases incrementally
|
||||
for chromosome in ga.population.chromosome_list:
|
||||
probability.append(probability[-1]+chromosome.fitness/fitness_sum)
|
||||
|
||||
probability = probability[1:]
|
||||
|
||||
# Loops until it reaches a desired mating pool size
|
||||
while (len(ga.population.mating_pool) < len(ga.population.get_all_chromosomes())*ga.parent_ratio):
|
||||
while (len(ga.population.get_mating_pool()) < ga.population.size()*ga.parent_ratio):
|
||||
|
||||
# Spin the roulette
|
||||
rand_number = random.random()
|
||||
|
||||
# Loop through the list of probabilities
|
||||
for i in range(len(probability)):
|
||||
# If the probability is greater than the random_number, then select that chromosome
|
||||
if (probability[i] >= rand_number):
|
||||
ga.population.mating_pool.append(ga.population.chromosome_list[i])
|
||||
# Find where the roulette landed.
|
||||
for index in range(len(probability)):
|
||||
if (probability[index] >= rand_number):
|
||||
ga.population.set_parent(index)
|
||||
break
|
||||
|
||||
|
||||
def stochastic_selection(ga):
|
||||
"""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
|
||||
that it will be selected. Instead of dividing the fitness by the sum of all fitnesses
|
||||
and incrementally increasing the chance something is selected, the stochastic method
|
||||
just divides by the highest fitness and selects randomly."""
|
||||
|
||||
# Make sure the population is sorted by fitness
|
||||
ga.population.sort_by_best_fitness(ga)
|
||||
|
||||
# Error if can't select parents
|
||||
if ga.selection_probability <= 0 or ga.selection_probability >= 1:
|
||||
print("Selection probability must be between 0 and 1 to select parents.")
|
||||
return
|
||||
|
||||
# Error if the highest fitness is not positive
|
||||
if ga.population.get_chromosome(0).get_fitness() <= 0:
|
||||
print("Error using stochastic roulette selection, best fitness must be positive.")
|
||||
print("Consider using tournament selection.")
|
||||
return
|
||||
|
||||
# Loops until it reaches a desired mating pool size
|
||||
while (len(ga.population.get_mating_pool()) < ga.population.size()*ga.parent_ratio):
|
||||
|
||||
# Selected chromosome
|
||||
index = random.randint(0, ga.population.size()-1)
|
||||
|
||||
# Probability of becoming a parent is fitness/max_fitness
|
||||
if random.uniform(ga.selection_probability, 1) < ga.population.get_chromosome(index).get_fitness() / ga.population.get_chromosome(0).get_fitness():
|
||||
ga.population.set_parent(index)
|
||||
|
||||
Reference in New Issue
Block a user