/*
* 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 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.
*
* If you work with CompositeGenes, 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.
*
* This CrossoverOperator supports both fixed and dynamic crossover rates.
* A fixed rate is one specified at construction time by the user. This
* operation is performed 1/m_crossoverRate as many times as there are
* Chromosomes in the population. Another possibility is giving the crossover
* rate as a percentage. A dynamic rate is determined by this class on the fly
* if no fixed rate is provided.
*
* @author Neil Rotstan
* @author Klaus Meffert
* @author Chris Knowles
* @since 1.0
*/
public class CrossoverOperator
extends BaseGeneticOperator implements Comparable {
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.48 $";
/**
* The current crossover rate used by this crossover operator (mutual
* exclusive to m_crossoverRatePercent and m_crossoverRateCalc).
*/
private int m_crossoverRate;
/**
* Crossover rate in percentage of population size (mutual exclusive to
* m_crossoverRate and m_crossoverRateCalc).
*/
private double m_crossoverRatePercent;
/**
* Calculator for dynamically determining the crossover rate (mutual exclusive
* to m_crossoverRate and m_crossoverRatePercent)
*/
private IUniversalRateCalculator m_crossoverRateCalc;
/**
* true: x-over before and after a randomly chosen x-over point
* false: only x-over after the chosen point.
*/
private boolean m_allowFullCrossOver;
/**
* true: also x-over chromosomes with age of zero (newly created chromosomes)
*/
private boolean m_xoverNewAge;
/**
* Constructs a new instance of this CrossoverOperator without a specified
* crossover rate, this results in dynamic crossover rate being turned off.
* This means that the crossover rate will be fixed at populationsize/2.<p>
* Attention: The configuration used is the one set with the static method
* Genotype.setConfiguration.
*
* @throws InvalidConfigurationException
*
* @author Chris Knowles
* @since 2.0
*/
public CrossoverOperator()
throws InvalidConfigurationException {
super(Genotype.getStaticConfiguration());
init();
}
/**
* Constructs a new instance of this CrossoverOperator without a specified
* crossover rate, this results in dynamic crossover rate being turned off.
* This means that the crossover rate will be fixed at populationsize/2.
*
* @param a_configuration the configuration to use
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.0
*/
public CrossoverOperator(final Configuration a_configuration)
throws InvalidConfigurationException {
super(a_configuration);
init();
}
/**
* Initializes certain parameters.
*
* @author Klaus Meffert
* @since 3.3.2
*/
protected void init() {
// Set the default crossoverRate.
// ------------------------------
m_crossoverRate = 6;
m_crossoverRatePercent = -1;
setCrossoverRateCalc(null);
setAllowFullCrossOver(true);
setXoverNewAge(true);
}
/**
* 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 Chris Knowles
* @author Klaus Meffert
* @since 3.0 (since 2.0 without a_configuration)
*/
public CrossoverOperator(final Configuration a_configuration,
final IUniversalRateCalculator
a_crossoverRateCalculator)
throws InvalidConfigurationException {
this(a_configuration, a_crossoverRateCalculator, true);
}
/**
* 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
* @param a_allowFullCrossOver true: x-over before AND after x-over point,
* false: only x-over after x-over point
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.3.2
*/
public CrossoverOperator(final Configuration a_configuration,
final IUniversalRateCalculator
a_crossoverRateCalculator,
boolean a_allowFullCrossOver)
throws InvalidConfigurationException {
super(a_configuration);
setCrossoverRateCalc(a_crossoverRateCalculator);
setAllowFullCrossOver(a_allowFullCrossOver);
}
/**
* Constructs a new instance of this CrossoverOperator with the given
* crossover rate.
*
* @param a_configuration the configuration to use
* @param a_desiredCrossoverRate the desired rate of crossover
* @throws InvalidConfigurationException
*
* @author Chris Knowles
* @author Klaus Meffert
* @since 3.0 (since 2.0 without a_configuration)
*/
public CrossoverOperator(final Configuration a_configuration,
final int a_desiredCrossoverRate)
throws InvalidConfigurationException {
this(a_configuration, a_desiredCrossoverRate, true);
}
/**
* Constructs a new instance of this CrossoverOperator with the given
* crossover rate. No new chromosomes are x-overed.
*
* @param a_configuration the configuration to use
* @param a_desiredCrossoverRate the desired rate of crossover
* @param a_allowFullCrossOver true: x-over before AND after x-over point,
* false: only x-over after x-over point
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.3.2
*/
public CrossoverOperator(final Configuration a_configuration,
final int a_desiredCrossoverRate,
boolean a_allowFullCrossOver)
throws InvalidConfigurationException {
this(a_configuration, a_desiredCrossoverRate, a_allowFullCrossOver, false);
}
/**
* Constructs a new instance of this CrossoverOperator with the given
* crossover rate.
*
* @param a_configuration the configuration to use
* @param a_desiredCrossoverRate the desired rate of crossover
* @param a_allowFullCrossOver true: x-over before AND after x-over point,
* false: only x-over after x-over point
* @throws InvalidConfigurationException
* @param a_xoverNewAge true: also x-over chromosomes with age of zero (newly
* created chromosomes)
*
* @author Klaus Meffert
* @since 3.3.2
*/
public CrossoverOperator(final Configuration a_configuration,
final int a_desiredCrossoverRate,
final boolean a_allowFullCrossOver,
final boolean a_xoverNewAge)
throws InvalidConfigurationException {
super(a_configuration);
if (a_desiredCrossoverRate < 1) {
throw new IllegalArgumentException("Crossover rate must be greater zero");
}
m_crossoverRate = a_desiredCrossoverRate;
m_crossoverRatePercent = -1;
setCrossoverRateCalc(null);
setAllowFullCrossOver(a_allowFullCrossOver);
setXoverNewAge(a_xoverNewAge);
}
/**
* Constructs a new instance of this CrossoverOperator with the given
* crossover rate. No new chromosomes are x-overed.
*
* @param a_configuration the configuration to use
* @param a_crossoverRatePercentage the desired rate of crossover in
* percentage of the population
* @throws InvalidConfigurationException
*
* @author Chris Knowles
* @author Klaus Meffert
* @since 3.0 (since 2.0 without a_configuration)
*/
public CrossoverOperator(final Configuration a_configuration,
final double a_crossoverRatePercentage)
throws InvalidConfigurationException {
this(a_configuration, a_crossoverRatePercentage, true);
}
/**
* Constructs a new instance of this CrossoverOperator with the given
* crossover rate. No new chromosomes are x-overed.
*
* @param a_configuration the configuration to use
* @param a_crossoverRatePercentage the desired rate of crossover in
* percentage of the population
* @param a_allowFullCrossOver true: x-over before AND after x-over point,
* false: only x-over after x-over point
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.3.2.
*/
public CrossoverOperator(final Configuration a_configuration,
final double a_crossoverRatePercentage,
boolean a_allowFullCrossOver)
throws InvalidConfigurationException {
this(a_configuration, a_crossoverRatePercentage, a_allowFullCrossOver, false);
}
/**
* Constructs a new instance of this CrossoverOperator with the given
* crossover rate.
*
* @param a_configuration the configuration to use
* @param a_crossoverRatePercentage the desired rate of crossover in
* percentage of the population
* @param a_allowFullCrossOver true: x-over before AND after x-over point,
* false: only x-over after x-over point
* @param a_xoverNewAge true: also x-over chromosomes with age of zero (newly
* created chromosomes)
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.3.2.
*/
public CrossoverOperator(final Configuration a_configuration,
final double a_crossoverRatePercentage,
final boolean a_allowFullCrossOver,
final boolean a_xoverNewAge)
throws InvalidConfigurationException {
super(a_configuration);
if (a_crossoverRatePercentage <= 0.0d) {
throw new IllegalArgumentException("Crossover rate must be greater zero");
}
m_crossoverRatePercent = a_crossoverRatePercentage;
m_crossoverRate = -1;
setCrossoverRateCalc(null);
setAllowFullCrossOver(a_allowFullCrossOver);
setXoverNewAge(a_xoverNewAge);
}
/**
* Does the crossing over.
*
* @param a_population the population of chromosomes from the current
* evolution prior to exposure to crossing over
* @param a_candidateChromosomes the pool of chromosomes that have been
* selected for the next evolved population
*
* @author Neil Rotstan
* @author Klaus Meffert
* @since 2.0
*/
public void operate(final Population a_population,
final List a_candidateChromosomes) {
// Work out the number of crossovers that should be performed.
// -----------------------------------------------------------
int size = Math.min(getConfiguration().getPopulationSize(),
a_population.size());
int numCrossovers = 0;
if (m_crossoverRate >= 0) {
numCrossovers = size / m_crossoverRate;
}
else if (m_crossoverRateCalc != null) {
numCrossovers = size / m_crossoverRateCalc.calculateCurrentRate();
}
else {
numCrossovers = (int) (size * m_crossoverRatePercent);
}
RandomGenerator generator = getConfiguration().getRandomGenerator();
IGeneticOperatorConstraint constraint = getConfiguration().
getJGAPFactory().getGeneticOperatorConstraint();
// 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 chrom1 = a_population.getChromosome(index1);
IChromosome chrom2 = a_population.getChromosome(index2);
// Verify that crossover is allowed.
// ---------------------------------
if (!isXoverNewAge() && chrom1.getAge() < 1 && chrom2.getAge() < 1) {
// Crossing over two newly created chromosomes is not seen as helpful
// here.
// ------------------------------------------------------------------
continue;
}
if (constraint != null) {
List v = new Vector();
v.add(chrom1);
v.add(chrom2);
if (!constraint.isValid(a_population, v, this)) {
// Constraint forbids crossing over.
// ---------------------------------
continue;
}
}
// Clone the chromosomes.
// ----------------------
IChromosome firstMate = (IChromosome) chrom1.clone();
IChromosome secondMate = (IChromosome) chrom2.clone();
// In case monitoring is active, support it.
// -----------------------------------------
if (m_monitorActive) {
firstMate.setUniqueIDTemplate(chrom1.getUniqueID(), 1);
firstMate.setUniqueIDTemplate(chrom2.getUniqueID(), 2);
secondMate.setUniqueIDTemplate(chrom1.getUniqueID(), 1);
secondMate.setUniqueIDTemplate(chrom2.getUniqueID(), 2);
}
// Cross over the chromosomes.
// ---------------------------
doCrossover(firstMate, secondMate, a_candidateChromosomes, generator);
}
}
protected void doCrossover(IChromosome firstMate, IChromosome secondMate,
List a_candidateChromosomes,
RandomGenerator generator) {
Gene[] firstGenes = firstMate.getGenes();
Gene[] secondGenes = secondMate.getGenes();
int locus = generator.nextInt(firstGenes.length);
// Swap the genes.
// ---------------
Gene gene1;
Gene gene2;
Object firstAllele;
for (int j = locus; j < firstGenes.length; j++) {
// Make a distinction for ICompositeGene for the first gene.
// ---------------------------------------------------------
int index = 0;
if (firstGenes[j] instanceof ICompositeGene) {
// Randomly determine gene to be considered.
// -----------------------------------------
index = generator.nextInt(firstGenes[j].size());
gene1 = ( (ICompositeGene) firstGenes[j]).geneAt(index);
}
else {
gene1 = firstGenes[j];
}
// Make a distinction for the second gene if CompositeGene.
// --------------------------------------------------------
if (secondGenes[j] instanceof ICompositeGene) {
gene2 = ( (ICompositeGene) secondGenes[j]).geneAt(index);
}
else {
gene2 = secondGenes[j];
}
if (m_monitorActive) {
gene1.setUniqueIDTemplate(gene2.getUniqueID(), 1);
gene2.setUniqueIDTemplate(gene1.getUniqueID(), 1);
}
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);
}
/**
* Sets the crossover rate calculator.
*
* @param a_crossoverRateCalculator the new calculator
*
* @author Chris Knowles
* @since 2.0
*/
private void setCrossoverRateCalc(final IUniversalRateCalculator
a_crossoverRateCalculator) {
m_crossoverRateCalc = a_crossoverRateCalculator;
if (a_crossoverRateCalculator != null) {
m_crossoverRate = -1;
m_crossoverRatePercent = -1d;
}
}
/**
* 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) {
/**@todo consider Configuration*/
if (a_other == null) {
return 1;
}
CrossoverOperator op = (CrossoverOperator) 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;
}
}
if (m_allowFullCrossOver != op.m_allowFullCrossOver) {
if (m_allowFullCrossOver) {
return 1;
}
else {
return -1;
}
}
if (m_xoverNewAge != op.m_xoverNewAge) {
if (m_xoverNewAge) {
return 1;
}
else {
return -1;
}
}
// Everything is equal. Return zero.
// ---------------------------------
return 0;
}
/**
* @param a_allowFullXOver x-over before and after a randomly chosen point
*
* @author Klaus Meffert
* @since 3.3.2
*/
public void setAllowFullCrossOver(boolean a_allowFullXOver) {
m_allowFullCrossOver = a_allowFullXOver;
}
/**
* @return x-over before and after a randomly chosen x-over point
*
* @author Klaus Meffert
* @since 3.3.2
*/
public boolean isAllowFullCrossOver() {
return m_allowFullCrossOver;
}
/**
* @return the crossover rate set
*
* @author Klaus Meffert
* @since 3.3.2
*/
public int getCrossOverRate() {
return m_crossoverRate;
}
/**
* @return the crossover rate set
*
* @author Klaus Meffert
* @since 3.3.2
*/
public double getCrossOverRatePercent() {
return m_crossoverRatePercent;
}
/**
* @param a_xoverNewAge true: also x-over chromosomes with age of zero (newly
* created chromosomes)
*
* @author Klaus Meffert
* @since 3.3.2
*/
public void setXoverNewAge(boolean a_xoverNewAge) {
m_xoverNewAge = a_xoverNewAge;
}
/**
* @return a_xoverNewAge true: also x-over chromosomes with age of zero (newly
* created chromosomes)
*
* @author Klaus Meffert
* @since 3.3.2
*/
public boolean isXoverNewAge() {
return m_xoverNewAge;
}
}