/*
* 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.*;
/**
* Swaps the genes instead of mutating them. This kind of operator is
* required by Traveling Salesman Problem.
*
* See J. Grefenstette, R. Gopal, R. Rosmaita, and D. Gucht.
* <i>Genetic algorithms for the traveling salesman problem</i>.
* In Proceedings of the Second International Conference on Genetic Algorithms.
* Lawrence Eribaum Associates, Mahwah, NJ, 1985.
* and also <a href="http://ecsl.cs.unr.edu/docs/techreports/gong/node3.html">
* Sushil J. Louis & Gong Li</a> }
*
* @author Audrius Meskauskas
* @author Neil Rotstan
* @author Klaus Meffert
* @since 2.0
*/
public class SwappingMutationOperator
extends MutationOperator {
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.21 $";
private int m_startOffset = 1;
/**
* Constructs a new instance of this operator.<p>
* Attention: The configuration used is the one set with the static method
* Genotype.setConfiguration.
*
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
*/
public SwappingMutationOperator()
throws InvalidConfigurationException {
super();
}
/**
* @param a_config the configuration to use
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.0
*/
public SwappingMutationOperator(final Configuration a_config)
throws InvalidConfigurationException {
super(a_config);
}
/**
* Constructs a new instance of this operator with a specified
* mutation rate calculator, which results in dynamic mutation being turned
* on.
*
* @param a_config the configuration to use
* @param a_mutationRateCalculator calculator for dynamic mutation rate
* computation
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.0 (previously: without a_config)
*/
public SwappingMutationOperator(final Configuration a_config,
final IUniversalRateCalculator
a_mutationRateCalculator)
throws InvalidConfigurationException {
super(a_config, a_mutationRateCalculator);
}
/**
* Constructs a new instance of this MutationOperator with the given
* mutation rate.
*
* @param a_config the configuration to use
* @param a_desiredMutationRate desired rate of mutation, expressed as
* the denominator of the 1 / X fraction. For example, 1000 would result
* in 1/1000 genes being mutated on average. A mutation rate of zero disables
* mutation entirely
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.0 (previously: without a_config)
*/
public SwappingMutationOperator(final Configuration a_config,
final int a_desiredMutationRate)
throws InvalidConfigurationException {
super(a_config, a_desiredMutationRate);
}
/**
* @param a_population the population of chromosomes from the current
* evolution prior to exposure to any genetic operators. Chromosomes in this
* array should not be modified. Please, notice, that the call in
* Genotype.evolve() to the implementations of GeneticOperator overgoes this
* due to performance issues
* @param a_candidateChromosomes the pool of chromosomes that have been
* selected for the next evolved population
*
* @author Audrius Meskauskas
* @author Klaus Meffert
* @since 2.0
*/
public void operate(final Population a_population,
List a_candidateChromosomes) {
// this was a private variable, now it is local reference.
final IUniversalRateCalculator m_mutationRateCalc = getMutationRateCalc();
// If the mutation rate is set to zero and dynamic mutation rate is
// disabled, then we don't perform any mutation.
// ----------------------------------------------------------------
if (getMutationRate() == 0 && m_mutationRateCalc == null) {
return;
}
// Determine the mutation rate. If dynamic rate is enabled, then
// calculate it based upon the number of genes in the chromosome.
// Otherwise, go with the mutation rate set upon construction.
// --------------------------------------------------------------
int currentRate;
if (m_mutationRateCalc != null) {
currentRate = m_mutationRateCalc.calculateCurrentRate();
}
else {
currentRate = getMutationRate();
}
RandomGenerator generator = getConfiguration().getRandomGenerator();
// It would be inefficient to create copies of each Chromosome just
// to decide whether to mutate them. Instead, we only make a copy
// once we've positively decided to perform a mutation.
// ----------------------------------------------------------------
int size = a_population.size();
for (int i = 0; i < size; i++) {
IChromosome x = a_population.getChromosome(i);
// This returns null if not mutated:
IChromosome xm = operate(x, currentRate, generator);
if (xm != null) {
a_candidateChromosomes.add(xm);
}
}
}
/**
* Operate on the given chromosome with the given mutation rate.
*
* @param a_chrom chromosome to operate
* @param a_rate mutation rate
* @param a_generator random generator to use (must not be null)
* @return mutated chromosome of null if no mutation has occured.
*
* @author Audrius Meskauskas
* @since 2.0
*/
protected IChromosome operate(final IChromosome a_chrom, final int a_rate,
final RandomGenerator a_generator) {
IChromosome chromosome = null;
// ----------------------------------------
for (int j = m_startOffset; j < a_chrom.size(); j++) {
// Ensure probability of 1/currentRate for applying mutation.
// ----------------------------------------------------------
if (a_generator.nextInt(a_rate) == 0) {
if (chromosome == null) {
chromosome = (IChromosome) a_chrom.clone();
// In case monitoring is active, support it.
// -----------------------------------------
if (m_monitorActive) {
chromosome.setUniqueIDTemplate(a_chrom.getUniqueID(), 1);
}
}
Gene[] genes = chromosome.getGenes();
Gene[] mutated = operate(a_generator, j, genes);
// setGenes is not required for this operator, but it may
// be needed for the derived operators.
// ------------------------------------------------------
try {
chromosome.setGenes(mutated);
}
catch (InvalidConfigurationException cex) {
throw new Error("Gene type not allowed by constraint checker", cex);
}
}
}
return chromosome;
}
/**
* Operate on the given array of genes. This method is only called
* when it is already clear that the mutation must occur under the given
* mutation rate.
*
* @param a_generator a random number generator that may be needed to
* perform a mutation
* @param a_target_gene an index of gene in the chromosome that will mutate
* @param a_genes the array of all genes in the chromosome
* @return the mutated gene array
*
* @author Audrius Meskauskas
* @since 2.0
*/
protected Gene[] operate(final RandomGenerator a_generator,
final int a_target_gene, final Gene[] a_genes) {
// swap this gene with the other one now:
// mutateGene(genes[j], generator);
// -------------------------------------
int other = m_startOffset
+ a_generator.nextInt(a_genes.length - m_startOffset);
Gene t = a_genes[a_target_gene];
a_genes[a_target_gene] = a_genes[other];
a_genes[other] = t;
if (m_monitorActive) {
a_genes[a_target_gene].setUniqueIDTemplate(a_genes[other].getUniqueID(), 1);
a_genes[other].setUniqueIDTemplate(a_genes[a_target_gene].getUniqueID(), 1);
}
return a_genes;
}
/**
* Sets a number of genes at the start of chromosome, that are
* excluded from the swapping. In the Salesman task, the first city
* in the list should (where the salesman leaves from) probably should
* not change as it is part of the list. The default value is 1.
*
* @param a_offset the offset to set
*
* @author Audrius Meskauskas
* @since 2.0
*/
public void setStartOffset(final int a_offset) {
m_startOffset = a_offset;
}
/**
* Gets a number of genes at the start of chromosome, that are
* excluded from the swapping. In the Salesman task, the first city
* in the list should (where the salesman leaves from) probably should
* not change as it is part of the list. The default value is 1.
*
* @return the start offset
*
* @author Audrius Meskauskas
* @since 2.0
*/
public int getStartOffset() {
return m_startOffset;
}
}