/**
* JGAP offers a dual license model(see below for specific license information):
* + The LGPL may be used anytime.
* + The MPL may be used if at least $20 have been donated to the JGAP project
* thru PayPal (see http://www.sourceforge.net/projects/jgap or, directly,
* http://sourceforge.net/donate/index.php?group_id=11618).
* Details about usage of JGAP under the MPL can be found at the homepage
* http://jgap.sourceforge.net/.
*
* Specific license information (MPL and LGPL)
* -------------------------------------------
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is 'JGAP - Java Genetic Algorithms Package'.
* The initial Developer of the Original Code is Neil Rotstan. Portions created
* by the initial Developer are Copyright (C) 2002- 2003 by Neil Rotstan.
* All Rights Reserved.
* Co-developer of the code is Klaus Meffert. Portions created by the co-
* developer are Copyright (C) 2003-2006 by Klaus Meffert. All Rights Reserved.
* Contributor(s): all the names of the contributors are added in the source
* code where applicable.
*
* Alternatively, the contents of this file may be used under the terms of the
* LGPL license (the "GNU LESSER PUBLIC LICENSE"), in which case the
* provisions of LGPL are applicable instead of those above. If you wish to
* allow use of your version of this file only under the terms of the LGPL
* License and not to allow others to use your version of this file under
* the MPL, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the LGPL.
* If you do not delete the provisions above, a recipient may use your version
* of this file under either the MPL or the LGPL.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MPL as stated above or under the terms of the LGPL.
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser Public License for more
* details.
*/
package org.jgap;
import java.lang.reflect.*;
import java.util.*;
/**
* Chromosomes represent potential solutions and consist of a fixed-length
* collection of genes. Each gene represents a discrete part of the solution.
* Each gene in the Chromosome may be backed by a different concrete
* implementation of the Gene interface, but all genes in a respective
* position (locus) must share the same concrete implementation across
* Chromosomes within a single population (genotype). In other words, gene 1
* in a chromosome must share the same concrete implementation as gene 1 in all
* other chromosomes in the population.
*
* @author Neil Rotstan
* @author Klaus Meffert
* @since 1.0
*/
public class Chromosome
extends BaseChromosome {
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.103 $";
/**
* Application-specific data that is attached to this Chromosome.
* This data may assist the application in evaluating this Chromosome
* in the fitness function. JGAP does not operate on the data, aside
* from allowing it to be set and retrieved, and considering it with
* comparations (if user opted in to do so).
*/
private Object m_applicationData;
/**
* Holds multiobjective values.
*
* @since 2.6
* @todo move to new subclass of Chromosome (and introduce new interface
* IMultiObjective with that)
*/
private List m_multiObjective;
/**
* Keeps track of whether or not this Chromosome has been selected by
* the natural selector to move on to the next generation.
*/
private boolean m_isSelectedForNextGeneration;
/**
* Stores the fitness value of this Chromosome as determined by the
* active fitness function. A value of -1 indicates that this field
* has not yet been set with this Chromosome's fitness values (valid
* fitness values are always positive).
*
* @since 2.0 (until 1.1: type int)
*/
protected double m_fitnessValue = FitnessFunction.NO_FITNESS_VALUE;
/**
* Method compareTo(): Should we also consider the application data when
* comparing? Default is "false", as "true" means a Chromosome's losing its
* identity when application data is set differently!
*
* @since 2.2
*/
private boolean m_compareAppData;
/**
* Optional helper class for checking if a given allele value to be set
* for a given gene is valid. If not, the allele value may not be set for the
* gene or the gene type (e.g. IntegerGene) is not allowed in general!
*
* @since 2.5
*/
private IGeneConstraintChecker m_geneAlleleChecker;
/**
* If set to true the method getFitnessValue() will always (re-)calculate the
* fitness value. This may be necessary in case of environments where the
* state changes without the chromosome to notice
*
* @since 3.2.2
*/
private boolean m_alwaysCalculate;
/**
* Default constructor, only provided for dynamic instantiation.<p>
* Attention: The configuration used is the one set with the static method
* Genotype.setConfiguration.
*
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 2.4
*/
public Chromosome()
throws InvalidConfigurationException {
this(Genotype.getStaticConfiguration());
}
/**
* Constructor, provided for dynamic or minimal instantiation.
*
* @param a_configuration the configuration to use
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.0
*/
public Chromosome(final Configuration a_configuration)
throws InvalidConfigurationException {
super(a_configuration);
m_alwaysCalculate = a_configuration.isAlwaysCalculateFitness();
}
/**
* Constructor, provided for instantiation via persistent representation.
*
* @param a_configuration the configuration to use
* @param a_persistentRepresentatuion valid persistent representation that
* was most likely obtained via getPersistentRepresentation()
* @throws InvalidConfigurationException
* @throws UnsupportedRepresentationException
*
* @author Klaus Meffert
* @since 3.2
*/
public Chromosome(final Configuration a_configuration,
String a_persistentRepresentatuion)
throws InvalidConfigurationException, UnsupportedRepresentationException {
this(a_configuration);
setValueFromPersistentRepresentation(a_persistentRepresentatuion);
}
/**
* Constructor for specifying the number of genes.
*
* @param a_configuration the configuration to use
* @param a_desiredSize number of genes the chromosome contains of
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 2.2
*/
public Chromosome(final Configuration a_configuration,
final int a_desiredSize)
throws InvalidConfigurationException {
this(a_configuration);
if (a_desiredSize <= 0) {
throw new IllegalArgumentException(
"Chromosome size must be greater than zero");
}
setGenes(new Gene[a_desiredSize]);
}
/**
* Constructs a Chromosome of the given size separate from any specific
* Configuration. This constructor will use the given sample Gene to
* construct a new Chromosome instance containing genes all of the same
* type as the sample Gene. This can be useful for constructing sample
* chromosomes that use the same Gene type for all of their genes and that
* are to be used to setup a Configuration object.
*
* @param a_configuration the configuration to use
* @param a_sampleGene a concrete sampleGene instance that will be used
* as a template for all of the genes in this Chromosome
* @param a_desiredSize the desired size (number of genes) of this Chromosome
* @throws InvalidConfigurationException
*
* @author Neil Rotstan
* @author Klaus Meffert
* @since 1.0
*/
public Chromosome(final Configuration a_configuration,
final Gene a_sampleGene, final int a_desiredSize)
throws InvalidConfigurationException {
this(a_configuration, a_desiredSize);
initFromGene(a_sampleGene);
}
public Chromosome(final Configuration a_configuration, Gene a_sampleGene,
int a_desiredSize,
IGeneConstraintChecker a_constraintChecker)
throws InvalidConfigurationException {
this(a_configuration, a_desiredSize);
initFromGene(a_sampleGene);
setConstraintChecker(a_constraintChecker);
}
protected void initFromGene(Gene a_sampleGene) {
// Do sanity checking to make sure the parameters we were
// given are valid.
// ------------------------------------------------------
if (a_sampleGene == null) {
throw new IllegalArgumentException(
"Sample Gene cannot be null.");
}
// Populate the array of genes it with new Gene instances
// created from the sample gene.
// ------------------------------------------------------
int size = size();
for (int i = 0; i < size; i++) {
setGene(i, a_sampleGene.newGene());
}
}
/**
* Constructs a Chromosome separate from any specific Configuration. This
* can be useful for constructing sample chromosomes that are to be used
* to setup a Configuration object.
*
* @param a_configuration the configuration to use
* @param a_initialGenes the initial genes of this Chromosome
* @throws InvalidConfigurationException
*
* @author Neil Rotstan
* @since 1.0
*/
public Chromosome(final Configuration a_configuration, Gene[] a_initialGenes)
throws InvalidConfigurationException {
this(a_configuration, a_initialGenes == null ? 0 : a_initialGenes.length);
checkGenes(a_initialGenes);
setGenes(a_initialGenes);
}
/**
* Constructs a Chromosome separate from any specific Configuration. This
* can be useful for constructing sample chromosomes that are to be used
* to setup a Configuration object. Additionally, a constraint checker can be
* specified. It is used right here to verify the validity of the gene types
* supplied.
*
* @param a_configuration the configuration to use
* @param a_initialGenes the initial genes of this Chromosome
* @param a_constraintChecker constraint checker to use
* @throws InvalidConfigurationException in case the constraint checker
* reports a configuration error
*
* @author Klaus Meffert
* @since 2.5
*/
public Chromosome(final Configuration a_configuration, Gene[] a_initialGenes,
IGeneConstraintChecker a_constraintChecker)
throws InvalidConfigurationException {
this(a_configuration, a_initialGenes.length);
checkGenes(a_initialGenes);
setGenes(a_initialGenes);
setConstraintChecker(a_constraintChecker);
}
/**
* Helper: called by constructors only to verify the initial genes.
*
* @param a_initialGenes the initial genes of this Chromosome to verify
*
* @author Klaus Meffert
* @since 2.5
*/
protected void checkGenes(Gene[] a_initialGenes) {
// Sanity checks: make sure the genes array isn't null and
// that none of the genes contained within it are null.
// Check against null already done in constructors!
// -------------------------------------------------------
for (int i = 0; i < a_initialGenes.length; i++) {
if (a_initialGenes[i] == null) {
throw new IllegalArgumentException(
"The gene at index " + i + " in the given array of " +
"genes was found to be null. No gene in the array " +
"may be null.");
}
}
}
/**
* Returns a copy of this Chromosome. The returned instance can evolve
* independently of this instance. Note that, if possible, this method
* will first attempt to acquire a Chromosome instance from the active
* ChromosomePool (if any) and set its value appropriately before
* returning it. If that is not possible, then a new Chromosome instance
* will be constructed and its value set appropriately before returning.
*
* @return copy of this Chromosome
* @throws IllegalStateException instead of CloneNotSupportedException
*
* @author Neil Rotstan
* @author Klaus Meffert
* @since 1.0
*/
public synchronized Object clone() {
// Before doing anything, make sure that a Configuration object
// has been set on this Chromosome. If not, then throw an
// IllegalStateException.
// ------------------------------------------------------------
if (getConfiguration() == null) {
throw new IllegalStateException(
"The active Configuration object must be set on this " +
"Chromosome prior to invocation of the clone() method.");
}
IChromosome copy = null;
// Now, first see if we can pull a Chromosome from the pool and just
// set its gene values (alleles) appropriately.
// ------------------------------------------------------------
IChromosomePool pool = getConfiguration().getChromosomePool();
if (pool != null) {
copy = pool.acquireChromosome();
if (copy != null) {
Gene[] genes = copy.getGenes();
for (int i = 0; i < size(); i++) {
genes[i].setAllele(getGene(i).getAllele());
}
}
}
try {
if (copy == null) {
// We couldn't fetch a Chromosome from the pool, so we need to create
// a new one. First we make a copy of each of the Genes. We explicity
// use the Gene at each respective gene location (locus) to create the
// new Gene that is to occupy that same locus in the new Chromosome.
// -------------------------------------------------------------------
int size = size();
if (size > 0) {
Gene[] copyOfGenes = new Gene[size];
for (int i = 0; i < size; i++) {
copyOfGenes[i] = getGene(i).newGene();
Object allele = getGene(i).getAllele();
if (allele != null) {
IJGAPFactory factory = getConfiguration().getJGAPFactory();
if (factory != null) {
ICloneHandler cloner = factory.
getCloneHandlerFor(allele, allele.getClass());
if (cloner != null) {
try {
allele = cloner.perform(allele, null, this);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
}
copyOfGenes[i].setAllele(allele);
}
// Now construct a new Chromosome with the copies of the genes and
// return it. Also clone the IApplicationData object later on.
// ---------------------------------------------------------------
if (getClass() == Chromosome.class) {
copy = new Chromosome(getConfiguration(), copyOfGenes);
}
else {
try {
// Try dynamic call of constructor. Attention: This may not
// work for inner classes!
// --------------------------------------------------------
Constructor[] constr = getClass().getDeclaredConstructors();
if (constr != null) {
for (int i = 0; i < constr.length; i++) {
Class[] params = constr[i].getParameterTypes();
if (params != null && params.length == 1) {
if (params[0] == Configuration.class) {
copy = (IChromosome) constr[i].newInstance(new Object[] {
getConfiguration()});
copy.setGenes(copyOfGenes);
}
}
}
}
if (copy == null) {
// Enforce alternative cloning to get at least something.
// ------------------------------------------------------
throw new Exception(
"No appropriate constructor for cloning found.");
}
} catch (Exception ex) {
ex.printStackTrace();
// Do it the old way with the danger of getting a stack overflow.
// --------------------------------------------------------------
copy = (IChromosome) getConfiguration().getSampleChromosome().clone();
copy.setGenes(copyOfGenes);
}
}
}
else {
if (getClass() == Chromosome.class) {
copy = new Chromosome(getConfiguration());
}
else {
copy = (IChromosome) getConfiguration().getSampleChromosome().clone();
}
}
}
copy.setFitnessValue(m_fitnessValue);
// Clone constraint checker.
// -------------------------
copy.setConstraintChecker(getConstraintChecker());
} catch (InvalidConfigurationException iex) {
throw new IllegalStateException(iex.getMessage());
}
// Also clone the IApplicationData object.
// ---------------------------------------
try {
copy.setApplicationData(cloneObject(getApplicationData()));
} catch (Exception ex) {
throw new IllegalStateException(ex.getMessage());
}
// Clone multi-objective object if necessary and possible.
// -------------------------------------------------------
if (m_multiObjective != null) {
if (getClass() == Chromosome.class) {
try {
( (Chromosome) copy).setMultiObjectives( (List) cloneObject(
m_multiObjective));
} catch (Exception ex) {
throw new IllegalStateException(ex.getMessage());
}
}
}
return copy;
}
/**
* Clones an object by using clone handlers. If no deep cloning possible, then
* return the reference.
*
* @param a_object the object to clone
* @return the cloned object, or the object itself if no coning supported
* @throws Exception
*
* @author Klaus Meffert
* @since 2.6
*/
protected Object cloneObject(Object a_object)
throws Exception {
return cloneObject(getConfiguration(), a_object, this);
}
/**
* Static convenience method.
* Clones an object by using clone handlers. If no deep cloning possible, then
* return the reference.
*
* @param a_config a valid configuration to obtain the JGAPFactory from
* @param a_object the object to clone
* @param a_master the super object of a_object, e.g. a chromosome in case of
* application to be cloned
* @return the cloned object, or the object itself if no coning supported
* @throws Exception
*
* @author Klaus Meffert
* @since 3.5
*/
public static Object cloneObject(Configuration a_config, Object a_object,
Object a_master)
throws Exception {
if (a_object == null) {
return null;
}
// Try to clone via a registered clone handler.
// --------------------------------------------
ICloneHandler cloner = a_config.getJGAPFactory().
getCloneHandlerFor(a_object, a_object.getClass());
if (cloner != null) {
return cloner.perform(a_object, null, a_master);
}
else {
// No cloning supported, so just return the reference.
// ---------------------------------------------------
return a_object;
}
}
/**
* Retrieves the fitness value of this Chromosome, as determined by the
* active fitness function. If a bulk fitness function is in use and
* has not yet assigned a fitness value to this Chromosome, then -1 is
* returned.<p>
* Attention: should not be called from toString() as the fitness value would
* be computed if it was initial!
*
* @return a positive double value representing the fitness of this
* Chromosome, or -1 if a bulk fitness function is in use and has not yet
* assigned a fitness value to this Chromosome
*
* @author Neil Rotstan
* @author Klaus Meffert
* @since 2.0
*/
public double getFitnessValue() {
if (m_fitnessValue >= 0.000d && !m_alwaysCalculate) {
return m_fitnessValue;
}
else {
return calcFitnessValue();
}
}
/**
* @return the lastly computed fitness value, or FitnessFunction.NO_FITNESS_VALUE
* in case no value has been computed yet.
*
* @author Klaus Meffert
*/
public double getFitnessValueDirectly() {
return m_fitnessValue;
}
/**
* @return fitness value of this chromosome determined via the registered
* fitness function
*
* @author Klaus Meffert
* @since 2.4
*/
protected double calcFitnessValue() {
if (getConfiguration() != null) {
FitnessFunction normalFitnessFunction = getConfiguration().
getFitnessFunction();
if (normalFitnessFunction != null) {
// Grab the "normal" fitness function and ask it to calculate our
// fitness value.
// --------------------------------------------------------------
m_fitnessValue = normalFitnessFunction.getFitnessValue(this);
}
}
return m_fitnessValue;
}
/**
* Sets the fitness value of this Chromosome. This method is for use
* by bulk fitness functions and should not be invokved from anything
* else (except test cases).
*
* @param a_newFitnessValue a positive integer representing the fitness
* of this Chromosome
*
* @author Neil Rotstan
* @since 1.0
*/
public void setFitnessValue(double a_newFitnessValue) {
if (a_newFitnessValue >= 0 &&
Math.abs(m_fitnessValue - a_newFitnessValue) > 0.0000001) {
m_fitnessValue = a_newFitnessValue;
}
}
/**
* Sets the fitness value of this Chromosome directly without any
* constraint checks, conversions or checks. Only use if you know what
* you do.
*
* @param a_newFitnessValue a positive integer representing the fitness
* of this Chromosome
*
* @author Klaus Meffert
*/
public void setFitnessValueDirectly(double a_newFitnessValue) {
m_fitnessValue = a_newFitnessValue;
}
/**
* @return a string representation of this Chromosome, useful for display
* purposes
*
* @author Neil Rotstan
* @author Klaus Meffert
* @since 1.0
*/
public String toString() {
StringBuffer representation = new StringBuffer();
representation.append(S_SIZE + ":" + size());
// Don't use getFitnessValue() here as it would then be initialized if
// it was not. We want to capture the current state here!
// -------------------------------------------------------------------
representation.append(", " + S_FITNESS_VALUE + ":" + m_fitnessValue);
representation.append(", " + S_ALLELES + ":");
representation.append("[");
// Append the representations of each of the genes' alleles.
// ---------------------------------------------------------
int size = size();
for (int i = 0; i < size; i++) {
if (i > 0) {
representation.append(", ");
}
if (getGene(i) == null) {
representation.append("null");
}
else {
representation.append(getGene(i).toString());
}
}
representation.append("]");
String appData;
if (getApplicationData() != null) {
appData = getApplicationData().toString();
}
else {
appData = "null";
}
representation.append(", " + S_APPLICATION_DATA + ":" + appData);
return representation.toString();
}
/**
* Convenience method that returns a new Chromosome instance with its
* genes values (alleles) randomized. Note that, if possible, this method
* will acquire a Chromosome instance from the active ChromosomePool
* (if any) and then randomize its gene values before returning it. If a
* Chromosome cannot be acquired from the pool, then a new instance will
* be constructed and its gene values randomized before returning it.
*
* @param a_configuration the configuration to use
* @return randomly initialized Chromosome
* @throws InvalidConfigurationException if the given Configuration
* instance is invalid
* @throws IllegalArgumentException if the given Configuration instance
* is null
*
* @author Neil Rotstan
* @author Klaus Meffert
* @since 1.0
*/
public static IChromosome randomInitialChromosome(
Configuration a_configuration)
throws InvalidConfigurationException {
// Sanity check: make sure the given configuration isn't null.
// -----------------------------------------------------------
if (a_configuration == null) {
throw new IllegalArgumentException(
"Configuration instance must not be null");
}
// Lock the configuration settings so that they can't be changed
// from now on.
// -------------------------------------------------------------
a_configuration.lockSettings();
// First see if we can get a Chromosome instance from the pool.
// If we can, we'll randomize its gene values (alleles) and then
// return it.
// -------------------------------------------------------------
IChromosomePool pool = a_configuration.getChromosomePool();
if (pool != null) {
IChromosome randomChromosome = pool.acquireChromosome();
if (randomChromosome != null) {
Gene[] genes = randomChromosome.getGenes();
RandomGenerator generator = a_configuration.getRandomGenerator();
for (int i = 0; i < genes.length; i++) {
genes[i].setToRandomValue(generator);
/**@todo what about Gene's energy?*/
}
randomChromosome.setFitnessValueDirectly(FitnessFunction.
NO_FITNESS_VALUE);
return randomChromosome;
}
}
// We weren't able to get a Chromosome from the pool, so we have to
// construct a new instance and build it from scratch.
// ------------------------------------------------------------------
IChromosome sampleChromosome =
a_configuration.getSampleChromosome();
sampleChromosome.setFitnessValue(FitnessFunction.NO_FITNESS_VALUE);
Gene[] sampleGenes = sampleChromosome.getGenes();
Gene[] newGenes = new Gene[sampleGenes.length];
RandomGenerator generator = a_configuration.getRandomGenerator();
for (int i = 0; i < newGenes.length; i++) {
// We use the newGene() method on each of the genes in the
// sample Chromosome to generate our new Gene instances for
// the Chromosome we're returning. This guarantees that the
// new Genes are setup with all of the correct internal state
// for the respective gene position they're going to inhabit.
// -----------------------------------------------------------
newGenes[i] = sampleGenes[i].newGene();
// If application data is set, try to clone it as well.
// ----------------------------------------------------
Object appData = sampleGenes[i].getApplicationData();
if(appData != null) {
try {
cloneObject(a_configuration, appData, sampleChromosome);
} catch (Exception ex) {
throw new InvalidConfigurationException("Application data of "
+"sample chromsome is not cloneable",ex);
}
}
// Set the gene's value (allele) to a random value.
// ------------------------------------------------
newGenes[i].setToRandomValue(generator);
/**@todo what about Gene's energy?*/
}
// Finally, construct the new chromosome with the new random
// genes values and return it.
// ---------------------------------------------------------
return new Chromosome(a_configuration, newGenes);
}
/**
* Compares this Chromosome against the specified object. The result is
* true if and the argument is an instance of the Chromosome class
* and has a set of genes equal to this one.
*
* @param 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 other) {
// If class is not equal, return false. Therefor catch
// ClasscastException's. The cleaner way (commented out below) would
// be too slow, indeed.
// -----------------------------------------------------------------
/*
if (other != null &&
!this.getClass ().getName ().equals (other.getClass ().getName ()))
{
return false;
}
*/
try {
return compareTo(other) == 0;
} catch (ClassCastException cex) {
return false;
}
}
/**
* Retrieve a hash code for this Chromosome. Does not considers the order
* of the Genes for all cases (especially when gene is empty).
*
* @return the hash code of this Chromosome
*
* @author Neil Rotstan
* @author Klaus Meffert
* @since 1.0
*/
public int hashCode() {
// Do what java.util.AbstractList does.
// ------------------------------------
int geneHashcode;
int hashCode = 1;
if (getGenes() != null) {
int size = size();
for (int i = 0; i < size; i++) {
Gene gene = getGene(i);
if (gene == null) {
geneHashcode = -55;
}
else {
geneHashcode = gene.hashCode();
}
hashCode = 31 * hashCode + geneHashcode;
}
}
return hashCode;
}
/**
* Compares the given Chromosome to this Chromosome. This chromosome is
* considered to be "less than" the given chromosome if it has a fewer
* number of genes or if any of its gene values (alleles) are less than
* their corresponding gene values in the other chromosome.
*
* @param other the Chromosome against which to compare this chromosome
* @return a negative number if this chromosome is "less than" the given
* chromosome, zero if they are equal to each other, and a positive number if
* this chromosome is "greater than" the given chromosome
*
* @author Neil Rotstan
* @author Klaus Meffert
* @since 1.0
*/
public int compareTo(Object other) {
// First, if the other Chromosome is null, then this chromosome is
// automatically the "greater" Chromosome.
// ---------------------------------------------------------------
if (other == null) {
return 1;
}
int size = size();
IChromosome otherChromosome = (IChromosome) other;
Gene[] otherGenes = otherChromosome.getGenes();
// If the other Chromosome doesn't have the same number of genes,
// then whichever has more is the "greater" Chromosome.
// --------------------------------------------------------------
if (otherChromosome.size() != size) {
return size() - otherChromosome.size();
}
// Next, compare the gene values (alleles) for differences. If
// one of the genes is not equal, then we return the result of its
// comparison.
// ---------------------------------------------------------------
for (int i = 0; i < size; i++) {
int comparison = getGene(i).compareTo(otherGenes[i]);
if (comparison != 0) {
return comparison;
}
}
// Compare current fitness value.
// ------------------------------
if (m_fitnessValue != otherChromosome.getFitnessValueDirectly()) {
FitnessEvaluator eval = getConfiguration().getFitnessEvaluator();
if (eval != null) {
if (eval.isFitter(m_fitnessValue,
otherChromosome.getFitnessValueDirectly())) {
return 1;
}
else {
return -1;
}
}
else {
// undetermined order, but unequal!
// --------------------------------
return -1;
}
}
if (m_compareAppData) {
// Compare application data.
// -------------------------
if (getApplicationData() == null) {
if (otherChromosome.getApplicationData() != null) {
return -1;
}
}
else if (otherChromosome.getApplicationData() == null) {
return 1;
}
else {
if (getApplicationData() instanceof Comparable) {
try {
return ( (Comparable) getApplicationData()).compareTo(
otherChromosome.getApplicationData());
} catch (ClassCastException cex) {
/**@todo improve*/
return -1;
}
}
else {
return getApplicationData().getClass().getName().compareTo(
otherChromosome.getApplicationData().getClass().getName());
}
}
}
// Everything is equal. Return zero.
// ---------------------------------
return 0;
}
/**
* Sets whether this Chromosome has been selected by the natural selector
* to continue to the next generation or manually (e.g. via an add-method).
*
* @param a_isSelected true if this Chromosome has been selected, false
* otherwise
*
* @author Neil Rotstan
* @since 1.0
*/
public void setIsSelectedForNextGeneration(boolean a_isSelected) {
m_isSelectedForNextGeneration = a_isSelected;
}
/**
* Retrieves whether this Chromosome has been selected by the natural
* selector to continue to the next generation.
*
* @return true if this Chromosome has been selected, false otherwise
*
* @author Neil Rotstan
* @since 1.0
*/
public boolean isSelectedForNextGeneration() {
return m_isSelectedForNextGeneration;
}
/**
* Invoked when this Chromosome is no longer needed and should perform
* any necessary cleanup. Note that this method will attempt to release
* this Chromosome instance to the active ChromosomePool, if any.
*
* @author Neil Rotstan
* @since 1.0
*/
public void cleanup() {
if (getConfiguration() == null) {
throw new IllegalStateException(
"The active Configuration object must be set on this " +
"Chromosome prior to invocation of the cleanup() method.");
}
// First, reset our internal state.
// --------------------------------
m_fitnessValue = getConfiguration().getFitnessFunction().
NO_FITNESS_VALUE;
m_isSelectedForNextGeneration = false;
// Next we want to try to release this Chromosome to a ChromosomePool
// if one has been setup so that we can save a little time and memory
// next time a Chromosome is needed.
// ------------------------------------------------------------------
// Now fetch the active ChromosomePool from the Configuration object
// and, if the pool exists, release this Chromosome to it.
// -----------------------------------------------------------------
IChromosomePool pool = getConfiguration().getChromosomePool();
if (pool != null) {
// Note that the pool will take care of any gene cleanup for us,
// so we don't need to worry about it here.
// -------------------------------------------------------------
pool.releaseChromosome(this);
}
else {
// No pool is available, so we need to finish cleaning up, which
// basically entails requesting each of our genes to clean
// themselves up as well.
// -------------------------------------------------------------
for (int i = 0; i < size(); i++) {
getGene(i).cleanup();
}
}
}
/**
* This sets the application-specific data that is attached to this
* Chromosome. Attaching application-specific data may be useful for
* some applications when it comes time to evaluate this Chromosome
* in the fitness function. JGAP ignores this data, except for cloning and
* comparison (latter only if opted in via setCompareApplicationData(..))
*
* @param a_newData the new application-specific data to attach to this
* Chromosome. Should be an instance of IApplicationData
*
* @author Neil Rotstan
* @since 1.1
*/
public void setApplicationData(Object a_newData) {
m_applicationData = a_newData;
}
/**
* Retrieves the application-specific data that is attached to this
* Chromosome. Attaching application-specific data may be useful for
* some applications when it comes time to evaluate this Chromosome
* in the fitness function. JGAP ignores this data functionally.
*
* @return the application-specific data previously attached to this
* Chromosome, or null if there is no data attached
*
* @author Neil Rotstan
* @since 1.1
*/
public Object getApplicationData() {
return m_applicationData;
}
/**
* Sets the genes for the chromosome.
* @param a_genes the genes to set for the chromosome
*
* @throws InvalidConfigurationException in case constraint checker is
* provided
*
* @author Klaus Meffert
*/
public void setGenes(Gene[] a_genes)
throws InvalidConfigurationException {
super.setGenes(a_genes);
verify(getConstraintChecker());
}
/**
* Should we also consider the application data when comparing? Default is
* "false" as "true" means a Chromosome is losing its identity when
* application data is set differently!
*
* @param a_doCompare true: consider application data in method compareTo
*
* @author Klaus Meffert
* @since 2.2
*/
public void setCompareApplicationData(boolean a_doCompare) {
m_compareAppData = a_doCompare;
}
/*
* @return should we also consider the application data when comparing?
*
* @author Klaus Meffert
* @since 2.2
*/
public boolean isCompareApplicationData() {
return m_compareAppData;
}
/**
* Sets the constraint checker to be used for this gene whenever method
* setAllele(Object) is called.
*
* @param a_constraintChecker the constraint checker to be set
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 2.5
*/
public void setConstraintChecker(IGeneConstraintChecker a_constraintChecker)
throws InvalidConfigurationException {
verify(a_constraintChecker);
m_geneAlleleChecker = a_constraintChecker;
}
/**
* @return IGeneConstraintChecker the constraint checker to be used whenever
* method setGenes(Gene[]) is called.
*
* @author Klaus Meffert
* @since 2.5
*/
public IGeneConstraintChecker getConstraintChecker() {
return m_geneAlleleChecker;
}
/**
* Verifies the state of the chromosome. Especially takes care of the
* given constraint checker.
* @param a_constraintChecker the constraint checker to verify
*
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 2.5
*/
protected void verify(IGeneConstraintChecker a_constraintChecker)
throws InvalidConfigurationException {
if (a_constraintChecker != null && getGenes() != null) {
int len = getGenes().length;
for (int i = 0; i < len; i++) {
Gene gene = getGene(i);
if (!a_constraintChecker.verify(gene, null, this, i)) {
throw new InvalidConfigurationException(
"The gene type "
+ gene.getClass().getName()
+ " is not allowed to be used in the chromosome due to the"
+ " constraint checker used.");
}
}
}
}
// ------------------------------------
// Begin of IInitializer implementation
// ------------------------------------
/**{@inheritDoc}*/
public boolean isHandlerFor(Object a_obj, Class a_class) {
if (a_class == Chromosome.class) {
return true;
}
else {
return false;
}
}
/**{@inheritDoc}*/
public Object perform(Object a_obj, Class a_class, Object a_params)
throws Exception {
return randomInitialChromosome(getConfiguration());
}
// ----------------------------------
// End of IInitializer implementation
// ----------------------------------
public void setMultiObjectives(List a_values) {
if (m_multiObjective == null) {
m_multiObjective = new Vector();
}
m_multiObjective.clear();
m_multiObjective.addAll(a_values);
}
public List getMultiObjectives() {
return m_multiObjective;
}
}