2

I'm coding with python 3.6 and am working on a Genetic Algorithm. When generating a new population, when I append the new values to the array all the values in the array are changed to the new value. Is there something wrong with my functions?

Code:

from fuzzywuzzy import fuzz
import numpy as np
import random
import time

def mutate(parent):
    x = random.randint(0,len(parent)-1)
    parent[x] = random.randint(0,9)
    print(parent)
    return parent

def gen(cur_gen, pop_size, fittest):
    if cur_gen == 1:
        population = []
        for _ in range(pop_size):
            add_to = []
            for _ in range(6):
                add_to.append(random.randint(0,9))
            population.append(add_to)
        return population
    else:
        population = []
        for _ in range(pop_size):
            print('\n')
            population.append(mutate(fittest))
            print(population)
        return population

def get_fittest(population):
    fitness = []
    for x in population:
        fitness.append(fuzz.ratio(x, [9,9,9,9,9,9]))
    fittest = fitness.index(max(fitness))
    fittest_fitness = fitness[fittest]
    fittest = population[fittest]
    return fittest, fittest_fitness

done = False
generation = 1
population = gen(generation, 10, [0,0,0,0,0,0])
print(population)

while not done:
    generation += 1
    time.sleep(0.5)
    print('Current Generation: ',generation)
    print('Fittest: ',get_fittest(population))
    if get_fittest(population)[1] == 100:
        done = True
    population = gen(generation, 10, get_fittest(population)[0])
print('Population: ',population)

Output:

Fittest:  ([7, 4, 2, 7, 8, 9], 72)


[3, 4, 2, 7, 8, 9]
[[3, 4, 2, 7, 8, 9]]


[3, 4, 2, 7, 5, 9]
[[3, 4, 2, 7, 5, 9], [3, 4, 2, 7, 5, 9]]


[3, 4, 2, 7, 4, 9]
[[3, 4, 2, 7, 4, 9], [3, 4, 2, 7, 4, 9], [3, 4, 2, 7, 4, 9]]


[3, 1, 2, 7, 4, 9]
[[3, 1, 2, 7, 4, 9], [3, 1, 2, 7, 4, 9], [3, 1, 2, 7, 4, 9], [3, 1, 2, 7, 4, 9]]


[3, 1, 2, 7, 4, 2]
[[3, 1, 2, 7, 4, 2], [3, 1, 2, 7, 4, 2], [3, 1, 2, 7, 4, 2], [3, 1, 2, 7, 4, 2], [3, 1, 2, 7, 4, 2]]


[3, 1, 2, 5, 4, 2]
[[3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2]]


[3, 1, 2, 5, 4, 2]
[[3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2]]


[3, 1, 2, 5, 4, 5]
[[3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5]]


[3, 1, 2, 5, 4, 3]
[[3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3]]
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
  • Could you update your question with the code and expected output? – Omari Celestine Mar 05 '19 at 03:39
  • 3
    Possible duplicate of [List of lists changes reflected across sublists unexpectedly](https://stackoverflow.com/questions/240178/list-of-lists-changes-reflected-across-sublists-unexpectedly) – Stephen Rauch Mar 05 '19 at 03:40
  • Why does your output not contain `Current Generation` or `Population`, and contain `Fittest` only once? – John Gordon Mar 05 '19 at 03:45

2 Answers2

0

It's right there in the name:

def mutate(parent):
    x = random.randint(0,len(parent)-1)
    parent[x] = random.randint(0,9)
    print(parent)
    return parent

mutate isn't making a new list, it's modifying the existing list in place and returning a reference to the same list it was passed. population.append(mutate(fittest)) is happily storing aliases to fittest over and over, and since fittest is never replaced, it just keeps getting mutated and new aliases to it stored.

If the goal is to store a snapshot of the list at a given stage each time, copy it, changing:

population.append(mutate(fittest))

to:

population.append(mutate(fittest)[:])

where a complete slice of the list makes a shallow copy. population.append(mutate(fittest).copy()) would also work on modern Python, as would import copy, then doing population.append(copy.copy(mutate(fittest))) or (if the contents might themselves be mutable, though they aren't in this case) population.append(copy.deepcopy(mutate(fittest))).

Note that as a rule, Python functions aren't really supposed to both mutate and return the mutated value, as it leads to confusion like this.

Pythonic code intended to mutate in place returns None (implicitly usually, by not returning at all), so the code you'd use would actually be:

def mutate(parent):
    x = random.randint(0,len(parent)-1)
    parent[x] = random.randint(0,9)
    print(parent)

and used with:

        mutate(fittest)  # Mutate fittest in place
        population.append(fittest[:])  # Append copy of most recently updated fittest
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
0

You keep passing fittest, a reference to the same list, to mutate, which modifies the list in-place, and append it to population, so changes to the list in the next iteration are reflected across all the references to fittest in the population list.

You should pass a copy of the fittest list to mutate instead:

population.append(mutate(fittest[:]))
blhsing
  • 91,368
  • 6
  • 71
  • 106