package io.vivarium.experiment;
import java.util.ArrayList;
import io.vivarium.core.CreatureBlueprint;
import io.vivarium.core.GridWorldBlueprint;
import io.vivarium.core.processor.NeuralNetworkBlueprint;
import io.vivarium.scripts.CreateWorld;
import io.vivarium.scripts.RunSimulation;
import io.vivarium.serialization.FileIO;
import io.vivarium.serialization.Format;
import io.vivarium.util.Rand;
import io.vivarium.util.concurrency.ThreadRandAllocator;
public class NormalizationConvergenceLocal
{
private static final int THREADS_PER_BATCH = 4;
private static final int BATCH_COUNT = 24;
private static Thread[] defaultThreads = new Thread[THREADS_PER_BATCH];
private static Thread[] normalizingThreads = new Thread[THREADS_PER_BATCH];
private static Thread[] longNormalizingThreads = new Thread[THREADS_PER_BATCH];
/**
* Runs the NormalizationConvergence experiment.
*
* Hypothesis: Normalizing creature genomes will cause speedier convergence and produce creatures with higher
* average fitness by reducing genetic drift in 'inactive' genes. Genetic drift in inactive genes is hypothesized to
* be dangerous because offspring have the risk of inheriting an active version when recombined from parents with
* sufficiently distant inactive genes (this is a product of the Gaussian recombination of scalar genes used in
* offspring creation)
*
* @param args
* @throws InterruptedException
* Not really...
*/
public static void main(String[] args) throws InterruptedException
{
// If we're running multi-threaded code, we need to use a multi-threaded random allocator
Rand.setAllocator(new ThreadRandAllocator());
// Make the blueprints with the default behavior
GridWorldBlueprint defaultWorldBlueprint = GridWorldBlueprint.makeDefault();
defaultWorldBlueprint.setSize(50);
ArrayList<CreatureBlueprint> defaultCreatureBlueprints = new ArrayList<>();
CreatureBlueprint defaultCreatureBlueprint = CreatureBlueprint.makeDefault();
defaultCreatureBlueprints.add(defaultCreatureBlueprint);
defaultWorldBlueprint.setCreatureBlueprints(defaultCreatureBlueprints);
// Save the default blueprints
FileIO.saveSerializer(defaultWorldBlueprint, "defaultBlueprint.viv", Format.JSON);
// Make the blueprints with the normalizing behavior
GridWorldBlueprint normalizingWorldBlueprint = GridWorldBlueprint.makeDefault();
normalizingWorldBlueprint.setSize(50);
ArrayList<CreatureBlueprint> normalizingCreatureBlueprints = new ArrayList<>();
CreatureBlueprint normalizingCreatureBlueprint = CreatureBlueprint.makeDefault();
((NeuralNetworkBlueprint) normalizingCreatureBlueprint.getProcessorBlueprints()[0])
.setNormalizeAfterMutation(1);
normalizingCreatureBlueprints.add(normalizingCreatureBlueprint);
normalizingWorldBlueprint.setCreatureBlueprints(normalizingCreatureBlueprints);
// Save the normalizing blueprints
FileIO.saveSerializer(normalizingWorldBlueprint, "normalizingBlueprint.viv", Format.JSON);
// Make the blueprints with the normalizing behavior and a longer length
GridWorldBlueprint longNormalizingWorldBlueprint = GridWorldBlueprint.makeDefault();
longNormalizingWorldBlueprint.setSize(50);
ArrayList<CreatureBlueprint> longNormalizingCreatureBlueprints = new ArrayList<>();
CreatureBlueprint longNormalizingCreatureBlueprint = CreatureBlueprint.makeDefault();
((NeuralNetworkBlueprint) longNormalizingCreatureBlueprint.getProcessorBlueprints()[0])
.setNormalizeAfterMutation(Math.sqrt(42));
longNormalizingCreatureBlueprints.add(longNormalizingCreatureBlueprint);
longNormalizingWorldBlueprint.setCreatureBlueprints(longNormalizingCreatureBlueprints);
// Save the normalizing blueprints
FileIO.saveSerializer(longNormalizingWorldBlueprint, "longBlueprint.viv", Format.JSON);
// Run all the simulation batches
System.out.println("Starting Simulation");
for (int batchIndex = 0; batchIndex < BATCH_COUNT; batchIndex++)
{
System.out.println("Starting Batch: " + batchIndex);
// Start a batch of simulations
for (int threadIndex = 0; threadIndex < THREADS_PER_BATCH; threadIndex++)
{
int threadNumber = THREADS_PER_BATCH * batchIndex + threadIndex + 1;
defaultThreads[threadIndex] = new WorldRunnerThread("default", threadNumber, 50, 2_000_000);
normalizingThreads[threadIndex] = new WorldRunnerThread("normalizing", threadNumber, 50, 2_000_000);
longNormalizingThreads[threadIndex] = new WorldRunnerThread("long", threadNumber, 50, 2_000_000);
defaultThreads[threadIndex].start();
normalizingThreads[threadIndex].start();
longNormalizingThreads[threadIndex].start();
}
System.out.println("Awaiting Batch: " + batchIndex);
// And wait for them all to finish
for (int threadIndex = 1; threadIndex < THREADS_PER_BATCH; threadIndex++)
{
defaultThreads[threadIndex].join();
normalizingThreads[threadIndex].join();
longNormalizingThreads[threadIndex].join();
}
System.out.println("Completed Batch: " + batchIndex);
}
System.out.println("Completed Simulation.");
}
private static class WorldRunnerThread extends Thread
{
private final String _prefix;
private final int _iteration;
private final int _steps;
private final int _ticksPerStep;
WorldRunnerThread(String prefix, int iteration, int steps, int ticksPerStep)
{
this._prefix = prefix;
this._iteration = iteration;
this._steps = steps;
this._ticksPerStep = ticksPerStep;
}
@Override
public void run()
{
// Create a world
{
String[] args = { "-b", _prefix + "Blueprint.viv", "-o",
_prefix + "World" + _iteration + "_step" + 0 + ".viv" };
CreateWorld.main(args);
}
// Run the world $steps times
for (int i = 0; i < _steps; i++)
{
// for $ticksPerStep each time
String[] args = { "-i", _prefix + "World" + _iteration + "_step" + i + ".viv", "-o",
_prefix + "World" + _iteration + "_step" + (i + 1) + ".viv", "-t", "" + _ticksPerStep };
RunSimulation.main(args);
}
}
}
}