/* * 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.gp.impl; import java.io.*; import java.util.*; import org.jgap.*; import org.jgap.gp.*; import org.jgap.gp.function.*; import org.jgap.util.*; import org.jgap.gp.terminal.Argument; /** * A GP program contains 1..n ProgramChromosome's. * * @author Klaus Meffert * @since 3.0 */ public class GPProgram extends GPProgramBase implements Serializable, Comparable, ICloneable, IBusinessKey { /** String containing the CVS revision. Read out via reflection!*/ private final static String CVS_REVISION = "$Revision: 1.23 $"; final static String PROGRAMCHROM_DELIMITER_HEADING = "<"; final static String PROGRAMCHROM_DELIMITER_CLOSING = ">"; final static String PROGRAMCHROM_DELIMITER = "#"; /** * Holds the chromosomes contained in this program. */ private ProgramChromosome[] m_chromosomes; /** * Default constructor, only for dynamic instantiation. * * @throws Exception * * @author Klaus Meffert * @since 3.3.4 */ public GPProgram() throws Exception { } /** * Master constructor. * * @param a_conf the configuration to use * @param a_types the type of each chromosome, the length is the number of * chromosomes * @param a_argTypes the types of the arguments to each chromosome, must be an * array of arrays, the first dimension of which is the number of chromosomes * and the second dimension of which is the number of arguments to the * chromosome * @param a_nodeSets the nodes which are allowed to be used by each chromosome, * must be an array of arrays, the first dimension of which is the number of * chromosomes and the second dimension of which is the number of nodes * @param a_minDepths contains the minimum depth allowed for each chromosome * @param a_maxDepths contains the maximum depth allowed for each chromosome * @param a_maxNodes reserve space for a_maxNodes number of nodes * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 3.0 */ public GPProgram(GPConfiguration a_conf, Class[] a_types, Class[][] a_argTypes, CommandGene[][] a_nodeSets, int[] a_minDepths, int[] a_maxDepths, int a_maxNodes) throws InvalidConfigurationException { super(a_conf); m_chromosomes = new ProgramChromosome[a_types.length]; setTypes(a_types); setArgTypes(a_argTypes); setNodeSets(a_nodeSets); setMaxDepths(a_maxDepths); setMinDepths(a_minDepths); setMaxNodes(a_maxNodes); } /** * Constructor to initialize a GPProgram with values of another GPProgram. * * @param a_prog the GPProgram to read the initialization values from * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 3.0 */ public GPProgram(IGPProgram a_prog) throws InvalidConfigurationException { super(a_prog); m_chromosomes = new ProgramChromosome[getTypes().length]; } /** * Sort of minimalistic constructor. Use only if you are aware of what you do. * * @param a_conf the configuration to use * @param a_numChromosomes the number of chromosomes to use with this program. * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 3.0 */ public GPProgram(GPConfiguration a_conf, int a_numChromosomes) throws InvalidConfigurationException { super(a_conf); m_chromosomes = new ProgramChromosome[a_numChromosomes]; } /** * @param a_index the chromosome to get * @return the ProgramChromosome with the given index * * @author Klaus Meffert * @since 3.0 */ public ProgramChromosome getChromosome(int a_index) { IGPProgram ind = m_chromosomes[a_index].getIndividual(); if (this != ind) { m_chromosomes[a_index].setIndividual(this); } return m_chromosomes[a_index]; } /** * Sets the given chromosome at the given index. * * @param a_index sic * @param a_chrom sic * * @author Klaus Meffert * @since 3.0 */ public void setChromosome(int a_index, ProgramChromosome a_chrom) { m_chromosomes[a_index] = a_chrom; } /** * Initialize the chromosomes within this GP program using the grow or the * full method. * * @param a_depth the maximum depth of the chromosome to create * @param a_grow true: use grow method; false: use full method * @param a_maxNodes maximum number of nodes allowed * @param a_fullModeAllowed for each chromosome: true means full mode allowed, * otherwise use grow mode * @param a_tries maximum number of tries to create a valid program * * @author Klaus Meffert * @since 3.0 */ public void growOrFull(int a_depth, boolean a_grow, int a_maxNodes, boolean[] a_fullModeAllowed, int a_tries) { GPConfiguration conf = getGPConfiguration(); // The number of chromosomes to create. // ------------------------------------ int size = m_chromosomes.length; for (int i = 0; i < size; i++) { try { // Construct a chromosome with place for a_maxNodes nodes. // ------------------------------------------------------- m_chromosomes[i] = new ProgramChromosome(conf, a_maxNodes, this); } catch (InvalidConfigurationException iex) { throw new RuntimeException(iex); } m_chromosomes[i].setArgTypes(getArgTypes()[i]); // If there are ADF's in the nodeSet, then set their type according to // the chromosome it references. // ------------------------------------------------------------------- int len = getNodeSets()[i].length; for (int j = 0; j < len; j++) { if (getNodeSets()[i][j] instanceof ADF) { ( (ADF) getNodeSets()[i][j]).setReturnType( getTypes()[ ( (ADF) getNodeSets()[i][j]).getChromosomeNum()]); } } } int depth; for (int i = 0; i < size; i++) { // Restrict depth to input params. // ------------------------------- if (getMaxDepths() != null && a_depth > getMaxDepths()[i]) { depth = getMaxDepths()[i]; } else { if (getMinDepths() != null && a_depth < getMinDepths()[i]) { depth = getMinDepths()[i]; } else { depth = a_depth; } } // Decide whether to use grow mode or full mode. // Here, the program is finally created. // --------------------------------------------- if (a_grow || !a_fullModeAllowed[i]) { m_chromosomes[i].growOrFull(i, depth, getType(i), getArgType(i), getNodeSet(i), true, a_tries); } else { m_chromosomes[i].growOrFull(i, depth, getType(i), getArgType(i), getNodeSet(i), false, a_tries); } } if (getGPConfiguration().isUseProgramCache()) { // Cache fitness value by checking if a program with same // representation was computed before. // ------------------------------------------------------ GPProgramInfo pcInfo = getGPConfiguration().readProgramCache(this); if (pcInfo == null) { pcInfo = putToCache(this); } else { setFitnessValue(pcInfo.getFitnessValue()); } } } /** * Put program to cache. * * @param a_program the program to put into the cache * @return GPProgramInfo info about the program * * @author Klaus Meffert * @since 3.4 */ protected GPProgramInfo putToCache(GPProgram a_program) { return getGPConfiguration().putToProgramCache(a_program); } /** * Initialize this program by using given chromosomes. * * @param a_argTypes the types of the arguments to each chromosome, must be an * array of arrays, the first dimension of which is the number of chromosomes * and the second dimension of which is the number of arguments to the * chromosome * @param a_nodeSets the nodes which are allowed to be used by each chromosome, * * @author Klaus Meffert * @since 3.2.2 */ public void growOrFull(Class[][] a_argTypes, CommandGene[][] a_nodeSets) { int size = m_chromosomes.length; for (int i = 0; i < size; i++) { m_chromosomes[i].setArgTypes(a_argTypes[i]); // If there are ADF's in the nodeSet, then set their type according to // the chromosome it references. // ------------------------------------------------------------------- int len = getNodeSets()[i].length; for (int j = 0; j < len; j++) { if (getNodeSets()[i][j] instanceof ADF) { ( (ADF) getNodeSets()[i][j]).setReturnType( getTypes()[ ( (ADF) getNodeSets()[i][j]).getChromosomeNum()]); } } } for (int i = 0; i < size; i++) { ProgramChromosome chrom = m_chromosomes[i]; chrom.setFunctionSet(a_nodeSets[i]); CommandGene[] functionSet = chrom.getFunctionSet(); CommandGene[] newFktSet = new CommandGene[functionSet.length + a_argTypes[i].length]; System.arraycopy(functionSet, 0, newFktSet, 0, functionSet.length); for (int ii = 0; ii < a_argTypes[i].length; ii++) { try { functionSet[a_nodeSets[i].length + ii] = new Argument(getGPConfiguration(), ii, a_argTypes[i][ii]); } catch (InvalidConfigurationException iex) { throw new RuntimeException(iex); } } chrom.redepth(); } if (getGPConfiguration().isUseProgramCache()) { // Cache fitness value by checking if a program with same // representation was computed before. GPProgramInfo pcInfo = getGPConfiguration().readProgramCache(this); if (pcInfo == null) { pcInfo = putToCache(this); } else { setFitnessValue(pcInfo.getFitnessValue()); } } } /** * @return the number of chromosomes in the program * * @author Klaus Meffert * @since 3.0 */ public int size() { return m_chromosomes.length; } /** * Builds a string that represents the output of the GPProgram in * left-hand-notion. * * @param a_startNode the node to start with * @return output in left-hand notion * * @author Klaus Meffert * @since 3.0 */ public String toString(int a_startNode) { if (a_startNode < 0) { return ""; } StringBuffer sb = new StringBuffer(); for (int i = 0; i < m_chromosomes.length; i++) { if (i > 0) { sb.append(" ==> "); } sb.append(m_chromosomes[i].toString(a_startNode)); } return sb.toString(); } /** * Builds a string that represents the normalized output of the GPProgram. * * @param a_startNode the node to start with * @return output in normalized notion * * @author Klaus Meffert * @since 3.0 */ public String toStringNorm(int a_startNode) { if (a_startNode < 0) { return ""; } StringBuffer sb = new StringBuffer(); for (int i = 0; i < m_chromosomes.length; i++) { if (i > 0) { sb.append(" ==> "); } m_chromosomes[i].setIndividual(this); sb.append(m_chromosomes[i].toStringNorm(a_startNode)); } return sb.toString(); } /** * Builds a string that represents the debug output of the GPProgram. * * @return class names of all program chromosomes * * @author Klaus Meffert * @since 3.0 */ public String toStringDebug() { StringBuffer sb = new StringBuffer(); for (int i = 0; i < m_chromosomes.length; i++) { if (i > 0) { sb.append(" ==> "); } m_chromosomes[i].setIndividual(this); sb.append(m_chromosomes[i].toStringDebug()); } return sb.toString(); } /** * Executes the given chromosome as an integer function. * * @param a_chromosomeNum the index of the chromosome to execute * @param a_args the arguments to use * @return the integer return value * * @author Klaus Meffert * @since 3.0 */ public int execute_int(int a_chromosomeNum, Object[] a_args) { m_chromosomes[a_chromosomeNum].setIndividual(this); return m_chromosomes[a_chromosomeNum].execute_int(a_args); } /** * Executes the given chromosome as a float function. * * @param a_chromosomeNum the index of the chromosome to execute * @param a_args the arguments to use * @return the floar return value * * @author Klaus Meffert * @since 3.0 */ public float execute_float(int a_chromosomeNum, Object[] a_args) { m_chromosomes[a_chromosomeNum].setIndividual(this); return m_chromosomes[a_chromosomeNum].execute_float(a_args); } /** * Executes the given chromosome as a double function. * * @param a_chromosomeNum the index of the chromosome to execute * @param a_args the arguments to use * @return the double return value * * @author Klaus Meffert * @since 3.0 */ public double execute_double(int a_chromosomeNum, Object[] a_args) { m_chromosomes[a_chromosomeNum].setIndividual(this); return m_chromosomes[a_chromosomeNum].execute_double(a_args); } /** * Executes the given chromosome as a boolean function. * * @param a_chromosomeNum the index of the chromosome to execute * @param a_args the arguments to use * @return the boolean return value * * @author Klaus Meffert * @since 3.2 */ public boolean execute_boolean(int a_chromosomeNum, Object[] a_args) { m_chromosomes[a_chromosomeNum].setIndividual(this); return m_chromosomes[a_chromosomeNum].execute_boolean(a_args); } /** * Executes the given chromosome as an object function. * * @param a_chromosomeNum the index of the chromosome to execute * @param a_args the arguments to use * @return the object return value * * @author Klaus Meffert * @since 3.0 */ public Object execute_object(int a_chromosomeNum, Object[] a_args) { m_chromosomes[a_chromosomeNum].setIndividual(this); return m_chromosomes[a_chromosomeNum].execute_object(a_args); } /** * Executes the given chromosome as an object function. * * @param a_chromosomeNum the index of the chromosome to execute * @param a_args the arguments to use * * @author Klaus Meffert * @since 3.0 */ public void execute_void(int a_chromosomeNum, Object[] a_args) { m_chromosomes[a_chromosomeNum].setIndividual(this); m_chromosomes[a_chromosomeNum].execute_void(a_args); } /** * Searches for a chromosome that has the given class and returns its index. * * @param a_chromosomeNum the index of the chromosome to start the search with * @param a_class the class to find * @return the index of the first chromosome found that is of a_class, or -1 * * @author Klaus Meffert * @since 3.0 */ public int getCommandOfClass(int a_chromosomeNum, Class a_class) { for (int i = a_chromosomeNum; i < m_chromosomes.length; i++) { int j = m_chromosomes[i].getCommandOfClass(0, a_class); if (j >= 0) { return j; } } return -1; } /** * Compares the given program to this program. * * @param a_other the program against which to compare this program * @return a negative number if this program is "less than" the given * program, zero if they are equal to each other, and a positive number if * this program is "greater than" the given program * * @author Klaus Meffert * @since 3.0 */ public int compareTo(Object a_other) { // First, if the other Chromosome is null, then this chromosome is // automatically the "greater" Chromosome. // --------------------------------------------------------------- if (a_other == null) { return 1; } int size = size(); GPProgram other = (GPProgram) a_other; ProgramChromosome[] otherChroms = other.m_chromosomes; // If the other Chromosome doesn't have the same number of genes, // then whichever has more is the "greater" Chromosome. // -------------------------------------------------------------- if (other.size() != size) { return size() - other.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. // --------------------------------------------------------------- Arrays.sort(m_chromosomes); Arrays.sort(otherChroms); for (int i = 0; i < size; i++) { int comparison = m_chromosomes[i].compareTo(otherChroms[i]); if (comparison != 0) { return comparison; } } // Everything is equal. Return zero. // --------------------------------- return 0; } /** * @return deep clone of the object instance * * @author Klaus Meffert * @since 3.2 */ public Object clone() { try { // Min and max depths can be null. // ------------------------------- int[] minDepthsClone; if (getMinDepths() != null) { minDepthsClone = (int[]) getMinDepths().clone(); } else { minDepthsClone = null; } int[] maxDepthsClone; if (getMaxDepths() != null) { maxDepthsClone = (int[]) getMaxDepths().clone(); } else { maxDepthsClone = null; } GPProgram result = new GPProgram(getGPConfiguration(), (Class[]) getTypes().clone(), (Class[][]) getArgTypes().clone(), (CommandGene[][]) getNodeSets().clone(), minDepthsClone, maxDepthsClone, getMaxNodes()); result.setFitnessValue(getFitnessValueDirectly()); // Try to clone application data. // ------------------------------ Object appData = getApplicationData(); if (appData != null) { ICloneHandler cloner = getGPConfiguration().getJGAPFactory(). getCloneHandlerFor(appData, null); if (cloner != null) { result.setApplicationData(cloner.perform(appData, null, null)); } else { result.setApplicationData(appData); } } for (int i = 0; i < m_chromosomes.length; i++) { if (m_chromosomes[i] == null) { break; } result.m_chromosomes[i] = (ProgramChromosome) m_chromosomes[i].clone(); } return result; } catch (Exception ex) { throw new CloneException(ex); } } /** * @return the persistent representation of the GP program, including all * chromosomes * * @author Klaus Meffert * @since 3.3 */ public String getPersistentRepresentation() { StringBuffer b = new StringBuffer(); for (ProgramChromosome chrom : m_chromosomes) { b.append(PROGRAMCHROM_DELIMITER_HEADING); b.append(encode( chrom.getClass().getName() + PROGRAMCHROM_DELIMITER + chrom.getPersistentRepresentation())); b.append(PROGRAMCHROM_DELIMITER_CLOSING); } return b.toString(); } protected String encode(String a_string) { return StringKit.encode(a_string); } protected String decode(String a_string) { return StringKit.decode(a_string); } /** * @return hopefully unique key representing the state of the GPProgram * * @author Klaus Meffert * @since 3.4 */ public String getBusinessKey() { return toStringNorm(0); } }