/* * 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 org.jgap.*; import org.jgap.gp.*; /** * Crossing over for GP ProgramChromosomes. * * @author Klaus Meffert * @since 3.0 */ public class BranchTypingCross extends CrossMethod implements Serializable, Comparable, Cloneable { /** String containing the CVS revision. Read out via reflection!*/ private final static String CVS_REVISION = "$Revision: 1.19 $"; private boolean m_simpleChromosomeSelection; /** * Standard constructor. * * @param a_config the configuration to use */ public BranchTypingCross(GPConfiguration a_config) { this(a_config, false); } /** * * @param a_config the configuration to use * @param a_simpleChromosomeSelection true: plainly select chromosomes, * false: select chromosomes proportionally to their size (=number of nodes * within a chromosome) * * @author Klaus Meffert * @since 3.4 */ public BranchTypingCross(GPConfiguration a_config, boolean a_simpleChromosomeSelection) { super(a_config); m_simpleChromosomeSelection = a_simpleChromosomeSelection; } /** * Crosses two individuals. A random chromosome is chosen for crossing based * on the proportion of nodes in each chromosome in the first individual. * * @param a_i1 the first individual to cross * @param a_i2 the second individual to cross * @return an array of the two resulting individuals * * @author Klaus Meffert * @since 3.0 */ public IGPProgram[] operate(final IGPProgram a_i1, final IGPProgram a_i2) { try { int chromosomeNum; if (!m_simpleChromosomeSelection) { // Determine which chromosome we'll cross, probabilistically determined // by the sizes of the chromosomes of the first individual. // This is equivalent to Koza's branch typing. // Advantage over plain selection: proportion of the chromosomes' sizes // is cared about. // -------------------------------------------------------------------- int[] sizes = new int[a_i1.size()]; int totalSize = 0; for (int i = 0; i < a_i1.size(); i++) { // Size of a chromosome = number of nodes. // --------------------------------------- sizes[i] = a_i1.getChromosome(i).getSize(0); totalSize += sizes[i]; } int nodeNum = getConfiguration().getRandomGenerator().nextInt( totalSize); // Select the chromosome in which node "nodeNum" resides. // ------------------------------------------------------ for (chromosomeNum = 0; chromosomeNum < a_i1.size(); chromosomeNum++) { nodeNum -= sizes[chromosomeNum]; if (nodeNum < 0) { break; } } } else { // Select a chromosome directly. // ----------------------------- chromosomeNum = getConfiguration().getRandomGenerator(). nextInt(a_i1.size()); } // Cross the selected chromosomes. // ------------------------------- /**@todo try to ensure uniqueness for unique commands: * after selecting first node, check if there is a unique node in the sub * tree. If so, check if it appears in the sub tree of the second node. */ ProgramChromosome[] newChromosomes = doCross( a_i1.getChromosome(chromosomeNum), a_i2.getChromosome(chromosomeNum)); // Create the new individuals by copying the uncrossed chromosomes // and setting the crossed chromosome. There's no need to deep-copy // the uncrossed chromosomes because they don't change. That is, // even if two individuals' chromosomes point to the same chromosome, // the only change in a chromosome is crossing, which generates // deep-copied chromosomes anyway. // ------------------------------------------------------------------ IGPProgram[] newIndividuals = { new GPProgram(a_i1), new GPProgram(a_i1)}; for (int i = 0; i < a_i1.size(); i++) if (i != chromosomeNum) { // Unchanged, not crossed, chromosomes. // ------------------------------------ newIndividuals[0].setChromosome(i, a_i1.getChromosome(i)); newIndividuals[1].setChromosome(i, a_i2.getChromosome(i)); } else { // The crossed chromosomes. // ------------------------ newIndividuals[0].setChromosome(i, newChromosomes[0]); newIndividuals[1].setChromosome(i, newChromosomes[1]); } return newIndividuals; } catch (InvalidConfigurationException iex) { return null; } } /** * Crosses two chromsomes using branch-typing. * A random point in the first chromosome is chosen, with a certain probability * it will be a function and with a rest probability it will be a terminal. A * random point in the second chromosome is chosen using the same probability * distribution, but the node chosen must be of the same type as the chosen * node in the first chromosome. * If a suitable point in the second chromosome couldn't be found then the * chromosomes are not crossed. * If a resulting chromosome's depth is larger than the maximum crossover * depth then that chromosome is simply copied from the original * rather than crossed. * * @param a_c0 the first chromosome to cross * @param a_c1 the second chromosome to cross * @return an array of the two resulting chromosomes * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 3.0 */ protected ProgramChromosome[] doCross(ProgramChromosome a_c0, ProgramChromosome a_c1) throws InvalidConfigurationException { ProgramChromosome[] c = { a_c0, a_c1}; // Choose a point in c1. // --------------------- int p0; RandomGenerator random = getConfiguration().getRandomGenerator(); if (random.nextFloat() < getConfiguration().getFunctionProb()) { // Choose a function. // ------------------ int nf = a_c0.numFunctions(); if (nf == 0) { // No functions there. // ------------------- return c; } int fctIndex = random.nextInt(nf); p0 = a_c0.getFunction(fctIndex); } else { // Choose a terminal. // ------------------ p0 = a_c0.getTerminal(random.nextInt(a_c0.numTerminals())); // Mutate the command's value. // ---------------------------- CommandGene command = a_c0.getNode(p0); if (random.nextDouble() <= getConfiguration().getMutationProb()) { if (IMutateable.class.isInstance(command)) { IMutateable term = (IMutateable) command; command = term.applyMutation(0, 0.3d); if (command != null) { // Check if mutant's function is allowed. // -------------------------------------- if (a_c0.getCommandOfClass(0, command.getClass()) >= 0) { a_c0.setGene(p0, command); } } } } } // Choose a point in c2 matching the type and subtype of p0. // --------------------------------------------------------- int p1; CommandGene nodeP0 = a_c0.getNode(p0); Class type_ = nodeP0.getReturnType(); int subType = nodeP0.getSubReturnType(); if (random.nextFloat() < getConfiguration().getFunctionProb()) { // Choose a function. // ------------------ int nf = a_c1.numFunctions(type_, subType); if (nf == 0) { // No functions of that type. // -------------------------- return c; } p1 = a_c1.getFunction(random.nextInt(nf), type_, subType); } else { // Choose a terminal. // ------------------ int nt = a_c1.numTerminals(type_, subType); if (nt == 0) { // No terminals of that type. // -------------------------- return c; } p1 = a_c1.getTerminal(random.nextInt(a_c1.numTerminals(type_, subType)), type_, subType); // Mutate the command's value. // ---------------------------- CommandGene command = a_c1.getNode(p1); if (random.nextDouble() <= getConfiguration().getMutationProb()) { if (IMutateable.class.isInstance(command)) { IMutateable term = (IMutateable) command; command = term.applyMutation(0, 0.3d); if (command != null) { // Check if mutant's function is allowed. // -------------------------------------- if (a_c0.getCommandOfClass(0, command.getClass()) >= 0) { a_c1.setGene(p1, command); } } } } } /**@todo solve in general*/ if (org.jgap.gp.function.SubProgram.class.isAssignableFrom(a_c1.getFunctions()[p1].getClass())) { ((IMutateable)a_c1.getFunctions()[p1]).applyMutation(0, 0.5d); } int s0 = a_c0.getSize(p0); //Number of nodes in c0 from index p0 int s1 = a_c1.getSize(p1); //Number of nodes in c1 from index p1 int d0 = a_c0.getDepth(p0); //Depth of c0 from index p0 int d1 = a_c1.getDepth(p1); //Depth of c1 from index p1 int c0s = a_c0.getSize(0); //Number of nodes in c0 int c1s = a_c1.getSize(0); //Number of nodes in c1 // Check for depth constraint for p1 inserted into c0. // --------------------------------------------------- if (d0 - 1 + d1/*s1*/ > getConfiguration().getMaxCrossoverDepth() || c0s - p0 - s0 < 0 || p0 + s1 + c0s - p0 - s0 >= a_c0.getFunctions().length) { // Choose the other parent. // ------------------------ c[0] = a_c1; } else { c[0] = new ProgramChromosome(getConfiguration(), a_c0.getFunctions().length, c[0].getFunctionSet(), c[0].getArgTypes(), a_c0.getIndividual()); System.arraycopy(a_c0.getFunctions(), 0, c[0].getFunctions(), 0, p0); System.arraycopy(a_c1.getFunctions(), p1, c[0].getFunctions(), p0, s1); System.arraycopy(a_c0.getFunctions(), p0 + s0, c[0].getFunctions(), p0 + s1, c0s - p0 - s0); c[0].redepth(); } // Check for depth constraint for p0 inserted into c1. // --------------------------------------------------- if (d1 - 1 + d0/*s0*/ > getConfiguration().getMaxCrossoverDepth() || c1s - p1 - s1 < 0 || p1 + s0 + c1s - p1 - s1 >= a_c1.getFunctions().length) { // Choose the other parent. // ------------------------ c[1] = a_c0; } else { c[1] = new ProgramChromosome(getConfiguration(), a_c1.getFunctions().length, c[1].getFunctionSet(), c[1].getArgTypes(), a_c1.getIndividual()); System.arraycopy(a_c1.getFunctions(), 0, c[1].getFunctions(), 0, p1); System.arraycopy(a_c0.getFunctions(), p0, c[1].getFunctions(), p1, s0); System.arraycopy(a_c1.getFunctions(), p1 + s1, c[1].getFunctions(), p1 + s0, c1s - p1 - s1); c[1].redepth(); } return c; } /** * The compareTo-method. * * @param a_other the other object to compare * @return 0 or 1 in this case, as BranchTypingCross objects keep no state * * @author Klaus Meffert * @since 3.0 */ public int compareTo(Object a_other) { BranchTypingCross other = (BranchTypingCross) a_other; if (other == null) { return 1; } return 0; } /** * The equals-method. * * @param a_other the other object to compare * @return always true for non-null BranchTypingCross objects because they * keep no state * * @author Klaus Meffert * @since 3.0 */ public boolean equals(Object a_other) { try { BranchTypingCross other = (BranchTypingCross) a_other; if (other == null) { return false; } else { return true; } } catch (ClassCastException cex) { return false; } } /** * @return deep clone of this instance * * @author Klaus Meffert * @since 3.2 */ public Object clone() { BranchTypingCross result = new BranchTypingCross(getConfiguration()); return result; } }