/* * 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; import java.io.*; import java.lang.reflect.*; import java.util.*; import org.jgap.impl.*; import org.jgap.util.*; /** * Base class for any implementation of interface IChromosome. * * @author Klaus Meffert * @since 3.0 */ public abstract class BaseChromosome implements IChromosome, IInitializer, IPersistentRepresentation, IBusinessKey { /** String containing the CVS revision. Read out via reflection!*/ private final static String CVS_REVISION = "$Revision: 1.15 $"; /** * This field separates gene class name from the gene persistent representation * string. '*' does not work properly with URLEncoder! */ public final static String GENE_DELIMITER = "#"; /** * Represents the heading delimiter that is used to separate genes in the * persistent representation of Chromosome instances. */ public final static String GENE_DELIMITER_HEADING = "<"; /** * Represents the closing delimiter that is used to separate genes in the * persistent representation of Chromosome instances. */ public final static String GENE_DELIMITER_CLOSING = ">"; /** * Separates chromosome-related information. */ public final static String CHROM_DELIMITER = "#"; /** * The configuration object to use */ private Configuration m_configuration; /** * The array of Genes contained in this Chromosome. */ private Gene[] m_genes; private int m_age; private int m_operatedOn; /** * Unique ID of the chromosome that allows to distinct it from other * chromosomes. In the best case, this ID is unique worldwide. */ private String m_uniqueID; /** * In case mutation, crossing over etc. happened, this sequence gives evidence * about the parent(s) of the current chromosome. */ private Map<Integer,String> m_uniqueIDTemplates; /** * The only constructor in this class. Sets the immutable configuration. * * @param a_configuration the configuration to set * @throws InvalidConfigurationException if configuration is null * * @author Klaus Meffert * @since 3.0 */ public BaseChromosome(Configuration a_configuration) throws InvalidConfigurationException { if (a_configuration == null) { throw new InvalidConfigurationException( "Configuration to be set must not" + " be null!"); } m_configuration = a_configuration; if (m_configuration.isUniqueKeysActive()) { m_uniqueIDTemplates = new HashMap(); IJGAPFactory factory = m_configuration.getJGAPFactory(); if (JGAPFactory.class.isAssignableFrom(factory.getClass())) { m_uniqueID = ( (JGAPFactory) (m_configuration.getJGAPFactory())). getUniqueKey(getClass().getName()); } } } /** * @return unique ID of the chromosome, which allows to distinct this instance * from others, in the best case worldwide * * @author Klaus Meffert * @since 3.5 */ public String getUniqueID() { return m_uniqueID; } /** * A template is a chromosome that is the logical predecessor of the current * chromosome. A template can occur in mutation or crossing over. In the * latter case can be at least two template chromosomes. This is why in this * setter method the parameter a_index exists. * * @param a_templateID the unique ID of the template * @param a_index the index of the template, e.g. in crossing over for the * second candidate chromosome this is 2 * * @author Klaus Meffert * @since 3.5 */ public void setUniqueIDTemplate(String a_templateID, int a_index) { m_uniqueIDTemplates.put(a_index, a_templateID); } /** * @param a_index the index of the template to retrieve the key for * @return String * * @author Klaus Meffert * @since 3.5 */ public String getUniqueIDTemplate(int a_index) { return m_uniqueIDTemplates.get(a_index); } /** * @return the configuration used * * @author Klaus Meffert * @since 3.0 */ public Configuration getConfiguration() { return m_configuration; } /** * Creates and returns a copy of this object. * * @return a clone of this instance * @throws IllegalStateException instead of CloneNotSupportedException * * @author Klaus Meffert * @since 3.0 */ public abstract Object clone(); /** * Increases the number of evolutionary rounds of chromosome in which it has * not been changed by one. * * @author Klaus Meffert * @since 3.2 */ public void increaseAge() { m_age++; } /** * Reset age of chromosome because it has been changed. * * @author Klaus Meffert * @since 3.2 */ public void resetAge() { m_age = 0; } /** * @return 0: Chromosome newly created in this generation. This means it * does not need being crossed over with another newly created one * * @author Klaus Meffert * @since 3.2 */ public int getAge() { return m_age; } /** * @param a_age set the age of the chromosome, see BestChromosomesSelector * for a use-case * * @author Klaus Meffert * @since 3.3.3 */ public void setAge(int a_age) { m_age = a_age; } /** * Increase information of number of genetic operations performed on * chromosome in current evolution round. * * @author Klaus Meffert * @since 3.2 */ public void increaseOperatedOn() { m_operatedOn++; } /** * Resets the information of how many genetic operators have been performed * on the chromosome in the current round of evolution. * * @author Klaus Meffert * @since 3.2 * */ public void resetOperatedOn() { m_operatedOn = 0; } /** * @return number of genetic operations performed on chromosome in current * evolution round * * @author Klaus Meffert * @since 3.2 */ public int operatedOn() { return m_operatedOn; } /** * Retrieves the set of genes that make up this Chromosome. This method * exists primarily for the benefit of GeneticOperators that require the * ability to manipulate Chromosomes at a low level. * * @return an array of the Genes contained within this Chromosome * * @author Neil Rotstan * @since 1.0 */ public synchronized Gene[] getGenes() { return m_genes; } /** * 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 * @since 3.2 (previously in class Chromosome) */ public void setGenes(Gene[] a_genes) throws InvalidConfigurationException { m_genes = a_genes; } /** * Returns the Gene at the given index (locus) within the Chromosome. The * first gene is at index zero and the last gene is at the index equal to * the size of this Chromosome - 1. * * @param a_desiredLocus index of the gene value to be returned * @return Gene at the given index * * @author Neil Rotstan * @since 1.0 */ public synchronized Gene getGene(int a_desiredLocus) { return m_genes[a_desiredLocus]; } public void setGene(int a_index, Gene a_gene) { m_genes[a_index] = a_gene; } /** * Returns the size of this Chromosome (the number of genes it contains). * A Chromosome's size is constant and will not change, until setGenes(...) * is used. * * @return number of genes contained within this Chromosome instance * * @author Neil Rotstan * @author Klaus Meffert * @since 1.0 */ public int size() { if (m_genes == null) { // only possible when using default constructor return 0; } else { return m_genes.length; } } /** * Returns a persistent representation of this chromosome, see interface Gene * for description. Similar to CompositeGene's routine. But does not include * all information of the chromosome (yet). * * @return string representation of this Chromosome's relevant parts of its * current state * @throws UnsupportedOperationException * * @author Klaus Meffert * @since 3.2 */ public String getPersistentRepresentation() { StringBuffer b = new StringBuffer(); // Persist the chromosome's fitness value. // --------------------------------------- b.append(getFitnessValueDirectly()); b.append(CHROM_DELIMITER); // Persist the genes. // ------------------ b.append(size()); b.append(CHROM_DELIMITER); getGenesPersistentRepresentation(b); return b.toString(); } /** * @return the persistent representation of the chromosome by considering * its genes. * * @author Klaus Meffert */ public StringBuffer getGenesPersistentRepresentation() { StringBuffer b = new StringBuffer(); getGenesPersistentRepresentation(b); return b; } /** * @return business key of the chromosome * * @author Klaus Meffert * @since 3.2 */ public String getBusinessKey() { return getGenesPersistentRepresentation().toString(); } /** * Retrieves the persistent representation of the chromosome by considering * its genes. * * @param a_buffer the variable to store the persistent representation in * * @author Klaus Meffert */ public void getGenesPersistentRepresentation(StringBuffer a_buffer) { Gene gene; int size = size(); for (int i = 0; i < size; i++) { gene = getGene(i); a_buffer.append(GENE_DELIMITER_HEADING); a_buffer.append(encode(gene.getClass().getName() + GENE_DELIMITER + gene.getPersistentRepresentation())); a_buffer.append(GENE_DELIMITER_CLOSING); } } protected String encode(String a_string) { return StringKit.encode(a_string); } protected String decode(String a_string) throws UnsupportedEncodingException { return StringKit.decode(a_string); } /** * Counterpart of getPersistentRepresentation. * * @param a_representation the string representation retrieved from a prior * call to the getPersistentRepresentation() method * * @throws UnsupportedRepresentationException * * @author Klaus Meffert * @since 3.2 */ public void setValueFromPersistentRepresentation(String a_representation) throws UnsupportedRepresentationException { if (a_representation != null) { try { List r = split(a_representation); String g; // Obtain fitness value. // --------------------- g = decode( (String) r.get(0)); setFitnessValue(Double.parseDouble(g)); r.remove(0);/**@todo we can do this faster!*/ // Obtain number of genes. // ----------------------- g = decode( (String) r.get(0)); int count = Integer.parseInt(g); setGenes(new Gene[count]); r.remove(0);/**@todo we can do this faster!*/ // Obtain the genes. // ----------------- Iterator iter = r.iterator(); StringTokenizer st; String clas; String representation; Gene gene; int index = 0; while (iter.hasNext()) { g = decode( (String) iter.next()); st = new StringTokenizer(g, GENE_DELIMITER); if (st.countTokens() != 2) throw new UnsupportedRepresentationException("In " + g + ", " + "expecting two tokens, separated by " + GENE_DELIMITER); clas = st.nextToken(); representation = st.nextToken(); gene = createGene(clas, representation); setGene(index++, gene); } } catch (Exception ex) { throw new UnsupportedRepresentationException(ex.toString()); } } } /** * Creates a new Gene instance.<p> * Taken from CompositeGene. * * @param a_geneClassName name of the gene class * @param a_persistentRepresentation persistent representation of the gene to * create (could be obtained via getPersistentRepresentation) * * @return newly created gene * @throws Exception * * @author Klaus Meffert * @since 3.2 */ protected Gene createGene(String a_geneClassName, String a_persistentRepresentation) throws Exception { Class geneClass = Class.forName(a_geneClassName); Constructor constr = geneClass.getConstructor(new Class[] {Configuration.class}); Gene gene = (Gene) constr.newInstance(new Object[] {getConfiguration()}); gene.setValueFromPersistentRepresentation(a_persistentRepresentation); return gene; } /** * Splits the input a_string into individual gene representations.<p> * Taken and adapted from CompositeGene. * * @param a_string the string to split * @return the elements of the returned array are the persistent * representation strings of the chromosome's components * @throws UnsupportedRepresentationException * * @author Klaus Meffert * @since 3.2 */ protected static final List split(String a_string) throws UnsupportedRepresentationException { List a = Collections.synchronizedList(new ArrayList()); // Header data. // ------------ int index = 0; StringTokenizer st0 = new StringTokenizer (a_string, CHROM_DELIMITER, false); if (!st0.hasMoreTokens()) { throw new UnsupportedRepresentationException("Fitness value expected!"); } String fitnessS = st0.nextToken(); a.add(fitnessS); index += fitnessS.length(); if (!st0.hasMoreTokens()) { throw new UnsupportedRepresentationException("Number of genes expected!"); } String numGenes = st0.nextToken(); a.add(numGenes); index += numGenes.length(); index += 2; //2 one-character delimiters if (!st0.hasMoreTokens()) { throw new UnsupportedRepresentationException("Gene data missing!"); } // Remove previously parsed content. // --------------------------------- a_string = a_string.substring(index); // Gene data. // ---------- StringTokenizer st = new StringTokenizer (a_string, GENE_DELIMITER_HEADING + GENE_DELIMITER_CLOSING, true); while (st.hasMoreTokens()) { if (!st.nextToken().equals(GENE_DELIMITER_HEADING)) { throw new UnsupportedRepresentationException(a_string + " no open tag"); } String n = st.nextToken(); if (n.equals(GENE_DELIMITER_CLOSING)) { a.add(""); /* Empty token */ } else { a.add(n); if (!st.nextToken().equals(GENE_DELIMITER_CLOSING)) { throw new UnsupportedRepresentationException (a_string + " no close tag"); } } } return a; } }