/*
Copyright 2006 by Sean Luke
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package ec.gp.breed;
import ec.*;
import ec.util.*;
import ec.gp.*;
/*
* MutateDemotePipeline.java
*
* Created: Wed Dec 15 21:41:30 1999
* By: Sean Luke
*/
/**
* MutateDemotePipeline works very similarly to the DemoteNode algorithm
* described in Kumar Chellapilla,
* "A Preliminary Investigation into Evolving Modular Programs without Subtree
* Crossover", GP98, and is also similar to the "insertion" operator found in
* Una-May O'Reilly's thesis,
*
* "An Analysis of Genetic Programming".
*
* MutateDemotePipeline tries picks a random tree, then picks
* randomly from all the demotable nodes in the tree, and demotes one.
* If its chosen tree has no demotable nodes, or demoting
* its chosen demotable node would make the tree too deep, it repeats
* the choose-tree-then-choose-node process. If after tries times
* it has failed to find a valid tree and demotable node, it gives up and simply
* copies the individual.
*
*
"Demotion" means to take a node n and insert a new node m
* between n and n's parent. n becomes a child of
* m; the place where it becomes a child is determined at random
* from all the type-compatible slots of m. The other child slots
* of m are filled with randomly-generated terminals.
* Chellapilla's version of the algorithm always
* places n in child slot 0 of m. Because this would be
* unneccessarily restrictive on strong typing, MutateDemotePipeline instead
* picks the slot at random from all available valid choices.
*
*
A "Demotable" node means a node which is capable of demotion
* given the existing function set. In general to demote a node foo,
* there must exist in the function set a nonterminal whose return type
* is type-compatible with the child slot foo holds in its parent;
* this nonterminal must also have a child slot which is type-compatible
* with foo's return type.
*
*
This method is very expensive in searching nodes for
* "demotability". However, if the number of types is 1 (the
* GP run is typeless) then the type-constraint-checking
* code is bypassed and the method runs a little faster.
*
Typical Number of Individuals Produced Per produce(...) call
...as many as the source 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 mutated tree)
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.breed.mutate-demote
* @author Sean Luke
* @version 1.0
*/
public class MutateDemotePipeline extends GPBreedingPipeline
{
public static final String P_MUTATEDEMOTE = "mutate-demote";
public static final String P_NUM_TRIES = "tries";
public static final String P_MAXDEPTH = "maxdepth";
public static final int NUM_SOURCES = 1;
/** 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;
/** Is our tree fixed? If not, this is -1 */
int tree;
public Parameter defaultBase() { return GPBreedDefaults.base().push(P_MUTATEDEMOTE); }
public int numSources() { return NUM_SOURCES; }
public void setup(final EvolutionState state, final Parameter base)
{
super.setup(state,base);
Parameter def = defaultBase();
numTries = state.parameters.getInt(base.push(P_NUM_TRIES),
def.push(P_NUM_TRIES),1);
if (numTries == 0)
state.output.fatal("MutateDemotePipeline 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 MutateDemotePipeline " + base + "has an invalid maximum depth (it must be >= 1).",base.push(P_MAXDEPTH),def.push(P_MAXDEPTH));
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");
}
}
private boolean demotable(final GPInitializer initializer,
final GPNode node, final GPFunctionSet set)
{
GPType t;
if (node.parent instanceof GPNode) // ugh, expensive
t = ((GPNode)(node.parent)).constraints(initializer).childtypes[node.argposition];
else
t = ((GPTree)(node.parent)).constraints(initializer).treetype;
// Now, out of the nonterminals compatible with that return type,
// do any also have a child compatible with that return type? This
// will be VERY expensive
for(int x=0;x 0
int demoteItem = state.random[thread].nextInt(numDemotable);
numDemotable=0;
// find it
for(int x=0;x 0
int demoteItem = state.random[thread].nextInt(numDemotable);
numDemotable=0;
// find it
// clone the node
GPNode cnode = (GPNode)(set.nonterminals[0][demoteItem].lightClone());
GPType[] chityp = cnode.constraints(((GPInitializer)state.initializer)).childtypes;
// choose a spot to hang the old parent under
int choice = state.random[thread].nextInt(cnode.children.length);
for(int z=0;z maxDepth) 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 n 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("MutateDemotePipeline 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");
GPIndividual j;
if (sources[0] instanceof BreedingPipeline)
// it's already a copy, so just smash the tree in
{
j=i;
}
else // need to copy it
{
j = (GPIndividual)(i.lightClone());
// Fill in various tree information that didn't get filled in there
j.trees = new GPTree[i.trees.length];
for(int x=0;x1) t = state.random[thread].nextInt(i.trees.length);
else t = 0;
else t = tree;
// is the tree demotable?
int numdemote = numDemotableNodes(initializer, j.trees[t].child,0,j.trees[t].constraints(initializer).functionset);
if (numdemote==0) continue; // uh oh, try again
// demote the node, or if we're unsuccessful, just leave it alone
pickDemotableNode(initializer, j.trees[t].child,state.random[thread].nextInt(numdemote),j.trees[t].constraints(initializer).functionset);
// does this node exceed the maximum depth limits?
if (!verifyPoint(demotableNode)) continue; // uh oh, try again
// demote it
demoteSomething(demotableNode,state,thread,j.trees[t].constraints(initializer).functionset);
j.evaluated = false;
break;
}
// add the new individual, replacing its previous source
inds[q] = j;
}
return n;
}
}
Ads help maintain this website.