CodeReclaimers / neat-python

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

DefaultGenome doesn't have get_pruned_copy #266

Open Regenhardt opened 1 year ago

Regenhardt commented 1 year ago

Describe the bug

Installing from PyPI and running draw_net from visualize.py, python throws AttributeError: 'DefaultGenome' object has no attribute 'get_pruned_copy' when genome.get_pruned_copy(config.genome_config) is being called.
Tried with Python39 and Python311.

To Reproduce Steps to reproduce the behavior:

  1. pip install neat-python
  2. Train a net like in the example (neat.DefaultGenome)
  3. Put the winner into visualize.draw_net with prune_unused=true
  4. AttributeError: 'DefaultGenome' object has no attribute 'get_pruned_copy'

Expected behavior

get_pruned_copy should be called.

Screenshots

Navigating to source on the neat.DefaultGenome type gets me to the file in site-packages, which ends like this:
grafik

This is the first part of the draw_net method (I added type annotations and formatted the code):
grafik

Desktop (please complete the following information):

Additional context
The genome comes from here:

    config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction,
                        neat.DefaultSpeciesSet, neat.DefaultStagnation,
                        'neat-config')
    p = neat.Population(config)
    p.add_reporter(neat.StdOutReporter(False))
    stats = neat.StatisticsReporter()
    p.add_reporter(stats)
    winner = p.run(eval_genomes, 100) 
Redro38 commented 1 year ago

I have the same problem. Did you find a solution?

Regenhardt commented 1 year ago

Nope, instead I just didn't use prune_unused. Could of course just copy the content of https://github.com/CodeReclaimers/neat-python/blob/master/neat/genome.py into site-packages/neat/genome.py.

rickdgray commented 1 year ago

I figured it out: the documentation and examples refer to the currently unrelease 0.93 version. The last release to the pip package repo was version 0.92 in 2017! That is quite old now; I think we're due for another release. But for now, you can simply follow the instructions to build and install from source to get the latest which runs correctly with the given examples.

CodeReclaimers commented 1 year ago

@rickdgray Apologies for the current state of the code, it's very overdue for cleanup. I accepted some large-scale changes I probably shouldn't have, and I need to go through, get these issues fixed up, and do a new maintenance release on PyPI.

rickdgray commented 1 year ago

@CodeReclaimers No worries, I know how it is.

You mentioned that there was a big change made that had some consequences; was this after 0.92? I see a few issues here so I was thinking the code base is not stable. But if 0.92 was actually stable, then I can just go use that.

I ask because I was planning to use this, but I'm on a tight time constraint and can't fuss with other issues, especially when the issues are actually affecting the results.

CodeReclaimers commented 1 year ago

I took some large pull requests in July 2017 despite not having the time to thoroughly wrap my head around them. It's not that the changes were bad, they were just so extensive that any intuition I had for the code as a whole went out the window, and it became a lot harder for me to spot/fix issues.

Version 0.91 was done before those changes, so it might be a safer place to start from if you need consistency. If nothing else, there's less code to trudge through if you need to make some tweaks.

rickdgray commented 1 year ago

Awesome! Thanks for the info

Gabriel-Kissin commented 10 months ago

I didn't want to change the contents of genome.py as @Regenhardt suggested. Instead I changed visualize.py - I thought that better to modify the visualisation script than change the main NEAT workhorse. Also, I copied visualize.py into my working directory and imported it so I could make changes in my own copy rather than in the library.

I made the following modifications:

1) comment out the current prune_unused lines:

    # if prune_unused:
    #     genome = genome.get_pruned_copy(config.genome_config)

2) add the following section instead:

    ###########################################################################

    if prune_unused:
        import copy
        connections = set()
        for cg in genome.connections.values():
            if cg.enabled or show_disabled:
                # connections.add((cg.in_node_id, cg.out_node_id))
                connections.add(cg.key)

        used_nodes = copy.copy(outputs)
        pending = copy.copy(outputs)
        while pending:
            #print(pending, used_nodes)
            new_pending = set()
            for a, b in connections:
                if b in pending and a not in used_nodes:
                    new_pending.add(a)
                    used_nodes.add(a)
            pending = new_pending
    else:
        used_nodes = set(genome.nodes.keys())

    ###########################################################################

( I was helped here by looking at this commit https://github.com/CodeReclaimers/neat-python/commit/1d796adbb022cd3f84b4aecf678b26824e42b7f2, used the earlier version but needed to change some small things)

3) There are 7 lines in visualize.py which start with outputs = set() which build output. Make sure that the section pasted in in (2) comes AFTER those lines, as they use the output set.

4) The section added in (2) builds used_nodes. Therefore we need to comment out the line already in visualize.py which builds it differently:

    # used_nodes = set(genome.nodes.keys())

Those changes should do the trick!