pythonordereddictionary

Python: Why does this dictionary change even if I don't access it?


I'm trying to implement a genetic algorithm and I'm running into a very weird issue.

I have the following loop for running the genetic algorithm:

while True:
    # Calc population fitness
    population_fitness, best_individual = self.fitness(pop)
    print(best_individual)
    print(max(population_fitness))
    # Select 50% of population
    selected_population = self.selection(pop, population_fitness)
    # Perform crossover
    new_pop = []
    for i in range(pop_size - 1):
        parent1 = selected_population[randrange(0, len(selected_population))]
        parent2 = selected_population[randrange(0, len(selected_population))]
        child = self.crossover(parent1, parent2)
        # Perform mutation
        if randrange(0, 100) > 92:
            child = self.mutation(child)
        new_pop.append(child)
    new_pop.append(best_individual)
    pop = new_pop
    print(pop[-1])

My goal is to keep the best individual from the population, and always copy that to the next population. The best individual is returned by the fitness function. The individuals are OrderedDicts containing some values I try to optimize. What I just don't get is that the the print statement in the fourth line returns a different dictionary to the print statement in the last line. I was thinking it might have to do something with the OrderedDict since I never used those before. Does anyone have any idea?


Solution

  • You print best_individual at the start of the loop. At the end of the loop you append it to the list and print the last element. The output of the print statements is not the same.

    They should be the same object, so if they're printing differently it means the object has been altered. Do any of the methods you've called alter any of the individuals in place (by reassigning keys)? I assume that the best_individual is included in the selected_population since you're apparently selecting 50% of the population based on the fitness information. So if self.crossover or self.mutation assign keys on the objects that are passed in as arguments (parent1, parent2, and child), then you could be accidentally altering the best_individual.

    You might think that if child is a new object then whatever is done to it in self.mutation(child) couldn't possibly be altering best_individual, but there's a potential gotcha I can guess at. You might accidentally be introducing aliasing between the parameters of the individuals. For example, if self.crossover(parent1, parent2) is creating a new individual by randomly choosing each of its key values from either parent1 or parent2, then the values in the new child will be the exact same objects as in the parent. If some of those values are mutable objects (like lists) and they are then altered in place in self.mutation(child) (instead of assigning new values for the keys), then when you mutate a child you'll also be mutating one of the parents, which could have been best_individual.

    If it's not obviously one of those things, some of this code must still be altering best_individual somehow (If pop comes from a instance variable on whatever this class is that you haven't shown us and its contents are aliased rather than copies, then any method at all could potentially be getting into wherever the pop came from and altering some of the individuals there, one of which is best_individual). Rather than staring at the code and guessing (or asking other people on the internet to stare at only a part of the code and guess), you should try to narrow down exactly where the best_individual is altered. If you know how to use the debugger, that's your best bet. If not (and you don't feel like learning now), just insert print("1: ", best_individual) statements between every single line (change the numbers so you can tell where each print output came from). That should at least tell you the call where best_individual first changed; then you look inside the code of that and see what was happening there.