Genotype.java :  » Development » jgap-3.4.4 » org » jgap » Java Open Source

Java Open Source » Development » jgap 3.4.4 
jgap 3.4.4 » org » jgap » Genotype.java
/*
 * This file is part of JGAP.
 *
 * JGAP offers a dual license model containing the LGPL as well as the MPL.
 *
 * For licensing information please see the file license.txt included with JGAP
 * or have a look at the top of class org.jgap.Chromosome which representatively
 * includes the JGAP license policy applicable for any file delivered with JGAP.
 */
package org.jgap;

import java.io.*;
import java.util.*;

import org.jgap.audit.*;
import org.jgap.distr.*;
import org.jgap.impl.job.*;

/**
 * Genotypes are fixed-length populations of chromosomes. As an instance of
 * a Genotype is evolved, all of its Chromosomes are also evolved. A Genotype
 * may be constructed normally via constructor, or the static
 * randomInitialGenotype() method can be used to generate a Genotype with a
 * randomized Chromosome population.
 * <p>
 * Please note that among all created Genotype instances there may only be one
 * configuration, used by all Genotype instances.
 *
 * @author Neil Rotstan
 * @author Klaus Meffert
 * @since 1.0
 */
public class Genotype
    implements Serializable, Runnable {
  /** String containing the CVS revision. Read out via reflection!*/
  private final static String CVS_REVISION = "$Revision: 1.105 $";

  /**
   * The current Configuration instance.
   * @since 1.0
   */
  private Configuration m_activeConfiguration;

  private transient static Configuration m_staticConfiguration;

  /**
   * The array of Chromosomes that make-up the Genotype's population.
   * @since 2.0
   */
  private Population m_population;

  /**
   * Constructs a new Genotype instance with the given array of Chromosomes and
   * the given active Configuration instance. Note that the Configuration object
   * must be in a valid state when this method is invoked, or a
   * InvalidConfigurationException will be thrown.
   *
   * @param a_configuration the Configuration object to use
   * @param a_initialChromosomes the Chromosome population to be managed by
   * this Genotype instance
   * @throws InvalidConfigurationException if the given Configuration object is
   * in an invalid state
   *
   * @author Neil Rotstan
   * @author Klaus Meffert
   * @since 1.0
   * @deprecated use Genotype(Configuration, Population) instead
   */
  public Genotype(Configuration a_configuration,
                  IChromosome[] a_initialChromosomes)
      throws InvalidConfigurationException {
    this(a_configuration, new Population(a_configuration, a_initialChromosomes));
  }

  /**
   * Constructs a new Genotype instance with the given array of
   * Chromosomes and the given active Configuration instance. Note
   * that the Configuration object must be in a valid state
   * when this method is invoked, or a InvalidconfigurationException
   * will be thrown.
   *
   * @param a_configuration the Configuration object to use
   * @param a_population the Chromosome population to be managed by this
   * Genotype instance
   * @throws InvalidConfigurationException
   *
   * @author Neil Rotstan
   * @author Klaus Meffert
   * @since 2.0
   */
  public Genotype(Configuration a_configuration, Population a_population)
      throws InvalidConfigurationException {
    // Sanity checks: Make sure neither the Configuration, nor the array
    // of Chromosomes, nor any of the Genes inside the array is null.
    // -----------------------------------------------------------------
    if (a_configuration == null) {
      throw new IllegalArgumentException(
          "The Configuration instance must not be null.");
    }
    if (a_population == null) {
      throw new IllegalArgumentException(
          "The Population must not be null.");
    }
    for (int i = 0; i < a_population.size(); i++) {
      if (a_population.getChromosome(i) == null) {
        throw new IllegalArgumentException(
            "The Chromosome instance at index " + i + " of the array of " +
            "Chromosomes is null. No Chromosomes instance in this array " +
            "must not be null.");
      }
    }
    m_population = a_population;
    // Lock the settings of the configuration object so that it cannot
    // be altered.
    // ---------------------------------------------------------------
    a_configuration.lockSettings();
    m_activeConfiguration = a_configuration;
  }

  /**
   * Don't use this constructor, it's only for internal use.
   *
   * @param a_configuration not used here!
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public Genotype(Configuration a_configuration)
      throws InvalidConfigurationException {
  }

  /**
   * Retrieves the array of Chromosomes that make up the population of this
   * Genotype instance.
   *
   * @return the Population of Chromosomes
   *
   * @author Neil Rotstan
   * @author Klaus Meffert
   * @since 1.0
   * @deprecated uses getPopulation() instead
   */
  public synchronized IChromosome[] getChromosomes() {
    Iterator it = getPopulation().iterator();
    IChromosome[] result = new Chromosome[getPopulation().size()];
    int i = 0;
    while (it.hasNext()) {
      result[i++] = (IChromosome) it.next();
    }
    return result;
  }

  /**
   * @return the current population of chromosomes
   *
   * @author Klaus Meffert
   * @since 2.1
   */
  public Population getPopulation() {
    return m_population;
  }

  /**
   * Retrieves the Chromosome in the Population with the highest fitness
   * value.
   *
   * @return the Chromosome with the highest fitness value, or null if there
   * are no chromosomes in this Genotype
   *
   * @author Neil Rotstan
   * @author Klaus Meffert
   * @since 1.0
   */
  public synchronized IChromosome getFittestChromosome() {
    return getPopulation().determineFittestChromosome();
  }

  /**
   * Retrieves the Chromosome in the Population with the highest fitness
   * value within the given indices.
   *
   * @param a_startIndex the index to start the determination with
   * @param a_endIndex the index to end the determination with
   * @return the Chromosome with the highest fitness value within the given
   * indices, or null if there are no chromosomes in this Genotype
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public synchronized IChromosome getFittestChromosome(int a_startIndex,
      int a_endIndex) {
    return getPopulation().determineFittestChromosome(a_startIndex, a_endIndex);
  }

  /**
   * Retrieves the top n Chromsomes in the population (the ones with the best
   * fitness values).
   *
   * @param a_numberOfChromosomes the number of chromosomes desired
   * @return the list of Chromosomes with the highest fitness values, or null
   * if there are no chromosomes in this Genotype
   *
   * @author Charles Kevin Hill
   * @since 2.4
   */
  public synchronized List getFittestChromosomes(int a_numberOfChromosomes) {
    return getPopulation().determineFittestChromosomes(a_numberOfChromosomes);
  }

  /**
   * Evolves the population of Chromosomes within this Genotype. This will
   * execute all of the genetic operators added to the present active
   * configuration and then invoke the natural selector to choose which
   * chromosomes will be included in the next generation population. Note
   * that the population size not always remains constant (dependent on the
   * NaturalSelectors used!).
   * To consecutively call this method, use evolve(int)!!!
   *
   * @author Neil Rotstan
   * @author Klaus Meffert
   * @since 1.0
   */
  public synchronized void evolve() {
    IBreeder breeder = getConfiguration().getBreeder();
    Population newPop = breeder.evolve(getPopulation(), getConfiguration());
    setPopulation(newPop);
  }

  /**
   * Evolves this Genotype the specified number of times. This is
   * equivalent to invoking the standard evolve() method the given number
   * of times in a row.
   *
   * @param a_numberOfEvolutions the number of times to evolve this Genotype
   * before returning
   *
   * @author Klaus Meffert
   * @since 1.1
   */
  public void evolve(int a_numberOfEvolutions) {
    for (int i = 0; i < a_numberOfEvolutions; i++) {
      evolve();
    }
    if (m_activeConfiguration.isKeepPopulationSizeConstant()) {
      keepPopSizeConstant(getPopulation(),
                          m_activeConfiguration.getPopulationSize());
    }
  }

  /**
   * Evolves this genotype until the given monitor asks to quit the evolution
   * cycle.
   *
   * @param a_monitor the monitor used to decide when to stop evolution
   *
   * @return messages of the registered evolution monitor. May indicate why the
   * evolution was asked to be stopped. May be empty, depending on the
   * implementation of the used monitor
   *
   * @author Klaus Meffert
   * @since 3.4.4
   */
  public List<String> evolve(IEvolutionMonitor a_monitor) {
    a_monitor.start(getConfiguration());
    List<String> messages = new Vector();
    do {
      getConfiguration();
      evolve();
      boolean goon = a_monitor.nextCycle(getPopulation(), messages);
      if (!goon) {
        break;
      }
    } while (true);
    return messages;
  }

  /**
   * @return string representation of this Genotype instance, useful for display
   * or debug purposes
   *
   * @author Neil Rotstan
   * @since 1.0
   */
  public String toString() {
    StringBuffer buffer = new StringBuffer();
    for (int i = 0; i < getPopulation().size(); i++) {
      buffer.append(getPopulation().getChromosome(i).toString());
      buffer.append(" [");
      buffer.append(getPopulation().getChromosome(i).getFitnessValueDirectly());
      buffer.append("]\n");
    }
    return buffer.toString();
  }

  /**
   * Convenience method that returns a newly constructed Genotype
   * instance configured according to the given Configuration instance.
   * The population of Chromosomes will be created according to the setup of
   * the sample Chromosome in the Configuration object, but the gene values
   * (alleles) will be set to random legal values.
   *
   * @param a_configuration the current active Configuration object
   * @return a newly constructed Genotype instance
   *
   * @throws IllegalArgumentException if the given Configuration object is null
   * @throws InvalidConfigurationException if the given Configuration
   * instance is not in a valid state
   *
   * @author Neil Rotstan
   * @author Klaus Meffert
   * @since 2.3
   */
  public static Genotype randomInitialGenotype(Configuration
      a_configuration)
      throws InvalidConfigurationException {
    if (a_configuration == null) {
      throw new IllegalArgumentException(
          "The Configuration instance may not be null.");
    }
    a_configuration.lockSettings();
    // Create an array of chromosomes equal to the desired size in the
    // active Configuration and then populate that array with Chromosome
    // instances constructed according to the setup in the sample
    // Chromosome, but with random gene values (alleles). The Chromosome
    // class randomInitialChromosome() method will take care of that for
    // us.
    // ------------------------------------------------------------------
    int populationSize = a_configuration.getPopulationSize();
    Population pop = new Population(a_configuration, populationSize);
    // Do randomized initialization.
    // -----------------------------
    Genotype result = new Genotype(a_configuration, pop);
    result.fillPopulation(populationSize);
    return result;
  }

  /**
   * Fills up the population with random chromosomes if necessary.
   *
   * @param a_num the number of chromosomes to add
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.2
   */
  public void fillPopulation(final int a_num)
      throws InvalidConfigurationException {
    IChromosome sampleChrom = getConfiguration().getSampleChromosome();
    Class sampleClass = sampleChrom.getClass();
    IInitializer chromIniter = getConfiguration().getJGAPFactory().
        getInitializerFor(sampleChrom, sampleClass);
    if (chromIniter == null) {
      throw new InvalidConfigurationException("No initializer found for class "
          + sampleClass);
    }
    try {
      for (int i = 0; i < a_num; i++) {
        getPopulation().addChromosome( (IChromosome) chromIniter.perform(
            sampleChrom,
            sampleClass, null));
      }
    } catch (Exception ex) {
      // Try to propagate exception, see "bug" 1661635.
      // ----------------------------------------------
      if (ex.getCause() != null) {
        throw new IllegalStateException(ex.getCause().toString());
      }
      else {
        throw new IllegalStateException(ex.getMessage());
      }
    }
  }

  /**
   * Compares this Genotype against the specified object. The result is true
   * if the argument is an instance of the Genotype class, has exactly the
   * same number of chromosomes as the given Genotype, and, for each
   * Chromosome in this Genotype, there is an equal chromosome in the
   * given Genotype. The chromosomes do not need to appear in the same order
   * within the population.
   *
   * @param a_other the object to compare against
   * @return true if the objects are the same, false otherwise
   *
   * @author Neil Rotstan
   * @author Klaus Meffert
   * @since 1.0
   */
  public boolean equals(Object a_other) {
    try {
      // First, if the other Genotype is null, then they're not equal.
      // -------------------------------------------------------------
      if (a_other == null) {
        return false;
      }
      Genotype otherGenotype = (Genotype) a_other;
      // First, make sure the other Genotype has the same number of
      // chromosomes as this one.
      // ----------------------------------------------------------
      if (getPopulation().size() != otherGenotype.getPopulation().size()) {
        return false;
      }
      // Next, prepare to compare the chromosomes of the other Genotype
      // against the chromosomes of this Genotype. To make this a lot
      // simpler, we first sort the chromosomes in both this Genotype
      // and the one we're comparing against. This won't affect the
      // genetic algorithm (it doesn't care about the order), but makes
      // it much easier to perform the comparison here.
      // --------------------------------------------------------------
      Collections.sort(getPopulation().getChromosomes());
      Collections.sort(otherGenotype.getPopulation().getChromosomes());
      for (int i = 0; i < getPopulation().size(); i++) {
        if (! (getPopulation().getChromosome(i).equals(
            otherGenotype.getPopulation().getChromosome(i)))) {
          return false;
        }
      }
      return true;
    } catch (ClassCastException e) {
      return false;
    }
  }

  /**
   * Applies all NaturalSelectors registered with the Configuration.
   *
   * @param a_processBeforeGeneticOperators true apply NaturalSelectors
   * applicable before GeneticOperators, false: apply the ones applicable
   * after GeneticOperators
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  protected void applyNaturalSelectors(
      boolean a_processBeforeGeneticOperators) {
    /**@todo optionally use working pool*/
    try {
      // Process all natural selectors applicable before executing the
      // genetic operators (reproduction, crossing over, mutation...).
      // -------------------------------------------------------------
      int selectorSize = m_activeConfiguration.getNaturalSelectorsSize(
          a_processBeforeGeneticOperators);
      if (selectorSize > 0) {
        int population_size = m_activeConfiguration.getPopulationSize();
        if (a_processBeforeGeneticOperators) {
          // Only select part of the previous population into this generation.
          // -----------------------------------------------------------------
          population_size = (int) Math.round(population_size *
              getConfiguration().getSelectFromPrevGen());
        }
        int single_selection_size;
        Population new_population;
        new_population = new Population(m_activeConfiguration,
                                        population_size);
        NaturalSelector selector;
        // Repopulate the population of chromosomes with those selected
        // by the natural selector. Iterate over all natural selectors.
        // ------------------------------------------------------------
        for (int i = 0; i < selectorSize; i++) {
          selector = m_activeConfiguration.getNaturalSelector(
              a_processBeforeGeneticOperators, i);
          if (i == selectorSize - 1 && i > 0) {
            // Ensure the last NaturalSelector adds the remaining Chromosomes.
            // ---------------------------------------------------------------
            single_selection_size = population_size - getPopulation().size();
          }
          else {
            single_selection_size = population_size / selectorSize;
          }
          // Do selection of Chromosomes.
          // ----------------------------
          selector.select(single_selection_size, getPopulation(),
                          new_population);
          // Clean up the natural selector.
          // ------------------------------
          selector.empty();
        }
        setPopulation(new Population(m_activeConfiguration));
        getPopulation().addChromosomes(new_population);
      }
    } catch (InvalidConfigurationException iex) {
      // This exception should never be reached
      throw new IllegalStateException(iex.getMessage());
    }
  }

  /**
   * Applies all GeneticOperators registered with the Configuration.
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public void applyGeneticOperators() {
    List geneticOperators = m_activeConfiguration.getGeneticOperators();
    Iterator operatorIterator = geneticOperators.iterator();
    while (operatorIterator.hasNext()) {
      GeneticOperator operator = (GeneticOperator) operatorIterator.next();
      applyGeneticOperator(operator, getPopulation(),
                           getPopulation().getChromosomes());
//      List workingPool = new Vector();
//      applyGeneticOperator(operator, getPopulation(),
//                           workingPool);
    }
  }

  /**
   * @return the configuration to use with the Genetic Algorithm
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public static Configuration getStaticConfiguration() {
    return m_staticConfiguration;
  }

  /**
   * Sets the configuration to use with the Genetic Algorithm.
   * @param a_configuration the configuration to use
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public static void setStaticConfiguration(Configuration a_configuration) {
    m_staticConfiguration = a_configuration;
  }

  public Configuration getConfiguration() {
    return m_activeConfiguration;
  }

  /***
   * Hashcode function for the genotype, tries to create a unique hashcode for
   * the chromosomes within the population. The logic for the hashcode is
   *
   * Step  Result
   * ----  ------
   *    1  31*0      + hashcode_0 = y(1)
   *    2  31*y(1)   + hashcode_1 = y(2)
   *    3  31*y(2)   + hashcode_2 = y(3)
   *    n  31*y(n-1) + hashcode_n-1 = y(n)
   *
   * Each hashcode is a number and the binary equivalent is computed and
   * returned.
   *
   * @return the computed hashcode
   *
   * @author vamsi
   * @since 2.1
   */
  public int hashCode() {
    int i, size = getPopulation().size();
    IChromosome s;
    int twopower = 1;
    // For empty genotype we want a special value different from other hashcode
    // implementations.
    // ------------------------------------------------------------------------
    int localHashCode = -573;
    for (i = 0; i < size; i++, twopower = 2 * twopower) {
      s = getPopulation().getChromosome(i);
      localHashCode = 31 * localHashCode + s.hashCode();
    }
    return localHashCode;
  }

  protected void setPopulation(Population a_pop) {
    m_population = a_pop;
  }

  /**
   * Overwritable method that calls a GeneticOperator to operate on a given
   * population and asks him to store the result in the list of chromosomes.
   * Override this method if you want to ensure that a_chromosomes is not
   * part of a_population resp. if you want to use a different list.
   *
   * @param a_operator the GeneticOperator to call
   * @param a_population the population to use
   * @param a_chromosomes the list of Chromosome objects to return
   *
   * @author Klaus Meffert
   * @since 2.4
   */
  protected void applyGeneticOperator(GeneticOperator a_operator,
                                      Population a_population,
                                      List a_chromosomes) {
    a_operator.operate(a_population, a_chromosomes);
  }

  /**
   * Cares that the population size does not exceed the given maximum size.
   *
   * @param a_pop the population to keep constant in size
   * @param a_maxSize the maximum size allowed for the population
   *
   * @author Klaus Meffert
   * @since 2.5
   */
  protected void keepPopSizeConstant(Population a_pop, int a_maxSize) {
    /**@todo use StandardPostSelector instead?*/
    int popSize = a_pop.size();
    // See request  1213752.
    // ---------------------
    while (popSize > a_maxSize) {
      // Remove a chromosome.
      // --------------------
      a_pop.removeChromosome(0);
      popSize--;
    }
  }

  /**
   * If used in a Thread: runs the evolution forever.
   * You have to implement a listener to stop computation sometime. See
   * examples.simpleBooleanThreaded for a possible implementation of such a
   * listener.
   *
   * @author Klaus Meffert
   * @since 3.01
   */
  public void run() {
    while (!Thread.currentThread().interrupted()) {
      evolve();
    }
  }

  /**
   * Splits a population into pieces that can be evolved independently.
   *
   * @param a_splitter splits the population
   * @return list of IEvolveJob objects
   * @throws Exception
   *
   * @author Klaus Meffert
   */
  public List<IEvolveJob> getEvolves(IPopulationSplitter a_splitter)
      throws Exception {
    // We return a list of IEvolveJob instances.
    // -----------------------------------------
    List result = new Vector();
    Population[] pops = a_splitter.split(getPopulation());
    // Feed the population chunks into different evolve jobs.
    // ------------------------------------------------------
    for (int i = 0; i < pops.length; i++) {
      Configuration newConf = (Configuration) getConfiguration().clone();
      EvolveData data = new EvolveData(newConf);
      if (pops[i] == null) {
        throw new IllegalStateException("Population must no be null"
                                        + " (Index: " + i
                                        + ", Splitter: "
                                        + a_splitter.getClass().getName() + ")");
      }
      data.setPopulation(pops[i]);
      data.setBreeder(newConf.getBreeder());
      IEvolveJob evolver = new EvolveJob(data);
      result.add(evolver);
    }
    getPopulation().clear();
    return result;
  }

  public void mergeResults(IPopulationMerger a_merger, EvolveResult[] a_results)
      throws Exception {
    int size = a_results.length;
    Population target = new Population(getConfiguration());
    for (int i = 0; i < size; i++) {
      EvolveResult singleResult = a_results[i];
      if (singleResult == null) {
        throw new IllegalStateException("Single result is null!");
      }
      Population pop = singleResult.getPopulation();
      /**@todo use/enhance IPopulationMerger*/
//      a_merger.mergePopulations()
      List goodOnes = pop.determineFittestChromosomes(3);
      for (int j = 0; j < goodOnes.size(); j++) {
        IChromosome goodOne = (IChromosome) goodOnes.get(j);
        target.addChromosome(goodOne);
      }
    }
    setPopulation(target);
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.