/*
Copyright 2006 by Sean Luke
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package ec.gp.koza;
import ec.*;
import ec.util.*;
import ec.gp.*;
/*
* MutationPipeline.java
*
* Created: Tue Oct 12 18:50:56 1999
* By: Sean Luke
*/
/**
* MutationPipeline is a GPBreedingPipeline which
* implements a strongly-typed version of the
* "Point Mutation" operator as described in Koza I.
* Actually, that's not quite true. Koza doesn't have any tree depth restrictions
* on his mutation operator. This one does -- if the tree gets deeper than
* the maximum tree depth, then the new subtree is rejected and another one is
* tried. Similar to how the Crosssover operator is implemented.
*
* Mutated trees are restricted to being maxdepth depth at
* most and at most maxsize number of nodes. If in
* tries attemptes, the pipeline cannot come up with a
* mutated tree within the depth limit, then it simply copies the
* original individual wholesale with no mutation.
*
*
One additional feature: if equal is true, then MutationPipeline
* will attempt to replace the subtree with a tree of approximately equal size.
* How this is done exactly, and how close it is, is entirely up to the pipeline's
* tree builder -- for example, Grow/Full/HalfBuilder don't support this at all, while
* RandomBranch will replace it with a tree of the same size or "slightly smaller"
* as described in the algorithm.
Typical Number of Individuals Produced Per produce(...) call
...as many as the child produces
Number of Sources
1
Parameters
base.tries
int >= 1
(number of times to try finding valid pairs of nodes)
base.maxdepth
int >= 1
(maximum valid depth of a crossed-over subtree)
base.maxsize
int >= 1
(maximum valid size, in nodes, of a crossed-over subtree)
base.ns
classname, inherits and != GPNodeSelector
(GPNodeSelector for tree)
base.build.0
classname, inherits and != GPNodeBuilder
(GPNodeBuilder for new subtree)
equal
bool = true or false (default)
(do we attempt to replace the subtree with a new one of roughly the same size?)
base.tree.0
0 < int < (num trees in individuals), if exists
(tree chosen for mutation; if parameter doesn't exist, tree is picked at random)
Default Base
gp.koza.mutate
Parameter bases
base.ns
nodeselect
base.build
builder
* @author Sean Luke
* @version 1.0
*/
public class MutationPipeline extends GPBreedingPipeline
{
private static final long serialVersionUID = 1;
public static final String P_NUM_TRIES = "tries";
public static final String P_MAXDEPTH = "maxdepth";
public static final String P_MAXSIZE = "maxsize";
public static final String P_MUTATION = "mutate";
public static final String P_BUILDER = "build";
public static final String P_EQUALSIZE = "equal";
public static final int INDS_PRODUCED = 1;
public static final int NUM_SOURCES = 1;
public static final int NO_SIZE_LIMIT = -1;
/** How the pipeline chooses a subtree to mutate */
public GPNodeSelector nodeselect;
/** How the pipeline builds a new subtree */
public GPNodeBuilder builder;
/** The number of times the pipeline tries to build a valid mutated
tree before it gives up and just passes on the original */
int numTries;
/** The maximum depth of a mutated tree */
int maxDepth;
/** The largest tree (measured as a nodecount) the pipeline is allowed to form. */
public int maxSize;
/** Do we try to replace the subtree with another of the same size? */
boolean equalSize;
/** Is our tree fixed? If not, this is -1 */
int tree;
public Parameter defaultBase() { return GPKozaDefaults.base().push(P_MUTATION); }
public int numSources() { return NUM_SOURCES; }
public Object clone()
{
MutationPipeline c = (MutationPipeline)(super.clone());
// deep-cloned stuff
c.nodeselect = (GPNodeSelector)(nodeselect.clone());
return c;
}
public void setup(final EvolutionState state, final Parameter base)
{
super.setup(state,base);
Parameter def = defaultBase();
Parameter p = base.push(P_NODESELECTOR).push(""+0);
Parameter d = def.push(P_NODESELECTOR).push(""+0);
nodeselect = (GPNodeSelector)
(state.parameters.getInstanceForParameter(
p,d, GPNodeSelector.class));
nodeselect.setup(state,p);
p = base.push(P_BUILDER).push(""+0);
d = def.push(P_BUILDER).push(""+0);
builder = (GPNodeBuilder)
(state.parameters.getInstanceForParameter(
p,d, GPNodeBuilder.class));
builder.setup(state,p);
numTries = state.parameters.getInt(
base.push(P_NUM_TRIES),def.push(P_NUM_TRIES),1);
if (numTries ==0)
state.output.fatal("Mutation Pipeline has an invalid number of tries (it must be >= 1).",base.push(P_NUM_TRIES),def.push(P_NUM_TRIES));
maxDepth = state.parameters.getInt(
base.push(P_MAXDEPTH),def.push(P_MAXDEPTH),1);
if (maxDepth==0)
state.output.fatal("The Mutation Pipeline " + base + "has an invalid maximum depth (it must be >= 1).",base.push(P_MAXDEPTH),def.push(P_MAXDEPTH));
maxSize = NO_SIZE_LIMIT;
if (state.parameters.exists(base.push(P_MAXSIZE), def.push(P_MAXSIZE)))
{
maxSize = state.parameters.getInt(base.push(P_MAXSIZE), def.push(P_MAXSIZE), 1);
if (maxSize < 1)
state.output.fatal("Maximum tree size, if defined, must be >= 1");
}
equalSize = state.parameters.getBoolean(
base.push(P_EQUALSIZE),def.push(P_EQUALSIZE),false);
tree = TREE_UNFIXED;
if (state.parameters.exists(base.push(P_TREE).push(""+0),
def.push(P_TREE).push(""+0)))
{
tree = state.parameters.getInt(base.push(P_TREE).push(""+0),
def.push(P_TREE).push(""+0),0);
if (tree==-1)
state.output.fatal("Tree fixed value, if defined, must be >= 0");
}
}
/** Returns true if inner1 can feasibly be swapped into inner2's position */
public boolean verifyPoints(GPNode inner1, GPNode inner2)
{
// We know they're swap-compatible since we generated inner1
// to be exactly that. So don't bother.
// next check to see if inner1 can fit in inner2's spot
if (inner1.depth()+inner2.atDepth() > maxDepth) return false;
// check for size
if (maxSize != NO_SIZE_LIMIT)
{
// first easy check
int inner1size = inner1.numNodes(GPNode.NODESEARCH_ALL);
int inner2size = inner2.numNodes(GPNode.NODESEARCH_ALL);
if (inner1size > inner2size) // need to test further
{
// let's keep on going for the more complex test
GPNode root2 = ((GPTree)(inner2.rootParent())).child;
int root2size = root2.numNodes(GPNode.NODESEARCH_ALL);
if (root2size - inner2size + inner1size > maxSize) // take root2, remove inner2 and swap in inner1. Is it still small enough?
return false;
}
}
// checks done!
return true;
}
public int produce(final int min,
final int max,
final int start,
final int subpopulation,
final Individual[] inds,
final EvolutionState state,
final int thread)
{
// grab individuals from our source and stick 'em right into inds.
// we'll modify them from there
int n = sources[0].produce(min,max,start,subpopulation,inds,state,thread);
// should we bother?
if (!state.random[thread].nextBoolean(likelihood))
return reproduce(n, start, subpopulation, inds, state, thread, false); // DON'T produce children from source -- we already did
GPInitializer initializer = ((GPInitializer)state.initializer);
// now let's mutate 'em
for(int q=start; q < n+start; q++)
{
GPIndividual i = (GPIndividual)inds[q];
if (tree!=TREE_UNFIXED && (tree<0 || tree >= i.trees.length))
// uh oh
state.output.fatal("GP Mutation Pipeline attempted to fix tree.0 to a value which was out of bounds of the array of the individual's trees. Check the pipeline's fixed tree values -- they may be negative or greater than the number of trees in an individual");
int t;
// pick random tree
if (tree==TREE_UNFIXED)
if (i.trees.length>1) t = state.random[thread].nextInt(i.trees.length);
else t = 0;
else t = tree;
// validity result...
boolean res = false;
// prepare the nodeselector
nodeselect.reset();
// pick a node
GPNode p1=null; // the node we pick
GPNode p2=null;
for(int x=0;x
Ads help maintain this website.