Free cookie consent management tool by TermsFeed Policy Generator

source: branches/OKBJavaConnector/ECJClient/src/ec/gp/koza/CrossoverPipeline.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: 18.2 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.koza;
9import ec.*;
10import ec.util.*;
11import ec.gp.*;
12
13/*
14 * CrossoverPipeline.java
15 *
16 * Created: Mon Aug 30 19:15:21 1999
17 * By: Sean Luke
18 */
19
20
21/**
22 * CrossoverPipeline is a GPBreedingPipeline which performs a strongly-typed
23 * version of
24 * Koza-style "Subtree Crossover".  Two individuals are selected,
25 * then a single tree is chosen in each such that the two trees
26 * have the same GPTreeConstraints.  Then a random node is chosen
27 * in each tree such that the two nodes have the same return type.
28 * If by swapping subtrees at these nodes the two trees will not
29 * violate maximum depth constraints, then the trees perform the
30 * swap, otherwise, they repeat the hunt for random nodes.
31 *
32 * <p>The pipeline tries at most <i>tries</i> times to a pair
33 * of random nodes BOTH with valid swap constraints.  If it
34 * cannot find any such pairs after <i>tries</i> times, it
35 * uses the pair of its last attempt.  If either of the nodes in the pair
36 * is valid, that node gets substituted with the other node.  Otherwise
37 * an individual invalid node isn't changed at all (it's "reproduced").
38 *
39 * <p><b>Compatibility with constraints.</b>
40 * Since Koza-I/II only tries 1 time, and then follows this policy, this is
41 * compatible with Koza.  lil-gp either tries 1 time, or tries forever.
42 * Either way, this is compatible with lil-gp.  My hacked
43 * <a href="http://www.cs.umd.edu/users/seanl/gp/">lil-gp kernel</a>
44 * either tries 1 time, <i>n</i> times, or forever.  This is compatible
45 * as well.
46 *
47 * <p>This pipeline typically produces up to 2 new individuals (the two newly-
48 * swapped individuals) per produce(...) call.  If the system only
49 * needs a single individual, the pipeline will throw one of the
50 * new individuals away.  The user can also have the pipeline always
51 * throw away the second new individual instead of adding it to the population.
52 * In this case, the pipeline will only typically
53 * produce 1 new individual per produce(...) call.
54
55 <p><b>Typical Number of Individuals Produced Per <tt>produce(...)</tt> call</b><br>
56 2 * minimum typical number of individuals produced by each source, unless tossSecondParent
57 is set, in which case it's simply the minimum typical number.
58
59 <p><b>Number of Sources</b><br>
60 2
61
62 <p><b>Parameters</b><br>
63 <table>
64 <tr><td valign=top><i>base</i>.<tt>tries</tt><br>
65 <font size=-1>int &gt;= 1</font></td>
66 <td valign=top>(number of times to try finding valid pairs of nodes)</td></tr>
67
68 <tr><td valign=top><i>base</i>.<tt>maxdepth</tt><br>
69 <font size=-1>int &gt;= 1</font></td>
70 <td valign=top>(maximum valid depth of a crossed-over subtree)</td></tr>
71 
72 <tr><td valign=top><i>base</i>.<tt>tree.0</tt><br>
73 <font size=-1>0 &lt; int &lt; (num trees in individuals), if exists</font></td>
74 <td valign=top>(first tree for the crossover; if parameter doesn't exist, tree is picked at random)</td></tr>
75
76 <tr><td valign=top><i>base</i>.<tt>tree.1</tt><br>
77 <font size=-1>0 &lt; int &lt; (num trees in individuals), if exists</font></td>
78 <td valign=top>(second tree for the crossover; if parameter doesn't exist, tree is picked at random.  This tree <b>must</b> have the same GPTreeConstraints as <tt>tree.0</tt>, if <tt>tree.0</tt> is defined.)</td></tr>
79
80 <tr><td valign=top><i>base</i>.<tt>ns.</tt><i>n</i><br>
81 <font size=-1>classname, inherits and != GPNodeSelector,<br>
82 or String <tt>same<tt></font></td>
83 <td valign=top>(GPNodeSelector for parent <i>n</i> (n is 0 or 1) If, for <tt>ns.1</tt> the value is <tt>same</tt>, then <tt>ns.1</tt> a copy of whatever <tt>ns.0</tt> is.  Note that the default version has no <i>n</i>)</td></tr>
84
85 <tr><td valign=top><i>base</i>.<tt>toss</tt><br>
86 <font size=-1>bool = <tt>true</tt> or <tt>false</tt> (default)</font>/td>
87 <td valign=top>(after crossing over with the first new individual, should its second sibling individual be thrown away instead of adding it to the population?)</td></tr>
88 </table>
89
90 <p><b>Default Base</b><br>
91 gp.koza.xover
92
93 <p><b>Parameter bases</b><br>
94 <table>
95 <tr><td valign=top><i>base</i>.<tt>ns.</tt><i>n</i><br>
96 <td>nodeselect<i>n</i> (<i>n</i> is 0 or 1)</td></tr>
97 </table>
98
99 *
100 * @author Sean Luke
101 * @version 1.0
102 */
103
104public class CrossoverPipeline extends GPBreedingPipeline
105    {
106    public static final String P_NUM_TRIES = "tries";
107    public static final String P_MAXDEPTH = "maxdepth";
108    public static final String P_CROSSOVER = "xover";
109    public static final String P_TOSS = "toss";
110    public static final int INDS_PRODUCED = 2;
111    public static final int NUM_SOURCES = 2;
112
113    /** How the pipeline selects a node from individual 1 */
114    public GPNodeSelector nodeselect1;
115
116    /** How the pipeline selects a node from individual 2 */
117    public GPNodeSelector nodeselect2;
118
119    /** Is the first tree fixed?  If not, this is -1 */
120    public int tree1;
121
122    /** Is the second tree fixed?  If not, this is -1 */
123    public int tree2;
124
125    /** How many times the pipeline attempts to pick nodes until it gives up. */
126    public int numTries;
127
128    /** The deepest tree the pipeline is allowed to form.  Single terminal trees are depth 1. */
129    public int maxDepth;
130
131    /** Should the pipeline discard the second parent after crossing over? */
132    public boolean tossSecondParent;
133
134    /** Temporary holding place for parents */
135    public GPIndividual parents[];
136
137    public CrossoverPipeline() { parents = new GPIndividual[2]; }
138
139    public Parameter defaultBase() { return GPKozaDefaults.base().push(P_CROSSOVER); }
140
141    public int numSources() { return NUM_SOURCES; }
142
143    public Object clone()
144        {
145        CrossoverPipeline c = (CrossoverPipeline)(super.clone());
146
147        // deep-cloned stuff
148        c.nodeselect1 = (GPNodeSelector)(nodeselect1.clone());
149        c.nodeselect2 = (GPNodeSelector)(nodeselect2.clone());
150        c.parents = (GPIndividual[]) parents.clone();
151
152        return c;
153        }
154
155    public void setup(final EvolutionState state, final Parameter base)
156        {
157        super.setup(state,base);
158
159        Parameter def = defaultBase();
160        Parameter p = base.push(P_NODESELECTOR).push("0");
161        Parameter d = def.push(P_NODESELECTOR).push("0");
162
163        nodeselect1 = (GPNodeSelector)
164            (state.parameters.getInstanceForParameter(
165                p,d, GPNodeSelector.class));
166        nodeselect1.setup(state,p);
167
168        p = base.push(P_NODESELECTOR).push("1");
169        d = def.push(P_NODESELECTOR).push("1");
170
171        if (state.parameters.exists(p,d) &&
172            state.parameters.getString(p,d).equals(V_SAME))
173            // can't just copy it this time; the selectors
174            // use internal caches.  So we have to clone it no matter what
175            nodeselect2 = (GPNodeSelector)(nodeselect1.clone());
176        else
177            {
178            nodeselect2 = (GPNodeSelector)
179                (state.parameters.getInstanceForParameter(
180                    p,d, GPNodeSelector.class));
181            nodeselect2.setup(state,p);
182            }
183
184        numTries = state.parameters.getInt(base.push(P_NUM_TRIES),
185            def.push(P_NUM_TRIES),1);
186        if (numTries == 0)
187            state.output.fatal("GPCrossover Pipeline has an invalid number of tries (it must be >= 1).",base.push(P_NUM_TRIES),def.push(P_NUM_TRIES));
188
189        maxDepth = state.parameters.getInt(base.push(P_MAXDEPTH),def.push(P_MAXDEPTH),1);
190        if (maxDepth==0)
191            state.output.fatal("GPCrossover Pipeline has an invalid maximum depth (it must be >= 1).",base.push(P_MAXDEPTH),def.push(P_MAXDEPTH));
192
193        tree1 = TREE_UNFIXED;
194        if (state.parameters.exists(base.push(P_TREE).push(""+0),
195                def.push(P_TREE).push(""+0)))
196            {
197            tree1 = state.parameters.getInt(base.push(P_TREE).push(""+0),
198                def.push(P_TREE).push(""+0),0);
199            if (tree1==-1)
200                state.output.fatal("Tree fixed value, if defined, must be >= 0");
201            }
202
203        tree2 = TREE_UNFIXED;
204        if (state.parameters.exists(base.push(P_TREE).push(""+1),
205                def.push(P_TREE).push(""+1)))
206            {
207            tree2 = state.parameters.getInt(base.push(P_TREE).push(""+1),
208                def.push(P_TREE).push(""+1),0);
209            if (tree2==-1)
210                state.output.fatal("Tree fixed value, if defined, must be >= 0");
211            }
212        tossSecondParent = state.parameters.getBoolean(base.push(P_TOSS),
213            def.push(P_TOSS),false);
214
215        }
216
217    /** Returns 2 * minimum number of typical individuals produced by any sources, else
218        1* minimum number if tossSecondParent is true. */
219    public int typicalIndsProduced()
220        {
221        return (tossSecondParent? minChildProduction(): minChildProduction()*2);
222        }
223
224    /** Returns true if inner1 can feasibly be swapped into inner2's position. */
225
226    public boolean verifyPoints(final GPInitializer initializer,
227        final GPNode inner1, final GPNode inner2)
228        {
229        // first check to see if inner1 is swap-compatible with inner2
230        // on a type basis
231        if (!inner1.swapCompatibleWith(initializer, inner2)) return false;
232
233        // next check to see if inner1 can fit in inner2's spot
234        if (inner1.depth()+inner2.atDepth() > maxDepth) return false;
235
236        // checks done!
237        return true;
238        }
239
240
241    public int produce(final int min,
242        final int max,
243        final int start,
244        final int subpopulation,
245        final Individual[] inds,
246        final EvolutionState state,
247        final int thread)
248
249        {
250        // how many individuals should we make?
251        int n = typicalIndsProduced();
252        if (n < min) n = min;
253        if (n > max) n = max;
254
255        // should we bother?
256        if (!state.random[thread].nextBoolean(likelihood))
257            return reproduce(n, start, subpopulation, inds, state, thread, true);  // DO produce children from source -- we've not done so already
258
259
260
261        GPInitializer initializer = ((GPInitializer)state.initializer);
262       
263        for(int q=start;q<n+start; /* no increment */)  // keep on going until we're filled up
264            {
265            // grab two individuals from our sources
266            if (sources[0]==sources[1])  // grab from the same source
267                sources[0].produce(2,2,0,subpopulation,parents,state,thread);
268            else // grab from different sources
269                {
270                sources[0].produce(1,1,0,subpopulation,parents,state,thread);
271                sources[1].produce(1,1,1,subpopulation,parents,state,thread);
272                }
273           
274            // at this point, parents[] contains our two selected individuals
275           
276            // are our tree values valid?
277            if (tree1!=TREE_UNFIXED && (tree1<0 || tree1 >= parents[0].trees.length))
278                // uh oh
279                state.output.fatal("GP Crossover Pipeline 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");
280            if (tree2!=TREE_UNFIXED && (tree2<0 || tree2 >= parents[1].trees.length))
281                // uh oh
282                state.output.fatal("GP Crossover Pipeline attempted to fix tree.1 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");
283
284            int t1=0; int t2=0;
285            if (tree1==TREE_UNFIXED || tree2==TREE_UNFIXED)
286                {
287                do
288                    // pick random trees  -- their GPTreeConstraints must be the same
289                    {
290                    if (tree1==TREE_UNFIXED)
291                        if (parents[0].trees.length > 1)
292                            t1 = state.random[thread].nextInt(parents[0].trees.length);
293                        else t1 = 0;
294                    else t1 = tree1;
295
296                    if (tree2==TREE_UNFIXED)
297                        if (parents[1].trees.length>1)
298                            t2 = state.random[thread].nextInt(parents[1].trees.length);
299                        else t2 = 0;
300                    else t2 = tree2;
301                    } while (parents[0].trees[t1].constraints(initializer) != parents[1].trees[t2].constraints(initializer));
302                }
303            else
304                {
305                t1 = tree1;
306                t2 = tree2;
307                // make sure the constraints are okay
308                if (parents[0].trees[t1].constraints(initializer)
309                    != parents[1].trees[t2].constraints(initializer)) // uh oh
310                    state.output.fatal("GP Crossover Pipeline's two tree choices are both specified by the user -- but their GPTreeConstraints are not the same");
311                }
312
313
314
315            // validity results...
316            boolean res1 = false;
317            boolean res2 = false;
318           
319           
320            // prepare the nodeselectors
321            nodeselect1.reset();
322            nodeselect2.reset();
323           
324           
325            // pick some nodes
326           
327            GPNode p1=null;
328            GPNode p2=null;
329           
330            for(int x=0;x<numTries;x++)
331                {
332                // pick a node in individual 1
333                p1 = nodeselect1.pickNode(state,subpopulation,thread,parents[0],parents[0].trees[t1]);
334               
335                // pick a node in individual 2
336                p2 = nodeselect2.pickNode(state,subpopulation,thread,parents[1],parents[1].trees[t2]);
337               
338                // check for depth and swap-compatibility limits
339                res1 = verifyPoints(initializer,p2,p1);  // p2 can fill p1's spot -- order is important!
340                if (n-(q-start)<2 || tossSecondParent) res2 = true;
341                else res2 = verifyPoints(initializer,p1,p2);  // p1 can fill p2's spot -- order is important!
342               
343                // did we get something that had both nodes verified?
344                // we reject if EITHER of them is invalid.  This is what lil-gp does.
345                // Koza only has numTries set to 1, so it's compatible as well.
346                if (res1 && res2) break;
347                }
348
349            // at this point, res1 AND res2 are valid, OR
350            // either res1 OR res2 is valid and we ran out of tries, OR
351            // neither res1 nor res2 is valid and we rand out of tries.
352            // So now we will transfer to a tree which has res1 or res2
353            // valid, otherwise it'll just get replicated.  This is
354            // compatible with both Koza and lil-gp.
355
356
357            // at this point I could check to see if my sources were breeding
358            // pipelines -- but I'm too lazy to write that code (it's a little
359            // complicated) to just swap one individual over or both over,
360            // -- it might still entail some copying.  Perhaps in the future.
361            // It would make things faster perhaps, not requiring all that
362            // cloning.
363
364           
365           
366            // Create some new individuals based on the old ones -- since
367            // GPTree doesn't deep-clone, this should be just fine.  Perhaps we
368            // should change this to proto off of the main species prototype, but
369            // we have to then copy so much stuff over; it's not worth it.
370                   
371            GPIndividual j1 = (GPIndividual)(parents[0].lightClone());
372            GPIndividual j2 = null;
373            if (n-(q-start)>=2 && !tossSecondParent) j2 = (GPIndividual)(parents[1].lightClone());
374           
375            // Fill in various tree information that didn't get filled in there
376            j1.trees = new GPTree[parents[0].trees.length];
377            if (n-(q-start)>=2 && !tossSecondParent) j2.trees = new GPTree[parents[1].trees.length];
378           
379            // at this point, p1 or p2, or both, may be null.
380            // If not, swap one in.  Else just copy the parent.
381           
382            for(int x=0;x<j1.trees.length;x++)
383                {
384                if (x==t1 && res1)  // we've got a tree with a kicking cross position!
385                    {
386                    j1.trees[x] = (GPTree)(parents[0].trees[x].lightClone());
387                    j1.trees[x].owner = j1;
388                    j1.trees[x].child = parents[0].trees[x].child.cloneReplacing(p2,p1);
389                    j1.trees[x].child.parent = j1.trees[x];
390                    j1.trees[x].child.argposition = 0;
391                    j1.evaluated = false;
392                    }  // it's changed
393                else
394                    {
395                    j1.trees[x] = (GPTree)(parents[0].trees[x].lightClone());
396                    j1.trees[x].owner = j1;
397                    j1.trees[x].child = (GPNode)(parents[0].trees[x].child.clone());
398                    j1.trees[x].child.parent = j1.trees[x];
399                    j1.trees[x].child.argposition = 0;
400                    }
401                }
402           
403            if (n-(q-start)>=2 && !tossSecondParent)
404                for(int x=0;x<j2.trees.length;x++)
405                    {
406                    if (x==t2 && res2)  // we've got a tree with a kicking cross position!
407                        {
408                        j2.trees[x] = (GPTree)(parents[1].trees[x].lightClone());           
409                        j2.trees[x].owner = j2;
410                        j2.trees[x].child = parents[1].trees[x].child.cloneReplacing(p1,p2);
411                        j2.trees[x].child.parent = j2.trees[x];
412                        j2.trees[x].child.argposition = 0;
413                        j2.evaluated = false;
414                        } // it's changed
415                    else
416                        {
417                        j2.trees[x] = (GPTree)(parents[1].trees[x].lightClone());           
418                        j2.trees[x].owner = j2;
419                        j2.trees[x].child = (GPNode)(parents[1].trees[x].child.clone());
420                        j2.trees[x].child.parent = j2.trees[x];
421                        j2.trees[x].child.argposition = 0;
422                        }
423                    }
424           
425            // add the individuals to the population
426            inds[q] = j1;
427            q++;
428            if (q<n+start && !tossSecondParent)
429                {
430                inds[q] = j2;
431                q++;
432                }
433            }
434        return n;
435        }
436    }
Note: See TracBrowser for help on using the repository browser.