schrum2 / MM-NEAT

Modular Multiobjective (Hyper) Neuro-Evolution of Augmenting Topologies + MAP-Elites: Java code for evolving intelligent agents in Ms. Pac-Man, Tetris, and more, as well as code for Procedural Content Generation in Mario, Zelda, Minecraft, and more!
http://people.southwestern.edu/~schrum2/re/mm-neat.php
Other
50 stars 20 forks source link

Load MAP Elites Archive correctly for post analysis #838

Closed schrum2 closed 1 year ago

schrum2 commented 2 years ago

MAP Elites currently has code for loading save directories to resume an interrupted evolution run, but the code is so seldom used that it expects an outdated archive directory structure and doesn't work. I want to add a way to take a completed evolutionary run and load the whole archive for observation, and fixing this loading code in the initialize method seems to be the way to do it.

schrum2 commented 2 years ago

Loading still does not work. Here is the current error:

Caused by: java.lang.NullPointerException
        at edu.southwestern.tasks.evocraft.characterizations.MinecraftMAPElitesBlockCountEmptyCountBinLabels.discard(MinecraftMAPElitesBlockCountEmptyCountBinLabels.java:73)
        at edu.southwestern.evolution.mapelites.Archive.add(Archive.java:162)
        at edu.southwestern.evolution.mapelites.MAPElites.lambda$1(MAPElites.java:421)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source)
        at java.util.Vector$VectorSpliterator.forEachRemaining(Unknown Source)
        at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
        at java.util.stream.ForEachOps$ForEachTask.compute(Unknown Source)
        at java.util.concurrent.CountedCompleter.exec(Unknown Source)
        at java.util.concurrent.ForkJoinTask.doExec(Unknown Source)
        at java.util.concurrent.ForkJoinTask.doInvoke(Unknown Source)
        at java.util.concurrent.ForkJoinTask.invoke(Unknown Source)
        at java.util.stream.ForEachOps$ForEachOp.evaluateParallel(Unknown Source)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(Unknown Source)
        at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
        at java.util.stream.ReferencePipeline.forEach(Unknown Source)
        at java.util.stream.ReferencePipeline$Head.forEach(Unknown Source)
        at edu.southwestern.evolution.mapelites.MAPElites.initialize(MAPElites.java:420)
        at edu.southwestern.experiment.evolution.SteadyStateExperiment.<init>(SteadyStateExperiment.java:29)
        at edu.southwestern.experiment.evolution.SteadyStateExperiment.<init>(SteadyStateExperiment.java:24)

Related to the discard method of bin labels

schrum2 commented 2 years ago

It takes longer to load, but it loads an archive. This seemed to work fine with a result that had no flying machines, but it seemed to get overwhelmed with a piston orientation run that had lots of flying machines. I'm not sure that all successful machines were included.

Need to clean up the output so that every empty bin doesn't show a stack trace, and also need to figure out when the program seemed to freeze. Additionally, there needs to be a way to keep the interactivity going after everything is loaded, so that the diamond block still works.

schrum2 commented 2 years ago

Added ability to keep interactive loop turned on, though I'm not certain how well it will work for re-loading a saved archive yet. Also cleaned up the stack traces.

Not sure at the moment whether the loading process misses anything or not. Will test later.

schrum2 commented 2 years ago

Keeping the interactive loop on prevents the program from exiting, but breaking diamond blocks didn't seem to respawn the shape. Need to look at more closely. Then again, this could be related to #841

schrum2 commented 2 years ago

Loading a whole archive that is so full a movement seems a bit overwhelming to the system. Still, I am seeing flying machines now, which might be due to the stricter fitness function (double checking that they fly). Still, it would be more useful to cycle through the archive shapes one by one at a single location than to place them in an archive for post evaluation. I'm already re-evaluating them anyway, so this should be an easy addition.

schrum2 commented 2 years ago

I think I'm satisfied with my current approach of simply applying postSpawnMinecraftBlocks.bat to a directory rather than individual files. However, this currently requires some manual curation of the set of shapes to observe based on fitness (only want final elites and not all). Maybe that could be done with a script?

schrum2 commented 2 years ago

Some of these scripts exist now, but still need to be pushed to the repo.

However, another issue comes out of fitness-based evolution. Too many shapes are created to look at all of them. However, the use of elitism seems to make it so that make shapes are just duplicates of previous ones. I'm trying to change the post-experiment to notice duplicates and not bother showing them again, but I think that some aspect of the block list for a shape may not have a hashcode or equality function which is leaving the identical shapes undetected.

schrum2 commented 1 year ago

I guess this has actually been done for a while