Reputation: 1401
I am trying to run a genetic algorithm that i wrote in python. Unfortunately when there is a mutation the fittest solution can be worse that the fittest solution of the previous generation despite using elitism to pass the fittest solution from the previous generation to the new one. Like so:
There are 86825 generations left
Invalid count is: 0
The fittest individual has a fitness of 16.9094.
The least fit individual has a fitness of 36.6535
*******************************************************************************
mutation on 107
There are 86824 generations left
Invalid count is: 3
The fittest individual has a fitness of 19.8637.
The least fit individual has a fitness of 1.1618e+09
I have tried to implement elitism which I thought would avoid this happening but it still happens. My algorithm executes in this order:
NUM_GEN = 100000
print "Running Genetic Algorithm for %d generations." % NUM_GEN
gen_count = 0
while gen_count < NUM_GEN:
#genetic.add_fitness_key()
genetic.add_fitness_fast()
fittest_list = np.append(fittest_list, genetic.fittest_fitness)
least_fit_list = np.append(least_fit_list, genetic.least_fitness)
genetic.sort_pop()
genetic.make_new_pop()
genetic.elitism()
genetic.population = genetic.new_pop
print "There are %g generations left" %(NUM_GEN-gen_count)
gen_count+=1
And the functions that are called are these:
def select_parent_from_tournament(self):
x = random.randint(0, 19)
player1 = self.population[x]
y = random.randint(0, 19)
player2 = self.population[y]
if player1['fitness'] <= player2['fitness']:
parent = player1['chrom_list']
else:
parent = player2['chrom_list']
return parent
def crossover(self):
crossover_point = random.randint(0, self.chromosome_size)*(self.string_length)
parent1 = self.select_parent_from_tournament()
parent2 = self.select_parent_from_tournament()
parent1 = self.mutate(parent1)
parent2 = self.mutate(parent2)
child1 = parent1[:crossover_point] + parent2[crossover_point:]
child2 = parent1[crossover_point:] + parent2[:crossover_point]
return child1, child2
def mutate(self, chromosome):
for i in range(len(chromosome)):
if random.random() < self.mutation_rate:
print 'mutation on %i' % i
if chromosome[i] =='0':
chromosome[i] = '1'
else:
chromosome[i] = '0'
return chromosome
def make_new_pop(self):
self.new_pop = []
for i in range(10):
dictionary1= {}
dictionary2 = {}
dictionary1['chrom_list'], dictionary2['chrom_list'] = \
self.crossover()
self.new_pop = np.append(self.new_pop, [dictionary1, dictionary2])
def elitism(self):
r = random.randint(0, 19)
self.new_pop[r] = self.population[0]
So I can't understand why the fittest solution from the old population isn't passed to the new population if there is a mutation?
Upvotes: 1
Views: 2027
Reputation: 4177
In your crossover method, you perform the parent1 = self.select_parent_from_tournament()
which returns the reference to the list of a chromosome from the original population. After that, you do a mutation which modifies the list (in the original population). Only after mutation you copy the content in a child via child1 = parent1[:crossover_point] + parent2[crossover_point:]
. As @Xavier was suggesting, you need to create a physical copy of your element from the original population in mutation()
.
In effect, your mutation()
method changed the original population.
As a side note, generally crossover and mutation are two different operations. Also, there are references in the literature suggesting that mutation probability should be kept very low. Otherwise it will keep you from converging.
Upvotes: 4