pythonclassoopartificial-intelligencecrossover

These two codes should do exactly the same thing, but the first one doesn't work as I expect


Python

Please explain why these two codes work differently.. Actually I am trying to make kind of AI where in initial generations the individuals will go in random directions. For keeping the code simple I have provided some random directions in Brain myself.

There is an Individual class that gives a brain to the individual. It also has a function that returns a child with EXACTLY the same brain (means same directions to go in) as the parent.

I have two codes:

First: When some directions is changed in the parent, the same thing is changed in the child too (or if changed in child, it gets changed in parent too) which I don't want to happen.

Second: This one is not completely mine (and that's why I don't really know why it works) but it works fine. Some direction changed in parent is not changed in the child and vice-versa.

Please someone explain me the difference and why first one didn't work. I would really appreciate your answer.


First one:

class Brain():
    def __init__(self):
        self.directions = [[1, 2], [5, 3], [7, 4], [1, 5]]

class Individual():
    def __init__(self):
        self.brain = Brain()

    def getChild(self):
        child = Individual()
        child.brain = self.brain
        return child

parent = Individual()
child = parent.getChild()

parent.brain.directions[0] = [5, 2]

print(parent.brain.directions)
print(child.brain.directions)

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

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



Second one:

class Brain():
    def __init__(self):
        self.directions = [[1, 2], [5, 3], [7, 4], [1, 5]]

    def clone(self):
        clone = Brain()
        for i, j in enumerate(self.directions):
            clone.directions[i] = j
        return clone

class Individual():
    def __init__(self):
        self.brain = Brain()

    def getChild(self):
        child = Individual()
        child.brain = self.brain.clone()
        return child

parent = Individual()
child = parent.getChild()

parent.brain.directions[0] = [5, 2]

print(parent.brain.directions)
print(child.brain.directions)

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

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


Solution

  • In the first code, setting child.brain = self.brain doesn't do what you're expecting. That is a shallow copy, meaning that it just creates a new pointer to the same instance of Brain(). So now child.brain and self.brain both point to the same Brain() in memory.

    In the second code, you are making a deep copy. Thus, you are actually allocating another Brain() in memory. Now child.brain and parent.brain point to their own separate instance of a Brain() in memory.