/*
 * 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.lang.reflect.*;
import java.util.*;

import org.jgap.audit.*;
import org.jgap.util.*;

/**
 * Makes up the population of a generation during evolution. Represented by a
 * list of chromosomes held in the Genotype.
 *
 * @author Klaus Meffert
 * @since 2.0
 */
public class Population
    implements Serializable, ICloneable, IPersistentRepresentation {
  /** String containing the CVS revision. Read out via reflection!*/
  private static final String CVS_REVISION = "$Revision: 1.66 $";

  /**
   * The array of Chromosomes that makeup the Genotype's population.
   */
  private List m_chromosomes;

  /**
   * The fittest Chromosome of the population.
   */
  private IChromosome m_fittestChromosome;

  /**
   * Indicates whether at least one of the chromosomes has been changed
   * (deleted, added, modified).
   */
  private boolean m_changed;

  /**
   * Indicates that the list of Chromosomes has been sorted.
   */
  private boolean m_sorted;

  private Configuration m_config;

  public final static String CHROM_DELIMITER = "~";

  /**
   * Represents the heading delimiter that is used to separate chromosomes in
   * the persistent representation.
   */
  public final static String CHROM_DELIMITER_HEADING = "[";

  /**
   * Represents the closing delimiter that is used to separate chromosomes in
   * the persistent representation.
   */
  public final static String CHROM_DELIMITER_CLOSING = "]";

  /*
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public Population(final Configuration a_config)
      throws InvalidConfigurationException {
    this(a_config, 100);
  }

  /*
   * Constructs the Population from a list of Chromosomes. Does not use cloning!
   *
   * @param a_chromosomes the Chromosome's to be used for building the
   * Population
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public Population(final Configuration a_config,
                    final IChromosome[] a_chromosomes)
      throws InvalidConfigurationException {
    this(a_config, a_chromosomes.length);
    synchronized (m_chromosomes) {
      for (int i = 0; i < a_chromosomes.length; i++) {
        // In here we could test for null chromosomes, but this is skipped
        // because of performance issues (although this may seem idiotic...)
        m_chromosomes.add(a_chromosomes[i]);
      }
    }
    setChanged(true);
  }

  /*
   * Constructs the Population from a single Chromosome. Does not use cloning!

   * @param a_chromosome the Chromosome to be used for building the Population
   *
   * @author Klaus Meffert
   * @since 3.2
   */
  public Population(final Configuration a_config,
                    final IChromosome a_chromosome)
      throws InvalidConfigurationException {
    this(a_config, 1);
    if (a_chromosome == null) {
      throw new IllegalArgumentException("Chromosome passed must not be null!");
    }
    synchronized (m_chromosomes) {
      m_chromosomes.add(a_chromosome);
    }
    setChanged(true);
  }

  /*
   * Constructs an empty Population with the given initial size.

   * @param a_size the initial size of the empty Population. The initial size
   * is not fix, it is just for optimized list creation.
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public Population(final Configuration a_config, final int a_size)
      throws InvalidConfigurationException {
    if (a_config == null) {
      throw new InvalidConfigurationException("Configuration must not be null!");
    }
    m_config = a_config;
    // Use a synchronized list (important for distributed computing!)
    m_chromosomes = new Vector(a_size);
    setChanged(true);
  }

  /*
   * Constructs an empty Population with initial array size 100.
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public Population()
      throws InvalidConfigurationException {
    this(Genotype.getStaticConfiguration());
  }

  public Configuration getConfiguration() {
    return m_config;
  }

  /**
   * Adds a Chromosome to this Population. Does nothing when given null.
   *
   * @param a_toAdd the Chromosome to add
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public void addChromosome(final IChromosome a_toAdd) {
    if (a_toAdd != null) {
      synchronized (m_chromosomes) {
        m_chromosomes.add(a_toAdd);
      }
      setChanged(true);
    }
  }

  /**
   * Adds all the Chromosomes in the given Population. Does nothing on null or
   * an empty Population.
   *
   * @param a_population the Population to add
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public void addChromosomes(final Population a_population) {
    if (a_population != null) {
      synchronized (m_chromosomes) {
        m_chromosomes.addAll(a_population.getChromosomes());
      }
      // The following would do the same:
//      if (a_population.getChromosomes() != null) {
//        int size = a_population.getChromosomes().size();
//        for (int i = 0; i < size; i++) {
//          IChromosome chrom = a_population.getChromosome(i);
//          m_chromosomes.add(chrom);
//        }
//      }
      setChanged(true);
    }
  }

  /**
   * Replaces all chromosomes in the population with the give list of
   * chromosomes.
   *
   * @param a_chromosomes the chromosomes to make the population up from
   *
   * @author Klaus Meffert
   */
  public void setChromosomes(final List a_chromosomes) {
    synchronized (m_chromosomes) {
      m_chromosomes = a_chromosomes;
    }
    setChanged(true);
  }

  /**
   * Sets in the given Chromosome on the given index in the list of chromosomes.
   * If the given index is exceeding the list by one, the chromosome is
   * appended.
   *
   * @param a_index the index to set the Chromosome in
   * @param a_chromosome the Chromosome to be set
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public void setChromosome(final int a_index, final IChromosome a_chromosome) {
    if (m_chromosomes.size() == a_index) {
      addChromosome(a_chromosome);
    }
    else {
      synchronized (m_chromosomes) {
        m_chromosomes.set(a_index, a_chromosome);
      }
      setChanged(true);
    }
  }

  /**
   * @return the list of Chromosome's in the Population. Don't modify the
   * retrieved list by using clear(), remove(int) etc. If you do so, you need to
   * call setChanged(true)
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public List getChromosomes() {
    return m_chromosomes;
  }

  /**
   * @param a_index the index of the Chromosome to be returned
   * @return Chromosome at given index in the Population
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public IChromosome getChromosome(final int a_index) {
    return (IChromosome) m_chromosomes.get(a_index);
  }

  /**
   * @return number of Chromosome's in the Population
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public int size() {
    return m_chromosomes.size();
  }

  /**
   * @return Iterator for the Chromosome list in the Population. Please be aware
   * that using remove() forces you to call setChanged(true)
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public Iterator iterator() {
    return m_chromosomes.iterator();
  }

  /**
   * @return the Population converted into a list of Chromosome's
   *
   * @author Klaus Meffert, Dan Clark
   * @since 2.0
   */
  public IChromosome[] toChromosomes() {
    return (IChromosome[]) m_chromosomes.toArray(
        new IChromosome[m_chromosomes.size()]);
  }

  /**
   * Determines the fittest Chromosome in the Population (the one with the
   * highest fitness value) and memorizes it. This is an optimized version
   * compared to calling determineFittesChromosomes(1).
   *
   * @return the fittest Chromosome of the Population
   *
   * @author Klaus Meffert
   * @since 2.0
   */
  public IChromosome determineFittestChromosome() {
    if (!m_changed && m_fittestChromosome != null) {
      return m_fittestChromosome;
    }
    Iterator it = m_chromosomes.iterator();
    FitnessEvaluator evaluator = getConfiguration().getFitnessEvaluator();
    double bestFitness;
    if (evaluator.isFitter(2.0d, 1.0d)) {
      bestFitness = -1.0d;
    }
    else {
      bestFitness = Double.MAX_VALUE;
    }
    double fitness;
    while (it.hasNext()) {
      IChromosome chrom = (IChromosome) it.next();
      fitness = chrom.getFitnessValue();
      if (evaluator.isFitter(fitness, bestFitness)
          || m_fittestChromosome == null) {
        m_fittestChromosome = chrom;
        bestFitness = fitness;
      }
    }
    setChanged(false);
    return m_fittestChromosome;
  }

  /**
   * Determines the fittest Chromosome in the population (the one with the
   * highest fitness value) within the given indices and memorizes it. This is
   * an optimized version compared to calling determineFittesChromosomes(1).
   *
   * @param a_startIndex index to begin the evaluation with
   * @param a_endIndex index to end the evaluation with
   * @return the fittest Chromosome of the population within the given indices
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public IChromosome determineFittestChromosome(int a_startIndex,
      int a_endIndex) {
    double bestFitness = -1.0d;
    FitnessEvaluator evaluator = getConfiguration().getFitnessEvaluator();
    double fitness;
    int startIndex = Math.max(0, a_startIndex);
    int endIndex = Math.min(m_chromosomes.size()-1, a_endIndex);
    m_fittestChromosome = null;
    for (int i = startIndex; i <= endIndex; i++) {
      IChromosome chrom = (IChromosome) m_chromosomes.get(i);
      fitness = chrom.getFitnessValue();
      if (evaluator.isFitter(fitness, bestFitness)
          || m_fittestChromosome == null) {
        m_fittestChromosome = chrom;
        bestFitness = fitness;
      }
    }
    return m_fittestChromosome;
  }

  /**
   * Mark that for the population the fittest chromosome may have changed.
   *
   * @param a_changed true: population's fittest chromosome may have changed,
   * false: fittest chromosome evaluated earlier is still valid
   *
   * @author Klaus Meffert
   * @since 2.2
   */
  protected void setChanged(final boolean a_changed) {
    m_changed = a_changed;
    setSorted(false);
  }

  /**
   * @return true: population's chromosomes (maybe) were changed,
   * false: not changed for sure
   *
   * @since 2.6
   */
  public boolean isChanged() {
    return m_changed;
  }

  /**
   * Mark the population as sorted.
   *
   * @param a_sorted true: mark population as sorted
   *
   * @author Klaus Meffert
   * @since 2.6
   */
  protected void setSorted(final boolean a_sorted) {
    m_sorted = a_sorted;
  }

  /**
   * Determines whether the given chromosome is contained within the population.
   * @param a_chromosome the chromosome to check
   * @return true: chromosome contained within population
   *
   * @author Klaus Meffert
   * @since 2.1
   */
  public boolean contains(final IChromosome a_chromosome) {
    return m_chromosomes.contains(a_chromosome);
  }

  /**
   * Removes a chromosome in the list at the given index. Method has package
   * visibility to signal that this is a method not to be used outside the
   * JGAP kernel under normal circumstances.
   *
   * @param a_index index of chromosome to be removed in list
   * @return removed Chromosome
   *
   * @author Klaus Meffert
   * @since 2.4
   */
  IChromosome removeChromosome(final int a_index) {
    if (a_index < 0 || a_index >= size()) {
      throw new IllegalArgumentException("Index must be within bounds!");
    }
    setChanged(true);
    return (IChromosome) m_chromosomes.remove(a_index);
  }

  /**
   * Cares that the population size does not exceed the maximum size given in
   * the configuration.
   *
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.2
   */
  public void keepPopSizeConstant() throws InvalidConfigurationException {
    int popSize = size();
    // See request  1213752.
    // ---------------------
    int maxSize = getConfiguration().getPopulationSize();
//    INaturalSelector selector = getConfiguration().getKeepPopConstantSelector();
//    if (popSize > maxSize) {
//      Population newPop = new Population(getConfiguration(), maxSize);
//      selector.select(maxSize, this, newPop);
//      m_chromosomes = newPop.getChromosomes();
//      setChanged(true);
//    }
    IEvolutionMonitor monitor = getConfiguration().getMonitor();
    boolean monitorActive = monitor != null;
    while (popSize > maxSize) {
      if (monitorActive) {
        // Fire monitor with population and index of chromosome to be removed.
        // -------------------------------------------------------------------
        monitor.event(IEvolutionMonitor.MONITOR_EVENT_REMOVE_CHROMOSOME,
            getConfiguration().getGenerationNr(),
            new Object[] {this, new Integer(0)});
      }
      // Remove a chromosome.
      // --------------------
      /**@todo use dedicated selector for that*/
      removeChromosome(0);
      popSize--;
    }
    }
  /**
   * Sorts the Chromosome list and returns the fittest n Chromosomes in
   * the population.
   *
   * @param a_numberOfChromosomes number of top performer chromosomes to be
   * returned
   * @return list of the fittest n Chromosomes of the population, or the fittest
   * x Chromosomes with x = number of chromosomes in case n > x.
   *
   * @author Charles Kevin Hill
   * @since 2.4
   */
  public List determineFittestChromosomes(final int a_numberOfChromosomes) {
    int numberOfChromosomes = Math.min(a_numberOfChromosomes,
                                       getChromosomes().size());
    if (numberOfChromosomes <= 0) {
      return null;
    }
    if (!m_changed && m_sorted) {
      return getChromosomes().subList(0, numberOfChromosomes);
    }
    // Sort the list of chromosomes using the fitness comparator
    sortByFitness();
    // Return the top n chromosomes
    return getChromosomes().subList(0, numberOfChromosomes);
  }

  /**
   * Sorts the chromosomes within the population according to their fitness
   * value using ChromosomFitnessComparator. The fittest chromosome is then
   * at index 0.
   *
   * @author Klaus Meffert
   * @since 2.6
   */
  public void sortByFitness() {
    // The following construction could be cached but wrt that the
    // evaluator registered with the configuration could change
    // --> Don't cache it!
    sort(new ChromosomeFitnessComparator(getConfiguration().
        getFitnessEvaluator()));
    setChanged(false);
    setSorted(true);
    m_fittestChromosome = (IChromosome) m_chromosomes.get(0);
  }

  /**
   * Sorts the chromosomes within the population utilzing the given comparator.
   *
   * @param a_comparator the comparator to utilize for sorting
   *
   * @author Klaus Meffert
   * @since 2.6
   */
  protected void sort(Comparator a_comparator) {
    Collections.sort(getChromosomes(), a_comparator);
  }

  /**
   * Returns the genotype of the population, i.e. the list of genes in the
   * Population.
   *
   * @param a_resolveCompositeGenes true: split encountered CompositeGenes
   * into their single (atomic) genes
   * @return genotype of the population
   *
   * @author Klaus Meffert
   * @since 2.3
   */
  public List getGenome(final boolean a_resolveCompositeGenes) {
    List result = new Vector();
    List chroms = getChromosomes();
    int len = chroms.size();
    for (int i = 0; i < len; i++) {
      IChromosome chrom = (IChromosome) chroms.get(i);
      Gene[] genes = chrom.getGenes();
      int len2 = genes.length;
      for (int j = 0; j < len2; j++) {
        Gene gene = genes[j];
        if (a_resolveCompositeGenes && gene instanceof ICompositeGene) {
          addCompositeGene(result, (ICompositeGene) gene);
        }
        else {
          addAtomicGene(result, gene);
        }
      }
    }
    return result;
  }

  /**
   * Adds all the genes of a CompositeGene to a result list.

* Note: Method calls itself recursively. * * @param a_result the list to add to * @param a_gene the gene to start with * * @author Klaus Meffert * @since 2.3 */ private void addCompositeGene(final List a_result, final Gene a_gene) { if (a_gene instanceof ICompositeGene) { int len = a_gene.size(); for (int i = 0; i < len; i++) { addCompositeGene(a_result, ( (ICompositeGene) a_gene).geneAt(i)); } } else { addAtomicGene(a_result, a_gene); } } /** * Helper method for addCompositeGene. * * @param a_result List * @param a_gene Gene * * @author Klaus Meffert * @since 2.3 */ private void addAtomicGene(final List a_result, final Gene a_gene) { a_result.add(a_gene); } public boolean isSorted() { return m_sorted; } /** * The equals-method. * * @param a_pop the population instance to compare with * @return true: given object equal to comparing one * * @author Klaus Meffert * @since 2.6 */ public boolean equals(Object a_pop) { try { return compareTo(a_pop) == 0; } catch (ClassCastException e) { // If the other object isn't an Population instance // then we're not equal. // ------------------------------------------------ return false; } } /** * This method is not producing symmetric results as -1 is more often returned * than 1 (see description of return value). * * @param a_pop the other population to compare * @return 1: a_pop is null or having fewer chromosomes or equal number * of chromosomes but at least one not contained. 0: both populations * containing exactly the same chromosomes. -1: this population contains fewer * chromosomes than a_pop * * @author Klaus Meffert * @since 2.6 */ public int compareTo(Object a_pop) { Population other = (Population) a_pop; if (a_pop == null) { return 1; } int size1 = size(); int size2 = other.size(); if (size1 != size2) { if (size1 < size2) { return -1; } else { return 1; } } List chroms2 = other.getChromosomes(); for (int i = 0; i < size1; i++) { if (!chroms2.contains(m_chromosomes.get(i))) { return 1; } } return 0; } /** * @return deeply cloned population instance * * @author Klaus Meffert * @since 3.2 */ public Object clone() { try { Population result = new Population(m_config); // Precautiously set changed to true in case cloning is not 1:1 // ------------------------------------------------------------ result.m_changed = true; result.m_sorted = false; result.m_fittestChromosome = m_fittestChromosome; int size = m_chromosomes.size(); for (int i = 0; i < size; i++) { IChromosome chrom = (IChromosome) m_chromosomes.get(i); result.addChromosome( (IChromosome) ((ICloneable)chrom).clone()); } return result; } catch (Exception ex) { throw new CloneException(ex); } } /** * Clears the list of chromosomes. Normally, this should not be necessary. * But especially in distributed computing, a fresh population has to be * provided sometimes. * * @author Klaus Meffert * @since 3.2 */ public void clear() { m_chromosomes.clear(); m_changed = true; m_sorted = true; m_fittestChromosome = null; } /** * Returns a persistent representation of this chromosome, see interface Gene * for description. Similar to CompositeGene's routine. But does not include * all information of the chromosome (yet). * * @return string representation of this Chromosome's relevant parts of its * current state * @throws UnsupportedOperationException * * @author Klaus Meffert * @since 3.2 */ public String getPersistentRepresentation() { StringBuffer b = new StringBuffer(); // Persist the chromosomes. // ------------------------ IChromosome chrom; for (int i = 0; i < m_chromosomes.size(); i++) { chrom = (IChromosome) m_chromosomes.get(i); if (! (chrom instanceof IPersistentRepresentation)) { throw new RuntimeException("Population contains a chromosome of type " + chrom.getClass().getName() + " which does not implement" + " IPersistentRepresentation!"); } b.append(CHROM_DELIMITER_HEADING); b.append(StringKit.encode(chrom.getClass().getName() + CHROM_DELIMITER + ( (IPersistentRepresentation) chrom). getPersistentRepresentation())); b.append(CHROM_DELIMITER_CLOSING); } return b.toString(); } /** * Counterpart of getPersistentRepresentation. * * @param a_representation the string representation retrieved from a prior * call to the getPersistentRepresentation() method * * @throws UnsupportedRepresentationException * * @author Klaus Meffert * @since 3.2 */ public void setValueFromPersistentRepresentation(String a_representation) throws UnsupportedRepresentationException { if (a_representation != null) { try { List r = split(a_representation); String g; m_chromosomes = new Vector(); // Obtain the chromosomes. // ----------------------- Iterator iter = r.iterator(); StringTokenizer st; String clas; String representation; IChromosome chrom; while (iter.hasNext()) { g = StringKit.decode( (String) iter.next()); st = new StringTokenizer(g, CHROM_DELIMITER); if (st.countTokens() != 2) throw new UnsupportedRepresentationException("In " + g + ", " + "expecting two tokens, separated by " + CHROM_DELIMITER); clas = st.nextToken(); representation = st.nextToken(); chrom = createChromosome(clas, representation); m_chromosomes.add(chrom); } setChanged(true); } catch (Exception ex) { throw new UnsupportedRepresentationException(ex.toString()); } } } /** * Creates a new Chromosome instance.

* Taken and adapted from CompositeGene. * * @param a_chromClassName name of the Chromosome class * @param a_persistentRepresentation persistent representation of the * Chromosome to create (could be obtained via getPersistentRepresentation) * * @return newly created Chromosome * @throws Exception * * @author Klaus Meffert * @since 3.2 */ protected IChromosome createChromosome(String a_chromClassName, String a_persistentRepresentation) throws Exception { Class chromClass = Class.forName(a_chromClassName); Constructor constr = chromClass.getConstructor(new Class[] {Configuration.class}); IChromosome chrom = (IChromosome) constr.newInstance(new Object[] { getConfiguration()}); ( (IPersistentRepresentation) chrom).setValueFromPersistentRepresentation( a_persistentRepresentation); return chrom; } /** * Splits the input a_string into individual chromosome representations.

* Taken and adapted from CompositeGene. * * @param a_string the string to split * @return the elements of the returned array are the persistent * representation strings of the population's components * @throws UnsupportedRepresentationException * * @author Klaus Meffert * @since 3.2 */ protected static final List split(String a_string) throws UnsupportedRepresentationException { List a = Collections.synchronizedList(new ArrayList()); // No Header data. // --------------- // Chromosome data. // ---------------- StringTokenizer st = new StringTokenizer (a_string, CHROM_DELIMITER_HEADING + CHROM_DELIMITER_CLOSING, true); while (st.hasMoreTokens()) { if (!st.nextToken().equals(CHROM_DELIMITER_HEADING)) { throw new UnsupportedRepresentationException(a_string + " no open tag"); } String n = st.nextToken(); if (n.equals(CHROM_DELIMITER_CLOSING)) { a.add(""); /* Empty token */ } else { a.add(n); if (!st.nextToken().equals(CHROM_DELIMITER_CLOSING)) { throw new UnsupportedRepresentationException (a_string + " no close tag"); } } } return a; } /*** * 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) * * @return the computed hashcode * * @author Klaus Meffert * @since 3.5 */ public int hashCode() { int i, size = size(); IChromosome s; int twopower = 1; // For empty population we want a special value different from other // hashcode implementations. // ------------------------------------------------------------------ int localHashCode = -593; for (i = 0; i < size; i++, twopower = 2 * twopower) { s = getChromosome(i); localHashCode = 31 * localHashCode + s.hashCode(); } return localHashCode; } }

Ads help maintain this website.