/*
* 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.RandomSequence.RANDOM_SEQUENCE;
import static org.epochx.grammar.Grammar.GRAMMAR;
import java.util.List;
import org.epochx.AbstractOperator;
import org.epochx.Config;
import org.epochx.Individual;
import org.epochx.RandomSequence;
import org.epochx.Config.ConfigKey;
import org.epochx.Config.Template;
import org.epochx.cfg.CFGIndividual;
import org.epochx.cfg.init.Grow;
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 mutation 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 SubtreeMutation 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>();
private final Grow grower;
// Configuration settings
private RandomSequence random;
private Double probability;
private Grammar grammar;
/**
* Constructs a <code>SubtreeMutation</code> with control parameters
* automatically loaded from the config
*/
public SubtreeMutation() {
this(true);
}
/**
* Constructs a <code>SubtreeMutation</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 SubtreeMutation(boolean autoConfig) {
grower = new Grow(false);
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}
* <li>{@link Grammar#GRAMMAR}
* </ul>
*/
protected void setup() {
random = Config.getInstance().get(RANDOM_SEQUENCE);
probability = Config.getInstance().get(PROBABILITY);
grower.setRandomSequence(random);
grower.setGrammar(grammar);
}
/**
* 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.TEMPLATE, RANDOM_SEQUENCE, PROBABILITY, GRAMMAR)) {
setup();
}
}
/**
* Performs a subtree mutation operation on the specified parent individual.
*
* <p>
* A <code>NonTerminalSymbol</code> is randomly selected as the mutation point
* in the individual's parse tree and the subtree rooted at that point is exchanged
* with a new randomly generated subtree.
*
* @param event the <code>EndOperator</code> event to be filled with information
* about this operation
* @param parents an array containing one individual to undergo mutation. The
* individual must be an instance of <code>CFGIndividual</code>.
* @return an array containing one <code>CFGIndividual</code>s that is the
* result of the mutation
*/
@Override
public CFGIndividual[] perform(EndOperator event, Individual ... parents) {
CFGIndividual child = (CFGIndividual) parents[0];
NonTerminalSymbol parseTree = child.getParseTree();
// This is v.inefficient because we have to fly up and down the tree lots of times.
List<Integer> nonTerminals = parseTree.getNonTerminalIndexes();
// Choose a node to change.
int point = nonTerminals.get(random.nextInt(nonTerminals.size()));
NonTerminalSymbol original = (NonTerminalSymbol) parseTree.getNthSymbol(point);
int originalDepth = original.getDepth();
// Add mutation into the end event
((EndEvent) event).setMutationPoint(point);
// Construct a new subtree from that node's grammar rule
//TODO Should allow any depth down to the maximum rather than the original subtrees depth
GrammarRule rule = original.getGrammarRule();
NonTerminalSymbol subtree = grower.growParseTree(originalDepth, rule);
// Add subtree into the end event
((EndEvent) event).setSubtree(subtree);
// Replace subtree
if (point == 0) {
child.setParseTree(subtree);
} else {
parseTree.setNthSymbol(point, subtree);
}
return new CFGIndividual[]{child};
}
/**
* Returns a <code>SubtreeMutationEndEvent</code> with the operator and
* parent set
*
* @param parent the individual that was operated on
* @return operator end event
*/
@Override
protected EndEvent getEndEvent(Individual ... parent) {
return new EndEvent(this, parent);
}
/**
* {@inheritDoc}
*
* <p>
* Subtree mutation operates on one individual.
*
* @return {@inheritDoc}
*/
@Override
public int inputSize() {
return 1;
}
/**
* {@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;
grower.setRandomSequence(random);
}
/**
* Returns the grammar that any mutated <code>CFGIndividual</code>s will satisfy with
* full parse trees
*
* @return the currently set grammar
*/
public Grammar getGrammar() {
return grammar;
}
/**
* Sets the grammar to be satisfied by the parse trees of the mutated <code>CFGIndividual</code>s.
* If automatic configuration is enabled then any value set here will be overwritten by the
* {@link Grammar#GRAMMAR} configuration setting on the next config event.
*
* @param grammar the grammar to set
*/
public void setGrammar(Grammar grammar) {
this.grammar = grammar;
grower.setGrammar(grammar);
}
/**
* An event fired at the end of a subtree mutation
*
* @see SubtreeMutation
*
* @since 2.0
*/
public class EndEvent extends OperatorEvent.EndOperator {
private NonTerminalSymbol subtree;
private int mutationPoint;
/**
* Constructs a <code>SubtreeMutationEndEvent</code> with the details of the event
*
* @param operator the operator that performed the crossover
* @param parents an array containing one individual that the operator was performed on
*/
public EndEvent(SubtreeMutation operator, Individual[] parents) {
super(operator, parents);
}
/**
* Returns an integer which is the index of the point chosen for the subtree mutation
* operation. The index is the position in the whole parse tree as would be returned
* by calling <code>getNthSymbol</code> on the root node.
*
* @return an integer which is the index of the mutation point
*/
public int getMutationPoint() {
return mutationPoint;
}
/**
* Sets the index of the symbol in the parse tree that was mutated
*
* @param mutationPoint index used as the mutation point
*/
public void setMutationPoint(int mutationPoint) {
this.mutationPoint = mutationPoint;
}
/**
* Returns the new subtree that was generated as a replacement for the subtree originally
* at the mutation point
*
* @return the root node of the new subtree
*/
public NonTerminalSymbol getSubtree() {
return subtree;
}
/**
* Sets the new subtree that was generated as a replacement for the subtree originally
* at the mutation point
*
* @param subtree the root node of the first subtree
*/
public void setSubtree(NonTerminalSymbol subtree) {
this.subtree = subtree;
}
}
}