How to perform Partial-mapped crossover in python3?

5.3k views Asked by At

I am new to genetic algorithms and made one the other day that recreated a target string. So I tried to make one that could make a Magic Square. It was ok until I got to the crossover part, realising I couldn't just do a single point crossover. So I attempted to perform a Partially Mapped Crossover, and I could not and still can't get it to work. I understand how the Partially Mapped Crossover works I just can't implement it into python. Since my code isn't complete yet I isolated the crossover function in a different program and changed it so the parents were a fixed list.

Can someone please correct my code or if it is completely wrong show me how to perform a Partial Mapped Crossover on 2 lists with integers 1 to 9? Also, I am sorry and understand that my naming of variables isn't that good but I was just trying to get the program to work making constant edits.

import random

parent1 = [1,2,3,4,5,6,7,8,9]
parent2 = [5,4,6,7,2,1,3,9,8]

firstCrossPoint = random.randint(0,len(parent1)-1)              #Creating parameters for random sublist
secondCrossPoint = random.randint(firstCrossPoint+1,len(parent1))

parent1MiddleCross = parent1[firstCrossPoint:secondCrossPoint]
parent2MiddleCross = parent2[firstCrossPoint:secondCrossPoint]

child1 = (parent1[:firstCrossPoint] + parent2MiddleCross + parent1[secondCrossPoint:])
child2 = (parent2[:firstCrossPoint] + parent1MiddleCross + parent2[secondCrossPoint:])

relationsWithDupes = []
for i in range(len(parent1MiddleCross)):
    relationsWithDupes.append([parent2MiddleCross[i], parent1MiddleCross[i]])

relations = []
for pair in relationsWithDupes:

    for i in range(len(relationsWithDupes)):
        if pair[0] in relationsWithDupes[i] or pair[1] in relationsWithDupes[i]:
            if pair != relationsWithDupes[i]:
                if pair[0] == relationsWithDupes[i][1]:
                    pair[0] = relationsWithDupes[i][0]

                else:
                    pair[1] = relationsWithDupes[i][1]

    if pair not in relations and pair[::-1] not in relations:
        relations.append(pair)

for i in child1[:firstCrossPoint]:
    for x in relations:
        if i == x[0]:
            i = x[1]

for i in child1[secondCrossPoint:]:
    for x in relations:
        if i == x[0]:
            i = x[1]

for i in child2[:firstCrossPoint]:
    for x in relations:
        if i == x[1]:
            i = x[0]

for i in child2[secondCrossPoint:]:
    for x in relations:
        if i == x[1]:
            i = x[0]

print(child1)
print(child2)
2

There are 2 answers

0
Loïc L. On

I just stumbled across this when looking for an implementation of PMX and it seems unnecessarily complicated? I've included below an alternative I've just coded if anyone comes across the same issue.

def PMX_crossover(parent1, parent2, seed):
    '''
    parent1 and parent2 are 1D np.array
    '''
    rng = np.random.default_rng(seed=seed)

    cutoff_1, cutoff_2 = np.sort(rng.choice(np.arange(len(parent1)+1), size=2, replace=False))

    def PMX_one_offspring(p1, p2):
        offspring = np.zeros(len(p1), dtype=p1.dtype)

        # Copy the mapping section (middle) from parent1
        offspring[cutoff_1:cutoff_2] = p1[cutoff_1:cutoff_2]

        # copy the rest from parent2 (provided it's not already there
        for i in np.concatenate([np.arange(0,cutoff_1), np.arange(cutoff_2,len(p1))]):
            candidate = p2[i]
            while candidate in p1[cutoff_1:cutoff_2]: # allows for several successive mappings
                print(f"Candidate {candidate} not valid in position {i}") # DEBUGONLY
                candidate = p2[np.where(p1 == candidate)[0][0]]
            offspring[i] = candidate
        return offspring

    offspring1 = PMX_one_offspring(parent1, parent2)
    offspring2 = PMX_one_offspring(parent2, parent1)

    return offspring1, offspring2
1
 V. Rathee On
import numpy as np

parent1 = [1,2,3,4,5,6,7,8,9]
parent2 = [5,4,6,7,2,1,3,9,8]

firstCrossPoint = np.random.randint(0,len(parent1)-2)
secondCrossPoint = np.random.randint(firstCrossPoint+1,len(parent1)-1)

print(firstCrossPoint, secondCrossPoint)

parent1MiddleCross = parent1[firstCrossPoint:secondCrossPoint]
parent2MiddleCross = parent2[firstCrossPoint:secondCrossPoint]

temp_child1 = parent1[:firstCrossPoint] + parent2MiddleCross + parent1[secondCrossPoint:]

temp_child2 = parent2[:firstCrossPoint] + parent1MiddleCross + parent2[secondCrossPoint:]

relations = []
for i in range(len(parent1MiddleCross)):
    relations.append([parent2MiddleCross[i], parent1MiddleCross[i]])

print(relations)

def recursion1 (temp_child , firstCrossPoint , secondCrossPoint , parent1MiddleCross , parent2MiddleCross) :
    child = np.array([0 for i in range(len(parent1))])
    for i,j in enumerate(temp_child[:firstCrossPoint]):
        c=0
        for x in relations:
            if j == x[0]:
                child[i]=x[1]
                c=1
                break
        if c==0:
            child[i]=j
    j=0
    for i in range(firstCrossPoint,secondCrossPoint):
        child[i]=parent2MiddleCross[j]
        j+=1

    for i,j in enumerate(temp_child[secondCrossPoint:]):
        c=0
        for x in relations:
            if j == x[0]:
                child[i+secondCrossPoint]=x[1]
                c=1
                break
        if c==0:
            child[i+secondCrossPoint]=j
    child_unique=np.unique(child)
    if len(child)>len(child_unique):
        child=recursion1(child,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross)
    return(child)

def recursion2(temp_child,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross):
    child = np.array([0 for i in range(len(parent1))])
    for i,j in enumerate(temp_child[:firstCrossPoint]):
        c=0
        for x in relations:
            if j == x[1]:
                child[i]=x[0]
                c=1
                break
        if c==0:
            child[i]=j
    j=0
    for i in range(firstCrossPoint,secondCrossPoint):
        child[i]=parent1MiddleCross[j]
        j+=1

    for i,j in enumerate(temp_child[secondCrossPoint:]):
        c=0
        for x in relations:
            if j == x[1]:
                child[i+secondCrossPoint]=x[0]
                c=1
                break
        if c==0:
            child[i+secondCrossPoint]=j
    child_unique=np.unique(child)
    if len(child)>len(child_unique):
        child=recursion2(child,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross)
    return(child)

child1=recursion1(temp_child1,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross)
child2=recursion2(temp_child2,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross)

print(child1)
print(child2)