/*
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.util.*;
import java.util.Enumeration;
/*
* GPTreeConstraints.java
*
* Created: Thu Oct 7 15:38:45 1999
* By: Sean Luke
*/
/**
* A GPTreeConstraints is a Clique which defines constraint information
* common to many different GPTree trees, namely the tree type,
* builder, and function set. GPTreeConstraints have unique names
* by which they are identified.
*
*
In adding new things to GPTreeConstraints, you should ask yourself
* the following questions: first, is this something that takes up too
* much memory to store in GPTrees themseves? second, is this something
* that needs to be accessed very rapidly, so cannot be implemented as
* a method call in a GPTree? third, can this be shared among different
* GPTrees?
*
Parameters
base.size
int >= 1 |
(number of tree constraints) |
base.n.name
String |
(name of tree constraint n) |
base.n.init
classname, inherits and != ec.gp.GPNodeBuilder |
(GP node builder for tree constraint n) |
base.n.returns
String |
(tree type for tree constraint n) |
base.n.fset
String |
(function set for tree constraint n) |
* @author Sean Luke
* @version 1.0
*/
public class GPTreeConstraints implements Clique
{
public static final int SIZE_OF_BYTE = 256;
public final static String P_NAME = "name";
public final static String P_SIZE = "size";
public final static String P_INIT = "init";
public static final String P_RETURNS = "returns";
public static final String P_FUNCTIONSET = "fset";
public String name;
/** The byte value of the constraints -- we can only have 256 of them */
public byte constraintNumber;
/** The builder for the tree */
public GPNodeBuilder init;
/** The type of the root of the tree */
public GPType treetype;
/** The function set for nodes in the tree */
public GPFunctionSet functionset;
public String toString() { return name; }
/** This must be called after the GPTypes and GPFunctionSets
have been set up. */
public final void setup(final EvolutionState state, final Parameter base)
{
// What's my name?
name = state.parameters.getString(base.push(P_NAME),null);
if (name==null)
state.output.fatal("No name was given for this function set.",
base.push(P_NAME));
// Register me
GPTreeConstraints old_constraints =
(GPTreeConstraints)(((GPInitializer)state.initializer).treeConstraintRepository.put(name,this));
if (old_constraints != null)
state.output.fatal("The GP tree constraint \"" + name + "\" has been defined multiple times.", base.push(P_NAME));
// Load my initializing builder
init = (GPNodeBuilder)(state.parameters.getInstanceForParameter(base.push(P_INIT),null,GPNodeBuilder.class));
init.setup(state,base.push(P_INIT));
// Load my return type
String s = state.parameters.getString(base.push(P_RETURNS),null);
if (s==null)
state.output.fatal("No return type given for the GPTreeConstraints " + name, base.push(P_RETURNS));
treetype = GPType.typeFor(s,state);
// Load my function set
s = state.parameters.getString(base.push(P_FUNCTIONSET),null);
if (s==null)
state.output.fatal("No function set given for the GPTreeConstraints " + name, base.push(P_RETURNS));
functionset = GPFunctionSet.functionSetFor(s,state);
state.output.exitIfErrors(); // otherwise checkFunctionSetValidity might crash below
// Determine the validity of the function set
// the way we do that is by gathering all the types that
// are transitively used, starting with treetype, as in:
Hashtable typ = new Hashtable();
checkFunctionSetValidity(state, typ, treetype);
// next we make sure that for every one of these types,
// there's a terminal with that return type, and *maybe*
// a nonterminal
Enumeration e = typ.elements();
while (e.hasMoreElements())
{
GPType t = (GPType)(e.nextElement());
GPNode[] i = functionset.nodes[t.type];
if (i.length==0) // yeesh
state.output.error("In function set " + functionset + " for the GPTreeConstraints " + this + ", no nodes at all are given with the return type " + t + " which is required by other functions in the function set or by the tree's return type. This almost certainly indicates a serious typing error.", base);
else
{
i = functionset.terminals[t.type];
if (i.length==0) // uh oh
state.output.warning("In function set " + functionset + " for the GPTreeConstraints " + this + ", no terminals are given with the return type " + t + " which is required by other functions in the function set or by the tree's return type. Nearly all tree-builders in ECJ require the ability to add a terminal of any type for which there is a nonterminal, and at any time. Without terminals, your code may not work. One common indication that a tree-builder has failed due to this problem is if you get the MersenneTwister error 'n must be positive'.", base);
i = functionset.nonterminals[t.type];
if (i.length==0) // uh oh
state.output.warning("In function set " + functionset + " for the GPTreeConstraints " + this + ", no *nonterminals* are given with the return type " + t + " which is required by other functions in the function set or by the tree's return type. This may or may not be a problem for you.", base);
}
}
state.output.exitIfErrors();
}
// When completed, done will hold all the types which are needed
// in the function set -- you can then check to make sure that
// they contain at least one terminal and (hopefully) at least
// one nonterminal.
private void checkFunctionSetValidity(final EvolutionState state,
final Hashtable done,
final GPType type)
{
// put type in the hashtable -- it's being used
done.put(type,type);
// Grab the array in nodes
GPNode[] i = functionset.nodes[type.type];
// For each argument type in a node in i, if it's not in done,
// then add it to done and call me on it
GPInitializer initializer = ((GPInitializer)state.initializer);
for (int x=0; x