Free cookie consent management tool by TermsFeed Policy Generator

source: branches/OKBJavaConnector/ECJClient/src/ec/gp/breed/MutateDemotePipeline.java @ 9449

Last change on this file since 9449 was 6152, checked in by bfarka, 14 years ago

added ecj and custom statistics to communicate with the okb services #1441

File size: 21.0 KB
Line 
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
8package ec.gp.breed;
9import ec.*;
10import ec.util.*;
11import ec.gp.*;
12
13/*
14 * MutateDemotePipeline.java
15 *
16 * Created: Wed Dec 15 21:41:30 1999
17 * By: Sean Luke
18 */
19
20/**
21 * MutateDemotePipeline works very similarly to the DemoteNode algorithm
22 * described in  Kumar Chellapilla,
23 * "A Preliminary Investigation into Evolving Modular Programs without Subtree
24 * Crossover", GP98, and is also similar to the "insertion" operator found in
25 * Una-May O'Reilly's thesis,
26 * <a href="http://www.ai.mit.edu/people/unamay/thesis.html">
27 * "An Analysis of Genetic Programming"</a>.
28 *
29 * <p>MutateDemotePipeline tries picks a random tree, then picks
30 * randomly from all the demotable nodes in the tree, and demotes one. 
31 * If its chosen tree has no demotable nodes, or demoting
32 * its chosen demotable node would make the tree too deep, it repeats
33 * the choose-tree-then-choose-node process.  If after <i>tries</i> times
34 * it has failed to find a valid tree and demotable node, it gives up and simply
35 * copies the individual.
36 *
37 * <p>"Demotion" means to take a node <i>n</i> and insert a new node <i>m</i>
38 * between <i>n</i> and <i>n</i>'s parent.  <i>n</i> becomes a child of
39 * <i>m</i>; the place where it becomes a child is determined at random
40 * from all the type-compatible slots of <i>m</i>.  The other child slots
41 * of <i>m</i> are filled with randomly-generated terminals. 
42 * Chellapilla's version of the algorithm always
43 * places <i>n</i> in child slot 0 of <i>m</i>.  Because this would be
44 * unneccessarily restrictive on strong typing, MutateDemotePipeline instead
45 * picks the slot at random from all available valid choices.
46 *
47 * <p>A "Demotable" node means a node which is capable of demotion
48 * given the existing function set.  In general to demote a node <i>foo</i>,
49 * there must exist in the function set a nonterminal whose return type
50 * is type-compatible with the child slot <i>foo</i> holds in its parent;
51 * this nonterminal must also have a child slot which is type-compatible
52 * with <i>foo</i>'s return type.
53 *
54 * <p>This method is very expensive in searching nodes for
55 * "demotability".  However, if the number of types is 1 (the
56 * GP run is typeless) then the type-constraint-checking
57 * code is bypassed and the method runs a little faster.
58 *
59
60 <p><b>Typical Number of Individuals Produced Per <tt>produce(...)</tt> call</b><br>
61 ...as many as the source produces
62
63 <p><b>Number of Sources</b><br>
64 1
65
66 <p><b>Parameters</b><br>
67 <table>
68 <tr><td valign=top><i>base</i>.<tt>tries</tt><br>
69 <font size=-1>int &gt;= 1</font></td>
70 <td valign=top>(number of times to try finding valid pairs of nodes)</td></tr>
71
72 <tr><td valign=top><i>base</i>.<tt>maxdepth</tt><br>
73 <font size=-1>int &gt;= 1</font></td>
74 <td valign=top>(maximum valid depth of a mutated tree)</td></tr>
75
76 <tr><td valign=top><i>base</i>.<tt>tree.0</tt><br>
77 <font size=-1>0 &lt; int &lt; (num trees in individuals), if exists</font></td>
78 <td valign=top>(tree chosen for mutation; if parameter doesn't exist, tree is picked at random)</td></tr>
79
80 </table>
81
82 <p><b>Default Base</b><br>
83 gp.breed.mutate-demote
84
85
86 * @author Sean Luke
87 * @version 1.0
88 */
89
90public class MutateDemotePipeline extends GPBreedingPipeline
91    {
92    public static final String P_MUTATEDEMOTE = "mutate-demote";
93    public static final String P_NUM_TRIES = "tries";
94    public static final String P_MAXDEPTH = "maxdepth";
95    public static final int NUM_SOURCES = 1;
96   
97    /** The number of times the pipeline tries to build a valid mutated
98        tree before it gives up and just passes on the original */
99    int numTries;
100
101    /** The maximum depth of a mutated tree */
102    int maxDepth;
103
104    /** Is our tree fixed?  If not, this is -1 */
105    int tree;
106
107    /** Temporary Node Gatherer */
108    private GPNodeGatherer gatherer;
109
110    public MutateDemotePipeline() { gatherer = new GPNodeGatherer(); }
111
112    public Parameter defaultBase() { return GPBreedDefaults.base().push(P_MUTATEDEMOTE); }
113
114    public int numSources() { return NUM_SOURCES; }
115
116    public void setup(final EvolutionState state, final Parameter base)
117        {
118        super.setup(state,base);
119       
120        Parameter def = defaultBase();
121
122        numTries = state.parameters.getInt(base.push(P_NUM_TRIES),
123            def.push(P_NUM_TRIES),1);
124        if (numTries == 0)
125            state.output.fatal("MutateDemotePipeline has an invalid number of tries (it must be >= 1).",base.push(P_NUM_TRIES),def.push(P_NUM_TRIES));
126   
127        maxDepth = state.parameters.getInt(base.push(P_MAXDEPTH),
128            def.push(P_MAXDEPTH),1);
129        if (maxDepth==0)
130            state.output.fatal("The MutateDemotePipeline " + base + "has an invalid maximum depth (it must be >= 1).",base.push(P_MAXDEPTH),def.push(P_MAXDEPTH));
131
132        tree = TREE_UNFIXED;
133        if (state.parameters.exists(base.push(P_TREE).push(""+0),
134                def.push(P_TREE).push(""+0)))
135            {
136            tree = state.parameters.getInt(base.push(P_TREE).push(""+0),
137                def.push(P_TREE).push(""+0),0);
138            if (tree==-1)
139                state.output.fatal("Tree fixed value, if defined, must be >= 0");
140            }
141        }
142
143    public Object clone()
144        {
145        MutateDemotePipeline obj = (MutateDemotePipeline)(super.clone());
146        obj.gatherer = new GPNodeGatherer();  // make sure they're different
147        return obj;
148        }
149
150
151    private boolean demotable(final GPInitializer initializer,
152        final GPNode node, final GPFunctionSet set)
153        {
154        GPType t;
155
156        if (node.parent instanceof GPNode)  // ugh, expensive
157            t = ((GPNode)(node.parent)).constraints(initializer).childtypes[node.argposition];
158        else
159            t = ((GPTree)(node.parent)).constraints(initializer).treetype;
160
161        // Now, out of the nonterminals compatible with that return type,
162        // do any also have a child compatible with that return type?  This
163        // will be VERY expensive
164
165        for(int x=0;x<set.nonterminals[t.type].length;x++)
166            for(int y=0;y<set.nonterminals[t.type][x].constraints(initializer).
167                    childtypes.length;y++)
168                if (set.nonterminals[t.type][x].constraints(initializer).childtypes[y].
169                    compatibleWith(initializer,node.constraints(initializer).returntype))
170                    return true;
171        return false;
172        }
173   
174
175    private void demoteSomething(final GPNode node, final EvolutionState state, final int thread, final GPFunctionSet set)
176        {
177        // if I have just one type, do it the easy way
178        if (((GPInitializer)state.initializer).numAtomicTypes +
179            ((GPInitializer)state.initializer).numSetTypes == 1)
180            _demoteSomethingTypeless(node,state,thread,set);
181        // otherwise, I gotta do the dirty work
182        else _demoteSomething(node,state,thread,set);
183        }
184
185
186    private void _demoteSomething(final GPNode node, final EvolutionState state, final int thread, final GPFunctionSet set)
187        {
188        int numDemotable = 0;
189
190        GPType t;
191        GPInitializer initializer = ((GPInitializer)state.initializer);
192       
193        if (node.parent instanceof GPNode)  // ugh, expensive
194            t = ((GPNode)(node.parent)).constraints(initializer).childtypes[node.argposition];
195        else
196            t = ((GPTree)(node.parent)).constraints(initializer).treetype;
197
198        // Now, determine how many nodes we can demote this under --
199        // note this doesn't select based on the total population
200        // of "available child positions", but on the total population
201        // of *nodes* regardless of if they have more than one possible
202        // valid "child position".
203
204        for(int x=0;x<set.nonterminals[t.type].length;x++)
205            for(int y=0;y<set.nonterminals[t.type][x].constraints(initializer).
206                    childtypes.length;y++)
207                if (set.nonterminals[t.type][x].constraints(initializer).childtypes[y].
208                    compatibleWith(initializer,node.constraints(initializer).returntype))
209                    {
210                    numDemotable++; break; // breaks out to enclosing for
211                    }
212
213        // pick a random item to demote -- numDemotable is assumed to be > 0
214        int demoteItem = state.random[thread].nextInt(numDemotable);
215
216        numDemotable=0;
217        // find it
218
219        for(int x=0;x<set.nonterminals[t.type].length;x++)
220            for(int y=0;y<set.nonterminals[t.type][x].constraints(initializer).
221                    childtypes.length;y++)
222                if (set.nonterminals[t.type][x].constraints(initializer).childtypes[y].
223                    compatibleWith(initializer,node.constraints(initializer).returntype))
224                    {
225                    if (numDemotable==demoteItem)
226                        {
227                        // clone the node
228                        GPNode cnode = (GPNode)(set.nonterminals[t.type][x].lightClone());
229
230                        // choose a spot to hang the old parent under
231                        int numSpots=0;
232                        GPType retyp = node.constraints(initializer).returntype;
233                        GPType[] chityp = cnode.constraints(initializer).childtypes;
234
235                        for(int z=0;z<cnode.children.length;z++)
236                            if (chityp[z].compatibleWith(initializer,retyp))
237                                numSpots++;
238                        int choice = state.random[thread].nextInt(numSpots);
239
240                        numSpots=0;
241                        for(int z=0;z<cnode.children.length;z++)
242                            if (chityp[z].compatibleWith(initializer,retyp))
243                                {
244                                if (numSpots==choice)
245                                    {
246                                    // demote the parent, inserting cnode
247                                    cnode.parent = node.parent;
248                                    cnode.argposition = node.argposition;
249                                    cnode.children[z] = node;
250                                    node.parent = cnode;
251                                    node.argposition = (byte)z;
252                                    if (cnode.parent instanceof GPNode)
253                                        ((GPNode)(cnode.parent)).
254                                            children[cnode.argposition] = cnode;
255                                    else ((GPTree)(cnode.parent)).child = cnode;
256
257                                    // this is important to ensure that the
258                                    // demotion only happens once!  Otherwise
259                                    // you'll get really nasty bugs
260                                    numSpots++;  // notice no break
261                                    }
262                                else
263                                    {
264                                    // hang a randomly-generated terminal off of cnode
265                                    GPNode term = (GPNode)(set.terminals[chityp[z].type][
266                                            state.random[thread].nextInt(
267                                                set.terminals[chityp[z].type].length)].lightClone());
268                                    cnode.children[z] = term;
269                                    term.parent = cnode; // just in case
270                                    term.argposition = (byte)z;  // just in case
271                                    term.resetNode(state,thread);  // let it randomize itself if necessary
272
273                                    // increase numSpots
274                                    numSpots++;  // notice no break
275                                    }
276                                }
277                            else
278                                {
279                                // hang a randomly-generated terminal off of cnode
280                                GPNode term = (GPNode)(set.terminals[chityp[z].type][
281                                        state.random[thread].nextInt(
282                                            set.terminals[chityp[z].type].length)].lightClone());
283                                cnode.children[z] = term;
284                                term.parent = cnode; // just in case
285                                term.argposition = (byte)z;  // just in case
286                                term.resetNode(state,thread);  // let it randomize itself if necessary
287                                }
288                        return;
289                        }
290                    else
291                        {
292                        numDemotable++; break; // breaks out to enclosing for
293                        }
294                    }
295        // should never reach here
296        throw new InternalError("Bug in demoteSomething -- should never be able to reach the end of the function");
297        }
298
299
300
301    private void _demoteSomethingTypeless(final GPNode node, final EvolutionState state, final int thread, final GPFunctionSet set)
302        {
303        int numDemotable = 0;
304
305        // since we're typeless, we can demote under any nonterminal
306        numDemotable = set.nonterminals[0].length;
307
308        // pick a random item to demote -- numDemotable is assumed to be > 0
309        int demoteItem = state.random[thread].nextInt(numDemotable);
310
311        numDemotable=0;
312        // find it
313
314        // clone the node
315        GPNode cnode = (GPNode)(set.nonterminals[0][demoteItem].lightClone());
316       
317        GPType[] chityp = cnode.constraints(((GPInitializer)state.initializer)).childtypes;
318
319        // choose a spot to hang the old parent under
320        int choice = state.random[thread].nextInt(cnode.children.length);
321       
322        for(int z=0;z<cnode.children.length;z++)
323            if (z==choice)
324                {
325                // demote the parent, inserting cnode
326                cnode.parent = node.parent;
327                cnode.argposition = node.argposition;
328                cnode.children[z] = node;
329                node.parent = cnode;
330                node.argposition = (byte)z;
331                if (cnode.parent instanceof GPNode)
332                    ((GPNode)(cnode.parent)).
333                        children[cnode.argposition] = cnode;
334                else ((GPTree)(cnode.parent)).child = cnode;
335                }
336            else
337                {
338                // hang a randomly-generated terminal off of cnode
339                GPNode term = (GPNode)(
340                    set.terminals[chityp[z].type][
341                        state.random[thread].nextInt(
342                            set.terminals[chityp[z].type].length)].lightClone());
343                cnode.children[z] = term;
344                term.parent = cnode; // just in case
345                term.argposition = (byte)z;  // just in case
346                term.resetNode(state,thread);  // let it randomize itself if necessary
347                }
348        }
349
350
351
352
353    private int numDemotableNodes(final GPInitializer initializer,
354        final GPNode root, int soFar, final GPFunctionSet set)
355        {
356        // if I have just one type, skip this and just return
357        // the number of nonterminals in the tree
358        if (initializer.numAtomicTypes +
359            initializer.numSetTypes == 1)
360            return root.numNodes(GPNode.NODESEARCH_ALL);
361        // otherwise, I gotta do the dirty work
362        else return _numDemotableNodes(initializer,root,soFar,set);
363        }
364
365
366    private int _numDemotableNodes(final GPInitializer initializer,
367        final GPNode root, int soFar, final GPFunctionSet set)
368        {
369        if (demotable(initializer,root, set)) soFar++;
370        for(int x=0;x<root.children.length;x++)
371            soFar = _numDemotableNodes(initializer,root.children[x],soFar, set);
372        return soFar;
373        }
374
375
376    private GPNode demotableNode;
377
378
379    private int pickDemotableNode(final GPInitializer initializer,
380        final GPNode root, int num, final GPFunctionSet set)
381        {
382        // if I have just one type, skip this and just
383        // the num-th nonterminal
384        if (initializer.numAtomicTypes +
385            initializer.numSetTypes == 1)
386            {
387            gatherer.node = null;
388            root.nodeInPosition(num,gatherer,GPNode.NODESEARCH_ALL);
389            if (gatherer.node==null) // uh oh
390                throw new InternalError("Internal error in pickDemotableNode, nodeInPosition didn't find a node!"); // should never happen
391            demotableNode = gatherer.node;
392            return -1; // what _pickDemotableNode() returns...
393            }
394        // otherwise, I gotta do the dirty work
395        else return _pickDemotableNode(initializer,root,num,set);
396        }
397   
398
399    // sticks the node in
400    private int _pickDemotableNode(final GPInitializer initializer,
401        final GPNode root, int num, final GPFunctionSet set)
402        {
403        if (demotable(initializer,root, set))
404            {
405            num--;
406            if (num==-1)  // found it
407                {
408                demotableNode = root;
409                return num;
410                }
411            }
412        for(int x=0;x<root.children.length;x++)
413            {
414            num = _pickDemotableNode(initializer, root.children[x],num,set);
415            if (num==-1) break;  // someone found it
416            }
417        return num;     
418        }
419   
420
421    /** Returns true if inner1's depth + atdepth +1 is within the depth bounds */
422
423    private boolean verifyPoint(GPNode inner1)
424        {
425        // We know they're swap-compatible since we generated inner1
426        // to be exactly that.  So don't bother.
427
428        // next check to see if inner1 can be demoted
429        if (inner1.depth()+inner1.atDepth()+1 > maxDepth) return false;
430
431        // checks done!
432        return true;
433        }
434
435
436
437    public int produce(final int min,
438        final int max,
439        final int start,
440        final int subpopulation,
441        final Individual[] inds,
442        final EvolutionState state,
443        final int thread)
444        {
445        // grab n individuals from our source and stick 'em right into inds.
446        // we'll modify them from there
447        int n = sources[0].produce(min,max,start,subpopulation,inds,state,thread);
448
449        // should we bother?
450        if (!state.random[thread].nextBoolean(likelihood))
451            return reproduce(n, start, subpopulation, inds, state, thread, false);  // DON'T produce children from source -- we already did
452
453
454        GPInitializer initializer = ((GPInitializer)state.initializer);
455
456        // now let's mutate 'em
457        for(int q=start; q < n+start; q++)
458            {
459            GPIndividual i = (GPIndividual)inds[q];
460           
461            if (tree!=TREE_UNFIXED && (tree<0 || tree >= i.trees.length))
462                // uh oh
463                state.output.fatal("MutateDemotePipeline attempted to fix tree.0 to a value which was out of bounds of the array of the individual's trees.  Check the pipeline's fixed tree values -- they may be negative or greater than the number of trees in an individual");
464           
465            GPIndividual j;
466            if (sources[0] instanceof BreedingPipeline)
467                // it's already a copy, so just smash the tree in
468                {
469                j=i;
470                }
471            else // need to copy it
472                {
473                j = (GPIndividual)(i.lightClone());
474               
475                // Fill in various tree information that didn't get filled in there
476                j.trees = new GPTree[i.trees.length];
477               
478                for(int x=0;x<j.trees.length;x++)
479                    {
480                    j.trees[x] = (GPTree)(i.trees[x].lightClone());
481                    j.trees[x].owner = j;
482                    j.trees[x].child = (GPNode)(i.trees[x].child.clone());
483                    j.trees[x].child.parent = j.trees[x];
484                    j.trees[x].child.argposition = 0;
485                    }
486                }
487
488            for (int x=0;x<numTries;x++)
489                {
490                int t;
491                // pick random tree
492                if (tree==TREE_UNFIXED)
493                    if (i.trees.length>1) t = state.random[thread].nextInt(i.trees.length);
494                    else t = 0;
495                else t = tree;
496               
497                // is the tree demotable?
498                int numdemote = numDemotableNodes(initializer, j.trees[t].child,0,j.trees[t].constraints(initializer).functionset);
499                if (numdemote==0) continue; // uh oh, try again
500               
501                // demote the node, or if we're unsuccessful, just leave it alone
502                pickDemotableNode(initializer, j.trees[t].child,state.random[thread].nextInt(numdemote),j.trees[t].constraints(initializer).functionset);
503               
504                // does this node exceed the maximum depth limits?
505                if (!verifyPoint(demotableNode)) continue; // uh oh, try again
506               
507                // demote it
508                demoteSomething(demotableNode,state,thread,j.trees[t].constraints(initializer).functionset);
509                j.evaluated = false;
510                break;
511                }
512
513            // add the new individual, replacing its previous source
514            inds[q] = j;
515            }
516        return n;
517        }
518    }
Note: See TracBrowser for help on using the repository browser.