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