/* 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