/* * 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.impl; import java.util.*; import org.jgap.*; /** * The averaging crossover operator randomly selects two Chromosomes from the * population and "mates" them by randomly picking a gene and then swapping that * gene and all subsequent genes between the two Chromosomes. The two modified * Chromosomes are then added to the list of candidate Chromosomes. This * operation is performed half as many times as there are Chromosomes in * the population. * Additionally, the loci of crossing over are cached for each index, i.e., * after randomizing the loci for each index once, they don't change again. * * If you work with CompositeGene's, this operator expects them to contain * genes of the same type (e.g. IntegerGene). If you have mixed types, please * provide your own crossover operator. * * @author Klaus Meffert * @since 2.0 */ public class AveragingCrossoverOperator extends BaseGeneticOperator { /** String containing the CVS revision. Read out via reflection!*/ private final static String CVS_REVISION = "$Revision: 1.29 $"; /** * Random generator for randomizing the loci for crossing over */ private RandomGenerator m_crossoverGenerator; /** * The current crossover rate used by this crossover operator. */ private int m_crossoverRate; /** * Cache for alreadycrandomized loci for crossing over */ private Map m_loci; /** * Calculator for dynamically determining the crossover rate. If set to * null the value of m_crossoverRate will be used instead. */ private IUniversalRateCalculator m_crossoverRateCalc; private void init() { m_loci = new Hashtable(); m_crossoverRate = 2; } /** * Using the same random generator for randomizing the loci for crossing * over as for selecting the genes to be crossed over.<p> * Attention: The configuration used is the one set with the static method * Genotype.setConfiguration. * * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 2.0 */ public AveragingCrossoverOperator() throws InvalidConfigurationException { this(Genotype.getStaticConfiguration(), (RandomGenerator)null); } /** * Using the same random generator for randomizing the loci for crossing * over as for selecting the genes to be crossed over. * * @param a_configuration the configuration to use * * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 3.0 */ public AveragingCrossoverOperator(final Configuration a_configuration) throws InvalidConfigurationException { this(a_configuration, (RandomGenerator)null); } /** * Using a different random generator for randomizing the loci for * crossing over than for selecting the genes to be crossed over * @param a_configuration the configuration to use * @param a_generatorForAveraging RandomGenerator to use * * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 3.0 (since 2.0 without a_configuration) */ public AveragingCrossoverOperator(final Configuration a_configuration, final RandomGenerator a_generatorForAveraging) throws InvalidConfigurationException { super(a_configuration); init(); m_crossoverGenerator = a_generatorForAveraging; } /** * Constructs a new instance of this CrossoverOperator with a specified * crossover rate calculator, which results in dynamic crossover being turned * on. * @param a_configuration the configuration to use * @param a_crossoverRateCalculator calculator for dynamic crossover rate * computation * * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 3.0 (since 2.0 without a_configuration) */ public AveragingCrossoverOperator(final Configuration a_configuration, final IUniversalRateCalculator a_crossoverRateCalculator) throws InvalidConfigurationException { super(a_configuration); init(); setCrossoverRateCalc(a_crossoverRateCalculator); } /** * Sets the crossover rate calculator * @param a_crossoverRateCalculator the new calculator * * @author Klaus Meffert * @since 2.0 */ private void setCrossoverRateCalc(final IUniversalRateCalculator a_crossoverRateCalculator) { m_crossoverRateCalc = a_crossoverRateCalculator; } /** * Crossover that acts as a perturbed mean of two individuals. * x_i = p*x1_i + (1-p)*x2_i * p - uniform random value over [0,1]. * Averaging over line means p is same for every i, * averaging over space if different p is chosen for each i. * See CrossoverOperator for general description, also see feature request * 708774 * * @param a_population Chromosome[] * @param a_candidateChromosomes List * * @author Klaus Meffert * @since 2.0 */ public void operate(final Population a_population, final List a_candidateChromosomes) { // Determine the number of crossovers that should be performed int size = Math.min(getConfiguration().getPopulationSize(), a_population.size()); int numCrossovers = 0; if (m_crossoverRateCalc == null) { numCrossovers = size / m_crossoverRate; } else { numCrossovers = size / m_crossoverRateCalc.calculateCurrentRate(); } RandomGenerator generator = getConfiguration().getRandomGenerator(); if (m_crossoverGenerator == null) { m_crossoverGenerator = generator; } // For each crossover, grab two random chromosomes, pick a random // locus (gene location), and then swap that gene and all genes // to the "right" (those with greater loci) of that gene between // the two chromosomes. // -------------------------------------------------------------- int index1, index2; for (int i = 0; i < numCrossovers; i++) { index1 = generator.nextInt(size); index2 = generator.nextInt(size); IChromosome origChrom1 = a_population.getChromosome(index1); IChromosome origChrom2 = a_population.getChromosome(index2); IChromosome firstMate = (IChromosome)origChrom1.clone(); IChromosome secondMate = (IChromosome)origChrom2.clone(); // In case monitoring is active, support it. // ----------------------------------------- if (m_monitorActive) { firstMate.setUniqueIDTemplate(origChrom1.getUniqueID(), 1); firstMate.setUniqueIDTemplate(origChrom2.getUniqueID(), 2); secondMate.setUniqueIDTemplate(origChrom1.getUniqueID(), 1); secondMate.setUniqueIDTemplate(origChrom2.getUniqueID(), 2); } Gene[] firstGenes = firstMate.getGenes(); Gene[] secondGenes = secondMate.getGenes(); int locus = getLocus(m_crossoverGenerator, i, firstGenes.length); // Swap the genes. // --------------- Gene gene1; Gene gene2; Object firstAllele; for (int j = locus; j < firstGenes.length; j++) { // Make a distinction to ICompositeGene for the first gene. // -------------------------------------------------------- if (firstGenes[j] instanceof ICompositeGene) { // Randomly determine gene to be considered index1 = generator.nextInt(firstGenes[j].size()); gene1 = ( (ICompositeGene) firstGenes[j]).geneAt(index1); } else { gene1 = firstGenes[j]; } // Make a distinction to ICompositeGene for the second gene. // --------------------------------------------------------- if (secondGenes[j] instanceof CompositeGene) { // Randomly determine gene to be considered index2 = generator.nextInt(secondGenes[j].size()); gene2 = ( (ICompositeGene) secondGenes[j]).geneAt(index2); } else { gene2 = secondGenes[j]; } firstAllele = gene1.getAllele(); gene1.setAllele(gene2.getAllele()); gene2.setAllele(firstAllele); } // Add the modified chromosomes to the candidate pool so that // they'll be considered for natural selection during the next // phase of evolution. // ----------------------------------------------------------- a_candidateChromosomes.add(firstMate); a_candidateChromosomes.add(secondMate); } } /** * Returns the crossover location for a given index. * For each index the crossover locatio is the same, therefor it is cached! * * @param a_generator to generate random values the first time * @param a_index the index of the crossover operation * @param a_max upper boundary for random generator * * @return crossover location for a given index * * @author Klaus Meffert * @since 2.0 */ protected int getLocus(final RandomGenerator a_generator, final int a_index, final int a_max) { Integer locus = (Integer) m_loci.get(new Integer(a_index)); if (locus == null) { locus = new Integer(a_generator.nextInt(a_max)); m_loci.put(new Integer(a_index), locus); } return locus.intValue(); } /** * Compares this GeneticOperator against the specified object. The result is * true if and the argument is an instance of this class and is equal wrt the * data. * * @param a_other the object to compare against * @return true: if the objects are the same, false otherwise * * @author Klaus Meffert * @since 2.6 */ public boolean equals(final Object a_other) { try { return compareTo(a_other) == 0; } catch (ClassCastException cex) { return false; } } /** * Compares the given object to this one. * * @param a_other the instance against which to compare this instance * @return a negative number if this instance is "less than" the given * instance, zero if they are equal to each other, and a positive number if * this is "greater than" the given instance * * @author Klaus Meffert * @since 2.6 */ public int compareTo(final Object a_other) { if (a_other == null) { return 1; } AveragingCrossoverOperator op = (AveragingCrossoverOperator) a_other; if (m_crossoverRateCalc == null) { if (op.m_crossoverRateCalc != null) { return -1; } } else { if (op.m_crossoverRateCalc == null) { return 1; } } if (m_crossoverRate != op.m_crossoverRate) { if (m_crossoverRate > op.m_crossoverRate) { return 1; } else { return -1; } } // Everything is equal. Return zero. // --------------------------------- return 0; } /** * @param a_rate crossover rate to use by this crossover operator * @author Klaus Meffert * @since 2.6 */ public void setCrossoverRate(int a_rate) { m_crossoverRate = a_rate; } }