CodeReclaimers / neat-python

Python implementation of the NEAT neuroevolution algorithm
BSD 3-Clause "New" or "Revised" License
1.41k stars 490 forks source link

New node mutation innovation number / key bug #228

Open krzyLadda opened 2 years ago

krzyLadda commented 2 years ago

An error in the algorithm is to give a new node number (key, innovation number) each time an "add node" mutation occurs. This causes each added node to have a new key, which is not correct and makes more difficult (less efficiently) to cross individuals. A node that is created in the same generation by breaking the same connection should have the same key. Stanley (Evolving neural networks through augmenting topologies 2002) wrote: ,,A possible problem is that the same structural innovation will receive different innovation numbers in the same generation if it occurs by chance more than once. How- ever, by keeping a list of the innovations that occurred in the current generation, it is possible to ensure that when the same structure arises more than once through in- dependent mutations in the same generation, each identical mutation is assigned the same innovation number. Thus, there is no resultant explosion of innovation numbers."

The solution is to define in reproduction.py in the method reproduce the dictionary newNodesInGen = {}, which is passed to child.mutate. Then we pass it to self.mutate_add_node, where we change the part of code from: ` # Choose a random connection to split

    conn_to_split = choice(list(self.connections.values()))

    new_node_id = config.get_new_node_key(self.nodes)`

to: ` # Choose a random connection to split

    conn_to_split = choice(list(self.connections.values()))
    if conn_to_split.key in newNodesInGen:  
        new_node_id = newNodesInGen[conn_to_split.key]  
    else: 
        new_node_id = config.get_new_node_key(self.nodes) 
        newNodesInGen[conn_to_split.key] = new_node_id`

The mutation dictionary is declared in such a place that it will be created anew in each generation.