DEAP Tournament Selection, Avoiding Crossovers with Duplicate Individuals

Viewed 474

I've inherited a piece of Python code that uses DEAP, with tournament selection to manage a crossover step:

#Early in the code
toolbox.register("select", tools.selTournament, tournsize=tournsize)

#Then later...
popu = toolbox.select(popu, k=len(popu))
popu = [toolbox.clone(ind) for ind in popu]
    for child1, child2 in zip(popu[::2], popu[1::2]):
        toolbox.mate(child1, child2)
        del child1.fitness.values, child2.fitness.values

It appears to me that this has no protection for an individual being crossed-over with itself, and I'd like to avoid that possibility.

Conceptually, this is simple enough, but I'm not sure how to implement it in Python.

Possible Solution 1: (see update below)

popu = toolbox.select(popu, k=len(popu))
for ind in range(1, len(popu), 2):
     while popu[ind] == popu[ind-1]:
          popu[ind] = toolbox.select(popu, k=1)

popu = [toolbox.clone(ind) for ind in popu]
     for child1, child2 in zip(popu[::2], popu[1::2]):
          toolbox.mate(child1, child2)
          del child1.fitness.values, child2.fitness.values

*If reselecting duplicates one-by-one is possible, I think putting the deduping before the cloning is correct, but I'm not 100% sure.

Possible Solution 2, a little more brute-force:

popu = toolbox.select(popu, k=len(popu))
for ind in range(1, len(popu), 2):
     while popu[ind] == popu[ind-1]:
          popu[ind] = popu[random.randint(0,len(popu)-1)]

popu = [toolbox.clone(ind) for ind in popu]
     for child1, child2 in zip(popu[::2], popu[1::2]):
          toolbox.mate(child1, child2)
          del child1.fitness.values, child2.fitness.values

Should either or both of those work (with the caveat that the reselection is occurring in the right place)?

UPDATE:

Possible solution #1 needs to be modified. Since toolbox.select returns a list, the individual popu item needs to be set to a list-item, and not the whole list itself, even when the list is length 1. Probably easier to show the solution, rather than write this out in prose...

Revised Possible Solution 1:

popu = toolbox.select(popu, k=len(popu))
for ind in range(1, len(popu), 2):
     while popu[ind] == popu[ind-1]:
          repopu = toolbox.select(popu, k=1)
          popu[ind] = repopu[0]

popu = [toolbox.clone(ind) for ind in popu]
     for child1, child2 in zip(popu[::2], popu[1::2]):
          toolbox.mate(child1, child2)
          del child1.fitness.values, child2.fitness.values

This at least gets us past the "it doesn't crash" stage, but is it doing what we want?

0 Answers
Related