[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 java.io.*; |
---|
| 11 | import ec.util.*; |
---|
| 12 | import ec.EvolutionState; |
---|
| 13 | |
---|
| 14 | /* |
---|
| 15 | * GPNode.java |
---|
| 16 | * |
---|
| 17 | * Created: Fri Aug 27 17:14:12 1999 |
---|
| 18 | * By: Sean Luke |
---|
| 19 | */ |
---|
| 20 | |
---|
| 21 | /** |
---|
| 22 | * GPNode is a GPNodeParent which is the abstract superclass of |
---|
| 23 | * all GP function nodes in trees. GPNode contains quite a few functions |
---|
| 24 | * for cloning subtrees in special ways, counting the number of nodes |
---|
| 25 | * in subtrees in special ways, and finding specific nodes in subtrees. |
---|
| 26 | * |
---|
| 27 | * GPNode's lightClone() method does not clone its children (it copies the |
---|
| 28 | * array, but that's it). If you want to deep-clone a tree or subtree, you |
---|
| 29 | * should use one of the cloneReplacing(...) methods instead. |
---|
| 30 | * |
---|
| 31 | * <p>GPNodes contain a number of important items: |
---|
| 32 | * <ul><li>A <i>constraints</i> object which defines the name of the node, |
---|
| 33 | * its arity, and its type constraints. This |
---|
| 34 | * object is shared with all GPNodes of the same function name/arity/returntype/childtypes. |
---|
| 35 | * <li>A <i>parent</i>. This is either another GPNode, or (if this node |
---|
| 36 | * is the root) a GPTree. |
---|
| 37 | * <li>Zero or more <i>children</i>, which are GPNodes. |
---|
| 38 | * <li>An argument position in its parent. |
---|
| 39 | * </ul> |
---|
| 40 | * |
---|
| 41 | |
---|
| 42 | * <p>In addition to serialization for checkpointing, GPNodes may read and write themselves to streams in three ways. |
---|
| 43 | * |
---|
| 44 | * <ul> |
---|
| 45 | * <li><b>writeNode(...,DataOutput)/readNode(...,DataInput)</b> This method |
---|
| 46 | * transmits or receives a GPNode in binary. It is the most efficient approach to sending |
---|
| 47 | * GPNodes over networks, etc. The default versions of writeNode/readNode both generate errors. |
---|
| 48 | * GPNode subclasses should override them to provide more functionality, particularly if you're planning on using |
---|
| 49 | * ECJ in a distributed fashion. Both of these functions are called by GPNode's readRootedTree/writeRootedTree |
---|
| 50 | * respectively, which handle the reading/printing of the trees as a whole. |
---|
| 51 | * |
---|
| 52 | * <li><b>printNode(...,PrintWriter)/readNode(...,LineNumberReader)</b> This |
---|
| 53 | * approach transmits or receives a GPNode in text encoded such that the GPNode is largely readable |
---|
| 54 | * by humans but can be read back in 100% by ECJ as well. To do this, these methods will typically encode numbers |
---|
| 55 | * using the <tt>ec.util.Code</tt> class. These methods are mostly used to write out populations to |
---|
| 56 | * files for inspection, slight modification, then reading back in later on. Both of these functions are called by GPNode's readRootedTree/writeRootedTree |
---|
| 57 | * respectively, which handle the reading/printing of the trees as a whole. Notably readRootedNode |
---|
| 58 | * will try to determine what kind of node is next, then call <b>readNode</b> on the prototype for that |
---|
| 59 | * node to generate the node. <b>printNode</b> by default calls toString() and |
---|
| 60 | * prints the result, though subclasses often override this to provide additional functionality (notably |
---|
| 61 | * ERCs). |
---|
| 62 | * |
---|
| 63 | * <li><b>printNodeForHumans(...,PrintWriter)</b> This |
---|
| 64 | * approach prints a GPNode in a fashion intended for human consumption only. |
---|
| 65 | * <b>printNodeForHumans</b> by default calls toStringForHumans() (which by default calls toString()) and |
---|
| 66 | * prints the result. printNodeForHumans is called by <b>printRootedTreeForHumans</b>, which handles |
---|
| 67 | * printing of the entire GPNode tree. |
---|
| 68 | * </ul> |
---|
| 69 | |
---|
| 70 | |
---|
| 71 | <p><b>Parameters</b><br> |
---|
| 72 | <table> |
---|
| 73 | <tr><td valign=top><i>base</i>.<tt>nc</tt><br> |
---|
| 74 | <font size=-1>String</font></td> |
---|
| 75 | <td valign=top>(name of the node constraints for the GPNode)</td></tr> |
---|
| 76 | </table> |
---|
| 77 | |
---|
| 78 | <p><b>Default Base</b><br> |
---|
| 79 | gp.node |
---|
| 80 | |
---|
| 81 | * |
---|
| 82 | * @author Sean Luke |
---|
| 83 | * @version 1.0 |
---|
| 84 | */ |
---|
| 85 | |
---|
| 86 | public abstract class GPNode implements GPNodeParent, Prototype |
---|
| 87 | { |
---|
| 88 | public static final String P_NODE = "node"; |
---|
| 89 | public static final String P_NODECONSTRAINTS = "nc"; |
---|
| 90 | public static final String GPNODEPRINTTAB = " "; |
---|
| 91 | public static final int MAXPRINTBYTES = 40; |
---|
| 92 | |
---|
| 93 | public static final int NODESEARCH_ALL = 0; |
---|
| 94 | public static final int NODESEARCH_TERMINALS = 1; |
---|
| 95 | public static final int NODESEARCH_NONTERMINALS = 2; |
---|
| 96 | public static final int NODESEARCH_CUSTOM = 3; |
---|
| 97 | |
---|
| 98 | public static final int SITUATION_NEWIND = 0; |
---|
| 99 | public static final int SITUATION_MUTATION = 1; |
---|
| 100 | |
---|
| 101 | // beats me if Java compilers will take advantage of the int->byte shortening. |
---|
| 102 | // They may want everything aligned, in which case they may buffer the object |
---|
| 103 | // anyway, hope not! |
---|
| 104 | |
---|
| 105 | /** The GPNode's parent. 4 bytes. :-( But it really helps simplify breeding. */ |
---|
| 106 | public GPNodeParent parent; |
---|
| 107 | public GPNode children[]; |
---|
| 108 | /** The argument position of the child in its parent. |
---|
| 109 | This is a byte to save space (GPNode is the critical object space-wise) -- |
---|
| 110 | besides, how often do you have 256 children? You can change this to a short |
---|
| 111 | or int easily if you absolutely need to. It's possible to eliminate even |
---|
| 112 | this and have the child find itself in its parent, but that's an O(children[]) |
---|
| 113 | operation, and probably not inlinable, so I figure a byte is okay. */ |
---|
| 114 | public byte argposition; |
---|
| 115 | /** The GPNode's constraints. This is a byte to save space -- how often do |
---|
| 116 | you have 256 different GPNodeConstraints? Well, I guess it's not infeasible. |
---|
| 117 | You can increase this to an int without much trouble. You typically |
---|
| 118 | shouldn't access the constraints through this variable -- use the constraints(state) |
---|
| 119 | method instead. */ |
---|
| 120 | public byte constraints; |
---|
| 121 | |
---|
| 122 | /* Returns the GPNode's constraints. A good JIT compiler should inline this. */ |
---|
| 123 | public final GPNodeConstraints constraints(final GPInitializer initializer) |
---|
| 124 | { |
---|
| 125 | return initializer.nodeConstraints[constraints]; |
---|
| 126 | } |
---|
| 127 | |
---|
| 128 | /** The default base for GPNodes -- defined even though |
---|
| 129 | GPNode is abstract so you don't have to in subclasses. */ |
---|
| 130 | public Parameter defaultBase() |
---|
| 131 | { |
---|
| 132 | return GPDefaults.base().push(P_NODE); |
---|
| 133 | } |
---|
| 134 | |
---|
| 135 | /** You ought to override this method to check to make sure that the |
---|
| 136 | constraints are valid as best you can tell. Things you might |
---|
| 137 | check for: |
---|
| 138 | |
---|
| 139 | <ul> |
---|
| 140 | <li> children.length is correct |
---|
| 141 | <li> certain arguments in constraints.childtypes are |
---|
| 142 | swap-compatible with each other |
---|
| 143 | <li> constraints.returntype is swap-compatible with appropriate |
---|
| 144 | arguments in constraints.childtypes |
---|
| 145 | </ul> |
---|
| 146 | |
---|
| 147 | You can't check for everything, of course, but you might try some |
---|
| 148 | obvious checks for blunders. The default version of this method |
---|
| 149 | is empty for now, but you should still call super.checkConstraints(state) |
---|
| 150 | just to be certain. |
---|
| 151 | |
---|
| 152 | The ultimate caller of this method must guarantee that he will eventually |
---|
| 153 | call state.output.exitIfErrors(), so you can freely use state.output.error |
---|
| 154 | instead of state.output.fatal(), which will help a lot. |
---|
| 155 | |
---|
| 156 | Warning: this method may get called more than once. |
---|
| 157 | */ |
---|
| 158 | |
---|
| 159 | public void checkConstraints(final EvolutionState state, |
---|
| 160 | final int tree, |
---|
| 161 | final GPIndividual typicalIndividual, |
---|
| 162 | final Parameter individualBase) |
---|
| 163 | { } |
---|
| 164 | |
---|
| 165 | /** |
---|
| 166 | Sets up a <i>prototypical</i> GPNode with those features all nodes of that |
---|
| 167 | prototype share, and nothing more. So no filled-in children, |
---|
| 168 | no argposition, no parent. Yet. |
---|
| 169 | |
---|
| 170 | This must be called <i>after</i> the GPTypes and GPNodeConstraints |
---|
| 171 | have been set up. Presently they're set up in GPInitializer, |
---|
| 172 | which gets called before this does, so we're safe. |
---|
| 173 | |
---|
| 174 | You should override this if you need to load some special features on |
---|
| 175 | a per-function basis. Note that base hangs off of a function set, so |
---|
| 176 | this method may get called for different instances in the same GPNode |
---|
| 177 | class if they're being set up as prototypes for different GPFunctionSets. |
---|
| 178 | |
---|
| 179 | If you absolutely need some global base, then you should use something |
---|
| 180 | hanging off of GPDefaults.base(). |
---|
| 181 | |
---|
| 182 | The ultimate caller of this method must guarantee that he will eventually |
---|
| 183 | call state.output.exitIfErrors(), so you can freely use state.output.error |
---|
| 184 | instead of state.output.fatal(), which will help a lot. |
---|
| 185 | */ |
---|
| 186 | |
---|
| 187 | public void setup(final EvolutionState state, final Parameter base) |
---|
| 188 | { |
---|
| 189 | Parameter def = defaultBase(); |
---|
| 190 | |
---|
| 191 | // determine my constraints -- at this point, the constraints should have been loaded. |
---|
| 192 | String s = state.parameters.getString(base.push(P_NODECONSTRAINTS), |
---|
| 193 | def.push(P_NODECONSTRAINTS)); |
---|
| 194 | if (s==null) |
---|
| 195 | state.output.fatal("No node constraints are defined for the GPNode " + |
---|
| 196 | toStringForError(),base.push(P_NODECONSTRAINTS), |
---|
| 197 | def.push(P_NODECONSTRAINTS)); |
---|
| 198 | else constraints = GPNodeConstraints.constraintsFor(s,state).constraintNumber; |
---|
| 199 | |
---|
| 200 | // The number of children is determined by the constraints. Though |
---|
| 201 | // for some special versions of GPNode, we may have to enforce certain |
---|
| 202 | // rules, checked in children versions of setup(...) |
---|
| 203 | |
---|
| 204 | GPNodeConstraints constraintsObj = constraints(((GPInitializer)state.initializer)); |
---|
| 205 | int len = constraintsObj.childtypes.length; |
---|
| 206 | if (len == 0) children = constraintsObj.zeroChildren; |
---|
| 207 | else children = new GPNode[len]; |
---|
| 208 | } |
---|
| 209 | |
---|
| 210 | /** Returns the argument type of the slot that I fit into in my parent. |
---|
| 211 | If I'm the root, returns the treetype of the GPTree. */ |
---|
| 212 | public final GPType parentType(final GPInitializer initializer) |
---|
| 213 | { |
---|
| 214 | if (parent instanceof GPNode) |
---|
| 215 | return ((GPNode)parent).constraints(initializer).childtypes[argposition]; |
---|
| 216 | else // it's a tree root |
---|
| 217 | return ((GPTree)parent).constraints(initializer).treetype; |
---|
| 218 | } |
---|
| 219 | |
---|
| 220 | |
---|
| 221 | /** Verification of validity of the node in the tree -- strictly for debugging purposes only */ |
---|
| 222 | final int verify(EvolutionState state, GPFunctionSet set, int index) |
---|
| 223 | { |
---|
| 224 | if (!(state.initializer instanceof GPInitializer)) |
---|
| 225 | { state.output.error("" + index + ": Initializer is not a GPInitializer"); return index+1; } |
---|
| 226 | |
---|
| 227 | GPInitializer initializer = (GPInitializer)(state.initializer); |
---|
| 228 | |
---|
| 229 | // 1. Is the parent and argposition right? |
---|
| 230 | if (parent == null) |
---|
| 231 | { state.output.error("" + index + ": null parent"); return index+1; } |
---|
| 232 | if (argposition < 0) |
---|
| 233 | { state.output.error("" + index + ": negative argposition"); return index+1; } |
---|
| 234 | if (parent instanceof GPTree && ((GPTree)parent).child != this) |
---|
| 235 | { state.output.error("" + index + ": I think I am a root node, but my GPTree does not think I am a root node"); return index+1; } |
---|
| 236 | if (parent instanceof GPTree && argposition != 0) |
---|
| 237 | { state.output.error("" + index + ": I think I am a root node, but my argposition is not 0"); return index+1; } |
---|
| 238 | if (parent instanceof GPNode && argposition >= ((GPNode)parent).children.length) |
---|
| 239 | { state.output.error("" + index + ": argposition outside range of parent's children array"); return index+1; } |
---|
| 240 | if (parent instanceof GPNode && ((GPNode)parent).children[argposition] != this) |
---|
| 241 | { state.output.error("" + index + ": I am not found in the provided argposition ("+argposition+") of my parent's children array"); return index+1; } |
---|
| 242 | |
---|
| 243 | // 2. Are the parents and argpositions right for my kids? [need to double check] |
---|
| 244 | if (children==null) |
---|
| 245 | { state.output.error("" + index + ": Null Children Array"); return index+1; } |
---|
| 246 | for(int x=0;x<children.length;x++) |
---|
| 247 | { |
---|
| 248 | if (children[x] == null) |
---|
| 249 | { state.output.error("" + index + ": Null Child (#" + x + " )"); return index+1; } |
---|
| 250 | if (children[x].parent != this) |
---|
| 251 | { state.output.error("" + index + ": child #"+x+" does not have me as a parent"); return index+1; } |
---|
| 252 | if (children[x].argposition < 0) |
---|
| 253 | { state.output.error("" + index + ": child #"+x+" argposition is negative"); return index+1; } |
---|
| 254 | if (children[x].argposition != x) |
---|
| 255 | { state.output.error("" + index + ": child #"+x+" argposition does not match position in the children array"); return index+1; } |
---|
| 256 | } |
---|
| 257 | |
---|
| 258 | // 3. Do I have valid constraints? |
---|
| 259 | if (constraints < 0 || constraints >= initializer.numNodeConstraints) |
---|
| 260 | { state.output.error("" + index + ": Preposterous node constraints (" + constraints + ")"); return index+1; } |
---|
| 261 | |
---|
| 262 | // 4. Am I swap-compatable with my parent? |
---|
| 263 | if (parent instanceof GPNode && !constraints(initializer).returntype.compatibleWith(initializer, |
---|
| 264 | ((GPNode)(parent)).constraints(initializer).childtypes[argposition])) |
---|
| 265 | { state.output.error("" + index + ": Incompatable GP type between me and my parent"); return index+1; } |
---|
| 266 | if (parent instanceof GPTree && !constraints(initializer).returntype.compatibleWith(initializer, |
---|
| 267 | ((GPTree)(parent)).constraints(initializer).treetype)) |
---|
| 268 | { state.output.error("" + index + ": I am root, but incompatable GP type between me and my tree return type"); return index+1; } |
---|
| 269 | |
---|
| 270 | // 5. Is my class in the GPFunctionSet? |
---|
| 271 | GPNode[] nodes = set.nodesByArity[constraints(initializer).returntype.type][children.length]; |
---|
| 272 | boolean there = false; |
---|
| 273 | for(int x=0;x<nodes.length;x++) |
---|
| 274 | if (nodes[x].getClass() == this.getClass()) { there = true; break; } |
---|
| 275 | if (!there) |
---|
| 276 | { state.output.error("" + index + ": I'm not in the function set."); return index+1; } |
---|
| 277 | |
---|
| 278 | // otherwise we've passed -- go to next node |
---|
| 279 | index++; |
---|
| 280 | for(int x=0;x<children.length;x++) |
---|
| 281 | index = children[x].verify(state, set, index); |
---|
| 282 | state.output.exitIfErrors(); |
---|
| 283 | return index; |
---|
| 284 | } |
---|
| 285 | |
---|
| 286 | |
---|
| 287 | /** Returns true if I can swap into node's position. */ |
---|
| 288 | |
---|
| 289 | public final boolean swapCompatibleWith(final GPInitializer initializer, |
---|
| 290 | final GPNode node) |
---|
| 291 | { |
---|
| 292 | // I'm atomically compatible with him; a fast check |
---|
| 293 | if (constraints(initializer).returntype==node.constraints(initializer).returntype) // no need to check for compatibility |
---|
| 294 | return true; |
---|
| 295 | |
---|
| 296 | // I'm set compatible with his parent's swap-position |
---|
| 297 | GPType type; |
---|
| 298 | if (node.parent instanceof GPNode) // it's a GPNode |
---|
| 299 | type = |
---|
| 300 | ((GPNode)(node.parent)).constraints(initializer).childtypes[node.argposition]; |
---|
| 301 | else // it's a tree root; I'm set compatible with the GPTree type |
---|
| 302 | type = |
---|
| 303 | ((GPTree)(node.parent)).constraints(initializer).treetype; |
---|
| 304 | |
---|
| 305 | return constraints(initializer).returntype.compatibleWith(initializer,type); |
---|
| 306 | } |
---|
| 307 | |
---|
| 308 | /** Returns the number of nodes, constrained by g.test(...) |
---|
| 309 | in the subtree for which this GPNode is root. This might |
---|
| 310 | be sped up by caching the value. O(n). */ |
---|
| 311 | public int numNodes(final GPNodeGatherer g) |
---|
| 312 | { |
---|
| 313 | int s=0; |
---|
| 314 | for(int x=0;x<children.length;x++) s += children[x].numNodes(g); |
---|
| 315 | return s + (g.test(this) ? 1 : 0); |
---|
| 316 | } |
---|
| 317 | |
---|
| 318 | /** Returns the number of nodes, constrained by nodesearch, |
---|
| 319 | in the subtree for which this GPNode is root. |
---|
| 320 | This might be sped up by cacheing the value somehow. O(n). */ |
---|
| 321 | public int numNodes(final int nodesearch) |
---|
| 322 | { |
---|
| 323 | int s=0; |
---|
| 324 | for(int x=0;x<children.length;x++) s += children[x].numNodes(nodesearch); |
---|
| 325 | return s + ((nodesearch==NODESEARCH_ALL || |
---|
| 326 | (nodesearch==NODESEARCH_TERMINALS && children.length==0) || |
---|
| 327 | (nodesearch==NODESEARCH_NONTERMINALS && children.length>0)) ? 1 : 0); |
---|
| 328 | } |
---|
| 329 | |
---|
| 330 | /** Returns the depth of the tree, which is a value >= 1. O(n). */ |
---|
| 331 | public int depth() |
---|
| 332 | { |
---|
| 333 | int d=0; |
---|
| 334 | int newdepth; |
---|
| 335 | for(int x=0;x<children.length;x++) |
---|
| 336 | { |
---|
| 337 | newdepth = children[x].depth(); |
---|
| 338 | if (newdepth>d) d = newdepth; |
---|
| 339 | } |
---|
| 340 | return d + 1; |
---|
| 341 | } |
---|
| 342 | |
---|
| 343 | /** Returns the path length of the tree, which is the sum of all paths from all nodes to the root. O(n). */ |
---|
| 344 | public int pathLength(int nodesearch) { return pathLength(NODESEARCH_ALL, 0); } |
---|
| 345 | |
---|
| 346 | int pathLength(int nodesearch, int currentDepth) |
---|
| 347 | { |
---|
| 348 | int sum = currentDepth; |
---|
| 349 | if (nodesearch == NODESEARCH_NONTERMINALS && children.length==0 || // I'm a leaf, don't include me |
---|
| 350 | nodesearch == NODESEARCH_TERMINALS && children.length > 0) // I'm a nonleaf, don't include me |
---|
| 351 | sum = 0; |
---|
| 352 | |
---|
| 353 | for(int x=0;x<children.length;x++) |
---|
| 354 | sum += pathLength(nodesearch, currentDepth + 1); |
---|
| 355 | return sum; |
---|
| 356 | } |
---|
| 357 | |
---|
| 358 | /** Returns the mean depth of the tree, which is path length (sum of all paths from all nodes to the root) divided by the number of nodes. O(n). */ |
---|
| 359 | int meanDepth(int nodesearch) |
---|
| 360 | { |
---|
| 361 | return pathLength(nodesearch) / numNodes(nodesearch); |
---|
| 362 | } |
---|
| 363 | |
---|
| 364 | /** Returns the depth at which I appear in the tree, which is a value >= 0. O(ln n) avg.*/ |
---|
| 365 | public int atDepth() |
---|
| 366 | { |
---|
| 367 | // -- new code, no need for recursion |
---|
| 368 | GPNodeParent cparent = parent; |
---|
| 369 | int count=0; |
---|
| 370 | |
---|
| 371 | while(cparent!=null && cparent instanceof GPNode) |
---|
| 372 | { |
---|
| 373 | count++; |
---|
| 374 | cparent = ((GPNode)(cparent)).parent; |
---|
| 375 | } |
---|
| 376 | return count; |
---|
| 377 | |
---|
| 378 | /* // -- old code |
---|
| 379 | if (parent==null) return 0; |
---|
| 380 | if (!(parent instanceof GPNode)) // found the root! |
---|
| 381 | return 0; |
---|
| 382 | else return 1 + ((GPNode)parent).atDepth(); |
---|
| 383 | */ |
---|
| 384 | } |
---|
| 385 | |
---|
| 386 | /** Returns the p'th node, constrained by nodesearch, |
---|
| 387 | in the subtree for which this GPNode is root. |
---|
| 388 | Use numNodes(nodesearch) to determine the total number. Or if |
---|
| 389 | you used numNodes(g), then when |
---|
| 390 | nodesearch == NODESEARCH_CUSTOM, g.test(...) is used |
---|
| 391 | as the constraining predicate. |
---|
| 392 | p ranges from 0 to this number minus 1. O(n). The |
---|
| 393 | resultant node is returned in <i>g</i>.*/ |
---|
| 394 | public int nodeInPosition(int p, final GPNodeGatherer g, final int nodesearch) |
---|
| 395 | { |
---|
| 396 | // am I of the type I'm looking for? |
---|
| 397 | if (nodesearch==NODESEARCH_ALL || |
---|
| 398 | (nodesearch==NODESEARCH_TERMINALS && children.length==0) || |
---|
| 399 | (nodesearch==NODESEARCH_NONTERMINALS && children.length>0) || |
---|
| 400 | (nodesearch==NODESEARCH_CUSTOM && g.test(this))) |
---|
| 401 | { |
---|
| 402 | // is the count now at 0? Is it me? |
---|
| 403 | if (p==0) |
---|
| 404 | { |
---|
| 405 | g.node = this; |
---|
| 406 | return -1; // found it |
---|
| 407 | } |
---|
| 408 | // if it's not me, drop the count by 1 |
---|
| 409 | else p--; |
---|
| 410 | } |
---|
| 411 | |
---|
| 412 | // regardless, check my children if I've not returned by now |
---|
| 413 | for(int x=0;x<children.length;x++) |
---|
| 414 | { |
---|
| 415 | p = children[x].nodeInPosition(p,g,nodesearch); |
---|
| 416 | if (p==-1) return -1; // found it |
---|
| 417 | } |
---|
| 418 | return p; |
---|
| 419 | } |
---|
| 420 | |
---|
| 421 | /** Returns the root ancestor of this node. O(ln n) average case, |
---|
| 422 | O(n) worst case. */ |
---|
| 423 | |
---|
| 424 | public GPNodeParent rootParent() |
---|
| 425 | { |
---|
| 426 | |
---|
| 427 | // -- new code, no need for recursion |
---|
| 428 | GPNodeParent cparent = this; |
---|
| 429 | while(cparent!=null && cparent instanceof GPNode) |
---|
| 430 | cparent = ((GPNode)(cparent)).parent; |
---|
| 431 | return cparent; |
---|
| 432 | } |
---|
| 433 | |
---|
| 434 | /** Returns true if the subtree rooted at this node contains subnode. O(n). */ |
---|
| 435 | public boolean contains(final GPNode subnode) |
---|
| 436 | { |
---|
| 437 | if (subnode==this) return true; |
---|
| 438 | for(int x=0;x<children.length;x++) |
---|
| 439 | if (children[x].contains(subnode)) return true; |
---|
| 440 | return false; |
---|
| 441 | } |
---|
| 442 | |
---|
| 443 | |
---|
| 444 | /** Starts a node in a new life immediately after it has been cloned. |
---|
| 445 | The default version of this function does nothing. The purpose of |
---|
| 446 | this function is to give ERCs a chance to set themselves to a new |
---|
| 447 | random value after they've been cloned from the prototype. |
---|
| 448 | You should not assume that the node is properly connected to other |
---|
| 449 | nodes in the tree at the point this method is called. */ |
---|
| 450 | |
---|
| 451 | public void resetNode(final EvolutionState state, final int thread) { } |
---|
| 452 | |
---|
| 453 | /** A convenience function for identifying a GPNode in an error message */ |
---|
| 454 | public String errorInfo() { return "GPNode " + toString() + " in the function set for tree " + ((GPTree)(rootParent())).treeNumber(); } |
---|
| 455 | |
---|
| 456 | |
---|
| 457 | public GPNode lightClone() |
---|
| 458 | { |
---|
| 459 | try |
---|
| 460 | { |
---|
| 461 | GPNode obj = (GPNode)(super.clone()); |
---|
| 462 | int len = children.length; |
---|
| 463 | if (len == 0) obj.children = children; // we'll share arrays -- probably just using GPNodeConstraints.zeroChildren anyway |
---|
| 464 | else obj.children = new GPNode[len]; |
---|
| 465 | return obj; |
---|
| 466 | } |
---|
| 467 | catch (CloneNotSupportedException e) |
---|
| 468 | { throw new InternalError(); } // never happens |
---|
| 469 | } |
---|
| 470 | |
---|
| 471 | /** Deep-clones the tree rooted at this node, and returns the entire |
---|
| 472 | copied tree. The result has everything set except for the root |
---|
| 473 | node's parent and argposition. This method is identical to |
---|
| 474 | cloneReplacing for historical reasons, except that it returns |
---|
| 475 | the object as an Object, not a GPNode. */ |
---|
| 476 | |
---|
| 477 | public Object clone() |
---|
| 478 | { |
---|
| 479 | GPNode newnode = (GPNode)(lightClone()); |
---|
| 480 | for(int x=0;x<children.length;x++) |
---|
| 481 | { |
---|
| 482 | newnode.children[x] = (GPNode)(children[x].cloneReplacing()); |
---|
| 483 | // if you think about it, the following CAN'T be implemented by |
---|
| 484 | // the children's clone method. So it's set here. |
---|
| 485 | newnode.children[x].parent = newnode; |
---|
| 486 | newnode.children[x].argposition = (byte)x; |
---|
| 487 | } |
---|
| 488 | return newnode; |
---|
| 489 | } |
---|
| 490 | |
---|
| 491 | /** Deep-clones the tree rooted at this node, and returns the entire |
---|
| 492 | copied tree. The result has everything set except for the root |
---|
| 493 | node's parent and argposition. This method is identical to |
---|
| 494 | cloneReplacing for historical reasons, except that it returns |
---|
| 495 | the object as a GPNode, not an Object. |
---|
| 496 | @deprecated use clone() instead. |
---|
| 497 | */ |
---|
| 498 | |
---|
| 499 | public final GPNode cloneReplacing() |
---|
| 500 | { |
---|
| 501 | return (GPNode)clone(); |
---|
| 502 | } |
---|
| 503 | |
---|
| 504 | |
---|
| 505 | /** Deep-clones the tree rooted at this node, and returns the entire |
---|
| 506 | copied tree. If the node oldSubtree is located somewhere in this |
---|
| 507 | tree, then its subtree is replaced with a deep-cloned copy of |
---|
| 508 | newSubtree. The result has everything set except for the root |
---|
| 509 | node's parent and argposition. */ |
---|
| 510 | |
---|
| 511 | public final GPNode cloneReplacing(final GPNode newSubtree, final GPNode oldSubtree) |
---|
| 512 | { |
---|
| 513 | if (this==oldSubtree) |
---|
| 514 | return newSubtree.cloneReplacing(); |
---|
| 515 | else |
---|
| 516 | { |
---|
| 517 | GPNode newnode = (GPNode)(lightClone()); |
---|
| 518 | for(int x=0;x<children.length;x++) |
---|
| 519 | { |
---|
| 520 | newnode.children[x] = (GPNode)(children[x].cloneReplacing(newSubtree,oldSubtree)); |
---|
| 521 | // if you think about it, the following CAN'T be implemented by |
---|
| 522 | // the children's clone method. So it's set here. |
---|
| 523 | newnode.children[x].parent = newnode; |
---|
| 524 | newnode.children[x].argposition = (byte)x; |
---|
| 525 | } |
---|
| 526 | return newnode; |
---|
| 527 | } |
---|
| 528 | } |
---|
| 529 | |
---|
| 530 | |
---|
| 531 | |
---|
| 532 | /** Deep-clones the tree rooted at this node, and returns the entire |
---|
| 533 | copied tree. If the node oldSubtree is located somewhere in this |
---|
| 534 | tree, then its subtree is replaced with |
---|
| 535 | newSubtree (<i>not</i> a copy of newSubtree). |
---|
| 536 | The result has everything set except for the root |
---|
| 537 | node's parent and argposition. */ |
---|
| 538 | |
---|
| 539 | public final GPNode cloneReplacingNoSubclone(final GPNode newSubtree, final GPNode oldSubtree) |
---|
| 540 | { |
---|
| 541 | if (this==oldSubtree) |
---|
| 542 | { |
---|
| 543 | return newSubtree; |
---|
| 544 | } |
---|
| 545 | else |
---|
| 546 | { |
---|
| 547 | GPNode newnode = (GPNode)(lightClone()); |
---|
| 548 | for(int x=0;x<children.length;x++) |
---|
| 549 | { |
---|
| 550 | newnode.children[x] = (GPNode)(children[x].cloneReplacingNoSubclone(newSubtree,oldSubtree)); |
---|
| 551 | // if you think about it, the following CAN'T be implemented by |
---|
| 552 | // the children's clone method. So it's set here. |
---|
| 553 | newnode.children[x].parent = newnode; |
---|
| 554 | newnode.children[x].argposition = (byte)x; |
---|
| 555 | } |
---|
| 556 | return newnode; |
---|
| 557 | } |
---|
| 558 | } |
---|
| 559 | |
---|
| 560 | |
---|
| 561 | |
---|
| 562 | |
---|
| 563 | |
---|
| 564 | |
---|
| 565 | /** Deep-clones the tree rooted at this node, and returns the entire |
---|
| 566 | copied tree. If a node in oldSubtrees is located somewhere in this |
---|
| 567 | tree, then its subtree is replaced with a deep-cloned copy of the |
---|
| 568 | subtree rooted at its equivalent number in |
---|
| 569 | newSubtrees. The result has everything set except for the root |
---|
| 570 | node's parent and argposition. */ |
---|
| 571 | |
---|
| 572 | public final GPNode cloneReplacing(final GPNode[] newSubtrees, final GPNode[] oldSubtrees) |
---|
| 573 | { |
---|
| 574 | // am I a candidate? |
---|
| 575 | int candidate = -1; |
---|
| 576 | for(int x=0;x<oldSubtrees.length;x++) |
---|
| 577 | if (this==oldSubtrees[x]) { candidate=x; break; } |
---|
| 578 | |
---|
| 579 | if (candidate >= 0) |
---|
| 580 | return newSubtrees[candidate].cloneReplacing(newSubtrees,oldSubtrees); |
---|
| 581 | else |
---|
| 582 | { |
---|
| 583 | GPNode newnode = (GPNode)(lightClone()); |
---|
| 584 | for(int x=0;x<children.length;x++) |
---|
| 585 | { |
---|
| 586 | newnode.children[x] = (GPNode)(children[x].cloneReplacing(newSubtrees,oldSubtrees)); |
---|
| 587 | // if you think about it, the following CAN'T be implemented by |
---|
| 588 | // the children's clone method. So it's set here. |
---|
| 589 | newnode.children[x].parent = newnode; |
---|
| 590 | newnode.children[x].argposition = (byte)x; |
---|
| 591 | } |
---|
| 592 | return newnode; |
---|
| 593 | } |
---|
| 594 | } |
---|
| 595 | |
---|
| 596 | |
---|
| 597 | |
---|
| 598 | /** Clones a new subtree, but with the single node oldNode |
---|
| 599 | (which may or may not be in the subtree) |
---|
| 600 | replaced with a newNode (not a clone of newNode). |
---|
| 601 | These nodes should be |
---|
| 602 | type-compatible both in argument and return types, and should have |
---|
| 603 | the same number of arguments obviously. This function will <i>not</i> |
---|
| 604 | check for this, and if they are not the result is undefined. */ |
---|
| 605 | |
---|
| 606 | |
---|
| 607 | public final GPNode cloneReplacingAtomic(final GPNode newNode, final GPNode oldNode) |
---|
| 608 | { |
---|
| 609 | int numArgs; |
---|
| 610 | GPNode curnode; |
---|
| 611 | if (this==oldNode) |
---|
| 612 | { |
---|
| 613 | numArgs = Math.max(newNode.children.length,children.length); |
---|
| 614 | curnode = newNode; |
---|
| 615 | } |
---|
| 616 | else |
---|
| 617 | { |
---|
| 618 | numArgs = children.length; |
---|
| 619 | curnode = (GPNode)lightClone(); |
---|
| 620 | } |
---|
| 621 | |
---|
| 622 | // populate |
---|
| 623 | |
---|
| 624 | for(int x=0;x<numArgs;x++) |
---|
| 625 | { |
---|
| 626 | curnode.children[x] = (GPNode)(children[x].cloneReplacingAtomic(newNode,oldNode)); |
---|
| 627 | // if you think about it, the following CAN'T be implemented by |
---|
| 628 | // the children's clone method. So it's set here. |
---|
| 629 | curnode.children[x].parent = curnode; |
---|
| 630 | curnode.children[x].argposition = (byte)x; |
---|
| 631 | } |
---|
| 632 | return curnode; |
---|
| 633 | } |
---|
| 634 | |
---|
| 635 | |
---|
| 636 | |
---|
| 637 | |
---|
| 638 | |
---|
| 639 | /** Clones a new subtree, but with each node in oldNodes[] respectively |
---|
| 640 | (which may or may not be in the subtree) replaced with |
---|
| 641 | the equivalent |
---|
| 642 | nodes in newNodes[] (and not clones). |
---|
| 643 | The length of oldNodes[] and newNodes[] should |
---|
| 644 | be the same of course. These nodes should be |
---|
| 645 | type-compatible both in argument and return types, and should have |
---|
| 646 | the same number of arguments obviously. This function will <i>not</i> |
---|
| 647 | check for this, and if they are not the result is undefined. */ |
---|
| 648 | |
---|
| 649 | |
---|
| 650 | public final GPNode cloneReplacingAtomic(final GPNode[] newNodes, final GPNode[] oldNodes) |
---|
| 651 | { |
---|
| 652 | int numArgs; |
---|
| 653 | GPNode curnode; |
---|
| 654 | int found = -1; |
---|
| 655 | |
---|
| 656 | for(int x=0;x<newNodes.length;x++) |
---|
| 657 | { |
---|
| 658 | if (this==oldNodes[x]) { found=x; break; } |
---|
| 659 | } |
---|
| 660 | |
---|
| 661 | if (found > -1) |
---|
| 662 | { |
---|
| 663 | numArgs = Math.max(newNodes[found].children.length, |
---|
| 664 | children.length); |
---|
| 665 | curnode = newNodes[found]; |
---|
| 666 | } |
---|
| 667 | else |
---|
| 668 | { |
---|
| 669 | numArgs = children.length; |
---|
| 670 | curnode = (GPNode)lightClone(); |
---|
| 671 | } |
---|
| 672 | |
---|
| 673 | // populate |
---|
| 674 | |
---|
| 675 | for(int x=0;x<numArgs;x++) |
---|
| 676 | { |
---|
| 677 | curnode.children[x] = (GPNode)(children[x].cloneReplacingAtomic(newNodes,oldNodes)); |
---|
| 678 | // if you think about it, the following CAN'T be implemented by |
---|
| 679 | // the children's clone method. So it's set here. |
---|
| 680 | curnode.children[x].parent = curnode; |
---|
| 681 | curnode.children[x].argposition = (byte)x; |
---|
| 682 | } |
---|
| 683 | return curnode; |
---|
| 684 | } |
---|
| 685 | |
---|
| 686 | |
---|
| 687 | |
---|
| 688 | |
---|
| 689 | |
---|
| 690 | /** Replaces the node with another node in its position in the tree. |
---|
| 691 | newNode should already have been cloned and ready to go. |
---|
| 692 | We presume that the other node is type-compatible and |
---|
| 693 | of the same arity (these things aren't checked). */ |
---|
| 694 | |
---|
| 695 | public final void replaceWith(final GPNode newNode) |
---|
| 696 | { |
---|
| 697 | // copy the parent and argposition |
---|
| 698 | newNode.parent = parent; |
---|
| 699 | newNode.argposition = argposition; |
---|
| 700 | |
---|
| 701 | // replace the parent pointer |
---|
| 702 | if (parent instanceof GPNode) |
---|
| 703 | ((GPNode)(parent)).children[argposition] = newNode; |
---|
| 704 | else |
---|
| 705 | ((GPTree)(parent)).child = newNode; |
---|
| 706 | |
---|
| 707 | // replace the child pointers |
---|
| 708 | for(byte x = 0;x<children.length;x++) |
---|
| 709 | { |
---|
| 710 | newNode.children[x] = children[x]; |
---|
| 711 | newNode.children[x].parent = newNode; |
---|
| 712 | newNode.children[x].argposition = x; |
---|
| 713 | } |
---|
| 714 | } |
---|
| 715 | |
---|
| 716 | /** Returns true if I and the provided node are the same kind of |
---|
| 717 | node -- that is, we could have both been cloned() and reset() from |
---|
| 718 | the same prototype node. The default form of this function returns |
---|
| 719 | true if I and the node have the same class, the same length children |
---|
| 720 | array, and the same constraints. You may wish to override this in |
---|
| 721 | certain circumstances. Here's an example of how nodeEquivalentTo(node) |
---|
| 722 | differs from nodeEquals(node): two ERCs, both of |
---|
| 723 | the same class, but one holding '1.23' and the other holding '2.45', which |
---|
| 724 | came from the same prototype node in the same function set. |
---|
| 725 | They should NOT be nodeEquals(...) but *should* be nodeEquivalent(...). */ |
---|
| 726 | public boolean nodeEquivalentTo(GPNode node) |
---|
| 727 | { |
---|
| 728 | return (this.getClass().equals(node.getClass()) && |
---|
| 729 | children.length == node.children.length && |
---|
| 730 | constraints == node.constraints); |
---|
| 731 | } |
---|
| 732 | |
---|
| 733 | |
---|
| 734 | /** Returns a hashcode usually associated with all nodes that are |
---|
| 735 | equal to you (using nodeEquals(...)). The default form |
---|
| 736 | of this method returns the hashcode of the node's class. |
---|
| 737 | ERCs in particular probably will want to override this method. |
---|
| 738 | */ |
---|
| 739 | public int nodeHashCode() |
---|
| 740 | { |
---|
| 741 | return (this.getClass().hashCode()); |
---|
| 742 | } |
---|
| 743 | |
---|
| 744 | /** Returns a hashcode associated with all the nodes in the tree. |
---|
| 745 | The default version adds the hash of the node plus its child |
---|
| 746 | trees, rotated one-off each time, which seems reasonable. */ |
---|
| 747 | public int rootedTreeHashCode() |
---|
| 748 | { |
---|
| 749 | int hash = nodeHashCode(); |
---|
| 750 | |
---|
| 751 | for(int x=0;x<children.length;x++) |
---|
| 752 | // rotate hash and XOR |
---|
| 753 | hash = |
---|
| 754 | (hash << 1 | hash >>> 31 ) ^ |
---|
| 755 | children[x].rootedTreeHashCode(); |
---|
| 756 | return hash; |
---|
| 757 | } |
---|
| 758 | |
---|
| 759 | /** Returns true if I am the "genetically" identical to this node, and our |
---|
| 760 | children arrays are the same length, though |
---|
| 761 | we may have different parents and children. The default form |
---|
| 762 | of this method simply calls the much weaker nodeEquivalentTo(node). |
---|
| 763 | You may need to override this to perform exact comparisons, if you're |
---|
| 764 | an ERC, ADF, or ADM for example. Here's an example of how nodeEquivalentTo(node) |
---|
| 765 | differs from nodeEquals(node): two ERCs, both of |
---|
| 766 | the same class, but one holding '1.23' and the other holding '2.45', which |
---|
| 767 | came from the same prototype node in the same function set. |
---|
| 768 | They should NOT be nodeEquals(...) but *should* be nodeEquivalent(...). */ |
---|
| 769 | public boolean nodeEquals(final GPNode node) |
---|
| 770 | { |
---|
| 771 | return nodeEquivalentTo(node); |
---|
| 772 | } |
---|
| 773 | |
---|
| 774 | /** Returns true if the two rooted trees are "genetically" equal, though |
---|
| 775 | they may have different parents. O(n). */ |
---|
| 776 | public boolean rootedTreeEquals(final GPNode node) |
---|
| 777 | { |
---|
| 778 | if (!nodeEquals(node)) return false; |
---|
| 779 | for (int x=0;x<children.length;x++) |
---|
| 780 | if (!(children[x].rootedTreeEquals(node.children[x]))) |
---|
| 781 | return false; |
---|
| 782 | return true; |
---|
| 783 | } |
---|
| 784 | |
---|
| 785 | /** Prints out a human-readable and Lisp-like atom for the node, |
---|
| 786 | and returns the number of bytes in the string that you sent |
---|
| 787 | to the log (use print(), |
---|
| 788 | not println()). The default version gets the atom from |
---|
| 789 | toStringForHumans(). |
---|
| 790 | */ |
---|
| 791 | public int printNodeForHumans(final EvolutionState state, |
---|
| 792 | final int log) |
---|
| 793 | { |
---|
| 794 | return printNodeForHumans(state, log, Output.V_VERBOSE); |
---|
| 795 | } |
---|
| 796 | |
---|
| 797 | /** Prints out a human-readable and Lisp-like atom for the node, |
---|
| 798 | and returns the number of bytes in the string that you sent |
---|
| 799 | to the log (use print(), |
---|
| 800 | not println()). The default version gets the atom from |
---|
| 801 | toStringForHumans(). |
---|
| 802 | @deprecated Verbosity no longer has an effect. |
---|
| 803 | */ |
---|
| 804 | public int printNodeForHumans(final EvolutionState state, |
---|
| 805 | final int log, |
---|
| 806 | final int verbosity) |
---|
| 807 | { |
---|
| 808 | String n = toStringForHumans(); |
---|
| 809 | state.output.print(n,log); |
---|
| 810 | return n.length(); |
---|
| 811 | } |
---|
| 812 | |
---|
| 813 | |
---|
| 814 | /** Prints out a COMPUTER-readable and Lisp-like atom for the node, which |
---|
| 815 | is also suitable for readNode to read, and returns |
---|
| 816 | the number of bytes in the string that you sent to the log (use print(), |
---|
| 817 | not println()). The default version gets the atom from toString(). |
---|
| 818 | O(1). |
---|
| 819 | */ |
---|
| 820 | public int printNode(final EvolutionState state, final int log) |
---|
| 821 | { |
---|
| 822 | printNode(state, log, Output.V_VERBOSE); |
---|
| 823 | String n = toString(); |
---|
| 824 | return n.length(); |
---|
| 825 | } |
---|
| 826 | |
---|
| 827 | /** Prints out a COMPUTER-readable and Lisp-like atom for the node, which |
---|
| 828 | is also suitable for readNode to read, and returns |
---|
| 829 | the number of bytes in the string that you sent to the log (use print(), |
---|
| 830 | not println()). The default version gets the atom from toString(). |
---|
| 831 | O(1). |
---|
| 832 | @deprecated Verbosity no longer has an effect. |
---|
| 833 | */ |
---|
| 834 | public int printNode(final EvolutionState state, final int log, |
---|
| 835 | final int verbosity) |
---|
| 836 | { |
---|
| 837 | String n = toString(); |
---|
| 838 | state.output.print(n,log); |
---|
| 839 | return n.length(); |
---|
| 840 | } |
---|
| 841 | |
---|
| 842 | |
---|
| 843 | /** Prints out a COMPUTER-readable and Lisp-like atom for the node, which |
---|
| 844 | is also suitable for readNode to read, and returns |
---|
| 845 | the number of bytes in the string that you sent to the log (use print(), |
---|
| 846 | not println()). The default version gets the atom from toString(). |
---|
| 847 | O(1). */ |
---|
| 848 | |
---|
| 849 | public int printNode(final EvolutionState state, |
---|
| 850 | final PrintWriter writer) |
---|
| 851 | { |
---|
| 852 | String n = toString(); |
---|
| 853 | writer.print(n); |
---|
| 854 | return n.length(); |
---|
| 855 | } |
---|
| 856 | |
---|
| 857 | /** Returns a Lisp-like atom for the node and any nodes of the same class. |
---|
| 858 | This will almost always be identical to the result of toString() (and the default |
---|
| 859 | does exactly this), but for ERCs it'll be different: toString will include the |
---|
| 860 | encoded constant data, whereas name() will not include this information and will |
---|
| 861 | be the same for all ERCs of this type. If two nodes are nodeEquivalentTo(...) |
---|
| 862 | each other, then they will have the same name(). If two nodes are nodeEquals(...) |
---|
| 863 | each other, then they will have the same toString(). */ |
---|
| 864 | |
---|
| 865 | public String name() { return toString(); } |
---|
| 866 | |
---|
| 867 | /** Returns a Lisp-like atom for the node which can be read in again by computer. |
---|
| 868 | If you need to encode an integer or a float or whatever for some reason |
---|
| 869 | (perhaps if it's an ERC), you should use the ec.util.Code library. */ |
---|
| 870 | |
---|
| 871 | public abstract String toString(); |
---|
| 872 | |
---|
| 873 | /** Returns a Lisp-like atom for the node which is intended for human |
---|
| 874 | consumption, and not to be read in again. The default version |
---|
| 875 | just calls toString(). */ |
---|
| 876 | |
---|
| 877 | public String toStringForHumans() { return toString(); } |
---|
| 878 | |
---|
| 879 | /** Returns a description of the node that can make it easy to identify |
---|
| 880 | in error messages (by default, at least its name and the tree it's found in). |
---|
| 881 | It's okay if this is a reasonably expensive procedure -- it won't be called |
---|
| 882 | a lot. */ |
---|
| 883 | |
---|
| 884 | public String toStringForError() |
---|
| 885 | { |
---|
| 886 | GPTree rootp = (GPTree)rootParent(); |
---|
| 887 | if (rootp!=null) |
---|
| 888 | { |
---|
| 889 | int tnum = ((GPTree)(rootParent())).treeNumber(); |
---|
| 890 | return toString() + (tnum == GPTree.NO_TREENUM ? "" : " in tree " + tnum); |
---|
| 891 | } |
---|
| 892 | else return toString(); |
---|
| 893 | } |
---|
| 894 | |
---|
| 895 | /** Produces the Graphviz code for a Graphviz tree of the subtree rooted at this node. |
---|
| 896 | For this to work, the output of toString() must not contain a double-quote. |
---|
| 897 | Note that this isn't particularly efficient and should only be used to generate |
---|
| 898 | occasional trees for display, not for storing individuals or sending them over networks. */ |
---|
| 899 | public String makeGraphvizTree() |
---|
| 900 | { |
---|
| 901 | return "digraph g {\nnode [shape=rectangle];\n" + makeGraphvizSubtree("n") + "}\n"; |
---|
| 902 | } |
---|
| 903 | |
---|
| 904 | /** Produces the inner code for a graphviz subtree. Called from makeGraphvizTree(). |
---|
| 905 | Note that this isn't particularly efficient and should only be used to generate |
---|
| 906 | occasional trees for display, not for storing individuals or sending them over networks. */ |
---|
| 907 | protected String makeGraphvizSubtree(String prefix) |
---|
| 908 | { |
---|
| 909 | String body = prefix + "[label = \"" + toStringForHumans() + "\"];\n"; |
---|
| 910 | for(int x = 0; x < children.length; x++) |
---|
| 911 | { |
---|
| 912 | String newprefix; |
---|
| 913 | if (x < 10) newprefix = prefix + x; |
---|
| 914 | else newprefix = prefix + "n" + x; // to distinguish it |
---|
| 915 | |
---|
| 916 | body = body + children[x].makeGraphvizSubtree(newprefix); |
---|
| 917 | body = body + prefix + " -> " + newprefix + ";\n"; |
---|
| 918 | } |
---|
| 919 | return body; |
---|
| 920 | } |
---|
| 921 | |
---|
| 922 | /** Produces the LaTeX code for a LaTeX tree of the subtree rooted at this node, using the <tt>epic</tt> |
---|
| 923 | and <tt>fancybox</tt> packages, as described in sections 10.5.2 (page 307) |
---|
| 924 | and 10.1.3 (page 278) of <i>The LaTeX Companion</i>, respectively. For this to |
---|
| 925 | work, the output of toStringForHumans() must not contain any weird latex characters, notably { or } or % or \, |
---|
| 926 | unless you know what you're doing. See the documentation for ec.gp.GPTree for information |
---|
| 927 | on how to take this code snippet and insert it into your LaTeX file. |
---|
| 928 | Note that this isn't particularly efficient and should only be used to generate |
---|
| 929 | occasional trees for display, not for storing individuals or sending them over networks. */ |
---|
| 930 | |
---|
| 931 | public String makeLatexTree() |
---|
| 932 | { |
---|
| 933 | if (children.length==0) |
---|
| 934 | return "\\gpbox{"+toStringForHumans()+"}"; |
---|
| 935 | |
---|
| 936 | String s = "\\begin{bundle}{\\gpbox{"+toStringForHumans()+"}}"; |
---|
| 937 | for(int x=0;x<children.length;x++) |
---|
| 938 | s = s + "\\chunk{"+children[x].makeLatexTree()+"}"; |
---|
| 939 | s = s + "\\end{bundle}"; |
---|
| 940 | return s; |
---|
| 941 | } |
---|
| 942 | |
---|
| 943 | /** Producess a String consisting of the tree in pseudo-C form, given that the parent already will wrap the |
---|
| 944 | expression in parentheses (or not). In pseudo-C form, functions with one child are printed out as a(b), |
---|
| 945 | functions with more than two children are printed out as a(b, c, d, ...), and functions with exactly two |
---|
| 946 | children are either printed as a(b, c) or in operator form as (b a c) -- for example, (b * c). Whether |
---|
| 947 | or not to do this depends on the setting of <tt>useOperatorForm</tt>. Additionally, terminals will be |
---|
| 948 | printed out either in variable form -- a -- or in zero-argument function form -- a() -- depending on |
---|
| 949 | the setting of <tt>printTerminalsAsVariables</tt>. |
---|
| 950 | Note that this isn't particularly efficient and should only be used to generate |
---|
| 951 | occasional trees for display, not for storing individuals or sending them over networks. |
---|
| 952 | */ |
---|
| 953 | |
---|
| 954 | public String makeCTree(boolean parentMadeParens, boolean printTerminalsAsVariables, boolean useOperatorForm) |
---|
| 955 | { |
---|
| 956 | if (children.length==0) |
---|
| 957 | return (printTerminalsAsVariables ? toStringForHumans() : toStringForHumans() + "()"); |
---|
| 958 | else if (children.length==1) |
---|
| 959 | return toStringForHumans() + "(" + children[0].makeCTree(true, printTerminalsAsVariables, useOperatorForm) + ")"; |
---|
| 960 | else if (children.length==2 && useOperatorForm) |
---|
| 961 | return (parentMadeParens ? "" : "(") + |
---|
| 962 | children[0].makeCTree(false, printTerminalsAsVariables, useOperatorForm) + " " + |
---|
| 963 | toStringForHumans() + " " + children[1].makeCTree(false, printTerminalsAsVariables, useOperatorForm) + |
---|
| 964 | (parentMadeParens ? "" : ")"); |
---|
| 965 | else |
---|
| 966 | { |
---|
| 967 | String s = toStringForHumans() + "(" + children[0].makeCTree(true, printTerminalsAsVariables, useOperatorForm); |
---|
| 968 | for(int x = 1; x < children.length;x++) |
---|
| 969 | s = s + ", " + children[x].makeCTree(true, printTerminalsAsVariables, useOperatorForm); |
---|
| 970 | return s + ")"; |
---|
| 971 | } |
---|
| 972 | } |
---|
| 973 | |
---|
| 974 | /** |
---|
| 975 | Produces a tree for human consumption in Lisp form similar to that generated by printTreeForHumans(). |
---|
| 976 | Note that this isn't particularly efficient and should only be used to generate |
---|
| 977 | occasional trees for display, not for storing individuals or sending them over networks. |
---|
| 978 | */ |
---|
| 979 | public String makeLispTree() |
---|
| 980 | { |
---|
| 981 | if (children.length==0) |
---|
| 982 | return toStringForHumans(); |
---|
| 983 | else |
---|
| 984 | { |
---|
| 985 | String s = "(" + toStringForHumans(); |
---|
| 986 | for(int x=0;x<children.length;x++) |
---|
| 987 | s = s + " " + children[x].makeLispTree(); |
---|
| 988 | return s + ")"; |
---|
| 989 | } |
---|
| 990 | } |
---|
| 991 | |
---|
| 992 | |
---|
| 993 | /** Prints out the tree on a single line, with no ending \n, in a fashion that can |
---|
| 994 | be read in later by computer. O(n). |
---|
| 995 | You should call this method with printbytes == 0. |
---|
| 996 | */ |
---|
| 997 | |
---|
| 998 | public int printRootedTree(final EvolutionState state, |
---|
| 999 | final int log, int printbytes) |
---|
| 1000 | { |
---|
| 1001 | return printRootedTree(state, log, Output.V_VERBOSE, printbytes); |
---|
| 1002 | } |
---|
| 1003 | |
---|
| 1004 | |
---|
| 1005 | /** Prints out the tree on a single line, with no ending \n, in a fashion that can |
---|
| 1006 | be read in later by computer. O(n). |
---|
| 1007 | You should call this method with printbytes == 0. |
---|
| 1008 | @Deprecated Verbosity no longer has an effect. |
---|
| 1009 | */ |
---|
| 1010 | |
---|
| 1011 | public int printRootedTree(final EvolutionState state, |
---|
| 1012 | final int log, final int verbosity, |
---|
| 1013 | int printbytes) |
---|
| 1014 | { |
---|
| 1015 | if (children.length>0) { state.output.print(" (",verbosity,log); printbytes += 2; } |
---|
| 1016 | else { state.output.print(" ",log); printbytes += 1; } |
---|
| 1017 | |
---|
| 1018 | printbytes += printNode(state,log); |
---|
| 1019 | |
---|
| 1020 | for (int x=0;x<children.length;x++) |
---|
| 1021 | printbytes = children[x].printRootedTree(state,log,printbytes); |
---|
| 1022 | if (children.length>0) { state.output.print(")",log); printbytes += 1; } |
---|
| 1023 | return printbytes; |
---|
| 1024 | } |
---|
| 1025 | |
---|
| 1026 | |
---|
| 1027 | /** Prints out the tree on a single line, with no ending \n, in a fashion that can |
---|
| 1028 | be read in later by computer. O(n). Returns the number of bytes printed. |
---|
| 1029 | You should call this method with printbytes == 0. */ |
---|
| 1030 | |
---|
| 1031 | public int printRootedTree(final EvolutionState state, final PrintWriter writer, |
---|
| 1032 | int printbytes) |
---|
| 1033 | { |
---|
| 1034 | if (children.length>0) { writer.print(" ("); printbytes += 2; } |
---|
| 1035 | else { writer.print(" "); printbytes += 1; } |
---|
| 1036 | |
---|
| 1037 | printbytes += printNode(state,writer); |
---|
| 1038 | |
---|
| 1039 | for (int x=0;x<children.length;x++) |
---|
| 1040 | printbytes = children[x].printRootedTree(state,writer,printbytes); |
---|
| 1041 | if (children.length>0) { writer.print(")"); printbytes += 1; } |
---|
| 1042 | return printbytes; |
---|
| 1043 | } |
---|
| 1044 | |
---|
| 1045 | |
---|
| 1046 | /** Prints out the tree in a readable Lisp-like multi-line fashion. O(n). |
---|
| 1047 | You should call this method with tablevel and printbytes == 0. |
---|
| 1048 | No ending '\n' is printed. |
---|
| 1049 | */ |
---|
| 1050 | |
---|
| 1051 | public int printRootedTreeForHumans(final EvolutionState state, final int log, |
---|
| 1052 | int tablevel, int printbytes) |
---|
| 1053 | { |
---|
| 1054 | return printRootedTreeForHumans(state, log, Output.V_VERBOSE, tablevel, printbytes); |
---|
| 1055 | } |
---|
| 1056 | |
---|
| 1057 | /** Prints out the tree in a readable Lisp-like multi-line fashion. O(n). |
---|
| 1058 | You should call this method with tablevel and printbytes == 0. |
---|
| 1059 | No ending '\n' is printed. |
---|
| 1060 | @deprecated Verbosity no longer has an effect. |
---|
| 1061 | */ |
---|
| 1062 | |
---|
| 1063 | public int printRootedTreeForHumans(final EvolutionState state, final int log, |
---|
| 1064 | final int verbosity, |
---|
| 1065 | int tablevel, int printbytes) |
---|
| 1066 | { |
---|
| 1067 | if (printbytes>MAXPRINTBYTES) |
---|
| 1068 | { |
---|
| 1069 | state.output.print("\n",log); |
---|
| 1070 | tablevel++; |
---|
| 1071 | printbytes = 0; |
---|
| 1072 | for(int x=0;x<tablevel;x++) |
---|
| 1073 | state.output.print(GPNODEPRINTTAB,log); |
---|
| 1074 | } |
---|
| 1075 | |
---|
| 1076 | if (children.length>0) { state.output.print(" (",log); printbytes += 2; } |
---|
| 1077 | else { state.output.print(" ",log); printbytes += 1; } |
---|
| 1078 | |
---|
| 1079 | printbytes += printNodeForHumans(state,log); |
---|
| 1080 | |
---|
| 1081 | for (int x=0;x<children.length;x++) |
---|
| 1082 | printbytes = children[x].printRootedTreeForHumans(state,log,tablevel,printbytes); |
---|
| 1083 | if (children.length>0) { state.output.print(")",log); printbytes += 1; } |
---|
| 1084 | return printbytes; |
---|
| 1085 | } |
---|
| 1086 | |
---|
| 1087 | |
---|
| 1088 | /** Reads the node symbol, |
---|
| 1089 | advancing the DecodeReturn to the first character in the string |
---|
| 1090 | beyond the node symbol, and returns a new, empty GPNode of the |
---|
| 1091 | appropriate class representing that symbol, else null if the |
---|
| 1092 | node symbol is not of the correct type for your GPNode class. You may |
---|
| 1093 | assume that initial whitespace has been eliminated. Generally should |
---|
| 1094 | be case-SENSITIVE, unlike in Lisp. The default |
---|
| 1095 | version usually works for "simple" function names, that is, not ERCs |
---|
| 1096 | or other stuff where you have to encode the symbol. */ |
---|
| 1097 | public GPNode readNode(DecodeReturn dret) |
---|
| 1098 | { |
---|
| 1099 | int len = dret.data.length(); |
---|
| 1100 | |
---|
| 1101 | // get my name |
---|
| 1102 | String str2 = toString(); |
---|
| 1103 | int len2 = str2.length(); |
---|
| 1104 | |
---|
| 1105 | if (dret.pos + len2 > len) // uh oh, not enough space |
---|
| 1106 | return null; |
---|
| 1107 | |
---|
| 1108 | // check it out |
---|
| 1109 | for(int x=0; x < len2 ; x++) |
---|
| 1110 | if (dret.data.charAt(dret.pos + x) != str2.charAt(x)) |
---|
| 1111 | return null; |
---|
| 1112 | |
---|
| 1113 | // looks good! Check to make sure that |
---|
| 1114 | // the symbol's all there is |
---|
| 1115 | if (dret.data.length() > dret.pos+len2) |
---|
| 1116 | { |
---|
| 1117 | char c = dret.data.charAt(dret.pos+len2); |
---|
| 1118 | if (!Character.isWhitespace(c) && |
---|
| 1119 | c != ')' && c != '(') // uh oh |
---|
| 1120 | return null; |
---|
| 1121 | } |
---|
| 1122 | |
---|
| 1123 | // we're happy! |
---|
| 1124 | dret.pos += len2; |
---|
| 1125 | return (GPNode)lightClone(); |
---|
| 1126 | } |
---|
| 1127 | |
---|
| 1128 | |
---|
| 1129 | public void writeRootedTree(final EvolutionState state,final GPType expectedType, |
---|
| 1130 | final GPFunctionSet set, final DataOutput dataOutput) throws IOException |
---|
| 1131 | { |
---|
| 1132 | dataOutput.writeInt(children.length); |
---|
| 1133 | boolean isTerminal = (children.length == 0); |
---|
| 1134 | |
---|
| 1135 | // identify the node |
---|
| 1136 | GPNode[] gpfi = isTerminal ? |
---|
| 1137 | set.terminals[expectedType.type] : |
---|
| 1138 | set.nonterminals[expectedType.type]; |
---|
| 1139 | |
---|
| 1140 | int index=0; |
---|
| 1141 | for( /*int index=0 */; index <gpfi.length;index++) |
---|
| 1142 | if ((gpfi[index].nodeEquivalentTo(this))) break; |
---|
| 1143 | |
---|
| 1144 | if (index==gpfi.length) // uh oh |
---|
| 1145 | state.output.fatal("No node in the function set can be found that is equivalent to the node " + this + |
---|
| 1146 | " when performing writeRootedTree(EvolutionState, GPType, GPFunctionSet, DataOutput)."); |
---|
| 1147 | dataOutput.writeInt(index); // what kind of node it is |
---|
| 1148 | writeNode(state,dataOutput); |
---|
| 1149 | |
---|
| 1150 | GPInitializer initializer = ((GPInitializer)state.initializer); |
---|
| 1151 | for(int x=0;x<children.length;x++) |
---|
| 1152 | children[x].writeRootedTree(state,constraints(initializer).childtypes[x],set,dataOutput); |
---|
| 1153 | } |
---|
| 1154 | |
---|
| 1155 | |
---|
| 1156 | public static GPNode readRootedTree(final EvolutionState state, |
---|
| 1157 | final DataInput dataInput, |
---|
| 1158 | GPType expectedType, |
---|
| 1159 | GPFunctionSet set, |
---|
| 1160 | GPNodeParent parent, |
---|
| 1161 | int argposition) throws IOException |
---|
| 1162 | { |
---|
| 1163 | int len = dataInput.readInt(); // num children |
---|
| 1164 | int index = dataInput.readInt(); // index in function set |
---|
| 1165 | |
---|
| 1166 | boolean isTerminal = (len == 0); |
---|
| 1167 | GPNode[] gpfi = isTerminal ? |
---|
| 1168 | set.terminals[expectedType.type] : |
---|
| 1169 | set.nonterminals[expectedType.type]; |
---|
| 1170 | |
---|
| 1171 | GPNode node = ((GPNode)(gpfi[index].lightClone())); |
---|
| 1172 | |
---|
| 1173 | if (node.children == null || node.children.length != len) |
---|
| 1174 | state.output.fatal("Mismatch in number of children (" + len + |
---|
| 1175 | ") when performing readRootedTree(...DataInput...) on " + node); |
---|
| 1176 | |
---|
| 1177 | node.parent = parent; |
---|
| 1178 | node.argposition = (byte)argposition; |
---|
| 1179 | node.readNode(state,dataInput); |
---|
| 1180 | |
---|
| 1181 | // do its children |
---|
| 1182 | GPInitializer initializer = ((GPInitializer)state.initializer); |
---|
| 1183 | for(int x=0;x<node.children.length;x++) |
---|
| 1184 | node.children[x] = readRootedTree(state,dataInput,node.constraints(initializer).childtypes[x],set, node, x); |
---|
| 1185 | |
---|
| 1186 | return node; |
---|
| 1187 | } |
---|
| 1188 | |
---|
| 1189 | /** Override this to write any additional node-specific information to dataOutput besides: the number of arguments, |
---|
| 1190 | the specific node class, the children, and the parent. The default version of this method does nothing. */ |
---|
| 1191 | public void writeNode(final EvolutionState state, final DataOutput dataOutput) throws IOException |
---|
| 1192 | { |
---|
| 1193 | // do nothing |
---|
| 1194 | } |
---|
| 1195 | |
---|
| 1196 | /** Override this to read any additional node-specific information from dataInput besides: the number of arguments, |
---|
| 1197 | the specific node class, the children, and the parent. The default version of this method does nothing. */ |
---|
| 1198 | public void readNode(final EvolutionState state, final DataInput dataInput) throws IOException |
---|
| 1199 | { |
---|
| 1200 | // do nothing |
---|
| 1201 | } |
---|
| 1202 | |
---|
| 1203 | /** Reads the node and its children from the form printed out by printRootedTree. */ |
---|
| 1204 | public static GPNode readRootedTree(int linenumber, |
---|
| 1205 | DecodeReturn dret, |
---|
| 1206 | GPType expectedType, |
---|
| 1207 | GPFunctionSet set, |
---|
| 1208 | GPNodeParent parent, |
---|
| 1209 | int argposition, |
---|
| 1210 | EvolutionState state) |
---|
| 1211 | { |
---|
| 1212 | final char REPLACEMENT_CHAR = '@'; |
---|
| 1213 | |
---|
| 1214 | // eliminate whitespace if any |
---|
| 1215 | boolean isTerminal = true; |
---|
| 1216 | int len = dret.data.length(); |
---|
| 1217 | for( ; dret.pos < len && |
---|
| 1218 | Character.isWhitespace(dret.data.charAt(dret.pos)) ; dret.pos++); |
---|
| 1219 | |
---|
| 1220 | // if I'm out of space, complain |
---|
| 1221 | |
---|
| 1222 | if (dret.pos >= len) |
---|
| 1223 | state.output.fatal("Reading line " + linenumber + ": " + "Premature end of tree structure -- did you forget a close-parenthesis?\nThe tree was" + dret.data); |
---|
| 1224 | |
---|
| 1225 | // if I've found a ')', complain |
---|
| 1226 | if (dret.data.charAt(dret.pos) == ')') |
---|
| 1227 | { |
---|
| 1228 | StringBuffer sb = new StringBuffer(dret.data); |
---|
| 1229 | sb.setCharAt(dret.pos,REPLACEMENT_CHAR); |
---|
| 1230 | dret.data = sb.toString(); |
---|
| 1231 | state.output.fatal("Reading line " + linenumber + ": " + "Premature ')' which I have replaced with a '" + REPLACEMENT_CHAR + "', in tree:\n" + dret.data); |
---|
| 1232 | } |
---|
| 1233 | |
---|
| 1234 | // determine if I'm a terminal or not |
---|
| 1235 | if (dret.data.charAt(dret.pos) == '(') |
---|
| 1236 | { |
---|
| 1237 | isTerminal=false; |
---|
| 1238 | dret.pos++; |
---|
| 1239 | // strip following whitespace |
---|
| 1240 | for( ; dret.pos < len && |
---|
| 1241 | Character.isWhitespace(dret.data.charAt(dret.pos)) ; dret.pos++); |
---|
| 1242 | } |
---|
| 1243 | |
---|
| 1244 | // check again if I'm out of space |
---|
| 1245 | |
---|
| 1246 | if (dret.pos >= len) |
---|
| 1247 | state.output.fatal("Reading line " + linenumber + ": " + "Premature end of tree structure -- did you forget a close-parenthesis?\nThe tree was" + dret.data); |
---|
| 1248 | |
---|
| 1249 | // check again if I found a ')' |
---|
| 1250 | if (dret.data.charAt(dret.pos) == ')') |
---|
| 1251 | { |
---|
| 1252 | StringBuffer sb = new StringBuffer(dret.data); |
---|
| 1253 | sb.setCharAt(dret.pos,REPLACEMENT_CHAR); |
---|
| 1254 | dret.data = sb.toString(); |
---|
| 1255 | state.output.fatal("Reading line " + linenumber + ": " + "Premature ')' which I have replaced with a '" + REPLACEMENT_CHAR + "', in tree:\n" + dret.data); |
---|
| 1256 | } |
---|
| 1257 | |
---|
| 1258 | |
---|
| 1259 | // find that node! |
---|
| 1260 | GPNode[] gpfi = isTerminal ? |
---|
| 1261 | set.terminals[expectedType.type] : |
---|
| 1262 | set.nonterminals[expectedType.type]; |
---|
| 1263 | |
---|
| 1264 | GPNode node = null; |
---|
| 1265 | for(int x=0;x<gpfi.length;x++) |
---|
| 1266 | if ((node = gpfi[x].readNode(dret)) != null) break; |
---|
| 1267 | |
---|
| 1268 | // did I find one? |
---|
| 1269 | |
---|
| 1270 | if (node==null) |
---|
| 1271 | { |
---|
| 1272 | if (dret.pos!=0) |
---|
| 1273 | { |
---|
| 1274 | StringBuffer sb = new StringBuffer(dret.data); |
---|
| 1275 | sb.setCharAt(dret.pos,REPLACEMENT_CHAR); |
---|
| 1276 | dret.data = sb.toString(); |
---|
| 1277 | } |
---|
| 1278 | else dret.data = "" + REPLACEMENT_CHAR + dret.data; |
---|
| 1279 | state.output.fatal("Reading line " + linenumber + ": " + "I came across a symbol which I could not match up with a type-valid node.\nI have replaced the position immediately before the node in question with a '" + REPLACEMENT_CHAR + "':\n" + dret.data); |
---|
| 1280 | } |
---|
| 1281 | |
---|
| 1282 | node.parent = parent; |
---|
| 1283 | node.argposition = (byte)argposition; |
---|
| 1284 | GPInitializer initializer = ((GPInitializer)state.initializer); |
---|
| 1285 | |
---|
| 1286 | // do its children |
---|
| 1287 | for(int x=0;x<node.children.length;x++) |
---|
| 1288 | node.children[x] = readRootedTree(linenumber,dret,node.constraints(initializer).childtypes[x],set,node,x,state); |
---|
| 1289 | |
---|
| 1290 | // if I'm not a terminal, look for a ')' |
---|
| 1291 | |
---|
| 1292 | if (!isTerminal) |
---|
| 1293 | { |
---|
| 1294 | // clear whitespace |
---|
| 1295 | for( ; dret.pos < len && |
---|
| 1296 | Character.isWhitespace(dret.data.charAt(dret.pos)) ; dret.pos++); |
---|
| 1297 | |
---|
| 1298 | if (dret.pos >= len) |
---|
| 1299 | state.output.fatal("Reading line " + linenumber + ": " + "Premature end of tree structure -- did you forget a close-parenthesis?\nThe tree was" + dret.data); |
---|
| 1300 | |
---|
| 1301 | if (dret.data.charAt(dret.pos) != ')') |
---|
| 1302 | { |
---|
| 1303 | if (dret.pos!=0) |
---|
| 1304 | { |
---|
| 1305 | StringBuffer sb = new StringBuffer(dret.data); |
---|
| 1306 | sb.setCharAt(dret.pos,REPLACEMENT_CHAR); |
---|
| 1307 | dret.data = sb.toString(); |
---|
| 1308 | } |
---|
| 1309 | else dret.data = "" + REPLACEMENT_CHAR + dret.data; |
---|
| 1310 | state.output.fatal("Reading line " + linenumber + ": " + "A nonterminal node has too many arguments. I have put a '" + |
---|
| 1311 | REPLACEMENT_CHAR + "' just before the offending argument.\n" + dret.data); |
---|
| 1312 | } |
---|
| 1313 | else dret.pos++; // get rid of the ')' |
---|
| 1314 | } |
---|
| 1315 | |
---|
| 1316 | // return the node |
---|
| 1317 | return node; |
---|
| 1318 | } |
---|
| 1319 | |
---|
| 1320 | |
---|
| 1321 | /** Evaluates the node with the given thread, state, individual, problem, and stack. |
---|
| 1322 | Your random number generator will be state.random[thread]. |
---|
| 1323 | The node should, as appropriate, evaluate child nodes with these same items |
---|
| 1324 | passed to eval(...). |
---|
| 1325 | |
---|
| 1326 | <p>About <b>input</b>: <tt>input</tt> is special; it is how data is passed between |
---|
| 1327 | parent and child nodes. If children "receive" data from their parent node when |
---|
| 1328 | it evaluates them, they should receive this data stored in <tt>input</tt>. |
---|
| 1329 | If (more likely) the parent "receives" results from its children, it should |
---|
| 1330 | pass them an <tt>input</tt> object, which they'll fill out, then it should |
---|
| 1331 | check this object for the returned value. |
---|
| 1332 | |
---|
| 1333 | <p>A tree is typically evaluated by dropping a GPData into the root. When the |
---|
| 1334 | root returns, the resultant <tt>input</tt> should hold the return value. |
---|
| 1335 | |
---|
| 1336 | <p>In general, you should not be creating new GPDatas. |
---|
| 1337 | If you think about it, in most conditions (excepting ADFs and ADMs) you |
---|
| 1338 | can use and reuse <tt>input</tt> for most communications purposes between |
---|
| 1339 | parents and children. |
---|
| 1340 | |
---|
| 1341 | <p>So, let's say that your GPNode function implements the boolean AND function, |
---|
| 1342 | and expects its children to return return boolean values (as it does itself). |
---|
| 1343 | You've implemented your GPData subclass to be, uh, <b>BooleanData</b>, which |
---|
| 1344 | looks like |
---|
| 1345 | |
---|
| 1346 | * <tt><pre>public class BooleanData extends GPData |
---|
| 1347 | * { |
---|
| 1348 | * public boolean result; |
---|
| 1349 | * public GPData copyTo(GPData gpd) |
---|
| 1350 | * { |
---|
| 1351 | * ((BooleanData)gpd).result = result; |
---|
| 1352 | * } |
---|
| 1353 | * }</pre></tt> |
---|
| 1354 | |
---|
| 1355 | <p>...so, you might implement your eval(...) function as follows: |
---|
| 1356 | |
---|
| 1357 | * <tt><pre>public void eval(final EvolutionState state, |
---|
| 1358 | * final int thread, |
---|
| 1359 | * final GPData input, |
---|
| 1360 | * final ADFStack stack, |
---|
| 1361 | * final GPIndividual individual, |
---|
| 1362 | * final Problem problem |
---|
| 1363 | * { |
---|
| 1364 | * BooleanData dat = (BooleanData)input; |
---|
| 1365 | * boolean x; |
---|
| 1366 | * |
---|
| 1367 | * // evaluate the first child |
---|
| 1368 | * children[0].eval(state,thread,input,stack,individual,problem); |
---|
| 1369 | * |
---|
| 1370 | * // store away its result |
---|
| 1371 | * x = dat.result; |
---|
| 1372 | * |
---|
| 1373 | * // evaluate the second child |
---|
| 1374 | * children[1].eval(state,thread,input,stack,individual,problem); |
---|
| 1375 | * |
---|
| 1376 | * // return (in input) the result of the two ANDed |
---|
| 1377 | * |
---|
| 1378 | * dat.result = dat.result && x; |
---|
| 1379 | * return; |
---|
| 1380 | * } |
---|
| 1381 | </pre></tt> |
---|
| 1382 | */ |
---|
| 1383 | |
---|
| 1384 | public abstract void eval(final EvolutionState state, |
---|
| 1385 | final int thread, |
---|
| 1386 | final GPData input, |
---|
| 1387 | final ADFStack stack, |
---|
| 1388 | final GPIndividual individual, |
---|
| 1389 | final Problem problem); |
---|
| 1390 | } |
---|
| 1391 | |
---|