[6152] | 1 | /* |
---|
| 2 | Copyright 2006 by Sean Luke |
---|
| 3 | Licensed under the Academic Free License version 3.0 |
---|
| 4 | See the file "LICENSE" for more information |
---|
| 5 | */ |
---|
| 6 | |
---|
| 7 | |
---|
| 8 | package ec.gp; |
---|
| 9 | import ec.*; |
---|
| 10 | import ec.util.*; |
---|
| 11 | |
---|
| 12 | /* |
---|
| 13 | * GPNodeBuilder.java |
---|
| 14 | * |
---|
| 15 | * Created: Thu Oct 7 17:31:41 1999 |
---|
| 16 | * By: Sean Luke |
---|
| 17 | */ |
---|
| 18 | |
---|
| 19 | /** |
---|
| 20 | * GPNodeBuilder is a Prototype which defines the superclass for objects |
---|
| 21 | * which create ("grow") GP trees, whether for population initialization, |
---|
| 22 | * subtree mutation, or whatnot. It defines a single abstract method, |
---|
| 23 | * newRootedTree(...), which must be implemented to grow the tree. |
---|
| 24 | * |
---|
| 25 | * <p>GPNodeBuilder also provides some facilities for user-specification |
---|
| 26 | * of probabilities of various tree sizes, which the tree builder can use |
---|
| 27 | * as it sees fit (or totally ignore). |
---|
| 28 | * There are two such facilities. First, the user might |
---|
| 29 | * specify a minimum and maximum range for tree sizes to be picked from; |
---|
| 30 | * trees would likely be picked uniformly from this range. Second, the |
---|
| 31 | * user might specify an array, <tt>num-sizes</tt> long, of probabilities of |
---|
| 32 | * tree sizes, in order to give a precise probability distribution. |
---|
| 33 | |
---|
| 34 | <p><b>Parameters</b><br> |
---|
| 35 | <table> |
---|
| 36 | <tr><td valign=top><i>base</i>.<tt>min-size</tt><br> |
---|
| 37 | <font size=-1>int >= 1, or undefined</font></td> |
---|
| 38 | <td valign=top>(smallest valid size, see discussion above)</td></tr> |
---|
| 39 | |
---|
| 40 | <tr><td valign=top><i>base</i>.<tt>max-size</tt><br> |
---|
| 41 | <font size=-1>int >= <tt>min-size</tt>, or undefined</font></td> |
---|
| 42 | <td valign=top>(largest valid size, see discussion above)</td></tr> |
---|
| 43 | |
---|
| 44 | <tr><td valign=top><i>base</i>.<tt>num-sizes</tt><br> |
---|
| 45 | <font size=-1>int >= 1, or underfined</font></td> |
---|
| 46 | <td valign=top>(number of sizes in the size distribution, see discussion above) |
---|
| 47 | </td></tr> |
---|
| 48 | |
---|
| 49 | <tr><td valign=top><i>base</i>.<tt>size</tt>.<i>n</i><br> |
---|
| 50 | <font size=-1>0.0 <= float <= 1.0</font>, or undefined</td> |
---|
| 51 | <td valign=top>(probability of choosing size <i>n</i>. See discussion above) |
---|
| 52 | </td></tr> |
---|
| 53 | </table> |
---|
| 54 | |
---|
| 55 | |
---|
| 56 | * @author Sean Luke |
---|
| 57 | * @version 1.0 |
---|
| 58 | */ |
---|
| 59 | |
---|
| 60 | public abstract class GPNodeBuilder implements Prototype |
---|
| 61 | { |
---|
| 62 | /** Produces a new rooted tree of GPNodes whose root's return type is |
---|
| 63 | swap-compatible with <i>type</i>. When you build a brand-new |
---|
| 64 | tree out of GPNodes cloned from the |
---|
| 65 | prototypes stored in the GPNode[] arrays, you must remember |
---|
| 66 | to call resetNode() on each cloned GPNode. This gives ERCs a chance |
---|
| 67 | to randomize themselves and set themselves up. |
---|
| 68 | |
---|
| 69 | <p>requestedSize is an |
---|
| 70 | optional argument which differs based on the GPNodeBuilder used. |
---|
| 71 | Typically it is set to a tree size that the calling method wants |
---|
| 72 | the GPNodeBuilder to produce; the GPNodeBuilder is not obligated to |
---|
| 73 | produce a tree of this size, but it should attempt to interpret this |
---|
| 74 | argument as appropriate for the given algorithm. To indicate that |
---|
| 75 | you don't care what size the tree should be, you can pass NOSIZEGIVEN. |
---|
| 76 | However if the algorithm <i>requires</i> you to provide a size, it |
---|
| 77 | will generate a fatal error to let you know. */ |
---|
| 78 | |
---|
| 79 | public static final int NOSIZEGIVEN = -1; |
---|
| 80 | public static final int CHECK_BOUNDARY = 8; |
---|
| 81 | public static final String P_MINSIZE = "min-size"; |
---|
| 82 | public static final String P_MAXSIZE = "max-size"; |
---|
| 83 | public static final String P_NUMSIZES = "num-sizes"; |
---|
| 84 | public static final String P_SIZE = "size"; |
---|
| 85 | |
---|
| 86 | public int minSize; /** the minium possible size -- if unused, it's 0 */ |
---|
| 87 | public int maxSize; /** the maximum possible size -- if unused, it's 0 */ |
---|
| 88 | public float[] sizeDistribution; /* sizeDistribution[x] represents |
---|
| 89 | the likelihood of size x appearing |
---|
| 90 | -- if unused, it's null */ |
---|
| 91 | |
---|
| 92 | /** Returns true if some size distribution (either minSize and maxSize, |
---|
| 93 | or sizeDistribution) is set up by the user in order to pick sizes randomly. */ |
---|
| 94 | public boolean canPick() |
---|
| 95 | { |
---|
| 96 | return (minSize!=0 || sizeDistribution !=null); |
---|
| 97 | } |
---|
| 98 | |
---|
| 99 | /** Assuming that either minSize and maxSize, or sizeDistribution, is defined, |
---|
| 100 | picks a random size from minSize...maxSize inclusive, or randomly |
---|
| 101 | from sizeDistribution. */ |
---|
| 102 | public int pickSize(final EvolutionState state, final int thread) |
---|
| 103 | { |
---|
| 104 | if (minSize>0) |
---|
| 105 | { |
---|
| 106 | // pick from minSize...maxSize |
---|
| 107 | return state.random[thread].nextInt(maxSize-minSize+1) + minSize; |
---|
| 108 | } |
---|
| 109 | else if (sizeDistribution!=null) |
---|
| 110 | { |
---|
| 111 | // pick from distribution |
---|
| 112 | return RandomChoice.pickFromDistribution( |
---|
| 113 | sizeDistribution, |
---|
| 114 | state.random[thread].nextFloat()) + 1 ; |
---|
| 115 | } |
---|
| 116 | else throw new InternalError("Neither minSize nor sizeDistribution is defined in GPNodeBuilder"); |
---|
| 117 | } |
---|
| 118 | |
---|
| 119 | |
---|
| 120 | public Object clone() |
---|
| 121 | { |
---|
| 122 | try |
---|
| 123 | { |
---|
| 124 | GPNodeBuilder c = (GPNodeBuilder)(super.clone()); |
---|
| 125 | |
---|
| 126 | if (sizeDistribution != null) c.sizeDistribution = |
---|
| 127 | (float[]) (sizeDistribution.clone()); |
---|
| 128 | |
---|
| 129 | return c; |
---|
| 130 | } |
---|
| 131 | catch (CloneNotSupportedException e) |
---|
| 132 | { throw new InternalError(); } // never happens |
---|
| 133 | } |
---|
| 134 | |
---|
| 135 | |
---|
| 136 | |
---|
| 137 | |
---|
| 138 | public void setup(final EvolutionState state, final Parameter base) |
---|
| 139 | { |
---|
| 140 | Parameter def = defaultBase(); |
---|
| 141 | |
---|
| 142 | // min and max size |
---|
| 143 | |
---|
| 144 | if (state.parameters.exists(base.push(P_MINSIZE), |
---|
| 145 | def.push(P_MINSIZE))) |
---|
| 146 | { |
---|
| 147 | if (!(state.parameters.exists(base.push(P_MAXSIZE), |
---|
| 148 | def.push(P_MAXSIZE)))) |
---|
| 149 | state.output.fatal("This GPNodeBuilder has a " + |
---|
| 150 | P_MINSIZE + " but not a " + P_MAXSIZE + "."); |
---|
| 151 | |
---|
| 152 | minSize = state.parameters.getInt( |
---|
| 153 | base.push(P_MINSIZE), def.push(P_MINSIZE),1); |
---|
| 154 | if (minSize==0) |
---|
| 155 | state.output.fatal("The GPNodeBuilder must have a min size >= 1.", |
---|
| 156 | base.push(P_MINSIZE), def.push(P_MINSIZE)); |
---|
| 157 | |
---|
| 158 | maxSize = state.parameters.getInt( |
---|
| 159 | base.push(P_MAXSIZE), def.push(P_MAXSIZE),1); |
---|
| 160 | if (maxSize==0) |
---|
| 161 | state.output.fatal("The GPNodeBuilder must have a max size >= 1.", |
---|
| 162 | base.push(P_MAXSIZE), def.push(P_MAXSIZE)); |
---|
| 163 | |
---|
| 164 | if (minSize > maxSize) |
---|
| 165 | state.output.fatal( |
---|
| 166 | "The GPNodeBuilder must have min size <= max size.", |
---|
| 167 | base.push(P_MINSIZE), def.push(P_MINSIZE)); |
---|
| 168 | } |
---|
| 169 | else if (state.parameters.exists(base.push(P_MAXSIZE), |
---|
| 170 | def.push(P_MAXSIZE))) |
---|
| 171 | state.output.fatal("This GPNodeBuilder has a " + |
---|
| 172 | P_MAXSIZE + " but not a " + P_MINSIZE + ".", |
---|
| 173 | base.push(P_MAXSIZE), def.push(P_MAXSIZE)); |
---|
| 174 | |
---|
| 175 | // load sizeDistribution |
---|
| 176 | |
---|
| 177 | else if (state.parameters.exists(base.push(P_NUMSIZES), |
---|
| 178 | def.push(P_NUMSIZES))) |
---|
| 179 | { |
---|
| 180 | int siz = state.parameters.getInt( |
---|
| 181 | base.push(P_NUMSIZES), def.push(P_NUMSIZES),1); |
---|
| 182 | if (siz==0) |
---|
| 183 | state.output.fatal("The number of sizes in the GPNodeBuilder's distribution must be >= 1. "); |
---|
| 184 | sizeDistribution = new float[siz]; |
---|
| 185 | if (state.parameters.exists(base.push(P_SIZE).push("0"), |
---|
| 186 | def.push(P_SIZE).push("0"))) |
---|
| 187 | state.output.warning( |
---|
| 188 | "GPNodeBuilder does not use size #0 in the distribution", |
---|
| 189 | base.push(P_SIZE).push("0"), |
---|
| 190 | def.push(P_SIZE).push("0")); |
---|
| 191 | |
---|
| 192 | float sum = 0.0f; |
---|
| 193 | for(int x=0;x<siz;x++) |
---|
| 194 | { |
---|
| 195 | sizeDistribution[x] = state.parameters.getFloat( |
---|
| 196 | base.push(P_SIZE).push(""+(x+1)), |
---|
| 197 | def.push(P_SIZE).push(""+(x+1)), 0.0f); |
---|
| 198 | if (sizeDistribution[x]<0.0) |
---|
| 199 | { |
---|
| 200 | state.output.warning( |
---|
| 201 | "Distribution value #" + x + " negative or not defined, assumed to be 0.0", |
---|
| 202 | base.push(P_SIZE).push(""+(x+1)), |
---|
| 203 | def.push(P_SIZE).push(""+(x+1))); |
---|
| 204 | sizeDistribution[x] = 0.0f; |
---|
| 205 | } |
---|
| 206 | sum += sizeDistribution[x]; |
---|
| 207 | } |
---|
| 208 | if (sum>1.0) |
---|
| 209 | state.output.warning( |
---|
| 210 | "Distribution sums to greater than 1.0", |
---|
| 211 | base.push(P_SIZE), |
---|
| 212 | def.push(P_SIZE)); |
---|
| 213 | if (sum==0.0) |
---|
| 214 | state.output.fatal( |
---|
| 215 | "Distribution is all 0's", |
---|
| 216 | base.push(P_SIZE), |
---|
| 217 | def.push(P_SIZE)); |
---|
| 218 | |
---|
| 219 | // normalize and prepare |
---|
| 220 | RandomChoice.organizeDistribution(sizeDistribution); |
---|
| 221 | } |
---|
| 222 | } |
---|
| 223 | |
---|
| 224 | public abstract GPNode newRootedTree(final EvolutionState state, |
---|
| 225 | final GPType type, |
---|
| 226 | final int thread, |
---|
| 227 | final GPNodeParent parent, |
---|
| 228 | final GPFunctionSet set, |
---|
| 229 | final int argposition, |
---|
| 230 | final int requestedSize); |
---|
| 231 | |
---|
| 232 | /** Issues a warning that no terminal was found with a return type of the given type, and that an algorithm |
---|
| 233 | had requested one. If fail is true, then a fatal is issued rather than a warning. The warning takes |
---|
| 234 | the form of a one-time big explanatory message, followed by a one-time-per-type message. */ |
---|
| 235 | protected void warnAboutNoTerminalWithType(GPType type, boolean fail, EvolutionState state) |
---|
| 236 | { |
---|
| 237 | // big explanation -- appears only once |
---|
| 238 | state.output.warnOnce("A GPNodeBuilder has been requested at least once to generate a one-node tree with " + |
---|
| 239 | "a return value type-compatable with a certain type; but there is no TERMINAL which is type-compatable " + |
---|
| 240 | "in this way. As a result, the algorithm was forced to use a NON-TERMINAL, making the tree larger than " + |
---|
| 241 | "requested, and exposing more child slots to fill, which if not carefully considered, could " + |
---|
| 242 | "recursively repeat this problem and eventually fill all memory."); |
---|
| 243 | |
---|
| 244 | // shorter explanation -- appears for each node builder and type combo |
---|
| 245 | if (fail) |
---|
| 246 | state.output.fatal("" + this.getClass() + " can't find a terminal type-compatable with " + type + |
---|
| 247 | " and cannot replace it with a nonterminal. You may need to try a different node-builder algorithm."); |
---|
| 248 | else |
---|
| 249 | state.output.warnOnce("" + this.getClass() + " can't find a terminal type-compatable with " + type); |
---|
| 250 | } |
---|
| 251 | |
---|
| 252 | /** If the given test is true, issues a warning that no terminal was found with a return type of the given type, and that an algorithm |
---|
| 253 | had requested one. If fail is true, then a fatal is issued rather than a warning. The warning takes |
---|
| 254 | the form of a one-time big explanatory message, followed by a one-time-per-type message. Returns the value of the test. |
---|
| 255 | This form makes it easy to insert warnings into if-statements. */ |
---|
| 256 | protected boolean warnAboutNonterminal(boolean test, GPType type, boolean fail, EvolutionState state) |
---|
| 257 | { |
---|
| 258 | if (test) warnAboutNonTerminalWithType(type, fail, state); |
---|
| 259 | return test; |
---|
| 260 | } |
---|
| 261 | |
---|
| 262 | /** Issues a warning that no nonterminal was found with a return type of the given type, and that an algorithm |
---|
| 263 | had requested one. If fail is true, then a fatal is issued rather than a warning. The warning takes |
---|
| 264 | the form of a one-time big explanatory message, followed by a one-time-per-type message. */ |
---|
| 265 | protected void warnAboutNonTerminalWithType(GPType type, boolean fail, EvolutionState state) |
---|
| 266 | { |
---|
| 267 | // big explanation -- appears only once |
---|
| 268 | state.output.warnOnce("A GPNodeBuilder has been requested at least once to generate a tree with " + |
---|
| 269 | "a return value type-compatable with a certain type; but there is no NON-TERMINAL which is type-compatable " + |
---|
| 270 | "in this way. As a result, the algorithm was forced to use a TERMINAL, making the tree smaller than " + |
---|
| 271 | "requested."); |
---|
| 272 | |
---|
| 273 | // shorter explanation -- appears for each node builder and type combo |
---|
| 274 | if (fail) |
---|
| 275 | state.output.fatal("" + this.getClass() + " can't find a non-terminal type-compatable with " + type + |
---|
| 276 | " and cannot replace it with a terminal. You may need to try a different node-builder algorithm."); |
---|
| 277 | else |
---|
| 278 | state.output.warnOnce("" + this.getClass() + " can't find a non-terminal type-compatable with " + type); |
---|
| 279 | } |
---|
| 280 | |
---|
| 281 | /** Issues a fatal error that no node (nonterminal or terminal) was found with a return type of the given type, and that an algorithm |
---|
| 282 | had requested one. */ |
---|
| 283 | protected void errorAboutNoNodeWithType(GPType type, EvolutionState state) |
---|
| 284 | { |
---|
| 285 | state.output.fatal("" + this.getClass() + " could find no terminal or nonterminal type-compatable with " + type); |
---|
| 286 | } |
---|
| 287 | } |
---|