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

Weighted Sum of Minecraft Fitness Functions #912

Closed schrum2 closed 1 year ago

schrum2 commented 1 year ago

Make a new abstract class called MinecraftWeightedSumFitnessFunction that extends the TimedEvaluationMinecraftFitnessFunction. This abstract class will have a constructor that takes two lists:

Given this information, this fitness function will compute fitness scores across all of the given list of MinecraftFitnessFunctions, but take care to separate them out into those that are TimedEvaluationMinecraftFitnessFunctions and those that are not. However, in order to compute the actual fitness score that will be returned, it will take the array of fitness scores returned, multiply each one by its corresponding weight, and add all the results together into a single fitness score.

Basically, this is a way of solving a multiobjective problem by reducing it to a single objective problem. One would expect this to not do very well, but it is a fair baseline to compare NSGA2 and MOME with.

The reason we are making a separate abstract fitness function for this is that the weights will likely need a bit of tuning. The classes that extend this class will just have a constructor that calls the super constructor with a specific list of fitness functions and weights.

schrum2 commented 1 year ago

I've made the abstract weighted sum fitness, but I still need to extend it with a specific fitness pairing to test it out.

schrum2 commented 1 year ago

Some classes need to be made that extend the new MinecraftWeightedSumFitnessFunction class. For starters, try one that uses both ChangeBlocksFitness and ChangeCenterOfMass, and make both of the weight values be 0.5. Then try running it with plain MAP Elites. If that works, then make a batch file for it.

JoannaBlatt commented 1 year ago

Caused by: java.lang.NullPointerException at edu.southwestern.tasks.mario.gan.Comm.processCommRecv(Comm.java:59) at edu.southwestern.tasks.mario.gan.Comm.commRecv(Comm.java:50) at edu.southwestern.tasks.evocraft.MinecraftClient.getMinecraftClient(MinecraftClient.java:77) at edu.southwestern.tasks.evocraft.MinecraftClient.clearAreaAroundCorner(MinecraftClient.java:812) at edu.southwestern.tasks.evocraft.MinecraftClient.clearAndVerify(MinecraftClient.java:774) at edu.southwestern.tasks.evocraft.fitness.TimedEvaluationMinecraftFitnessFunction.multipleFitnessScores(TimedEvaluationMinecraftFitnessFunction.java:92) at edu.southwestern.tasks.evocraft.fitness.MinecraftWeightedSumFitnessFunction.fitnessScore(MinecraftWeightedSumFitnessFunction.java:49) at edu.southwestern.tasks.evocraft.MinecraftShapeTask.lambda$2(MinecraftShapeTask.java:448) at java.util.stream.ReferencePipeline$6$1.accept(ReferencePipeline.java:244) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) at java.util.stream.Nodes$SizedCollectorTask.compute(Nodes.java:1878) at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731) at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) at java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:401) at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:734) at java.util.stream.Nodes.collectDouble(Nodes.java:438) at java.util.stream.DoublePipeline.evaluateToNode(DoublePipeline.java:139) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:541) at java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260) at java.util.stream.DoublePipeline.toArray(DoublePipeline.java:508) at edu.southwestern.tasks.evocraft.MinecraftShapeTask.calculateFitnessScores(MinecraftShapeTask.java:448) at edu.southwestern.tasks.evocraft.MinecraftShapeTask.evaluateOneShape(MinecraftShapeTask.java:330) at edu.southwestern.tasks.evocraft.MinecraftShapeTask.evaluateOneShape(MinecraftShapeTask.java:308) at edu.southwestern.tasks.evocraft.MinecraftLonerShapeTask.oneEval(MinecraftLonerShapeTask.java:282) at edu.southwestern.tasks.NoisyLonerTask.evaluate(NoisyLonerTask.java:126) at edu.southwestern.evolution.mapelites.MAPElites.lambda$initialize$0(MAPElites.java:417) at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291) at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731) at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1067) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1703) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:172)

schrum2 commented 1 year ago

Exception resolved

JoannaBlatt commented 1 year ago

Was able to run the main method for the ChangeBlockAndChangeCenterOfMassWeightedSumFitness

did not seem to return interesting results. No flying machines.

schrum2 commented 1 year ago

The next step here is to find a way to log the component fitness scores. I think it will be a bit messy, requiring several methods that return double[] to instead return Pair<double[],double[]>. I'll give more details on this later.

schrum2 commented 1 year ago

My most recent commit above takes some steps toward making it possible to track the component scores, but it is not done yet.

JoannaBlatt commented 1 year ago

Pure fitness can log weighted sum but map elites crashes when it tries to run

schrum2 commented 1 year ago

MAP Elites should no longer crash when run with a weighted sum fitness function, though I have not verified this yet. Please check.

However, even though it should not crash, it definitely won't log the desired information yet. You will need to change some of the logging code in MAP Elites itself. In particular, there is a line in MAPElites where this happens:

            Float[] elite = ArrayUtils.toObject(archive.getEliteScores());
            final int pseudoGeneration = iterations/individualsPerGeneration;
            archiveLog.log(pseudoGeneration + "\t" + StringUtils.join(elite, "\t").replaceAll("-Infinity", "X"));

This code is responsible for logging the objective scores across all bins for the one and only one objective. We need to have an operation similar to this for each component objective of a weighted sum. More generally, the scores we are interested in logging will be within the otherStats field of each Score instance in a bin, and all scores should have the same number of entries in this array. So, there needs to be a separate log and plot file for each index in otherStats. Similarly, there needs to be a method in Archive like getEliteScores(), except instead of getting the fitness score for each bin, it gets the score in a specific otherStats index for each bin, as in:

getOtherStatsScores(int index)

Given what this returns, the data can be logged and plotted the same way.

MAPElites also uses this code to compute the max fitness:

            Float maximumFitness = StatisticsUtilities.maximum(elite);

and this code to compute QD scores with respect to the fitness scores:

            final double qdScore = calculateQDScore(elite);

If you were to simply send an array of otherStats values to these methods, then you would compute both a max score in the other stat, as well as a QD score across the otherStat. This would all need to be logged and plotted with distinct files, but it mostly comes down to copying what is already there with just small tweaks. A method can probably be extracted that creates plt files that differ only in the name of the log file they are plotting, and then the logging commands themselves can just be different log files (though you will need an array of log files for each of the otherStats values).

schrum2 commented 1 year ago

Definitely want to get this logging into MAP Elites before summer ends @JoannaBlatt

JoannaBlatt commented 1 year ago

I might have created an issue with pseudoArchive setting due to passing an arraylist of logs that are not set up in MuLambda. This may have backwards compatibility issues.

JoannaBlatt commented 1 year ago

Currently logs. Handling in setupLogging currently similar to momeLogs, but could probably be handled differently. Issues with negative scores are present. It should probably be accounted for elsewhere.

schrum2 commented 1 year ago

Running tests now, but all scores should be in the range 0.0 to 1.0 now. Won't be able to check results until Monday though.

schrum2 commented 1 year ago

Scores to range from 0 to 1 in a separate test that I did, so I'm closing this issue. However, there is a correction for negative scores needed with QD calculations, and that is tracked in this new issue #943