Package com.fray.evo

Source Code of com.fray.evo.EvolutionChamber

package com.fray.evo;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jgap.Chromosome;
import org.jgap.Configuration;
import org.jgap.Gene;
import org.jgap.Genotype;
import org.jgap.IChromosome;
import org.jgap.InvalidConfigurationException;
import org.jgap.event.GeneticEvent;
import org.jgap.event.GeneticEventListener;
import org.jgap.impl.DefaultConfiguration;
import org.jgap.impl.IntegerGene;

import com.fray.evo.action.EcAction;
import com.fray.evo.fitness.EcFitnessType;

public final class EvolutionChamber
{
  private static final Logger logger = Logger.getLogger(EvolutionChamber.class.getName());
 
  /**
   * The version of EvolutionChamber.
   */
  public static final String  VERSION = "0026";

  /**
   * The seed file.
   */
  private File        SEEDS_EVO        = null;
 
  /**
   * The backup seed file (in case execution stops while the file is half written).
   */
  private File        SEEDS_EVO_2        = null;

  /**
   * Chromosome length refers to the Genetic Algorithm's maximum number of potential actions that can be executed.
   */
  private int          CHROMOSOME_LENGTH    = 120;

  /**
   * Population size refers to the number of chromosomes in the gene pool for a population.
   */
  private int          POPULATION_SIZE      = 200;
 
  /**
   * Base mutation rate refers to the rate at which EcGeneticUtil mutations occur.
   */
  private double        BASE_MUTATION_RATE    = 5;

  /**
   * Number of threads to run genetic simulators on.
   */
  private int          NUM_THREADS        = Runtime.getRuntime().availableProcessors();
 
  /**
   * Maximum allowed number of threads to run genetic simulators on.
   */
  private int          MAX_NUM_THREADS      = Runtime.getRuntime().availableProcessors() * 4;
 
  /**
   * The threads that are used for the running simulation.
   */
  private List<Thread>    threads          = Collections.synchronizedList(new ArrayList<Thread>());
 
  /**
   * True to kill all running threads, false to let them continue running.
   */
  private boolean        killThreads        = false;
 
  /**
   * The best fitness score a simulation generated.
   */
  private Double        bestScore        = 0.0;
 
  private Integer        stagnationLimit      = 0;
  private Double        waterMark        = 0.0;
  private int          STAGNATION_LIMIT_MIN  = 200;
 
  /**
   * A list of all previously-run simulations.
   */
  private List<EcBuildOrder>  history          = new ArrayList<EcBuildOrder>();
 
  /**
   * The final goal of the current simulation.
   */
  private EcState        destination        = EcState.defaultDestination();
 
  /**
   * Allows for the results of a simulation to be outputted in real time.
   */
  private EcReportable      reportInterface;
 
  /**
   * The best fitness score for each thread.
   */
  private Double[]      bestScores;
 
  private Integer[]      evolutionsSinceDiscovery;
  private EcEvolver[]      evolvers;
        private int                             forgottenEvolutions;
  private boolean        firstrun        = true;
  private boolean        newbestscore      = false;
 
  /**
   * The caluation to use for determining the fitness score of a gene.
   */
  private EcFitnessType    fitnessType        = EcFitnessType.STANDARD;
 
  /**
   * the minimum of required actions, determined by analyzing the destination state
   */
  private List<Class<? extends EcAction>> requiredActions = new ArrayList<Class<? extends EcAction>>();
 
  public EvolutionChamber()
  {
  }
 
  /**
   * Creates a new object, loading the seeds from the given files.
   * @param seeds
   * @param backupSeeds
   */
  public EvolutionChamber(File seeds, File backupSeeds)
  {
    SEEDS_EVO = seeds;
    SEEDS_EVO_2 = backupSeeds;
    loadSeeds();
  }
 
  public List<EcBuildOrder> getHistory()
  {
    return history;
  }
 
  public int getEvolutionsSinceDiscovery(int thread)
  {
    return evolutionsSinceDiscovery[thread];
  }
 
  /**
   * Determines if a simulation is currently running.
   * @return true if a simulation is running, false if not.
   */
  public boolean isRunning()
  {
    return threads.size() > 0;
  }
 
  public Double[] getBestScores()
  {
    return bestScores;
  }
 
  public int getStagnationLimit()
  {
    return stagnationLimit;
  }
 
  public int getChromosomeLength()
  {
    return CHROMOSOME_LENGTH;
  }
 
  public double getBaseMutationRate()
  {
    return BASE_MUTATION_RATE;
  }
 
  public void setReportInterface(EcReportable reportInterface)
  {
    this.reportInterface = reportInterface;
  }

  public void setSeedFile(File file)
  {
    SEEDS_EVO = file;
  }
 
  public void setBackupSeedFile(File file)
  {
    SEEDS_EVO_2 = file;
  }
 
  /**
   * Sets the caluation to use for determining the fitness score of a gene.
   *
   * @param fitnessType the fitness type
   */
  public void setFitnessType(EcFitnessType fitnessType)
  {
    this.fitnessType = fitnessType;
  }
 
  /**
   * Gets the total number of games played since the simulation started.
   * @return
   */
  public long getGamesPlayed()
  {
    long total = 0;
    for (int i = 0; i < evolvers.length; i++)
    {
      EcEvolver evolver = evolvers[i];
      total += evolver.getEvaluations();
    }
    return total+forgottenEvolutions;
  }

  /**
   * Starts the simulation (asychronously).
   * @throws InvalidConfigurationException
   */
  public void go() throws InvalidConfigurationException
  {
    reset();
   
    EcState s = importSource();
    EcState d = getInternalDestination();
   
    requiredActions = EcRequirementTree.createActionList(d);

    d.settings.fitnessType = fitnessType;
    CHROMOSOME_LENGTH = d.getEstimatedActions() + 70;

    // We are using the 'many small villages' vs 'one large city' method of
    // evolution.
    for (int threadIndex = 0; threadIndex < NUM_THREADS; threadIndex++)
    {
      spawnEvolutionaryChamber(s, d, threadIndex);
    }
  }

  /**
   * Resets the object so it can run a new simulation.
   */
  private synchronized void reset()
  {
    killThreads = false;
    firstrun = true;
    haveSavedBefore = false;
    bestScore = 0.0;
    bestScores = new Double[NUM_THREADS];
    evolutionsSinceDiscovery = new Integer[NUM_THREADS];
    evolvers = new EcEvolver[NUM_THREADS];
                forgottenEvolutions = 0;
  }

  /**
   * Stops the simulation.
   */
  public void stopAllThreads()
  {
    killThreads = true;
    for (Thread t : new ArrayList<Thread>(threads))
      try
      {
        t.join();
      }
      catch (InterruptedException e)
      {
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        logger.severe(sw.toString());
      }
    threads.clear();
  }

  private void spawnEvolutionaryChamber(final EcState source, final EcState destination, final int threadIndex)
      throws InvalidConfigurationException
  {
    reset(threadIndex);

    final EcEvolver myFunc = new EcEvolver(source, destination, requiredActions);
    evolvers[threadIndex] = myFunc;

    final Configuration conf = constructConfiguration(threadIndex, myFunc);

    final Genotype population = Genotype.randomInitialGenotype(conf);

    //Please justify this code.
    //-Lomilar
//    if (!firstrun)
//    {
//      int totalevoSinceDiscoveryOnBest = 0;
//      int numBestThreads = 0;
//      synchronized (bestScores)
//      {
//        for (int i = 0; i < bestScores.length; i++)
//        {
//          if (bestScores[i] >= bestScore)
//          {
//            numBestThreads++;
//            totalevoSinceDiscoveryOnBest += evolutionsSinceDiscovery[i];
//          }
//        }
//      }
//
//      if (!(totalevoSinceDiscoveryOnBest > Math.max(stagnationLimit, STAGNATION_LIMIT_MIN) * numBestThreads)
//          && numBestThreads < Math.max(Math.ceil(NUM_THREADS / 3), 1))
//      {
//        loadOldBuildOrders(population, conf, myFunc);
//      }
//    }
//    else
      if (firstrun && threadIndex == 0)
    {
      loadOldBuildOrders(population, conf, myFunc);
    }
    else if (firstrun && threadIndex == NUM_THREADS - 1)
    {
      firstrun = false;
    }

    final Thread thread = new Thread(population);
    conf.getEventManager().addEventListener(GeneticEvent.GENOTYPE_EVOLVED_EVENT, new GeneticEventListener()
    {
      @Override
      public void geneticEventFired(GeneticEvent a_firedEvent)
      {
        Collections.shuffle(conf.getGeneticOperators());
        BASE_MUTATION_RATE += .001;
        if (BASE_MUTATION_RATE >= (double) CHROMOSOME_LENGTH / 2.0 )
          BASE_MUTATION_RATE = 1;
        IChromosome fittestChromosome = population.getFittestChromosome();
        if (killThreads)
        {
          threads.remove(thread);
          thread.interrupt();
        }
        double fitnessValue = fittestChromosome.getFitnessValue();
        if (fitnessValue > bestScores[threadIndex])
        {
          bestScores[threadIndex] = fitnessValue;
          evolutionsSinceDiscovery[threadIndex] = 0;
          BASE_MUTATION_RATE = 1;
          if (reportInterface != null)
            reportInterface.threadScore(threadIndex, getOutput(myFunc, fittestChromosome, fitnessValue));
        }
        else
          evolutionsSinceDiscovery[threadIndex]++;

        int highestevosSinceDiscovery = 0;
        for (int i = 0; i < bestScores.length; i++)
        {
          if (bestScores[i] >= bestScore)
          {
            if (evolutionsSinceDiscovery[i] > highestevosSinceDiscovery)
              highestevosSinceDiscovery = evolutionsSinceDiscovery[i];
          }
        }

        stagnationLimit = Math.max(STAGNATION_LIMIT_MIN, (int) Math.ceil(highestevosSinceDiscovery * (.5)));

        if (fitnessValue < bestScore)
        {
          if (evolutionsSinceDiscovery[threadIndex] > stagnationLimit
              && fitnessValue < waterMark)
          {
            suicide(source, destination, threadIndex, thread);
          }
          else if (evolutionsSinceDiscovery[threadIndex] > stagnationLimit * 3)
          {
            suicide(source, destination, threadIndex, thread);
          }
        }
        else if (evolutionsSinceDiscovery[threadIndex] > stagnationLimit)
        {
          if (newbestscore)
          {
            waterMark = fitnessValue;
          }

          int totalevoSinceDiscoveryOnBest = 0;
          int numBestThreads = 0;

          for (int i = 0; i < bestScores.length; i++)
          {
            if (bestScores[i] >= bestScore)
            {
              numBestThreads++;
              totalevoSinceDiscoveryOnBest += evolutionsSinceDiscovery[i];
            }
          }

          if (totalevoSinceDiscoveryOnBest > stagnationLimit * 3
              * numBestThreads)
          {
            suicide(source, destination, threadIndex, thread);
          }
        }

        synchronized (bestScore)
        {
          if (fitnessValue > bestScore)
          {
            BASE_MUTATION_RATE = 1;
            bestScore = fitnessValue;
            newbestscore = true;

            String exactBuildOrder = getOutput(myFunc, fittestChromosome, fitnessValue);
            String buildOrder = getBuildOrder(myFunc, fittestChromosome);
            String yabotBuildOrder = getYabotBuildOrder(myFunc, fittestChromosome);

            if (reportInterface != null)
              reportInterface.bestScore(myFunc.evaluateGetBuildOrder(fittestChromosome), bestScore
                  .intValue(), exactBuildOrder, buildOrder, yabotBuildOrder);

            if (logger.isLoggable(Level.FINE))
            {
              logger.fine(chromosomeToString(fittestChromosome));
            }
            saveSeeds(fittestChromosome);
          }
        }
      }

    });
    thread.setPriority(Thread.MIN_PRIORITY);
    thread.start();
    threads.add(thread);
  }

  /**
   * Restarts a thread.
   * @param source
   * @param destination
   * @param threadIndex
   * @param thread
   */
  private void suicide(final EcState source, final EcState destination, final int threadIndex,
      final Thread thread)
  {
            forgottenEvolutions += evolvers[threadIndex].getEvaluations();
    // Stagnation. Suicide village and try again.
    logger.fine("Restarting thread " + threadIndex);
    try
    {
      spawnEvolutionaryChamber(source, destination, threadIndex);
    }
    catch (InvalidConfigurationException e)
    {
      StringWriter sw = new StringWriter();
      e.printStackTrace(new PrintWriter(sw));
      logger.severe(sw.toString());
    }
    threads.remove(thread);
    thread.interrupt();
  }

  private void reset(final int threadIndex)
  {
    bestScores[threadIndex] = 0.0;
    evolutionsSinceDiscovery[threadIndex] = 0;
    evolvers[threadIndex] = null;
  }

  /**
   * Generates a build order string.
   * @param myFunc
   * @param fittestChromosome
   * @param fitnessValue
   * @return
   */
  private String getOutput(final EcEvolver myFunc, IChromosome fittestChromosome, double fitnessValue)
  {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    PrintStream ps = new PrintStream(byteArrayOutputStream);
    if (reportInterface != null)
      myFunc.setLoggingStream(ps);

    displayBuildOrder(myFunc, fittestChromosome);
    ps.println(new Date() + ": " + fitnessValue);
    String results = new String(byteArrayOutputStream.toByteArray());
    return results;
  }

  /**
   * Generates a build order string.
   * @param myFunc
   * @param fittestChromosome
   * @return
   */
  public String getBuildOrder(final EcEvolver myFunc, IChromosome fittestChromosome)
  {
    return myFunc.getBuildOrder(fittestChromosome);
  }

  /**
   * Builds the YABOT representation of a build order.
   * @param myFunc
   * @param fittestChromosome
   * @return
   */
  public String getYabotBuildOrder(final EcEvolver myFunc, IChromosome fittestChromosome)
  {
    return myFunc.getYabotBuildOrder(fittestChromosome);
  }

  private Configuration constructConfiguration(final int threadIndex, final EcEvolver myFunc)
      throws InvalidConfigurationException
  {
    DefaultConfiguration.reset(threadIndex + " thread.");
    final Configuration conf = new DefaultConfiguration(threadIndex + " thread.", threadIndex + " thread.");
    conf.setFitnessFunction(myFunc);
    conf.addGeneticOperator(EcGeneticUtil.getCleansingOperator(this));
    conf.addGeneticOperator(EcGeneticUtil.getOverlordingOperator(this, requiredActions));
    conf.addGeneticOperator(EcGeneticUtil.getInsertionOperator(this));
    conf.addGeneticOperator(EcGeneticUtil.getDeletionOperator(this));
    conf.addGeneticOperator(EcGeneticUtil.getTwiddleOperator(this));
    conf.addGeneticOperator(EcGeneticUtil.getSwapOperator(this));
    conf.setPopulationSize(POPULATION_SIZE);
    conf.setSelectFromPrevGen(1);
    conf.setPreservFittestIndividual(false);
    conf.setAlwaysCaculateFitness(false);
    conf.setKeepPopulationSizeConstant(false);
    Gene[] initialGenes = importInitialGenes(conf);
    Chromosome c = new Chromosome(conf, initialGenes);
    conf.setSampleChromosome(c);
    return conf;
  }
 
  /**
   * Converts a {@link IChromosome} to its string representation.
   * @param chromosome
   * @return
   */
  private static String chromosomeToString(IChromosome chromosome)
  {
    int i = 0;
    StringBuilder sb = new StringBuilder();
    for (Gene g : chromosome.getGenes())
    {
      if (i++ == 100)
        break;
      if (((Integer) g.getAllele()).intValue() >= 10)
        sb.append(((char) ((int) 'a' + (Integer) g.getAllele() - 10)));
      else
        sb.append(g.getAllele().toString());
    }
    return sb.toString();
  }

  private static void displayBuildOrder(final EcEvolver myFunc, IChromosome fittestChromosome)
  {
    myFunc.enableLogging(true);
    myFunc.evaluateGetBuildOrder(fittestChromosome);
    myFunc.enableLogging(false);
  }

  private synchronized void loadOldBuildOrders(Genotype population, final Configuration conf, final EcEvolver myFunc)
  {
    loadSeeds();

    int cindex = 0;

    Collections.sort(history, new Comparator<EcBuildOrder>()
    {
      @Override
      public int compare(EcBuildOrder arg0, EcBuildOrder arg1)
      {
        //sort by fitness descending
        double score = 0;
        try
        {
          score = myFunc.getFitnessValue(buildChromosome(conf, arg1))
              - myFunc.getFitnessValue(buildChromosome(conf, arg0));
        }
        catch (InvalidConfigurationException e)
        {
          StringWriter sw = new StringWriter();
          e.printStackTrace(new PrintWriter(sw));
          logger.severe(sw.toString());
        }
        return (int) score;
      }
    });
    for (EcBuildOrder bo : history)
    {
      try
      {
        Chromosome c = buildChromosome(conf, bo);
        logger.fine(myFunc.getFitnessValue(c) + "");
        population.getPopulation().setChromosome(cindex++, c);
      }
      catch (InvalidConfigurationException e)
      {
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        logger.severe(sw.toString());
      }
    }
  }

  /**
   * Loads the seeds from the seed file.
   */
  public void loadSeeds()
  {
    try
    {
      if (SEEDS_EVO != null)
      {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SEEDS_EVO));
        history = (List<EcBuildOrder>) ois.readObject();
        ois.close();
      }
      else
      {
        throw new IOException();
      }
    }
    catch (FileNotFoundException e)
    {
      logger.fine("Seeds file not found.");
    }
    catch (InvalidClassException ex)
    {
      logger.fine("Seeds file is in old format. Starting over. :-(");
    }
    catch (ClassNotFoundException e)
    {
      logger.fine("Seeds file is in old format. Starting over. :-(");
    }
    catch (IOException e)
    {
      try
      {
        if (SEEDS_EVO_2 != null)
        {
          ObjectInputStream ois;
          ois = new ObjectInputStream(new FileInputStream(SEEDS_EVO_2));
          history = (List<EcBuildOrder>) ois.readObject();
          ois.close();
        }
      }
      catch (FileNotFoundException e1)
      {
        logger.fine("Seeds file not found.");
      }
      catch (InvalidClassException e1)
      {
        logger.fine("Seeds 2 file is in old format. Starting over. :-(");
      }
      catch (ClassNotFoundException e1)
      {
        logger.fine("Seeds 2 file is in old format. Starting over. :-(");
      }
      catch (IOException e1)
      {
        StringWriter sw = new StringWriter();
        e1.printStackTrace(new PrintWriter(sw));
        logger.severe(sw.toString());
      }
    }
  }

  private boolean  haveSavedBefore  = false;

  private synchronized void saveSeeds(IChromosome fittestChromosome)
  {
    EcBuildOrder bo = new EcBuildOrder(importDestination());
    try
    {
      bo = EcEvolver.populateBuildOrder(bo, fittestChromosome, requiredActions);
      if (haveSavedBefore)
        history.remove(history.size() - 1);
      haveSavedBefore = true;
      history.add(bo);
    }
    catch (CloneNotSupportedException e)
    {
      StringWriter sw = new StringWriter();
      e.printStackTrace(new PrintWriter(sw));
      logger.severe(sw.toString());
    }

    saveSeeds();
  }

  /**
   * Saves the seeds to the seed file.
   */
  public void saveSeeds()
  {
    try
    {
      if (SEEDS_EVO != null)
      {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SEEDS_EVO, false));
        oos.writeObject(history);
        oos.close();
      }
      if (SEEDS_EVO_2 != null)
      {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SEEDS_EVO_2, false));
        oos.writeObject(history);
        oos.close();
      }
    }
    catch (IOException e)
    {
      StringWriter sw = new StringWriter();
      e.printStackTrace(new PrintWriter(sw));
      logger.severe(sw.toString());
    }
  }

  private Gene[] importInitialGenes(Configuration conf)
  {
    ArrayList<Gene> genes = new ArrayList<Gene>();
    for (int i = 0; i < CHROMOSOME_LENGTH; i++)
      try
      {
        IntegerGene g = new IntegerGene(conf, 0, requiredActions.size() - 1);
        g.setAllele(0);
        genes.add(g);
      }
      catch (InvalidConfigurationException e)
      {
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        logger.severe(sw.toString());
      }
    return genes.toArray(new Gene[genes.size()]);
  }

  private EcBuildOrder importSource()
  {
    EcBuildOrder ecBuildOrder = new EcBuildOrder();
    ecBuildOrder.targetSeconds = importDestination().targetSeconds;
    ecBuildOrder.settings = importDestination().settings;
    ecBuildOrder.scoutDrone = importDestination().scoutDrone;
    return ecBuildOrder;
  }

  private EcState importDestination()
  {
    try
    {
      return (EcState) getInternalDestination().clone();
    }
    catch (CloneNotSupportedException e)
    {
      StringWriter sw = new StringWriter();
      e.printStackTrace(new PrintWriter(sw));
      logger.severe(sw.toString());
    }
    return null;
  }

  private Chromosome buildChromosome(Configuration conf, EcBuildOrder bo) throws InvalidConfigurationException
  {
    ArrayList<Gene> genes = new ArrayList<Gene>();
    int CC = 0;
    for (EcAction a : bo.actions)
    {
      if (++CC > CHROMOSOME_LENGTH)
        continue;
      IntegerGene g = new IntegerGene(conf, 0, requiredActions.size() - 1);
      Integer allele = EcAction.findAllele(requiredActions, a);
      if (allele == null)
        break;
      g.setAllele(allele);
      genes.add(g);

    }
    while (genes.size() < CHROMOSOME_LENGTH)
    {
      IntegerGene g = new IntegerGene(conf, 0, requiredActions.size() - 1);
      g.setAllele(0);
      genes.add(g);
    }
    Chromosome c = new Chromosome(conf);
    c.setGenes(genes.toArray(new Gene[genes.size()]));
    c.setIsSelectedForNextGeneration(true);
    return c;
  }

  /**
   * Sets the number of threads the simulation will use.
   * Defaults to the number of processors the system has.
   * @param threads the number of threads
   */
  public void setThreads(int threads)
  {
    int availableProcessors = MAX_NUM_THREADS;
    NUM_THREADS = threads;
    if (NUM_THREADS > availableProcessors || NUM_THREADS < 1)
      NUM_THREADS = availableProcessors;
  }

  /**
   * Sets the final goal that the simulation must reach.
   * @param destination the final goal
   */
  public void setDestination(EcState destination)
  {
    destination.mergedWaypoints = null;
    this.destination = destination;
  }

  public EcState getInternalDestination()
  {
    return destination;
  }
   
  /**
   * Gets the number of threads the simulation will use.
   * @return the number of threads
   */
  public int getThreads()
  {
    return NUM_THREADS;
  }
 
  /**
   * gets the list of required actions for the current destination
   * @return
   */
  public List<Class<? extends EcAction>> getActions(){
    return requiredActions;
  }
}
TOP

Related Classes of com.fray.evo.EvolutionChamber

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.