0001: /**
0002: * JGAP offers a dual license model(see below for specific license information):
0003: * + The LGPL may be used anytime.
0004: * + The MPL may be used if at least $20 have been donated to the JGAP project
0005: * thru PayPal (see http://www.sourceforge.net/projects/jgap or, directly,
0006: * http://sourceforge.net/donate/index.php?group_id=11618).
0007: * Details about usage of JGAP under the MPL can be found at the homepage
0008: * http://jgap.sourceforge.net/.
0009: *
0010: * Specific license information (MPL and LGPL)
0011: * -------------------------------------------
0012: * The contents of this file are subject to the Mozilla Public License Version
0013: * 1.1 (the "License"); you may not use this file except in compliance with the
0014: * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
0015: *
0016: * Software distributed under the License is distributed on an "AS IS" basis,
0017: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
0018: * the specific language governing rights and limitations under the License.
0019: *
0020: * The Original Code is 'JGAP - Java Genetic Algorithms Package'.
0021: * The Initial Developer of the Original Code is Neil Rotstan. Portions created
0022: * by the Initial Developer are Copyright (C) 2002- 2003 by Neil Rotstan.
0023: * All Rights Reserved.
0024: * Co-developer of the code is Klaus Meffert. Portions created by the co-
0025: * developer are Copyright (C) 2003-2006 by Klaus Meffert. All Rights Reserved.
0026: * Contributor(s): all the names of the contributors are added in the source
0027: * code where applicable.
0028: *
0029: * Alternatively, the contents of this file may be used under the terms of the
0030: * LGPL license (the "GNU LESSER PUBLIC LICENSE"), in which case the
0031: * provisions of LGPL are applicable instead of those above. If you wish to
0032: * allow use of your version of this file only under the terms of the LGPL
0033: * License and not to allow others to use your version of this file under
0034: * the MPL, indicate your decision by deleting the provisions above and
0035: * replace them with the notice and other provisions required by the LGPL.
0036: * If you do not delete the provisions above, a recipient may use your version
0037: * of this file under either the MPL or the LGPL.
0038: *
0039: * This library is free software; you can redistribute it and/or modify it
0040: * under the terms of the MPL as stated above or under the terms of the LGPL.
0041: * This library is distributed in the hope that it will be useful, but WITHOUT
0042: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
0043: * FOR A PARTICULAR PURPOSE. See the GNU Lesser Public License for more
0044: * details.
0045: */package org.jgap;
0046:
0047: import java.util.*;
0048:
0049: /**
0050: * Chromosomes represent potential solutions and consist of a fixed-length
0051: * collection of genes. Each gene represents a discrete part of the solution.
0052: * Each gene in the Chromosome may be backed by a different concrete
0053: * implementation of the Gene interface, but all genes in a respective
0054: * position (locus) must share the same concrete implementation across
0055: * Chromosomes within a single population (genotype). In other words, gene 1
0056: * in a chromosome must share the same concrete implementation as gene 1 in all
0057: * other chromosomes in the population.
0058: *
0059: * @author Neil Rotstan
0060: * @author Klaus Meffert
0061: * @since 1.0
0062: */
0063: public class Chromosome extends BaseChromosome {
0064: /** String containing the CVS revision. Read out via reflection!*/
0065: private final static String CVS_REVISION = "$Revision: 1.95 $";
0066: /**
0067: * Application-specific data that is attached to this Chromosome.
0068: * This data may assist the application in evaluating this Chromosome
0069: * in the fitness function. JGAP does not operate on the data, aside
0070: * from allowing it to be set and retrieved, and considering it with
0071: * comparations (if user opted in to do so).
0072: */
0073: private Object m_applicationData;
0074:
0075: /**
0076: * Holds multiobjective values.
0077: *
0078: * @since 2.6
0079: * @todo move to new subclass of Chromosome (and introduce new interface
0080: * IMultiObjective with that)
0081: */
0082: private List m_multiObjective;
0083:
0084: /**
0085: * Keeps track of whether or not this Chromosome has been selected by
0086: * the natural selector to move on to the next generation.
0087: */
0088: private boolean m_isSelectedForNextGeneration;
0089:
0090: /**
0091: * Stores the fitness value of this Chromosome as determined by the
0092: * active fitness function. A value of -1 indicates that this field
0093: * has not yet been set with this Chromosome's fitness values (valid
0094: * fitness values are always positive).
0095: *
0096: * @since 2.0 (until 1.1: type int)
0097: */
0098: protected double m_fitnessValue = FitnessFunction.NO_FITNESS_VALUE;
0099:
0100: /**
0101: * Method compareTo(): Should we also consider the application data when
0102: * comparing? Default is "false", as "true" means a Chromosome's losing its
0103: * identity when application data is set differently!
0104: *
0105: * @since 2.2
0106: */
0107: private boolean m_compareAppData;
0108:
0109: /**
0110: * Optional helper class for checking if a given allele value to be set
0111: * for a given gene is valid. If not, the allele value may not be set for the
0112: * gene or the gene type (e.g. IntegerGene) is not allowed in general!
0113: *
0114: * @since 2.5
0115: */
0116: private IGeneConstraintChecker m_geneAlleleChecker;
0117:
0118: /**
0119: * If set to true the method getFitnessValue() will always (re-)calculate the
0120: * fitness value. This may be necessary in case of environments where the
0121: * state changes without the chromosome to notice
0122: *
0123: * @since 3.2.2
0124: */
0125: private boolean m_alwaysCalculate;
0126:
0127: /**
0128: * Default constructor, only provided for dynamic instantiation.<p>
0129: * Attention: The configuration used is the one set with the static method
0130: * Genotype.setConfiguration.
0131: *
0132: * @throws InvalidConfigurationException
0133: *
0134: * @author Klaus Meffert
0135: * @since 2.4
0136: */
0137: public Chromosome() throws InvalidConfigurationException {
0138: this (Genotype.getStaticConfiguration());
0139: }
0140:
0141: /**
0142: * Constructor, provided for dynamic or minimal instantiation.
0143: *
0144: * @param a_configuration the configuration to use
0145: * @throws InvalidConfigurationException
0146: *
0147: * @author Klaus Meffert
0148: * @since 3.0
0149: */
0150: public Chromosome(final Configuration a_configuration)
0151: throws InvalidConfigurationException {
0152: super (a_configuration);
0153: m_alwaysCalculate = a_configuration.isAlwaysCalculateFitness();
0154: }
0155:
0156: /**
0157: * Constructor, provided for instantiation via persistent representation.
0158: *
0159: * @param a_configuration the configuration to use
0160: * @param a_persistentRepresentatuion valid persistent representation that
0161: * was most likely obtained via getPersistentRepresentation()
0162: * @throws InvalidConfigurationException
0163: * @throws UnsupportedRepresentationException
0164: *
0165: * @author Klaus Meffert
0166: * @since 3.2
0167: */
0168: public Chromosome(final Configuration a_configuration,
0169: String a_persistentRepresentatuion)
0170: throws InvalidConfigurationException,
0171: UnsupportedRepresentationException {
0172: this (a_configuration);
0173: setValueFromPersistentRepresentation(a_persistentRepresentatuion);
0174: }
0175:
0176: /**
0177: * Constructor for specifying the number of genes.
0178: *
0179: * @param a_configuration the configuration to use
0180: * @param a_desiredSize number of genes the chromosome contains of
0181: * @throws InvalidConfigurationException
0182: *
0183: * @author Klaus Meffert
0184: * @since 2.2
0185: */
0186: public Chromosome(final Configuration a_configuration,
0187: final int a_desiredSize)
0188: throws InvalidConfigurationException {
0189: this (a_configuration);
0190: if (a_desiredSize <= 0) {
0191: throw new IllegalArgumentException(
0192: "Chromosome size must be greater than zero");
0193: }
0194: setGenes(new Gene[a_desiredSize]);
0195: }
0196:
0197: /**
0198: * Constructs a Chromosome of the given size separate from any specific
0199: * Configuration. This constructor will use the given sample Gene to
0200: * construct a new Chromosome instance containing genes all of the same
0201: * type as the sample Gene. This can be useful for constructing sample
0202: * chromosomes that use the same Gene type for all of their genes and that
0203: * are to be used to setup a Configuration object.
0204: *
0205: * @param a_configuration the configuration to use
0206: * @param a_sampleGene a concrete sampleGene instance that will be used
0207: * as a template for all of the genes in this Chromosome
0208: * @param a_desiredSize the desired size (number of genes) of this Chromosome
0209: * @throws InvalidConfigurationException
0210: *
0211: * @author Neil Rotstan
0212: * @author Klaus Meffert
0213: * @since 1.0
0214: */
0215: public Chromosome(final Configuration a_configuration,
0216: final Gene a_sampleGene, final int a_desiredSize)
0217: throws InvalidConfigurationException {
0218: this (a_configuration, a_desiredSize);
0219: initFromGene(a_sampleGene);
0220: }
0221:
0222: public Chromosome(final Configuration a_configuration,
0223: Gene a_sampleGene, int a_desiredSize,
0224: IGeneConstraintChecker a_constraintChecker)
0225: throws InvalidConfigurationException {
0226: this (a_configuration, a_desiredSize);
0227: initFromGene(a_sampleGene);
0228: setConstraintChecker(a_constraintChecker);
0229: }
0230:
0231: protected void initFromGene(Gene a_sampleGene) {
0232: // Do sanity checking to make sure the parameters we were
0233: // given are valid.
0234: // ------------------------------------------------------
0235: if (a_sampleGene == null) {
0236: throw new IllegalArgumentException(
0237: "Sample Gene cannot be null.");
0238: }
0239: // Populate the array of genes it with new Gene instances
0240: // created from the sample gene.
0241: // ------------------------------------------------------
0242: int size = size();
0243: for (int i = 0; i < size; i++) {
0244: setGene(i, a_sampleGene.newGene());
0245: }
0246: }
0247:
0248: /**
0249: * Constructs a Chromosome separate from any specific Configuration. This
0250: * can be useful for constructing sample chromosomes that are to be used
0251: * to setup a Configuration object.
0252: *
0253: * @param a_configuration the configuration to use
0254: * @param a_initialGenes the initial genes of this Chromosome
0255: * @throws InvalidConfigurationException
0256: *
0257: * @author Neil Rotstan
0258: * @since 1.0
0259: */
0260: public Chromosome(final Configuration a_configuration,
0261: Gene[] a_initialGenes) throws InvalidConfigurationException {
0262: this (a_configuration, a_initialGenes == null ? 0
0263: : a_initialGenes.length);
0264: checkGenes(a_initialGenes);
0265: setGenes(a_initialGenes);
0266: }
0267:
0268: /**
0269: * Constructs a Chromosome separate from any specific Configuration. This
0270: * can be useful for constructing sample chromosomes that are to be used
0271: * to setup a Configuration object. Additionally, a constraint checker can be
0272: * specified. It is used right here to verify the validity of the gene types
0273: * supplied.
0274: *
0275: * @param a_configuration the configuration to use
0276: * @param a_initialGenes the initial genes of this Chromosome
0277: * @param a_constraintChecker constraint checker to use
0278: * @throws InvalidConfigurationException in case the constraint checker
0279: * reports a configuration error
0280: *
0281: * @author Klaus Meffert
0282: * @since 2.5
0283: */
0284: public Chromosome(final Configuration a_configuration,
0285: Gene[] a_initialGenes,
0286: IGeneConstraintChecker a_constraintChecker)
0287: throws InvalidConfigurationException {
0288: this (a_configuration, a_initialGenes.length);
0289: checkGenes(a_initialGenes);
0290: setGenes(a_initialGenes);
0291: setConstraintChecker(a_constraintChecker);
0292: }
0293:
0294: /**
0295: * Helper: called by constructors only to verify the initial genes.
0296: *
0297: * @param a_initialGenes the initial genes of this Chromosome to verify
0298: *
0299: * @author Klaus Meffert
0300: * @since 2.5
0301: */
0302: protected void checkGenes(Gene[] a_initialGenes) {
0303: // Sanity checks: make sure the genes array isn't null and
0304: // that none of the genes contained within it are null.
0305: // Check against null already done in constructors!
0306: // -------------------------------------------------------
0307: for (int i = 0; i < a_initialGenes.length; i++) {
0308: if (a_initialGenes[i] == null) {
0309: throw new IllegalArgumentException(
0310: "The gene at index "
0311: + i
0312: + " in the given array of "
0313: + "genes was found to be null. No gene in the array "
0314: + "may be null.");
0315: }
0316: }
0317: }
0318:
0319: /**
0320: * Returns a copy of this Chromosome. The returned instance can evolve
0321: * independently of this instance. Note that, if possible, this method
0322: * will first attempt to acquire a Chromosome instance from the active
0323: * ChromosomePool (if any) and set its value appropriately before
0324: * returning it. If that is not possible, then a new Chromosome instance
0325: * will be constructed and its value set appropriately before returning.
0326: *
0327: * @return copy of this Chromosome
0328: * @throws IllegalStateException instead of CloneNotSupportedException
0329: *
0330: * @author Neil Rotstan
0331: * @author Klaus Meffert
0332: * @since 1.0
0333: */
0334: public synchronized Object clone() {
0335: // Before doing anything, make sure that a Configuration object
0336: // has been set on this Chromosome. If not, then throw an
0337: // IllegalStateException.
0338: // ------------------------------------------------------------
0339: if (getConfiguration() == null) {
0340: throw new IllegalStateException(
0341: "The active Configuration object must be set on this "
0342: + "Chromosome prior to invocation of the clone() method.");
0343: }
0344: IChromosome copy = null;
0345: // Now, first see if we can pull a Chromosome from the pool and just
0346: // set its gene values (alleles) appropriately.
0347: // ------------------------------------------------------------
0348: IChromosomePool pool = getConfiguration().getChromosomePool();
0349: if (pool != null) {
0350: copy = pool.acquireChromosome();
0351: if (copy != null) {
0352: Gene[] genes = copy.getGenes();
0353: for (int i = 0; i < size(); i++) {
0354: genes[i].setAllele(getGene(i).getAllele());
0355: }
0356: }
0357: }
0358: try {
0359: if (copy == null) {
0360: // We couldn't fetch a Chromosome from the pool, so we need to create
0361: // a new one. First we make a copy of each of the Genes. We explicity
0362: // use the Gene at each respective gene location (locus) to create the
0363: // new Gene that is to occupy that same locus in the new Chromosome.
0364: // -------------------------------------------------------------------
0365: int size = size();
0366: if (size > 0) {
0367: Gene[] copyOfGenes = new Gene[size];
0368: for (int i = 0; i < copyOfGenes.length; i++) {
0369: copyOfGenes[i] = getGene(i).newGene();
0370: copyOfGenes[i]
0371: .setAllele(getGene(i).getAllele());
0372: }
0373: // Now construct a new Chromosome with the copies of the genes and
0374: // return it. Also clone the IApplicationData object.
0375: // ---------------------------------------------------------------
0376: /**@todo clone Config!*/
0377: copy = new Chromosome(getConfiguration(),
0378: copyOfGenes);
0379: } else {
0380: copy = new Chromosome(getConfiguration());
0381: }
0382: copy.setFitnessValue(m_fitnessValue);
0383: }
0384: // Clone constraint checker.
0385: // -------------------------
0386: copy.setConstraintChecker(getConstraintChecker());
0387: } catch (InvalidConfigurationException iex) {
0388: throw new IllegalStateException(iex.getMessage());
0389: }
0390: // Also clone the IApplicationData object.
0391: // ---------------------------------------
0392: try {
0393: copy.setApplicationData(cloneObject(getApplicationData()));
0394: } catch (Exception ex) {
0395: throw new IllegalStateException(ex.getMessage());
0396: }
0397: return copy;
0398: }
0399:
0400: /**
0401: * Clones an object by using clone handlers. If no deep cloning possible, then
0402: * return the reference.
0403: *
0404: * @param a_object the object to clone
0405: * @return the cloned object, or the object itself if no coning supported
0406: * @throws Exception
0407: *
0408: * @author Klaus Meffert
0409: * @since 2.6
0410: */
0411: protected Object cloneObject(Object a_object) throws Exception {
0412: if (a_object == null) {
0413: return null;
0414: }
0415: // Try to clone via a registered clone handler.
0416: // --------------------------------------------
0417: ICloneHandler cloner = getConfiguration().getJGAPFactory()
0418: .getCloneHandlerFor(a_object, a_object.getClass());
0419: if (cloner != null) {
0420: return cloner.perform(a_object, null, this );
0421: } else {
0422: // No cloning supported, so just return the reference.
0423: // ---------------------------------------------------
0424: return a_object;
0425: }
0426: }
0427:
0428: /**
0429: * Retrieves the fitness value of this Chromosome, as determined by the
0430: * active fitness function. If a bulk fitness function is in use and
0431: * has not yet assigned a fitness value to this Chromosome, then -1 is
0432: * returned.<p>
0433: * Attention: should not be called from toString() as the fitness value would
0434: * be computed if it was initial!
0435: *
0436: * @return a positive double value representing the fitness of this
0437: * Chromosome, or -1 if a bulk fitness function is in use and has not yet
0438: * assigned a fitness value to this Chromosome
0439: *
0440: * @author Neil Rotstan
0441: * @author Klaus Meffert
0442: * @since 2.0
0443: */
0444: public double getFitnessValue() {
0445: if (m_fitnessValue >= 0.000d && !m_alwaysCalculate) {
0446: return m_fitnessValue;
0447: } else {
0448: return calcFitnessValue();
0449: }
0450: }
0451:
0452: /**
0453: * @return the lastly computed fitness value, or FitnessFunction.NO_FITNESS_VALUE
0454: * in case no value has been computed yet.
0455: *
0456: * @author Klaus Meffert
0457: */
0458: public double getFitnessValueDirectly() {
0459: return m_fitnessValue;
0460: }
0461:
0462: /**
0463: * @return fitness value of this chromosome determined via the registered
0464: * fitness function
0465: *
0466: * @author Klaus Meffert
0467: * @since 2.4
0468: */
0469: protected double calcFitnessValue() {
0470: if (getConfiguration() != null) {
0471: FitnessFunction normalFitnessFunction = getConfiguration()
0472: .getFitnessFunction();
0473: if (normalFitnessFunction != null) {
0474: // Grab the "normal" fitness function and ask it to calculate our
0475: // fitness value.
0476: // --------------------------------------------------------------
0477: m_fitnessValue = normalFitnessFunction
0478: .getFitnessValue(this );
0479: }
0480: }
0481: return m_fitnessValue;
0482: }
0483:
0484: /**
0485: * Sets the fitness value of this Chromosome. This method is for use
0486: * by bulk fitness functions and should not be invokved from anything
0487: * else (except test cases).
0488: *
0489: * @param a_newFitnessValue a positive integer representing the fitness
0490: * of this Chromosome
0491: *
0492: * @author Neil Rotstan
0493: * @since 1.0
0494: */
0495: public void setFitnessValue(double a_newFitnessValue) {
0496: if (a_newFitnessValue >= 0
0497: && Math.abs(m_fitnessValue - a_newFitnessValue) > 0.0000001) {
0498: m_fitnessValue = a_newFitnessValue;
0499: }
0500: }
0501:
0502: /**
0503: * Sets the fitness value of this Chromosome directly without any
0504: * constraint checks, conversions or checks. Only use if you know what
0505: * you do.
0506: *
0507: * @param a_newFitnessValue a positive integer representing the fitness
0508: * of this Chromosome
0509: *
0510: * @author Klaus Meffert
0511: */
0512: public void setFitnessValueDirectly(double a_newFitnessValue) {
0513: m_fitnessValue = a_newFitnessValue;
0514: }
0515:
0516: /**
0517: * @return a string representation of this Chromosome, useful for display
0518: * purposes
0519: *
0520: * @author Neil Rotstan
0521: * @author Klaus Meffert
0522: * @since 1.0
0523: */
0524: public String toString() {
0525: StringBuffer representation = new StringBuffer();
0526: representation.append(S_SIZE + ":" + size());
0527: // Don't use getFitnessValue() here as it would then be initialized if
0528: // it was not. We want to capture the current state here!
0529: // -------------------------------------------------------------------
0530: representation.append(", " + S_FITNESS_VALUE + ":"
0531: + m_fitnessValue);
0532: representation.append(", " + S_ALLELES + ":");
0533: representation.append("[");
0534: // Append the representations of each of the genes' alleles.
0535: // ---------------------------------------------------------
0536: int size = size();
0537: for (int i = 0; i < size; i++) {
0538: if (i > 0) {
0539: representation.append(", ");
0540: }
0541: if (getGene(i) == null) {
0542: representation.append("null");
0543: } else {
0544: representation.append(getGene(i).toString());
0545: }
0546: }
0547: representation.append("]");
0548: String appData;
0549: if (getApplicationData() != null) {
0550: appData = getApplicationData().toString();
0551: } else {
0552: appData = "null";
0553: }
0554: representation
0555: .append(", " + S_APPLICATION_DATA + ":" + appData);
0556: return representation.toString();
0557: }
0558:
0559: /**
0560: * Convenience method that returns a new Chromosome instance with its
0561: * genes values (alleles) randomized. Note that, if possible, this method
0562: * will acquire a Chromosome instance from the active ChromosomePool
0563: * (if any) and then randomize its gene values before returning it. If a
0564: * Chromosome cannot be acquired from the pool, then a new instance will
0565: * be constructed and its gene values randomized before returning it.
0566: *
0567: * @param a_configuration the configuration to use
0568: * @return randomly initialized Chromosome
0569: * @throws InvalidConfigurationException if the given Configuration
0570: * instance is invalid
0571: * @throws IllegalArgumentException if the given Configuration instance
0572: * is null
0573: *
0574: * @author Neil Rotstan
0575: * @author Klaus Meffert
0576: * @since 1.0
0577: */
0578: public static IChromosome randomInitialChromosome(
0579: Configuration a_configuration)
0580: throws InvalidConfigurationException {
0581: // Sanity check: make sure the given configuration isn't null.
0582: // -----------------------------------------------------------
0583: if (a_configuration == null) {
0584: throw new IllegalArgumentException(
0585: "Configuration instance must not be null");
0586: }
0587: // Lock the configuration settings so that they can't be changed
0588: // from now on.
0589: // -------------------------------------------------------------
0590: a_configuration.lockSettings();
0591: // First see if we can get a Chromosome instance from the pool.
0592: // If we can, we'll randomize its gene values (alleles) and then
0593: // return it.
0594: // -------------------------------------------------------------
0595: IChromosomePool pool = a_configuration.getChromosomePool();
0596: if (pool != null) {
0597: IChromosome randomChromosome = pool.acquireChromosome();
0598: if (randomChromosome != null) {
0599: Gene[] genes = randomChromosome.getGenes();
0600: RandomGenerator generator = a_configuration
0601: .getRandomGenerator();
0602: for (int i = 0; i < genes.length; i++) {
0603: genes[i].setToRandomValue(generator);
0604: /**@todo what about Gene's energy?*/
0605: }
0606: randomChromosome
0607: .setFitnessValueDirectly(FitnessFunction.NO_FITNESS_VALUE);
0608: return randomChromosome;
0609: }
0610: }
0611: // We weren't able to get a Chromosome from the pool, so we have to
0612: // construct a new instance and build it from scratch.
0613: // ------------------------------------------------------------------
0614: IChromosome sampleChromosome = a_configuration
0615: .getSampleChromosome();
0616: sampleChromosome
0617: .setFitnessValue(FitnessFunction.NO_FITNESS_VALUE);
0618: Gene[] sampleGenes = sampleChromosome.getGenes();
0619: Gene[] newGenes = new Gene[sampleGenes.length];
0620: RandomGenerator generator = a_configuration
0621: .getRandomGenerator();
0622: for (int i = 0; i < newGenes.length; i++) {
0623: // We use the newGene() method on each of the genes in the
0624: // sample Chromosome to generate our new Gene instances for
0625: // the Chromosome we're returning. This guarantees that the
0626: // new Genes are setup with all of the correct internal state
0627: // for the respective gene position they're going to inhabit.
0628: // -----------------------------------------------------------
0629: newGenes[i] = sampleGenes[i].newGene();
0630: // Set the gene's value (allele) to a random value.
0631: // ------------------------------------------------
0632: newGenes[i].setToRandomValue(generator);
0633: /**@todo what about Gene's energy?*/
0634: }
0635: // Finally, construct the new chromosome with the new random
0636: // genes values and return it.
0637: // ---------------------------------------------------------
0638: return new Chromosome(a_configuration, newGenes);
0639: }
0640:
0641: /**
0642: * Compares this Chromosome against the specified object. The result is
0643: * true if and the argument is an instance of the Chromosome class
0644: * and has a set of genes equal to this one.
0645: *
0646: * @param other the object to compare against
0647: * @return true: if the objects are the same, false otherwise
0648: *
0649: * @author Neil Rotstan
0650: * @author Klaus Meffert
0651: * @since 1.0
0652: */
0653: public boolean equals(Object other) {
0654: // If class is not equal, return false. Therefor catch
0655: // ClasscastException's. The cleaner way (commented out below) would
0656: // be too slow, indeed.
0657: // -----------------------------------------------------------------
0658: /*
0659: if (other != null &&
0660: !this.getClass ().getName ().equals (other.getClass ().getName ()))
0661: {
0662: return false;
0663: }
0664: */
0665: try {
0666: return compareTo(other) == 0;
0667: } catch (ClassCastException cex) {
0668: return false;
0669: }
0670: }
0671:
0672: /**
0673: * Retrieve a hash code for this Chromosome. Does not considers the order
0674: * of the Genes for all cases (especially when gene is empty).
0675: *
0676: * @return the hash code of this Chromosome
0677: *
0678: * @author Neil Rotstan
0679: * @author Klaus Meffert
0680: * @since 1.0
0681: */
0682: public int hashCode() {
0683: // Do what java.util.AbstractList does.
0684: // ------------------------------------
0685: int geneHashcode;
0686: int hashCode = 1;
0687: if (getGenes() != null) {
0688: int size = size();
0689: for (int i = 0; i < size; i++) {
0690: Gene gene = getGene(i);
0691: if (gene == null) {
0692: geneHashcode = -55;
0693: } else {
0694: geneHashcode = gene.hashCode();
0695: }
0696: hashCode = 31 * hashCode + geneHashcode;
0697: }
0698: }
0699: return hashCode;
0700: }
0701:
0702: /**
0703: * Compares the given Chromosome to this Chromosome. This chromosome is
0704: * considered to be "less than" the given chromosome if it has a fewer
0705: * number of genes or if any of its gene values (alleles) are less than
0706: * their corresponding gene values in the other chromosome.
0707: *
0708: * @param other the Chromosome against which to compare this chromosome
0709: * @return a negative number if this chromosome is "less than" the given
0710: * chromosome, zero if they are equal to each other, and a positive number if
0711: * this chromosome is "greater than" the given chromosome
0712: *
0713: * @author Neil Rotstan
0714: * @author Klaus Meffert
0715: * @since 1.0
0716: */
0717: public int compareTo(Object other) {
0718: // First, if the other Chromosome is null, then this chromosome is
0719: // automatically the "greater" Chromosome.
0720: // ---------------------------------------------------------------
0721: if (other == null) {
0722: return 1;
0723: }
0724: int size = size();
0725: IChromosome otherChromosome = (IChromosome) other;
0726: Gene[] otherGenes = otherChromosome.getGenes();
0727: // If the other Chromosome doesn't have the same number of genes,
0728: // then whichever has more is the "greater" Chromosome.
0729: // --------------------------------------------------------------
0730: if (otherChromosome.size() != size) {
0731: return size() - otherChromosome.size();
0732: }
0733: // Next, compare the gene values (alleles) for differences. If
0734: // one of the genes is not equal, then we return the result of its
0735: // comparison.
0736: // ---------------------------------------------------------------
0737: for (int i = 0; i < size; i++) {
0738: int comparison = getGene(i).compareTo(otherGenes[i]);
0739: if (comparison != 0) {
0740: return comparison;
0741: }
0742: }
0743: // Compare current fitness value.
0744: // ------------------------------
0745: if (m_fitnessValue != otherChromosome.getFitnessValueDirectly()) {
0746: FitnessEvaluator eval = getConfiguration()
0747: .getFitnessEvaluator();
0748: if (eval != null) {
0749: if (eval.isFitter(m_fitnessValue, otherChromosome
0750: .getFitnessValueDirectly())) {
0751: return 1;
0752: } else {
0753: return -1;
0754: }
0755: } else {
0756: // undetermined order, but unequal!
0757: // --------------------------------
0758: return -1;
0759: }
0760: }
0761: if (m_compareAppData) {
0762: // Compare application data.
0763: // -------------------------
0764: if (getApplicationData() == null) {
0765: if (otherChromosome.getApplicationData() != null) {
0766: return -1;
0767: }
0768: } else if (otherChromosome.getApplicationData() == null) {
0769: return 1;
0770: } else {
0771: if (getApplicationData() instanceof Comparable) {
0772: try {
0773: return ((Comparable) getApplicationData())
0774: .compareTo(otherChromosome
0775: .getApplicationData());
0776: } catch (ClassCastException cex) {
0777: /**@todo improve*/
0778: return -1;
0779: }
0780: } else {
0781: return getApplicationData().getClass().getName()
0782: .compareTo(
0783: otherChromosome
0784: .getApplicationData()
0785: .getClass().getName());
0786: }
0787: }
0788: }
0789: // Everything is equal. Return zero.
0790: // ---------------------------------
0791: return 0;
0792: }
0793:
0794: /**
0795: * Sets whether this Chromosome has been selected by the natural selector
0796: * to continue to the next generation or manually (e.g. via an add-method).
0797: *
0798: * @param a_isSelected true if this Chromosome has been selected, false
0799: * otherwise
0800: *
0801: * @author Neil Rotstan
0802: * @since 1.0
0803: */
0804: public void setIsSelectedForNextGeneration(boolean a_isSelected) {
0805: m_isSelectedForNextGeneration = a_isSelected;
0806: }
0807:
0808: /**
0809: * Retrieves whether this Chromosome has been selected by the natural
0810: * selector to continue to the next generation.
0811: *
0812: * @return true if this Chromosome has been selected, false otherwise
0813: *
0814: * @author Neil Rotstan
0815: * @since 1.0
0816: */
0817: public boolean isSelectedForNextGeneration() {
0818: return m_isSelectedForNextGeneration;
0819: }
0820:
0821: /**
0822: * Invoked when this Chromosome is no longer needed and should perform
0823: * any necessary cleanup. Note that this method will attempt to release
0824: * this Chromosome instance to the active ChromosomePool, if any.
0825: *
0826: * @author Neil Rotstan
0827: * @since 1.0
0828: */
0829: public void cleanup() {
0830: if (getConfiguration() == null) {
0831: throw new IllegalStateException(
0832: "The active Configuration object must be set on this "
0833: + "Chromosome prior to invocation of the cleanup() method.");
0834: }
0835: // First, reset our internal state.
0836: // --------------------------------
0837: m_fitnessValue = getConfiguration().getFitnessFunction().NO_FITNESS_VALUE;
0838: m_isSelectedForNextGeneration = false;
0839: // Next we want to try to release this Chromosome to a ChromosomePool
0840: // if one has been setup so that we can save a little time and memory
0841: // next time a Chromosome is needed.
0842: // ------------------------------------------------------------------
0843: // Now fetch the active ChromosomePool from the Configuration object
0844: // and, if the pool exists, release this Chromosome to it.
0845: // -----------------------------------------------------------------
0846: IChromosomePool pool = getConfiguration().getChromosomePool();
0847: if (pool != null) {
0848: // Note that the pool will take care of any gene cleanup for us,
0849: // so we don't need to worry about it here.
0850: // -------------------------------------------------------------
0851: pool.releaseChromosome(this );
0852: } else {
0853: // No pool is available, so we need to finish cleaning up, which
0854: // basically entails requesting each of our genes to clean
0855: // themselves up as well.
0856: // -------------------------------------------------------------
0857: for (int i = 0; i < size(); i++) {
0858: getGene(i).cleanup();
0859: }
0860: }
0861: }
0862:
0863: /**
0864: * This sets the application-specific data that is attached to this
0865: * Chromosome. Attaching application-specific data may be useful for
0866: * some applications when it comes time to evaluate this Chromosome
0867: * in the fitness function. JGAP ignores this data, except for cloning and
0868: * comparison (latter only if opted in via setCompareApplicationData(..))
0869: *
0870: * @param a_newData the new application-specific data to attach to this
0871: * Chromosome. Should be an instance of IApplicationData
0872: *
0873: * @author Neil Rotstan
0874: * @since 1.1
0875: */
0876: public void setApplicationData(Object a_newData) {
0877: m_applicationData = a_newData;
0878: }
0879:
0880: /**
0881: * Retrieves the application-specific data that is attached to this
0882: * Chromosome. Attaching application-specific data may be useful for
0883: * some applications when it comes time to evaluate this Chromosome
0884: * in the fitness function. JGAP ignores this data functionally.
0885: *
0886: * @return the application-specific data previously attached to this
0887: * Chromosome, or null if there is no data attached
0888: *
0889: * @author Neil Rotstan
0890: * @since 1.1
0891: */
0892: public Object getApplicationData() {
0893: return m_applicationData;
0894: }
0895:
0896: /**
0897: * Sets the genes for the chromosome.
0898: * @param a_genes the genes to set for the chromosome
0899: *
0900: * @throws InvalidConfigurationException in case constraint checker is
0901: * provided
0902: *
0903: * @author Klaus Meffert
0904: */
0905: public void setGenes(Gene[] a_genes)
0906: throws InvalidConfigurationException {
0907: super .setGenes(a_genes);
0908: verify(getConstraintChecker());
0909: }
0910:
0911: /**
0912: * Should we also consider the application data when comparing? Default is
0913: * "false" as "true" means a Chromosome is losing its identity when
0914: * application data is set differently!
0915: *
0916: * @param a_doCompare true: consider application data in method compareTo
0917: *
0918: * @author Klaus Meffert
0919: * @since 2.2
0920: */
0921: public void setCompareApplicationData(boolean a_doCompare) {
0922: m_compareAppData = a_doCompare;
0923: }
0924:
0925: /*
0926: * @return should we also consider the application data when comparing?
0927: *
0928: * @author Klaus Meffert
0929: * @since 2.2
0930: */
0931: public boolean isCompareApplicationData() {
0932: return m_compareAppData;
0933: }
0934:
0935: /**
0936: * Sets the constraint checker to be used for this gene whenever method
0937: * setAllele(Object) is called.
0938: *
0939: * @param a_constraintChecker the constraint checker to be set
0940: * @throws InvalidConfigurationException
0941: *
0942: * @author Klaus Meffert
0943: * @since 2.5
0944: */
0945: public void setConstraintChecker(
0946: IGeneConstraintChecker a_constraintChecker)
0947: throws InvalidConfigurationException {
0948: verify(a_constraintChecker);
0949: m_geneAlleleChecker = a_constraintChecker;
0950: }
0951:
0952: /**
0953: * @return IGeneConstraintChecker the constraint checker to be used whenever
0954: * method setGenes(Gene[]) is called.
0955: *
0956: * @author Klaus Meffert
0957: * @since 2.5
0958: */
0959: public IGeneConstraintChecker getConstraintChecker() {
0960: return m_geneAlleleChecker;
0961: }
0962:
0963: /**
0964: * Verifies the state of the chromosome. Especially takes care of the
0965: * given constraint checker.
0966: * @param a_constraintChecker the constraint checker to verify
0967: *
0968: * @throws InvalidConfigurationException
0969: *
0970: * @author Klaus Meffert
0971: * @since 2.5
0972: */
0973: protected void verify(IGeneConstraintChecker a_constraintChecker)
0974: throws InvalidConfigurationException {
0975: if (a_constraintChecker != null && getGenes() != null) {
0976: int len = getGenes().length;
0977: for (int i = 0; i < len; i++) {
0978: Gene gene = getGene(i);
0979: if (!a_constraintChecker.verify(gene, null, this , i)) {
0980: throw new InvalidConfigurationException(
0981: "The gene type "
0982: + gene.getClass().getName()
0983: + " is not allowed to be used in the chromosome due to the"
0984: + " constraint checker used.");
0985: }
0986: }
0987: }
0988: }
0989:
0990: // ------------------------------------
0991: // Begin of IInitializer implementation
0992: // ------------------------------------
0993:
0994: /**{@inheritDoc}*/
0995: public boolean isHandlerFor(Object a_obj, Class a_class) {
0996: if (a_class == Chromosome.class) {
0997: return true;
0998: } else {
0999: return false;
1000: }
1001: }
1002:
1003: /**{@inheritDoc}*/
1004: public Object perform(Object a_obj, Class a_class, Object a_params)
1005: throws Exception {
1006: return randomInitialChromosome(getConfiguration());
1007: }
1008:
1009: // ----------------------------------
1010: // End of IInitializer implementation
1011: // ----------------------------------
1012: public void setMultiObjectives(List a_values) {
1013: if (m_multiObjective == null) {
1014: m_multiObjective = new Vector();
1015: }
1016: m_multiObjective.clear();
1017: m_multiObjective.addAll(a_values);
1018: }
1019:
1020: public List getMultiObjectives() {
1021: return m_multiObjective;
1022: }
1023:
1024: }
|