/* Copyright 2006 by Sean Luke Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package ec.gp; import ec.*; import ec.util.*; import java.io.*; /* * ADF.java * * Created: Mon Oct 25 18:42:09 1999 * By: Sean Luke */ /** * An ADF is a GPNode which implements an "Automatically Defined Function", * as described in Koza II. * *
In this system, the ADF facility consists of several classes: ADF, * ADM, ADFStack, ADFContext, and ADFArgument. ADFs, and their cousins * ADMs ("Automatically Defined Macros [Lee Spector]"), appear as * typical function nodes in a GP tree. However, they have a special * associated tree in the individual's tree forest which * they evaluate as a kind of a "subfunction". * *
When an ADF is evaluated, it first evaluates all of its children * and stores away their results. It then evaluates its associated tree. * In the associated tree may exist one or more ADF Argument Terminals, * defined by the ADFArgument class. These terminal nodes are associated * with a single number which represents the "argument" in the original ADF * which evaluated their tree. When an Argument Terminal is evaluated, * it returns the stored result for that child number in the parent ADF. * Ultimately, when the associated tree completes its evaluation, the ADF * returns that value. * *
ADMs work slightly differently. When an ADM is evaluated, it * immediately evaluates its associated tree without first evaluating * any children. When an Argument Terminal is evaluated, it evaluates * the subtree of the appropriate child number in the parent ADM and returns * that result. These subtrees can be evaluated many times. When the * associated tree completes its evaluation, the ADM returns that value. * *
Obviously, if you have Argument Terminals in a tree, that tree must * be only callable by ADFs and ADMs, otherwise the Argument Terminals * won't have anything to return. Furthermore, you must make sure that * you don't have an Argument Terminal in a tree whose number is higher * than the smallest arity (number of arguments) of a calling ADF or ADM. * *
The mechanism behind ADFs and ADMs is complex, requiring two specially- * stored stacks (contained in the ADFStack object) of ADFContexts. For * information on how this mechanism works, see ADFStack. * *
Parameters
base.tree int >= 0 |
(The "associated tree" of the ADF) |
base.name String, can be undefined |
(A simple "name" of the ADF to distinguish it from other ADF functions in your function set. Use only letters, numbers, hyphens, and underscores. Lowercase is best.) |
Default Base
gp.adf
* @see ec.gp.ADFStack
* @author Sean Luke
* @version 1.0
*/
public class ADF extends GPNode
{
public static final String P_ADF = "adf";
public static final String P_ASSOCIATEDTREE = "tree";
public static final String P_FUNCTIONNAME = "name";
/** The ADF's associated tree */
public int associatedTree;
/** The "function name" of the ADF, to distinguish it from other GP
functions you might provide. */
public String name;
public String name() { return name; }
public Parameter defaultBase()
{
return GPDefaults.base().push(P_ADF);
}
public void writeNode(final EvolutionState state, final DataOutput dataOutput) throws IOException
{
dataOutput.writeInt(associatedTree);
dataOutput.writeUTF(name);
}
public void readNode(final EvolutionState state, final DataInput dataInput) throws IOException
{
associatedTree = dataInput.readInt();
name = dataInput.readUTF();
}
/** Returns name.hashCode() + class.hashCode() + associatedTree. Hope
that's reasonably random. */
public int nodeHashCode()
{
return (this.getClass().hashCode() + name.hashCode() + associatedTree);
}
/** Determines node equality by comparing the class, associated tree, and
function name of the nodes. */
public boolean nodeEquals(final GPNode node)
{
if (!this.getClass().equals(node.getClass()) ||
children.length != node.children.length) return false;
ADF adf = (ADF)node;
return (associatedTree==adf.associatedTree && name.equals(adf.name));
}
/** Checks type-compatibility constraints between the ADF, its argument terminals, and the tree type of its associated tree, and also checks to make sure the tree exists, there aren't invalid argument terminals in it, and there are sufficient argument terminals (a warning). Whew! */
public void checkConstraints(final EvolutionState state,
final int tree,
final GPIndividual typicalIndividual,
final Parameter individualBase)
{
super.checkConstraints(state,tree,typicalIndividual,individualBase);
// does the associated tree exist?
if (associatedTree < 0 || associatedTree > typicalIndividual.trees.length)
state.output.error("The node " + toStringForError() + " of individual " +
individualBase + " must have an associated tree that is >= 0 and < " + typicalIndividual.trees.length);
else
{
// is the associated tree of the correct type? Issue an error.
GPInitializer initializer = ((GPInitializer)state.initializer);
if (!constraints(initializer).returntype.compatibleWith(initializer,
typicalIndividual.trees[associatedTree].constraints(initializer).treetype))
state.output.error("The return type of the node " + toStringForError()
+ " of individual " +
individualBase + "is not type-compatible with the tree type of its associated tree.");
GPNode[][] funcs =
typicalIndividual.trees[associatedTree].
constraints(initializer).functionset.nodes;
ADFArgument validArgument[] = new ADFArgument[children.length];
for(int w=0;w