/* * 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.lang.reflect.*; import java.util.*; import org.apache.log4j.*; import org.jgap.*; import org.jgap.gp.*; import org.jgap.gp.terminal.*; import org.jgap.util.*; /** * Chromosome representing a single GP Program. * * @author Klaus Meffert * @since 3.0 */ public class ProgramChromosome extends BaseGPChromosome implements Comparable, Cloneable, IBusinessKey { /** String containing the CVS revision. Read out via reflection!*/ private final static String CVS_REVISION = "$Revision: 1.49 $"; final static String PERSISTENT_FIELD_DELIMITER = ":"; final static String GENE_DELIMITER_HEADING = "<"; final static String GENE_DELIMITER_CLOSING = ">"; final static String GENE_DELIMITER = "#"; private transient static Logger LOGGER = Logger.getLogger(ProgramChromosome.class); /** * The list of allowed functions/terminals. */ private CommandGene[] m_functionSet; /** * Array to hold the depths of each node. */ private int[] m_depth; /** * Array to hold the types of the arguments to this Chromosome. */ private Class[] argTypes; private transient int m_index; private transient int m_maxDepth; /** * The array of genes contained in this chromosome. */ private CommandGene[] m_genes; /** * 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; /** * 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 3.0 */ private boolean m_compareAppData; public ProgramChromosome(GPConfiguration a_conf, int a_size) throws InvalidConfigurationException { super(a_conf); if (a_size <= 0) { throw new IllegalArgumentException( "Chromosome size must be greater than zero"); } init(a_size); } public ProgramChromosome(GPConfiguration a_conf, int a_size, IGPProgram a_ind) throws InvalidConfigurationException { super(a_conf, a_ind); if (a_size <= 0) { throw new IllegalArgumentException( "Chromosome size must be greater than zero"); } if (a_ind == null) { throw new IllegalArgumentException("Individual must not be null"); } init(a_size); } public ProgramChromosome(GPConfiguration a_conf, int a_size, CommandGene[] a_functionSet, Class[] a_argTypes, IGPProgram a_ind) throws InvalidConfigurationException { super(a_conf, a_ind); if (a_size <= 0) { throw new IllegalArgumentException( "Chromosome size must be greater than zero"); } if (a_ind == null) { throw new IllegalArgumentException("Individual must not be null"); } m_functionSet = a_functionSet; argTypes = a_argTypes; init(a_size); } public ProgramChromosome(GPConfiguration a_conf, CommandGene[] a_initialGenes) throws InvalidConfigurationException { super(a_conf); int i = 0; while (i < a_initialGenes.length && a_initialGenes[i] != null) { i++; } init(a_initialGenes.length); for (int k = 0; k < i; k++) { m_genes[k] = a_initialGenes[k]; } } public ProgramChromosome(final GPConfiguration a_conf) throws InvalidConfigurationException { super(a_conf); init(); } /** * Default constructor. Only use for dynamic instantiation. * * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 3.0 */ public ProgramChromosome() throws InvalidConfigurationException { this(GPGenotype.getStaticGPConfiguration()); } private void init() throws InvalidConfigurationException { init(getGPConfiguration().getPopulationSize()); } private void init(final int a_size) throws InvalidConfigurationException { m_depth = new int[a_size]; m_genes = new CommandGene[a_size]; /**@todo is speedup possible by using dynamic list?*/ } public void setArgTypes(Class[] a_argTypes) { argTypes = a_argTypes; } public synchronized Object clone() { try { int size = m_genes.length; CommandGene[] genes = new CommandGene[size]; for (int i = 0; i < size; i++) { if (m_genes[i] == null) { break; } // Try deep clone of genes. // ------------------------ if (ICloneable.class.isAssignableFrom(m_genes[i].getClass())) { genes[i] = (CommandGene) ( (ICloneable) m_genes[i]).clone(); } else { // No deep clone possible. // ----------------------- genes[i] = m_genes[i]; } } ProgramChromosome chrom = new ProgramChromosome( (GPConfiguration) getGPConfiguration(), (CommandGene[]) genes); chrom.argTypes = (Class[]) argTypes.clone(); if (getFunctionSet() != null) { chrom.setFunctionSet( (CommandGene[]) getFunctionSet().clone()); } if (m_depth != null) { chrom.m_depth = (int[]) m_depth.clone(); } chrom.setIndividual(getIndividual()); return chrom; } catch (Exception cex) { // Rethrow to have a more convenient handling. // ------------------------------------------- throw new IllegalStateException(cex); } } /** * Clean up the chromosome. * * @author Klaus Meffert * @since 3.0 */ public void cleanup() { int len = m_genes.length; for (int i = 0; i < len; i++) { if (m_genes[i] == null) { break; } m_genes[i].cleanup(); } } /** * Initialize this chromosome using the grow or the full method. * * @param a_num the chromosome's index in the individual of this chromosome * @param a_depth the maximum depth of the chromosome to create * @param a_type the type of the chromosome to create * @param a_argTypes the array of argument types for this chromosome * @param a_functionSet the set of nodes valid to pick from * @param a_grow true: use grow method; false: use full method * @param a_tries maximum number of tries to create a valid program * * @author Klaus Meffert * @since 3.0 */ public void growOrFull(final int a_num, final int a_depth, final Class a_type, final Class[] a_argTypes, final CommandGene[] a_functionSet, boolean a_grow, int a_tries) { try { argTypes = a_argTypes; setFunctionSet(new CommandGene[a_functionSet.length + a_argTypes.length]); System.arraycopy(a_functionSet, 0, getFunctionSet(), 0, a_functionSet.length); for (int i = 0; i < a_argTypes.length; i++) { m_functionSet[a_functionSet.length + i] = new Argument(getGPConfiguration(), i, a_argTypes[i]); } // Initialization of genotype according to specific problem requirements. // ---------------------------------------------------------------------- CommandGene n; IGPInitStrategy programIniter = getGPConfiguration().getInitStrategy(); if (programIniter == null) { n = null; } else { try { n = programIniter.init(this, a_num); } catch (Exception ex) { throw new IllegalStateException(ex); } } // Build the (rest of the) GP program. // ----------------------------------- int localDepth = a_depth; m_index = 0; m_maxDepth = localDepth; growOrFullNode(a_num, localDepth, a_type, 0, m_functionSet, n, 0, a_grow, -1, false); // Give the chance of validating the whole program. // ------------------------------------------------ if (!getGPConfiguration().validateNode(this, null, n, a_tries, a_num, 0, a_type, m_functionSet, a_depth, a_grow, -1, true)) { throw new IllegalStateException("Randomly created program violates" + " configuration constraints (symptom 3)."); } redepth(); } catch (InvalidConfigurationException iex) { throw new IllegalStateException(iex.getMessage()); } } /** * Output program in left-hand notion (e.g.: "+ X Y" for "X + Y"). * * @param a_startNode node to start with * @return output in left-hand notion * * @author Klaus Meffert * @since 3.0 */ public String toString(final int a_startNode) { if (a_startNode < 0) { return ""; } // Replace any occurance of placeholders (e.g. &1, &2...) in the function's // name. // ------------------------------------------------------------------------ String funcName = m_genes[a_startNode].toString(); int j = 1; do { String placeHolder = "&" + j; int foundIndex = funcName.indexOf(placeHolder); if (foundIndex < 0) { break; } funcName = funcName.replaceFirst(placeHolder, ""); j++; } while (true); // Now remove any leading and trailing spaces. // ------------------------------------------- if (j > 0) { funcName = funcName.trim(); } IGPProgram ind = getIndividual(); if (getFunctions()[a_startNode].getArity(ind) == 0) { return funcName + " "; } String str = ""; str += funcName + " ( "; int arity = m_genes[a_startNode].getArity(ind); for (int i = 0; i < arity; i++) { str += toString(getChild(a_startNode, i)); } if (a_startNode == 0) { str += ")"; } else { str += ") "; } return str; } /** * Output program in "natural" notion (e.g.: "X + Y" for "X + Y"). * * @param a_startNode the node to start with, e.g. 0 for a complete dump of * the program * @return output in normalized notion * * @author Klaus Meffert * @since 3.0 */ public String toStringNorm(final int a_startNode) { if (a_startNode < 0) { return ""; } IGPProgram ind = getIndividual(); if (m_genes[a_startNode].getArity(ind) == 0) { return getFunctions()[a_startNode].toString(); } String str = ""; boolean paramOutput = false; if (m_genes[a_startNode].getArity(ind) > 0) { if (m_genes[a_startNode].toString().indexOf("&1") >= 0) { paramOutput = true; } } if (m_genes[a_startNode].getArity(ind) == 1 || paramOutput) { str += getFunctions()[a_startNode].toString(); } if (a_startNode > 0) { str = "(" + str; } for (int i = 0; i < m_genes[a_startNode].getArity(ind); i++) { String childString = toStringNorm(getChild(a_startNode, i)); String placeHolder = "&" + (i + 1); int placeholderIndex = str.indexOf(placeHolder); if (placeholderIndex >= 0) { str = str.replaceFirst(placeHolder, childString); } else { str += childString; } if (i == 0 && m_genes[a_startNode].getArity(ind) != 1 && !paramOutput) { str += " " + m_genes[a_startNode].toString() + " "; } } if (a_startNode > 0) { str += ")"; } return str; } /** * @return business key of the chromosome * * @author Klaus Meffert * @since 3.4 */ public String getBusinessKey() { return toStringNorm(0); } /** * @return debug representation of progrm chromosome, containing class names * of all children * * @author Klaus Meffert * @since 3.4 */ public String toStringDebug() { IGPProgram ind = getIndividual(); if (m_genes[0].getArity(ind) == 0) { return getClass().getName(); } String s = ""; for (int i = 0; i < m_genes[0].getArity(ind); i++) { String childString = toStringNorm(getChild(0, i)); s = s + "<" + childString + " >"; } return s; } /** * Determines whether there exists a function or terminal in the given node * set with the given return and sub return type. * * @param a_returnType the return type to look for * @param a_subReturnType the sub return type to look for * @param a_nodeSet the array of nodes to look through * @param a_function true to look for a function, false to look for a terminal * @param a_growing true: grow mode, false: full mode * * @return true if such a node exists, false otherwise * * @author Klaus Meffert * @since 3.0 */ public boolean isPossible(Class a_returnType, int a_subReturnType, CommandGene[] a_nodeSet, boolean a_function, boolean a_growing) { IGPProgram ind = getIndividual(); for (int i = 0; i < a_nodeSet.length; i++) { if (a_nodeSet[i].getReturnType() == a_returnType && (a_subReturnType == 0 || a_subReturnType == a_nodeSet[i].getSubReturnType())) { if (a_nodeSet[i].getArity(ind) == 0 && (!a_function || a_growing)) { return true; } if (a_nodeSet[i].getArity(ind) != 0 && a_function) { return true; } } } return false; } /** * Randomly chooses a valid node from the functions set. * * @param a_chromIndex index of the chromosome in the individual (0..n-1) * @param a_returnType the return type of node to choose * @param a_subReturnType the sub return type to look for * @param a_functionSet the functions to use * @param a_function true to choose a function, false to choose a terminal * @param a_growing true to ignore the function parameter, false otherwise * @return the node chosen * * @author Klaus Meffert * @since 3.0 */ protected CommandGene selectNode(int a_chromIndex, Class a_returnType, int a_subReturnType, CommandGene[] a_functionSet, boolean a_function, boolean a_growing) { // Determine possible functions. // ----------------------------- Vector<CommandGene> possibleFunctions=new Vector<CommandGene>(0); IGPProgram ind = getIndividual(); for (int i = 0; i < a_functionSet.length; i++) { if (a_functionSet[i].getReturnType() == a_returnType && (a_subReturnType == 0 || a_subReturnType == a_functionSet[i].getSubReturnType())) { if (a_functionSet[i].getArity(ind) == 0 && (!a_function || a_growing)) { possibleFunctions.add(a_functionSet[i]); } if (a_functionSet[i].getArity(ind) != 0 && a_function) { possibleFunctions.add(a_functionSet[i]); } } } // Error handing in case no valid function found. // ---------------------------------------------- if (possibleFunctions.isEmpty()) { if (a_growing && (a_returnType == CommandGene.VoidClass || a_returnType == Void.class)) { // We simply return a NOP, it does nothing :-) // ------------------------------------------- try { return new NOP(getGPConfiguration(), a_subReturnType); } catch (InvalidConfigurationException iex) { // Should never happen. // -------------------- throw new RuntimeException(iex); } } final String errormsg = "Chromosome (depth " + getDepth(0) + ", index " + a_chromIndex + ") requires a " + (a_function ? ("function" + (a_growing ? " or terminal" : "")) : "terminal") + " of return type " + a_returnType + " (sub return type " + a_subReturnType + ")" + " but there is no such node available"; if (!getGPConfiguration().isStrictProgramCreation()) { // Allow another try in case it is allowed. // ---------------------------------------- throw new IllegalStateException(errormsg); } else { // Interrupt the whole evolution process. // -------------------------------------- throw new RuntimeException(errormsg); } } // Select a function randomly. // --------------------------- int index = getGPConfiguration().getRandomGenerator().nextInt( possibleFunctions.size()); CommandGene n = possibleFunctions.elementAt(index); return n; } /** * Create a tree of nodes using the grow or the full method. * * @param a_num the chromosome's index in the individual of this chromosome * @param a_depth the maximum depth of the tree to create * @param a_returnType the return type the lastly evaluated node must have * @param a_subReturnType the sub return type to look for * @param a_functionSet the set of function valid to pick from * @param a_rootNode null, or parent node of the node to develop * @param a_recurseLevel 0 for first call * @param a_grow true: use grow method; false: use full method * @param a_childNum index of the child in the parent node to which it belongs * (-1 if node is root node) * @param a_validateNode true: check if node selected is valid (when called * recursively a_validateNode is set to true) * * @return possible modified set of functions (e.g. to avoid having a unique * command more than once) * * @author Klaus Meffert * @since 3.0 */ protected CommandGene[] growOrFullNode(int a_num, int a_depth, Class a_returnType, int a_subReturnType, CommandGene[] a_functionSet, CommandGene a_rootNode, int a_recurseLevel, boolean a_grow, int a_childNum, boolean a_validateNode) { boolean mutated = false; boolean uncloned = true; GPConfiguration conf = getGPConfiguration(); RandomGenerator random = conf.getRandomGenerator(); if (a_rootNode == null || a_validateNode) { int tries = 0; int evolutionRound = getGPConfiguration().getGenerationNr(); boolean aFunction = a_depth >= 1; // Clone the array, not the content of the array. // ---------------------------------------------- CommandGene[] localFunctionSet = (CommandGene[]) a_functionSet.clone(); int len = a_functionSet.length; do { CommandGene node = selectNode(a_num, a_returnType, a_subReturnType, localFunctionSet, aFunction, a_grow); if (!conf.validateNode(this, node, a_rootNode, tries++, a_num, a_recurseLevel, a_returnType, localFunctionSet, a_depth, a_grow, a_childNum, false)) { // In the first round of evolution ensure to always have one valid // individual as we need a prototype for cloning! // --------------------------------------------------------------- if (evolutionRound > 0 || tries <= len*2) { // Remove invalid node from local function set. // -------------------------------------------- localFunctionSet = remove(localFunctionSet, node); if (localFunctionSet.length == 0) { throw new IllegalStateException("No appropriate function found" + " during program creation!"); } continue; } } // Optionally use a mutant/clone of the originally selected command // instead of reusing the same command instance. // ---------------------------------------------------------------- if (random.nextDouble() <= conf.getMutationProb()) { if (IMutateable.class.isAssignableFrom(node.getClass())) { try { CommandGene node2 = ( (IMutateable) node).applyMutation(0, random.nextDouble()); // Check if mutant's function is allowed. // -------------------------------------- if (getCommandOfClass(0, node2.getClass()) >= 0) { mutated = true; if (node2 != node) { node = node2; uncloned = false; } } } catch (InvalidConfigurationException iex) { // Ignore but log. // --------------- LOGGER.warn("Ignored problem", iex); } } } // Avoid using commands more than once if allowed only once. // --------------------------------------------------------- if (IUniqueCommand.class.isAssignableFrom(node.getClass())) { a_functionSet = remove(a_functionSet, node); } a_rootNode = node; break; } while (true); } // Generate the new node. // ---------------------- m_depth[m_index] = m_maxDepth - a_depth; // Optional dynamize the arity for commands with a flexible number // of children. Normally, dynamizeArity does nothing, see declaration // of method in CommandGene, which can be overridden in sub classes. // ------------------------------------------------------------------- boolean dynamize = random.nextDouble() <= conf.getDynamizeArityProb(); if (dynamize) { a_rootNode.dynamizeArity(); } // Clone node if possible and not already done via mutation. // --------------------------------------------------------- if (uncloned && !conf.isNoCommandGeneCloning() && a_rootNode instanceof ICloneable) { /**@todo we could optionally use the clone handler*/ a_rootNode = (CommandGene) ( (ICloneable) a_rootNode).clone(); m_genes[m_index++] = a_rootNode; } else { m_genes[m_index++] = a_rootNode; } if (a_depth >= 1) { IGPProgram ind = getIndividual(); int arity = a_rootNode.getArity(ind); for (int i = 0; i < arity; i++) { // Ensure required depth is cared about. // ------------------------------------- if (m_index < m_depth.length) { a_functionSet = growOrFullNode(a_num, a_depth - 1, a_rootNode.getChildType(getIndividual(), i), a_rootNode.getSubChildType(i), a_functionSet, a_rootNode, a_recurseLevel + 1, a_grow, i, true); } else { // No valid program could be generated. Abort. // ------------------------------------------- throw new IllegalStateException("Randomly created program violates" + " configuration constraints (symptom 1). It may be that you" + " specified a too small number of maxNodes to use" + " (current arity: " + i + ", overall arity: " + arity + ")!"); } } } else { if (a_rootNode.getArity(getIndividual()) > 0) { // No valid program could be generated. Abort. // ------------------------------------------- throw new IllegalStateException("Randomly created program violates" + " configuration constraints" + " (symptom 2)"); } } return a_functionSet; } /** * Recalculate the depth of each node. * * @author Klaus Meffert * @since 3.0 */ public void redepth() { m_depth[0] = 0; redepth(0); } /** * Calculate the depth of the next node and the indices of the children * of the current node. * The depth of the next node is just one plus the depth of the current node. * The index of the first child is always the next node. The index of the * second child is found by recursively calling this method on the tree * starting with the first child. * * @param a_index the index of the reference depth * @return the index of the next node of the same depth as the current node * (i.e. the next sibling node) * * @author Klaus Meffert * @since 3.0 */ protected int redepth(int a_index) { int num = a_index + 1; CommandGene command = getNode(a_index); if (command == null) { throw new IllegalStateException("ProgramChromosome invalid at index " + a_index + " (command gene is null)"); } IGPProgram ind = getIndividual(); int arity = command.getArity(ind); for (int i = 0; i < arity; i++) { if (num < m_depth.length) { m_depth[num] = m_depth[a_index] + 1; // children[i][n] = num; num = redepth(num); if (num < 0) { break; } } else { return -1; } } return num; } /** * Gets the a_child'th child of the a_index'th node in this chromosome. This * is the same as the a_child'th node whose depth is one more than the depth * of the a_index'th node. * * @param a_index the node number of the parent * @param a_child the child number (starting from 0) of the parent * @return the node number of the child, or -1 if not found * * @author Klaus Meffert * @since 3.01 */ public int getChild(int a_index, int a_child) { /**@todo speedup*/ int len = getFunctions().length; for (int i = a_index + 1; i < len; i++) { if (m_depth[i] <= m_depth[a_index]) { return -1; } if (m_depth[i] == m_depth[a_index] + 1) { if (--a_child < 0) { return i; } } } throw new RuntimeException("Bad child " + a_child + " of node with index = " + a_index); } public int getChild(CommandGene a_node, int a_child) { /**@todo speedup*/ int len = getFunctions().length; int index = -1; for (int i = 0; i < len; i++) { if (m_genes[i] == a_node) { index = i; break; } } if (index == -1) { return -2; } for (int i = index + 1; i < len; i++) { if (m_depth[i] <= m_depth[index]) { return -1; } if (m_depth[i] == m_depth[index] + 1) { if (--a_child < 0) { return i; } } } throw new RuntimeException("Bad child " + a_child + " of node with index = " + index); } public CommandGene[] getFunctionSet() { return m_functionSet; } public void setFunctionSet(CommandGene[] a_functionSet) { m_functionSet = a_functionSet; } public CommandGene[] getFunctions() { return m_genes; } public void setFunctions(CommandGene[] a_functions) throws InvalidConfigurationException { m_genes = a_functions; } /** * Gets the number of nodes in the branch starting at the a_index'th node. * * @param a_index the index of the node at which to start counting * @return the number of nodes in the branch starting at the a_index'th node * * @author Klaus Meffert * @since 3.0 */ public int getSize(int a_index) { int i; // Get the node at which the depth is <= depth[n]. // ----------------------------------------------- for (i = a_index + 1; i < m_genes.length && m_genes[i] != null; i++) { if (m_depth[i] <= m_depth[a_index]) { break; } } return i - a_index; } /** * Gets the depth of the branch starting at the a_index'th node. * * @param a_index the index of the node at which to check the depth * @return the depth of the branch starting at the a_index'th node * * @author Klaus Meffert * @since 3.0 */ public int getDepth(int a_index) { int maxdepth = m_depth[a_index]; for (int i = a_index + 1; i < m_genes.length && m_genes[i] != null; i++) { if (m_depth[i] <= m_depth[a_index]) { break; } if (m_depth[i] > maxdepth) { maxdepth = m_depth[i]; } } return maxdepth - m_depth[a_index]; } /** * Gets the node which is the parent of the given node in this chromosome. If * the child is at depth d then the parent is the first function at depth d-1 * when iterating backwards through the function list starting from the child. * * @param a_child the child node * @return the parent node, or null if the child is the root node * * @author Klaus Meffert * @since 3.0 */ public int getParentNode(int a_child) { if (a_child >= m_genes.length || m_genes[a_child] == null) { return -1; } for (int i = a_child - 1; i >= 0; i--) { if (m_depth[i] == m_depth[a_child] - 1) { return i; } } return -1; } /** * Checks whether a node with a given type is contained in the program. * * @param a_type the type to look for * @param a_exactMatch true: look for exactly the given type: false: also look * for sub types * @return true specific node found * * @author Klaus Meffert * @since 3.2.1 */ public CommandGene getNode(Class a_type, boolean a_exactMatch) { return getNode(a_type, a_exactMatch, 0); } public CommandGene getNode(Class a_type, boolean a_exactMatch, int a_startIndex) { int size = m_genes.length; for (int i = a_startIndex; i < size; i++) { if (m_genes[i] != null) { if (a_exactMatch) { if (m_genes[i].getClass() == a_type) { m_genes[i].nodeIndex = i; /**@todo work over*/ return m_genes[i]; } } else { if (a_type.isAssignableFrom(m_genes[i].getClass())) { m_genes[i].nodeIndex = i; /**@todo work over*/ return m_genes[i]; } } } else { break; } } return null; } /** * Executes this node as a boolean. * * @param args the arguments for execution * @return the boolean return value of this node * @throws UnsupportedOperationException if the type of this node is not * boolean * * @author Klaus Meffert * @since 3.0 */ public boolean execute_boolean(Object[] args) { boolean rtn = m_genes[0].execute_boolean(this, 0, args); cleanup(); return rtn; } /** * Executes this node as a boolean. * * @param n the index of the parent node * @param child the child number of the node to execute * @param args the arguments for execution * @return the boolean return value of this node * @throws UnsupportedOperationException if the type of this node is not * boolean * * @author Klaus Meffert * @since 3.0 */ public boolean execute_boolean(int n, int child, Object[] args) { if (child == 0) { return m_genes[n + 1].execute_boolean(this, n + 1, args); } int other = getChild(n, child); return m_genes[other].execute_boolean(this, other, args); } /** * Executes this node, returning nothing. * * @param args the arguments for execution * @throws UnsupportedOperationException if the type of this node is not void * * @author Klaus Meffert * @since 3.0 */ public void execute_void(Object[] args) { m_genes[0].execute_void(this, 0, args); cleanup(); } public void execute_void(int n, int child, Object[] args) { if (child == 0) { m_genes[n + 1].execute_void(this, n + 1, args); } else { int other = getChild(n, child); m_genes[other].execute_void(this, other, args); } } /** * Executes this node as an integer. * * @param args the arguments for execution * @return the integer return value of this node * @throws UnsupportedOperationException if the type of this node is not * integer * * @author Klaus Meffert * @since 3.0 */ public int execute_int(Object[] args) { int rtn = m_genes[0].execute_int(this, 0, args); cleanup(); return rtn; } public int execute_int(int n, int child, Object[] args) { if (child == 0) { return m_genes[n + 1].execute_int(this, n + 1, args); } else { int other = getChild(n, child); return m_genes[other].execute_int(this, other, args); } } /** * Executes this node as a long. * * @param args the arguments for execution * @return the long return value of this node * @throws UnsupportedOperationException if the type of this node is not long * * @author Klaus Meffert * @since 3.0 */ public long execute_long(Object[] args) { long rtn = m_genes[0].execute_long(this, 0, args); cleanup(); return rtn; } public long execute_long(int n, int child, Object[] args) { if (child == 0) { return m_genes[n + 1].execute_long(this, n + 1, args); } int other = getChild(n, child); return m_genes[other].execute_long(this, other, args); } /** * Executes this node as a float. * * @param args the arguments for execution * @return the float return value of this node * @throws UnsupportedOperationException if the type of this node is not float * * @author Klaus Meffert * @since 3.0 */ public float execute_float(Object[] args) { float rtn = m_genes[0].execute_float(this, 0, args); cleanup(); return rtn; } public float execute_float(int n, int child, Object[] args) { if (child == 0) { return m_genes[n + 1].execute_float(this, n + 1, args); } int other = getChild(n, child); return m_genes[other].execute_float(this, other, args); } /** * Executes this node as a double. * * @param args the arguments for execution * @return the double return value of this node * @throws UnsupportedOperationException if this node's type is not double * * @author Klaus Meffert * @since 3.0 */ public double execute_double(Object[] args) { double rtn = m_genes[0].execute_double(this, 0, args); cleanup(); return rtn; } public double execute_double(int n, int child, Object[] args) { if (child == 0) { return m_genes[n + 1].execute_double(this, n + 1, args); } int other = getChild(n, child); return m_genes[other].execute_double(this, other, args); } /** * Executes this node as an object. * * @param args the arguments for execution * @return the object return value of this node * @throws UnsupportedOperationException if the type of this node is not * of type Object * * @author Klaus Meffert * @since 3.0 */ public Object execute_object(Object[] args) { Object rtn = m_genes[0].execute_object(this, 0, args); cleanup(); return rtn; } public Object execute_object(int n, int child, Object[] args) { if (child == 0) { return m_genes[n + 1].execute_object(this, n + 1, args); } int other = getChild(n, child); return m_genes[other].execute_object(this, other, args); } /** * Executes this node without knowing its return type. * * @param args the arguments for execution * @return the Object which wraps the return value of this node, or null * if the return type is null or unknown * * @author Klaus Meffert * @since 3.0 */ public Object execute(Object[] args) { return m_genes[0].execute_object(this, 0, args); } public Object execute(int n, int child, Object[] args) { return execute_object(n, child, args); } public void setGene(int index, CommandGene a_gene) { if (a_gene == null) { throw new IllegalArgumentException("Gene must not be null!"); } m_genes[index] = a_gene; } public Class[] getArgTypes() { return argTypes; } public int getArity() { return argTypes.length; } /** * @return number of functions and terminals present * * @author Klaus Meffert * @since 3.0 */ public int size() { int i = 0; while (i < m_genes.length && m_genes[i] != null) { i++; } return i; } /** * 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 a_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 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(); ProgramChromosome otherChromosome = (ProgramChromosome) a_other; CommandGene[] otherGenes = otherChromosome.m_genes; // 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 = m_genes[i].compareTo(otherGenes[i]); if (comparison != 0) { return comparison; } } /**@todo compare m_functionSet*/ if (isCompareApplicationData()) { // 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) { return -1; } } else { return getApplicationData().getClass().getName().compareTo( otherChromosome.getApplicationData().getClass().getName()); } } } // Everything is equal. Return zero. // --------------------------------- return 0; } /** * Compares this chromosome against the specified object. * * @param a_other the object to compare against * @return true: if the objects are the same, false otherwise * * @author Klaus Meffert * @since 3.0 */ public boolean equals(Object a_other) { try { return compareTo(a_other) == 0; } catch (ClassCastException cex) { return false; } } /** * 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 3.0 */ public void setCompareApplicationData(boolean a_doCompare) { m_compareAppData = a_doCompare; } /* * @return should we also consider the application data when comparing? * * @author Klaus Meffert * @since 3.0 */ public boolean isCompareApplicationData() { return m_compareAppData; } /** * 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 Klaus Meffert * @since 3.0 */ public Object getApplicationData() { return m_applicationData; } /** * 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_locus index of the gene value to be returned * @return Gene at the given index * * @author Klaus Meffert * @since 3.0 */ public synchronized CommandGene getGene(int a_locus) { return m_genes[a_locus]; } private CommandGene[] remove(CommandGene[] a_functionSet, CommandGene node) { int size = a_functionSet.length; for (int i = 0; i < size; i++) { if (a_functionSet[i] == node) { // Remove found element CommandGene[] result = new CommandGene[size - 1]; if (i > 0) { System.arraycopy(a_functionSet, 0, result, 0, i); } if (size - i > 1) { System.arraycopy(a_functionSet, i + 1, result, i, size - i - 1); } return result; } } return a_functionSet; } protected String encode(String a_string) { return StringKit.encode(a_string); } protected String decode(String a_string) { return StringKit.decode(a_string); } /** * @return the persistent representation of the chromosome, including all * genes * * @author Klaus Meffert * @since 3.3 */ public String getPersistentRepresentation() { StringBuffer b = new StringBuffer(); // Store current state. // -------------------- // String state = PERSISTENT_FIELD_DELIMITER+m_functionSet // Process the contained genes. // ---------------------------- for (CommandGene gene : m_genes) { if (gene == null) { break; } b.append(GENE_DELIMITER_HEADING); b.append(encode( gene.getClass().getName() + GENE_DELIMITER + gene.getPersistentRepresentation())); b.append(GENE_DELIMITER_CLOSING); } return b.toString(); } /** * * @param a_representation String * @throws UnsupportedRepresentationException * * @author Klaus Meffert * @since 3.3 */ public void setValueFromPersistentRepresentation(final String a_representation) throws UnsupportedRepresentationException { if (a_representation != null) { try { List r = split(a_representation); Iterator iter = r.iterator(); StringTokenizer st; String clas; String representation; String g; CommandGene gene; List genes = new Vector(); 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); genes.add(gene); } m_genes = (CommandGene[]) genes.toArray(new CommandGene[0]); } catch (Exception ex) { throw new UnsupportedRepresentationException(ex.toString()); } } } /** * Creates a new instance of gene. * * @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.3 */ protected CommandGene createGene(String a_geneClassName, String a_persistentRepresentation) throws Exception { Class geneClass = Class.forName(a_geneClassName); Constructor constr = geneClass.getConstructor(new Class[] {GPConfiguration.class}); CommandGene gene = (CommandGene) constr.newInstance(new Object[] { getGPConfiguration()}); gene.setValueFromPersistentRepresentation(a_persistentRepresentation); return gene; } /** * Splits a_string into individual gene representations. * * @param a_string the string to split * @return the elements of the returned array are the persistent * representation strings of the gene's components * @throws UnsupportedRepresentationException * * @author Klaus Meffert * @since 3.3 */ protected static final List split(String a_string) throws UnsupportedRepresentationException { List a = Collections.synchronizedList(new ArrayList()); 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 opening tag"); } String n = st.nextToken(); if (n.equals(GENE_DELIMITER_CLOSING)) { // Empty token. a.add(""); } else { a.add(n); if (!st.nextToken().equals(GENE_DELIMITER_CLOSING)) { throw new UnsupportedRepresentationException (a_string + " no closing tag"); } } } return a; } }