1 | /* |
---|
2 | Copyright 2006 by Alexander Chircop |
---|
3 | Licensed under the Academic Free License version 3.0 |
---|
4 | See the file "LICENSE" for more information |
---|
5 | */ |
---|
6 | |
---|
7 | |
---|
8 | /* |
---|
9 | * RandTree as described by Hitoshi Iba |
---|
10 | * Author: Alexander Chircop |
---|
11 | * Date: 28th Nov 2000 |
---|
12 | */ |
---|
13 | |
---|
14 | package ec.gp.build; |
---|
15 | import ec.gp.*; |
---|
16 | import ec.*; |
---|
17 | import ec.util.*; |
---|
18 | import java.util.*; |
---|
19 | |
---|
20 | public class RandTree extends GPNodeBuilder |
---|
21 | { |
---|
22 | public static final String P_RANDOMBRANCH = "randtree"; |
---|
23 | int[] arities; |
---|
24 | boolean aritySetupDone=false; |
---|
25 | |
---|
26 | LinkedList permutations; |
---|
27 | |
---|
28 | private class ArityObject extends Object |
---|
29 | { |
---|
30 | public int arity; |
---|
31 | public ArityObject(int a) { arity=a; } |
---|
32 | } |
---|
33 | |
---|
34 | public Parameter defaultBase() |
---|
35 | { |
---|
36 | return GPBuildDefaults.base().push(P_RANDOMBRANCH); |
---|
37 | } |
---|
38 | |
---|
39 | public void setup(final EvolutionState state, final Parameter base) |
---|
40 | { |
---|
41 | super.setup(state,base); |
---|
42 | |
---|
43 | // we use size distributions -- did the user specify any? |
---|
44 | if (!canPick()) |
---|
45 | state.output.fatal("RandTree requires some kind of size distribution set, either with " + P_MINSIZE + "/" + P_MAXSIZE + ", or with " + P_NUMSIZES + ".", |
---|
46 | base, defaultBase()); |
---|
47 | } |
---|
48 | |
---|
49 | // Added method to enhance linked list functionality with ArityObject |
---|
50 | private boolean contains(LinkedList initArities,int a) |
---|
51 | { |
---|
52 | boolean truth=false; |
---|
53 | int counter=0; |
---|
54 | ArityObject b; |
---|
55 | |
---|
56 | if (initArities.size()!=0) |
---|
57 | while ((counter<initArities.size()) && (!truth)) |
---|
58 | { |
---|
59 | b=(ArityObject)initArities.get(counter); |
---|
60 | if (b.arity==a) {truth=true;} |
---|
61 | counter++; |
---|
62 | } |
---|
63 | return truth; |
---|
64 | } |
---|
65 | |
---|
66 | private void remove(LinkedList initArities,int a) |
---|
67 | { |
---|
68 | int counter=0; |
---|
69 | boolean removed=false; |
---|
70 | while((counter<initArities.size()) && (!removed)) |
---|
71 | { |
---|
72 | ArityObject b=(ArityObject)initArities.get(counter); |
---|
73 | if (b.arity==a) |
---|
74 | { |
---|
75 | initArities.remove(counter); |
---|
76 | removed=true; |
---|
77 | } |
---|
78 | counter++; |
---|
79 | } |
---|
80 | } |
---|
81 | |
---|
82 | public void setupArities(final EvolutionState state,final GPFunctionSet set) |
---|
83 | { |
---|
84 | int noOfArities=0,current=0,marker=0,counter=0,a; |
---|
85 | LinkedList initArities=new LinkedList(); |
---|
86 | GPInitializer initializer = ((GPInitializer)state.initializer); |
---|
87 | // count available arities and place on linked list |
---|
88 | while(current<set.nodes[0].length) |
---|
89 | { |
---|
90 | { |
---|
91 | a=set.nodes[0][current].constraints(initializer).childtypes.length; |
---|
92 | if((!contains(initArities,a)) && (a!=0)) |
---|
93 | { |
---|
94 | initArities.add(new ArityObject(a)); |
---|
95 | noOfArities++; |
---|
96 | } |
---|
97 | } |
---|
98 | current++; |
---|
99 | } |
---|
100 | |
---|
101 | if (initArities.size()==0) {state.output.fatal("Arity count failed... counted 0.");} |
---|
102 | |
---|
103 | // identify different available arities and place on array |
---|
104 | arities=new int[noOfArities]; |
---|
105 | current=0; |
---|
106 | |
---|
107 | while(current<noOfArities) |
---|
108 | { |
---|
109 | // finds maximum arity on the list |
---|
110 | marker=0; |
---|
111 | for (counter=0;counter<initArities.size();counter++) |
---|
112 | { |
---|
113 | ArityObject max=(ArityObject) initArities.get(counter); |
---|
114 | if (max.arity > marker) |
---|
115 | { |
---|
116 | marker=max.arity; |
---|
117 | } |
---|
118 | } |
---|
119 | |
---|
120 | // Place maximum found on the array |
---|
121 | arities[current]=marker; |
---|
122 | remove(initArities,marker); |
---|
123 | current++; |
---|
124 | } |
---|
125 | |
---|
126 | aritySetupDone=true; |
---|
127 | } |
---|
128 | |
---|
129 | private long fact(long num) |
---|
130 | { |
---|
131 | if (num==0) { return 1; } |
---|
132 | else { return num*fact(num-1); } |
---|
133 | } |
---|
134 | |
---|
135 | private int[] select(LinkedList permutations,int size) |
---|
136 | { |
---|
137 | int counter1,counter2,total=0; |
---|
138 | long residue,denominator=1; |
---|
139 | int selection; |
---|
140 | int[] current; |
---|
141 | int[] quantity=new int[permutations.size()]; |
---|
142 | |
---|
143 | for (counter1=0;counter1<permutations.size();counter1++) |
---|
144 | { |
---|
145 | current=(int[])permutations.get(counter1); |
---|
146 | residue=size; |
---|
147 | // Quick internal calculations |
---|
148 | for (counter2=0;counter2<current.length;counter2++) |
---|
149 | { |
---|
150 | residue -= current[counter2]; |
---|
151 | denominator *= fact(current[counter2]); |
---|
152 | } |
---|
153 | quantity[counter1] = (int) (fact(size-1)/(denominator * fact(residue))); |
---|
154 | total += quantity[counter1]; |
---|
155 | } |
---|
156 | |
---|
157 | double[] prob=new double[quantity.length]; |
---|
158 | // quantities found... now build array for probabilities |
---|
159 | for (counter1=0;counter1<quantity.length;counter1++) |
---|
160 | { |
---|
161 | prob[counter1] = (double)quantity[counter1]/(double)total; |
---|
162 | // I don't think we need to check for negative values here -- Sean |
---|
163 | } |
---|
164 | RandomChoice.organizeDistribution(prob); |
---|
165 | double s=0.0; |
---|
166 | selection = RandomChoice.pickFromDistribution(prob,s); |
---|
167 | |
---|
168 | return (int[])permutations.get(selection); |
---|
169 | } |
---|
170 | |
---|
171 | public GPNode newRootedTree(final EvolutionState state, |
---|
172 | final GPType type, |
---|
173 | final int thread, |
---|
174 | final GPNodeParent parent, |
---|
175 | final GPFunctionSet set, |
---|
176 | final int argposition, |
---|
177 | final int requestedSize) |
---|
178 | { |
---|
179 | int treeSize; |
---|
180 | boolean valid=false; |
---|
181 | String word=new String(); |
---|
182 | |
---|
183 | treeSize=pickSize(state,thread); |
---|
184 | |
---|
185 | if (!aritySetupDone) { setupArities(state,set); } |
---|
186 | |
---|
187 | int[] temp=new int[arities.length]; |
---|
188 | permutations=new LinkedList(); |
---|
189 | Permute(0,temp,treeSize-1); |
---|
190 | if (permutations.size()==0) { state.output.fatal("Not able to build combination of nodes."); } |
---|
191 | int[] scheme=select(permutations,treeSize); |
---|
192 | word=buildDyckWord(treeSize,arities,scheme,state,thread); |
---|
193 | int cycle=0; |
---|
194 | while(!valid) |
---|
195 | { |
---|
196 | valid=checkDyckWord(word); |
---|
197 | if (!valid) |
---|
198 | { |
---|
199 | word=word.substring(word.length()-1,word.length()).concat(word.substring(0,word.length()-1)); |
---|
200 | cycle++; |
---|
201 | if (cycle>=(treeSize*2)-1) {state.output.fatal("Not able to find valid permutation for generated Dyck word: "+word);} |
---|
202 | } |
---|
203 | } |
---|
204 | return buildTree(state,thread,parent,argposition,set,word); |
---|
205 | } |
---|
206 | |
---|
207 | // recursive function to work out all combinations and push them onto ArrayList |
---|
208 | private void Permute(int current,int[] sol,int size) |
---|
209 | { |
---|
210 | int counter=0,result=0; |
---|
211 | // base case |
---|
212 | if (current==arities.length-1) /* set last one to maximum allowable */ |
---|
213 | { |
---|
214 | while(result<=size) |
---|
215 | { |
---|
216 | counter++; |
---|
217 | result=result+arities[current]; |
---|
218 | } |
---|
219 | result=result-arities[current]; |
---|
220 | counter--; |
---|
221 | if (result<0) |
---|
222 | { |
---|
223 | result=0; |
---|
224 | counter=0; |
---|
225 | } |
---|
226 | sol[current]=counter; |
---|
227 | |
---|
228 | //Adding this solution to the list. |
---|
229 | permutations.add(sol); |
---|
230 | } |
---|
231 | // end of base case |
---|
232 | else |
---|
233 | { |
---|
234 | while(result<=size) |
---|
235 | { |
---|
236 | if (result<=size) |
---|
237 | { |
---|
238 | sol[current]=counter; |
---|
239 | Permute(current+1,sol,size-result); |
---|
240 | } |
---|
241 | result=result+arities[current]; |
---|
242 | counter++; |
---|
243 | } |
---|
244 | } |
---|
245 | } |
---|
246 | |
---|
247 | public String buildDyckWord(int requestedSize,int[] arities,int[] s,EvolutionState state,int thread) |
---|
248 | { |
---|
249 | int counter,choices,choice,pos,arity=0,checksum=0,size=0; |
---|
250 | int[] scheme=new int[s.length]; |
---|
251 | |
---|
252 | String dyck=new String(""); |
---|
253 | String addStr=new String(); |
---|
254 | |
---|
255 | scheme=s; |
---|
256 | for(counter=0;counter<scheme.length;counter++) |
---|
257 | { |
---|
258 | checksum += scheme[counter]*arities[counter]; |
---|
259 | } |
---|
260 | |
---|
261 | size=checksum+1; |
---|
262 | if (size!=requestedSize) { state.output.message("A tree of the requested size could not be built. Using smaller size.");} |
---|
263 | choices=size; |
---|
264 | |
---|
265 | for(counter=0;counter<size;counter++) |
---|
266 | { |
---|
267 | dyck=dyck.concat("x*"); |
---|
268 | } |
---|
269 | |
---|
270 | // Find a non-0 arity to insert |
---|
271 | counter=0; |
---|
272 | while((arity==0) && (counter<scheme.length)) |
---|
273 | { |
---|
274 | if (scheme[counter]>0) |
---|
275 | { |
---|
276 | arity=arities[counter]; |
---|
277 | scheme[counter]--; |
---|
278 | } |
---|
279 | counter++; |
---|
280 | } |
---|
281 | |
---|
282 | while(arity!=0) |
---|
283 | { |
---|
284 | choice=state.random[thread].nextInt(choices--)+1; |
---|
285 | pos=-1; |
---|
286 | counter=0; |
---|
287 | // find insertion position within the string |
---|
288 | while(counter!=choice) |
---|
289 | { |
---|
290 | pos++; |
---|
291 | if (dyck.charAt(pos)=='*') { counter++; } |
---|
292 | } |
---|
293 | // building no of y's in string |
---|
294 | addStr=""; |
---|
295 | while (addStr.length()<arity) { addStr=addStr.concat("y"); } |
---|
296 | |
---|
297 | // finally put the string together again |
---|
298 | dyck=dyck.substring(0,pos).concat(addStr).concat(dyck.substring(pos+1,dyck.length())); |
---|
299 | |
---|
300 | // Find another non-0 arity to insert |
---|
301 | counter=0; |
---|
302 | arity=0; |
---|
303 | while((arity==0) && (counter<scheme.length)) |
---|
304 | { |
---|
305 | if (scheme[counter]>0) |
---|
306 | { |
---|
307 | arity=arities[counter]; |
---|
308 | scheme[counter]--; |
---|
309 | } |
---|
310 | counter++; |
---|
311 | } |
---|
312 | } |
---|
313 | //Clean up leftover *'s |
---|
314 | for (counter=0;counter<dyck.length();counter++) |
---|
315 | { |
---|
316 | if(dyck.charAt(counter)=='*') |
---|
317 | { |
---|
318 | dyck=dyck.substring(0,counter).concat(dyck.substring(counter+1,dyck.length())); |
---|
319 | } |
---|
320 | } |
---|
321 | return dyck; |
---|
322 | } |
---|
323 | |
---|
324 | // function to check validity of Dyck word |
---|
325 | public boolean checkDyckWord(String dyck) |
---|
326 | { |
---|
327 | int counter=0; |
---|
328 | boolean underflow=false; |
---|
329 | String stack=new String(); |
---|
330 | while ((counter<dyck.length()) && (!underflow)) |
---|
331 | { |
---|
332 | switch (dyck.charAt(counter)) |
---|
333 | { |
---|
334 | case 'x': |
---|
335 | { |
---|
336 | stack=stack.concat("x"); |
---|
337 | break; |
---|
338 | } |
---|
339 | case 'y': |
---|
340 | { |
---|
341 | if (stack.length()<=1) |
---|
342 | { |
---|
343 | underflow=true; |
---|
344 | stack=""; |
---|
345 | } |
---|
346 | else |
---|
347 | { |
---|
348 | stack=stack.substring(0,stack.length()-1); |
---|
349 | } |
---|
350 | break; |
---|
351 | } |
---|
352 | } |
---|
353 | counter++; |
---|
354 | } |
---|
355 | if (stack.length()!=1) |
---|
356 | { |
---|
357 | return false; |
---|
358 | } |
---|
359 | else |
---|
360 | { |
---|
361 | return true; |
---|
362 | } |
---|
363 | } |
---|
364 | |
---|
365 | // This function parses the dyck word and puts random nodes into their slots. |
---|
366 | private GPNode buildTree(final EvolutionState state, |
---|
367 | final int thread, |
---|
368 | final GPNodeParent parent, |
---|
369 | final int argposition, |
---|
370 | final GPFunctionSet set, |
---|
371 | final String dyckWord) |
---|
372 | { |
---|
373 | int counter=0; |
---|
374 | Stack s=new Stack(); |
---|
375 | char nextChar; |
---|
376 | |
---|
377 | // Parsing dyck word from left to right and building tree |
---|
378 | for (counter=0;counter<dyckWord.length();counter++) |
---|
379 | { |
---|
380 | if (counter<dyckWord.length()-1) { nextChar=dyckWord.charAt(counter+1);} else { nextChar='*'; } |
---|
381 | if ((nextChar=='x') || (nextChar=='*')) /* terminal node */ |
---|
382 | { |
---|
383 | GPNode[] nn = set.terminals[0]; |
---|
384 | GPNode n = (GPNode)(nn[state.random[thread].nextInt(nn.length)].lightClone()); |
---|
385 | n.resetNode(state,thread); // give ERCs a chance to randomize |
---|
386 | s.push(n); |
---|
387 | } |
---|
388 | else if (nextChar=='y') /* non-terminal */ |
---|
389 | { |
---|
390 | // finding arity of connection |
---|
391 | int Ycount=0; /* arity */ |
---|
392 | boolean nextCharY; |
---|
393 | nextCharY=(nextChar=='y'); |
---|
394 | counter++; |
---|
395 | while ((counter<dyckWord.length()) && (nextCharY)) |
---|
396 | { |
---|
397 | if (dyckWord.charAt(counter)=='y') { Ycount++; } |
---|
398 | if (counter<dyckWord.length()-1) { nextCharY=(dyckWord.charAt(counter+1)=='y'); } |
---|
399 | counter++; |
---|
400 | } |
---|
401 | |
---|
402 | //Arity found. Now just choose non terminal at random. |
---|
403 | GPNode[] nonTerms=set.nodesByArity[0][Ycount]; |
---|
404 | GPNode nT=(GPNode) (nonTerms[state.random[thread].nextInt(nonTerms.length)].lightClone()); |
---|
405 | // Non terminal chosen, now attaching children |
---|
406 | int childcount=Ycount; |
---|
407 | while (childcount>0) |
---|
408 | { |
---|
409 | childcount--; |
---|
410 | if (s.size()==0) { state.output.fatal("Stack underflow when building tree."); } |
---|
411 | GPNode child=(GPNode) s.pop(); |
---|
412 | child.parent=nT; |
---|
413 | child.argposition=(byte)childcount; |
---|
414 | nT.children[childcount]=child; |
---|
415 | } |
---|
416 | nT.argposition=0; |
---|
417 | nT.parent=null; |
---|
418 | s.push(nT); |
---|
419 | if (counter!=dyckWord.length()) counter--; |
---|
420 | } |
---|
421 | } |
---|
422 | return (GPNode) s.pop(); |
---|
423 | } |
---|
424 | } |
---|