/*
* Copyright 2007-2013
* Licensed under GNU Lesser General Public License
*
* This file is part of EpochX: genetic programming software for research
*
* EpochX is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EpochX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with EpochX. If not, see <http://www.gnu.org/licenses/>.
*
* The latest version is available from: http://www.epochx.org
*/
package org.epochx.cfg.operator;
import static org.epochx.Config.Template.TEMPLATE;
import static org.epochx.RandomSequence.RANDOM_SEQUENCE;
import java.util.*;
import org.epochx.AbstractOperator;
import org.epochx.Config;
import org.epochx.Individual;
import org.epochx.RandomSequence;
import org.epochx.Config.ConfigKey;
import org.epochx.cfg.CFGIndividual;
import org.epochx.event.ConfigEvent;
import org.epochx.event.EventManager;
import org.epochx.event.Listener;
import org.epochx.event.OperatorEvent;
import org.epochx.event.OperatorEvent.EndOperator;
import org.epochx.grammar.*;
/**
* This class performs a subtree crossover on a <code>CFGIndividual</code>, as described
* by Whigham in his paper "Grammatically-based genetic programming".
*
* <p>
* A <code>NonTerminalSymbol</code> is randomly selected as the crossover point
* in each individual's parse tree and the two subtrees rooted at those nodes are
* exchanged.
*
* @since 2.0
*/
public class SubtreeCrossover extends AbstractOperator implements Listener<ConfigEvent> {
/**
* The key for setting and retrieving the probability of this operator being applied
*/
public static final ConfigKey<Double> PROBABILITY = new ConfigKey<Double>();
// Configuration settings
private RandomSequence random;
private Double probability;
/**
* Constructs a <code>SubtreeCrossover</code> with control parameters
* automatically loaded from the config
*/
public SubtreeCrossover() {
this(true);
}
/**
* Constructs a <code>SubtreeCrossover</code> with control parameters initially
* loaded from the config. If the <code>autoConfig</code> argument is set to
* <code>true</code> then the configuration will be automatically updated when
* the config is modified.
*
* @param autoConfig whether this operator should automatically update its
* configuration settings from the config
*/
public SubtreeCrossover(boolean autoConfig) {
setup();
if (autoConfig) {
EventManager.getInstance().add(ConfigEvent.class, this);
}
}
/**
* Sets up this operator with the appropriate configuration settings.
* This method is called whenever a <code>ConfigEvent</code> occurs for a
* change in any of the following configuration parameters:
* <ul>
* <li>{@link RandomSequence#RANDOM_SEQUENCE}
* <li>{@link #PROBABILITY}
* </ul>
*/
protected void setup() {
random = Config.getInstance().get(RANDOM_SEQUENCE);
probability = Config.getInstance().get(PROBABILITY);
}
/**
* Receives configuration events and triggers this operator to configure its
* parameters if the <code>ConfigEvent</code> is for one of its required
* parameters.
*
* @param event {@inheritDoc}
*/
@Override
public void onEvent(ConfigEvent event) {
if (event.isKindOf(TEMPLATE, RANDOM_SEQUENCE, PROBABILITY)) {
setup();
}
}
/**
* Performs a subtree crossover operation on the specified parent individuals.
*
* <p>
* A <code>NonTerminalSymbol</code> is randomly selected as the crossover point
* in each individual's parse tree and the two subtrees rooted at those nodes are
* exchanged.
*
* @param event the <code>EndOperator</code> event to be filled with information
* about this operation
* @param parents an array of two individuals to undergo subtree crossover. Both
* individuals must be instances of <code>CFGIndividual</code>.
* @return an array containing two <code>CFGIndividual</code>s that are the
* result of the crossover
*/
@Override
public CFGIndividual[] perform(EndOperator event, Individual ... parents) {
CFGIndividual child1 = (CFGIndividual) parents[0];
CFGIndividual child2 = (CFGIndividual) parents[1];
NonTerminalSymbol parseTree1 = child1.getParseTree();
NonTerminalSymbol parseTree2 = child2.getParseTree();
List<NonTerminalSymbol> nonTerminals1 = parseTree1.getNonTerminalSymbols();
List<NonTerminalSymbol> nonTerminals2 = parseTree2.getNonTerminalSymbols();
// Choose a non-terminal at random
int point1 = random.nextInt(nonTerminals1.size());
NonTerminalSymbol subtree1 = nonTerminals1.get(point1);
// Generate a list of matching non-terminals from the second program.
List<NonTerminalSymbol> matchingNonTerminals = new ArrayList<NonTerminalSymbol>();
for (NonTerminalSymbol nt: nonTerminals2) {
if (nt.getGrammarRule().equals(subtree1.getGrammarRule())) {
matchingNonTerminals.add(nt);
}
}
if (matchingNonTerminals.isEmpty()) {
// No valid points in second program, cancel crossover.
return null;
} else {
// Randomly choose a second point out of the matching non-terminals.
int point2 = random.nextInt(matchingNonTerminals.size());
NonTerminalSymbol subtree2 = matchingNonTerminals.get(point2);
// Add crossover points to the end event
((EndEvent) event).setCrossoverPoint1(point1);
((EndEvent) event).setCrossoverPoint1(point2);
// Swap the non-terminals' children.
List<Symbol> temp = subtree1.getChildren();
subtree1.setChildren(subtree2.getChildren());
subtree2.setChildren(temp);
// Add subtrees into the end event
((EndEvent) event).setSubtree1(subtree1);
((EndEvent) event).setSubtree2(subtree2);
}
return new CFGIndividual[]{child1, child2};
}
/**
* Returns a <code>SubtreeCrossoverEndEvent</code> with the operator and parents set
*
* @param parents the parents that were operated on
* @return operator end event
*/
@Override
protected EndEvent getEndEvent(Individual ... parents) {
return new EndEvent(this, parents);
}
/**
* {@inheritDoc}
*
* <p>
* Subtree crossover operates on 2 individuals.
*
* @return {@inheritDoc}
*/
@Override
public int inputSize() {
return 2;
}
/**
* {@inheritDoc}
*/
@Override
public double probability() {
return probability;
}
/**
* Sets the probability of this operator being selected. If automatic configuration is
* enabled then any value set here will be overwritten by the {@link #PROBABILITY}
* configuration setting on the next config event.
*
* @param probability the new probability to set
*/
public void setProbability(double probability) {
this.probability = probability;
}
/**
* Returns the random number sequence in use
*
* @return the currently set random sequence
*/
public RandomSequence getRandomSequence() {
return random;
}
/**
* Sets the random number sequence to use. If automatic configuration is
* enabled then any value set here will be overwritten by the
* {@link RandomSequence#RANDOM_SEQUENCE} configuration setting on the next
* config event.
*
* @param random the random number generator to set
*/
public void setRandomSequence(RandomSequence random) {
this.random = random;
}
/**
* An event fired at the end of a subtree crossover
*
* @see SubtreeCrossover
*
* @since 2.0
*/
public class EndEvent extends OperatorEvent.EndOperator {
private NonTerminalSymbol subtree1;
private NonTerminalSymbol subtree2;
private int crossoverPoint1;
private int crossoverPoint2;
/**
* Constructs a <code>SubtreeCrossoverEndEvent</code> with the details of the
* event
*
* @param operator the operator that performed the crossover
* @param parents an array of two individuals that the operator was
* performed on
*/
public EndEvent(SubtreeCrossover operator, Individual[] parents) {
super(operator, parents);
}
/**
* Returns an integer which is the index of the point chosen for the subtree crossover
* operation. The index is from the list of all non-terminal symbols in the parse tree
* of the second program, as would be returned by the <code>getNonTerminalSymbols</code>
* method.
*
* @return an integer which is the index of the crossover point
*/
public int getCrossoverPoint1() {
return crossoverPoint1;
}
/**
* Sets the crossover position in the first parent's list of non-terminals
*
* @param crossoverPoint1 index used as the crossover point in the first parent
*/
public void setCrossoverPoint1(int crossoverPoint1) {
this.crossoverPoint1 = crossoverPoint1;
}
/**
* Returns an integer which is the index of the point chosen for the subtree crossover
* operation. The index is from the list of all non-terminal symbols in the parse tree
* of the second program, as would be returned by the <code>getNonTerminalSymbols</code>
* method.
*
* @return an integer which is the index of the crossover point
*/
public int getCrossoverPoint2() {
return crossoverPoint2;
}
/**
* Sets the crossover position in the second parent's list of non-terminals
*
* @param crossoverPoint2 index used as the crossover point in the second parent
*/
public void setCrossoverPoint2(int crossoverPoint2) {
this.crossoverPoint2 = crossoverPoint2;
}
/**
* Returns the subtree taken from the first parent that was exchanged with the
* subtree from the second parent (as returned by <code>getSubtree2()</code>)
*
* @return the root node of the first subtree
*/
public NonTerminalSymbol getSubtree1() {
return subtree1;
}
/**
* Sets the subtree taken from the first parent that was exchanged with the subtree
* from the second parent
*
* @param subtree1 the root node of the first subtree
*/
public void setSubtree1(NonTerminalSymbol subtree1) {
this.subtree1 = subtree1;
}
/**
* Returns the subtree taken from the second parent that was exchanged with the
* subtree from the first parent (as returned by <code>getSubtree1()</code>)
*
* @return the root node of the second subtree
*/
public NonTerminalSymbol getSubtree2() {
return subtree2;
}
/**
* Sets the subtree taken from the second parent that was exchanged with the subtree
* from the first parent
*
* @param subtree1 the root node of the second subtree
*/
public void setSubtree2(NonTerminalSymbol subtree2) {
this.subtree2 = subtree2;
}
}
}