Free cookie consent management tool by TermsFeed Policy Generator

source: branches/OKBJavaConnector/ECJClient/src/ec/coevolve/CompetitiveEvaluator.java @ 9598

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

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

File size: 25.3 KB
Line 
1/*
2  Copyright 2006 by Sean Luke and George Mason University
3  Licensed under the Academic Free License version 3.0
4  See the file "LICENSE" for more information
5*/
6
7
8package ec.coevolve;
9import ec.*;
10import ec.util.*;
11
12/**
13 * CompetitiveEvaluator.java
14 *
15
16 <p>CompetitiveEvaluator is a Evaluator which performs <i>competitive fitness evaluations</i>. 
17 Competitive fitness is where individuals' fitness is determined by testing them against
18 other members of the same subpopulation.  Competitive fitness topologies differ from
19 co-evolution topologies in that co-evolution is a term I generally reserve for
20 multiple sbupopulations which breed separately but compete against other subpopulations
21 during evaluation time.  Individuals are evaluated regardless of whether or not they've
22 been evaluated in the past.
23
24 <p>Your Problem is responsible for setting up the fitness appropriately. 
25 CompetitiveEvaluator expects to use Problems which adhere to the GroupedProblemForm interface,
26 which defines a new evaluate(...) function, plus a preprocess(...) and postprocess(...) function.
27
28 <p>This competitive fitness evaluator is single-threaded -- maybe we'll hack in multithreading later.
29 And it only has two individuals competing during any fitness evaluation.  The order of individuals in the
30 subpopulation will be changed during the evaluation process.  There are seven evaluation topologies
31 presently supported:
32
33 <p><dl>
34 <dt><b>Single Elimination Tournament</b><dd>
35 All members of the population are paired up and evaluated.  In each pair, the "winner" is the individual
36 which winds up with the superior fitness.  If neither fitness is superior, then the "winner" is picked
37 at random.  Then all the winners are paired up and evaluated, and so on, just like in a single elimination
38 tournament.  It is important that the <b>population size be a <i>power of two</i></b>, else some individuals
39 will not have the same number of "wins" as others and may lose the tournament as a result.
40
41 <dt><b>Round Robin</b><dd>
42 Every member of the population are paired up and evaluated with all other members of the population, not
43 not including the member itself (we might add in self-play as a future later if people ask for it, it's
44 easy to hack in).
45
46 <dt><b>K-Random-Opponents-One-Way</b><dd>
47 Each individual's fitness is calculated based on K competitions against random opponents.
48 For details, see "A Comparison of Two Competitive Fitness Functions" by Liviu Panait and
49 Sean Luke in the Proceedings of GECCO 2002.
50
51 <dt><b>K-Random-Opponents-Two-Way</b><dd>
52 Each individual's fitness is calculated based on K competitions against random opponents. The advantage of
53 this method over <b>K-Random-Opponents-One-Way</b> is a reduced number of competitions (when I competes
54 against J, both I's and J's fitnesses are updated, while in the previous method only one of the individuals
55 has its fitness updated).
56 For details, see "A Comparison of Two Competitive Fitness Functions" by Liviu Panait and
57 Sean Luke in the Proceedings of GECCO 2002.
58 </dl>
59
60 <p><b>Parameters</b><br>
61 <table>
62 <tr><td valign=top><i>base.</i><tt>style</tt><br>
63 <font size=-1>string with possible values: </font></td>
64 <td valign=top>(the style of the tournament)<br>
65 <i>single-elim-tournament</i> (a single elimination tournament)<br>
66 <i>round-robin</i> (a round robin tournament)<br>
67 <i>rand-1-way</i> (K-Random-Opponents, each game counts for only one of the players)<br>
68 <i>rand-2-way</i> (K-Random-Opponents, each game counts for both players)<br>
69 </td></tr>
70
71 <tr><td valign=top><i>base.</i><tt>group-size</tt><br>
72 <font size=-1> int</font></td>
73 <td valign=top>(how many individuals per group, used in rand-1-way</i> and <i>rand-2-way</i> tournaments)<br>
74 <i>group-size</i> &gt;= 1 for <i>rand-1-way</i> or <i>rand-2-way</i><br>
75 </td></tr>
76
77 <tr><td valign=top><i>base.</i><tt>over-eval</tt><br>
78 <font size=-1> bool = <tt>true</tt> or <tt>false</tt> (default)</font></td>
79 <td valign=top>(if the tournament style leads to an individual playing more games than others (as can be the case for rand-2-way),
80 should the extra games be used for his fitness evaluatiuon?)</td></tr>
81
82 </table>
83
84 *
85 * @author Sean Luke & Liviu Panait
86 * @version 1.0
87 */
88
89
90public class CompetitiveEvaluator extends Evaluator
91    {
92    public static final int STYLE_SINGLE_ELIMINATION=1;
93    public static final int STYLE_ROUND_ROBIN=2;
94    public static final int STYLE_N_RANDOM_COMPETITORS_ONEWAY=3;
95    public static final int STYLE_N_RANDOM_COMPETITORS_TWOWAY=4;
96
97    public static final String P_COMPETE_STYLE = "style";
98    public int style;
99
100    public static final String P_GROUP_SIZE = "group-size";
101    public int groupSize;
102
103    public static final String P_OVER_EVAL = "over-eval";
104    public boolean allowOverEvaluation;
105
106    public void setup( final EvolutionState state, final Parameter base )
107        {
108        super.setup( state, base );
109        String temp;
110        temp = state.parameters.getStringWithDefault( base.push( P_COMPETE_STYLE ), null, "" );
111        if( temp.equalsIgnoreCase( "single-elim-tournament" ) )
112            {
113            style = STYLE_SINGLE_ELIMINATION;
114            }
115        else if( temp.equalsIgnoreCase( "round-robin" ) )
116            {
117            style = STYLE_ROUND_ROBIN;
118            }
119        else if( temp.equalsIgnoreCase( "rand-1-way" ) )
120            {
121            style = STYLE_N_RANDOM_COMPETITORS_ONEWAY;
122            }
123        else if( temp.equalsIgnoreCase( "rand-2-way" ) )
124            {
125            style = STYLE_N_RANDOM_COMPETITORS_TWOWAY;
126            }
127        else if (temp.equalsIgnoreCase( "rand-2-ways" ) )
128            {
129            state.output.fatal("'rand-2-ways' is no longer a valid style name: use 'rand-2-way'",
130                base.push(P_COMPETE_STYLE), null);
131            }
132        else
133            {
134            state.output.fatal( "Incorrect value for parameter. Acceptable values: " +
135                "single-elim-tournament, round-robin, rand-1-way, rand-2-way" , base.push( P_COMPETE_STYLE ) );
136            }
137
138        if( style == STYLE_N_RANDOM_COMPETITORS_ONEWAY || style == STYLE_N_RANDOM_COMPETITORS_TWOWAY )
139            {
140            groupSize = state.parameters.getInt( base.push( P_GROUP_SIZE ), null, 1 );
141            if( groupSize < 1 )
142                {
143                state.output.fatal( "Incorrect value for parameter", base.push( P_GROUP_SIZE ) );
144                }
145            }
146        allowOverEvaluation = state.parameters.getBoolean( base.push( P_OVER_EVAL ), null, false );
147        }
148
149    public boolean runComplete( final EvolutionState state )
150        {
151        return false;
152        }
153
154    public void randomizeOrder(final EvolutionState state, final Individual[] individuals)
155        {
156        // copy the inds into a new array, then dump them randomly into the
157        // subpopulation again
158        Individual[] queue = new Individual[individuals.length];
159        int len = queue.length;
160        System.arraycopy(individuals,0,queue,0,len);
161
162        for(int x=len;x>0;x--)
163            {
164            int i = state.random[0].nextInt(x);
165            individuals[x-1] = queue[i];
166            // get rid of queue[i] by swapping the highest guy there and then
167            // decreasing the highest value  :-)
168            queue[i] = queue[x-1];
169            }
170        }
171
172    /**
173     * An evaluator that performs coevolutionary evaluation.  Like SimpleEvaluator,
174     * it applies evolution pipelines, one per thread, to various subchunks of
175     * a new population.
176     */
177    public void evaluatePopulation(final EvolutionState state)
178        {
179        int numinds[] = new int[state.evalthreads];
180        int from[] = new int[state.evalthreads];
181       
182        for (int y=0;y<state.evalthreads;y++)
183            {
184            // figure numinds
185            if (y<state.evalthreads-1) // not last one
186                numinds[y] = state.population.subpops[0].individuals.length/
187                    state.evalthreads;
188            else
189                numinds[y] =
190                    state.population.subpops[0].individuals.length/
191                    state.evalthreads +
192                   
193                    (state.population.subpops[0].individuals.length -
194                        (state.population.subpops[0].individuals.length /
195                        state.evalthreads)
196                    *state.evalthreads);
197            // figure from
198            from[y] = (state.population.subpops[0].individuals.length/
199                state.evalthreads) * y;
200            }
201       
202        randomizeOrder( state, state.population.subpops[0].individuals );
203       
204        GroupedProblemForm prob = (GroupedProblemForm)(p_problem.clone());
205
206        prob.preprocessPopulation(state,state.population, style == STYLE_SINGLE_ELIMINATION);
207               
208        switch(style)
209            {
210            case STYLE_SINGLE_ELIMINATION:
211                evalSingleElimination( state, state.population.subpops[0].individuals, 0, prob);
212                break;
213            case STYLE_ROUND_ROBIN:
214                evalRoundRobin( state, from, numinds, state.population.subpops[0].individuals, 0, prob );
215                break;
216            case STYLE_N_RANDOM_COMPETITORS_ONEWAY:
217                evalNRandomOneWay( state, from, numinds, state.population.subpops[0].individuals, 0, prob );
218                break;
219            case STYLE_N_RANDOM_COMPETITORS_TWOWAY:
220                evalNRandomTwoWay( state, from, numinds, state.population.subpops[0].individuals, 0, prob );
221                break;
222            }
223   
224        prob.postprocessPopulation(state, state.population, style == STYLE_SINGLE_ELIMINATION);
225        }
226   
227    public void evalSingleElimination( final EvolutionState state,
228        final Individual[] individuals,
229        final int subpop,
230        final GroupedProblemForm prob )
231        {
232
233        // for a single-elimination tournament, the subpop[0] size must be 2^n for
234        // some value n.  We don't check that here!  Check it in setup.
235       
236        // create the tournament array
237        Individual[] tourn = new Individual[individuals.length];
238        System.arraycopy( individuals, 0, tourn, 0, individuals.length );
239        int len = tourn.length;
240        Individual[] competition = new Individual[2];
241        int[] subpops = new int[] { subpop, subpop };
242        boolean[] updates = new boolean[2];
243        updates[0] = updates[1] = true;
244
245        // the "top half" of our array will be losers.
246        // the bottom half will be winners.  Then we cut our array in half and repeat.
247        while( len > 1 )
248            {
249            for(int x=0;x<len/2;x++)
250                {
251                competition[0] = tourn[x];
252                competition[1] = tourn[len-x-1];
253
254                prob.evaluate(state,competition,updates,true,subpops, 0);
255                }
256
257            for(int x=0;x<len/2;x++)
258                {
259                // if the second individual is better, than we switch them around
260                if( tourn[len-x-1].fitness.betterThan(tourn[x].fitness) )
261                    {
262                    Individual temp = tourn[x];
263                    tourn[x] = tourn[len-x-1];
264                    tourn[len-x-1] = temp;
265                    }
266
267                }
268
269            // last part of the tournament: deal with odd values of len!
270            if( len%2 != 0 )
271                len = 1 + len/2;
272            else
273                len /= 2;
274            }
275        }
276
277
278    public void evalRoundRobin( final EvolutionState state,
279        int[] from, int[] numinds,
280        final Individual[] individuals, int subpop,
281        final GroupedProblemForm prob )
282        {
283        if (state.evalthreads==1)
284            evalRoundRobinPopChunk(state,from[0],numinds[0],0,individuals, subpop, prob);
285        else
286            {
287            Thread[] t = new Thread[state.evalthreads];
288           
289            // start up the threads
290            for (int y=0;y<state.evalthreads;y++)
291                {
292                CompetitiveEvaluatorThread r = new RoundRobinCompetitiveEvaluatorThread();
293                r.threadnum = y;
294                r.numinds = numinds[y];
295                r.from = from[y];
296                r.me = this;
297                r.subpop = subpop;
298                r.state = state;
299                r.p = prob;
300                r.inds = individuals;
301                t[y] = new Thread(r);
302                t[y].start();
303                }
304           
305            // gather the threads
306            for (int y=0;y<state.evalthreads;y++)
307                try { t[y].join(); }
308                catch(InterruptedException e)
309                    {
310                    state.output.fatal("Whoa! The main evaluation thread got interrupted!  Dying...");
311                    }
312            }
313       
314        }
315
316    /**
317     * A private helper function for evalutatePopulation which evaluates a chunk
318     * of individuals in a subpopulation for a given thread.
319     *
320     * Although this method is declared public (for the benefit of a private
321     * helper class in this file), you should not call it.
322     *
323     * @param state
324     * @param numinds
325     * @param from
326     * @param threadnum
327     * @param prob
328     */
329    public void evalRoundRobinPopChunk(final EvolutionState state,
330        int from, int numinds, int threadnum,
331        final Individual[] individuals, int subpop,
332        final GroupedProblemForm prob)
333        {
334        Individual[] competition = new Individual[2];
335        int[] subpops = new int[] { subpop, subpop };
336        boolean[] updates = new boolean[2];
337        updates[0] = updates[1] = true;
338        int upperBound = from+numinds;
339       
340        // evaluate chunk of population against entire population
341        // since an individual x will be evaluated against all
342        // other individuals <x in other threads, only evaluate it against
343        // individuals >x in this thread.
344        for(int x=from;x<upperBound;x++)
345            for(int y=x+1;y<individuals.length;y++)
346                {
347                competition[0] = individuals[x];
348                competition[1] = individuals[y];
349                prob.evaluate(state,competition,updates,false, subpops, 0);
350                }
351        }
352
353
354    public void evalNRandomOneWay( final EvolutionState state,
355        int[] from, int[] numinds,
356        final Individual[] individuals, int subpop,
357        final GroupedProblemForm prob )
358        {
359        if (state.evalthreads==1)
360            evalNRandomOneWayPopChunk(state,from[0],numinds[0],0,individuals, subpop, prob);
361        else
362            {
363            Thread[] t = new Thread[state.evalthreads];
364           
365            // start up the threads
366            for (int y=0;y<state.evalthreads;y++)
367                {
368                CompetitiveEvaluatorThread r = new NRandomOneWayCompetitiveEvaluatorThread();
369                r.threadnum = y;
370                r.numinds = numinds[y];
371                r.from = from[y];
372                r.subpop = subpop;
373                r.me = this;
374                r.state = state;
375                r.p = prob;
376                r.inds = individuals;
377                t[y] = new Thread(r);
378                t[y].start();
379                }
380           
381            // gather the threads
382            for (int y=0;y<state.evalthreads;y++)
383                try { t[y].join(); }
384                catch(InterruptedException e)
385                    {
386                    state.output.fatal("Whoa! The main evaluation thread got interrupted!  Dying...");
387                    }
388            }
389        }
390   
391    public void evalNRandomOneWayPopChunk( final EvolutionState state,
392        int from, int numinds, int threadnum,
393        final Individual[] individuals,
394        final int subpop,
395        final GroupedProblemForm prob )
396        {
397        Individual[] queue = new Individual[individuals.length];
398        int len = queue.length;
399        System.arraycopy(individuals,0,queue,0,len);
400
401        Individual[] competition = new Individual[2];
402        int subpops[] = new int[] { subpop, subpop };
403        boolean[] updates = new boolean[2];
404        updates[0] = true;
405        updates[1] = false;
406        int upperBound = from+numinds;
407       
408        for(int x=from;x<upperBound;x++)
409            {
410            competition[0] = individuals[x];
411            // fill up our tournament
412            for(int y=0;y<groupSize;)
413                {
414                // swap to end and remove
415                int index = state.random[0].nextInt(len-y);
416                competition[1] = queue[index];
417                queue[index] = queue[len-y-1];
418                queue[len-y-1] = competition[1];
419                // if the opponent is not the actual individual, we can
420                // have a competition
421                if( competition[1] != individuals[x] )
422                    {
423                    prob.evaluate(state,competition,updates,false,subpops, 0);
424                    y++;
425                    }
426                }
427            }
428        }
429
430    public void evalNRandomTwoWay( final EvolutionState state,
431        int[] from, int[] numinds,
432        final Individual[] individuals, int subpop,
433        final GroupedProblemForm prob )
434        {
435        if (state.evalthreads==1)
436            evalNRandomTwoWayPopChunk(state,from[0],numinds[0],0,individuals, subpop, prob);
437        else
438            {
439            Thread[] t = new Thread[state.evalthreads];
440           
441            // start up the threads
442            for (int y=0;y<state.evalthreads;y++)
443                {
444                CompetitiveEvaluatorThread r = new NRandomTwoWayCompetitiveEvaluatorThread();
445                r.threadnum = y;
446                r.numinds = numinds[y];
447                r.from = from[y];
448                r.me = this;
449                r.subpop = subpop;
450                r.state = state;
451                r.p = prob;
452                r.inds = individuals;
453                t[y] = new Thread(r);
454                t[y].start();
455                }
456           
457            // gather the threads
458            for (int y=0;y<state.evalthreads;y++)
459                try { t[y].join(); }
460                catch(InterruptedException e)
461                    {
462                    state.output.fatal("Whoa! The main evaluation thread got interrupted!  Dying...");
463                    }
464            }
465        }
466   
467    public void evalNRandomTwoWayPopChunk( final EvolutionState state,
468        int from, int numinds, int threadnum,
469        final Individual[] individuals,
470        final int subpop,
471        final GroupedProblemForm prob )
472        {
473
474        // the number of games played for each player
475        EncapsulatedIndividual[] individualsOrdered = new EncapsulatedIndividual[individuals.length];
476        EncapsulatedIndividual[] queue = new EncapsulatedIndividual[individuals.length];
477        for( int i = 0 ; i < individuals.length ; i++ )
478            individualsOrdered[i] = new EncapsulatedIndividual( individuals[i], 0 );
479
480        Individual[] competition = new Individual[2];
481        int[] subpops = new int[] { subpop, subpop };
482        boolean[] updates = new boolean[2];
483        updates[0] = true;
484        int upperBound = from+numinds;
485       
486        for(int x=from;x<upperBound;x++)
487            {
488            System.arraycopy(individualsOrdered,0,queue,0,queue.length);
489            competition[0] = queue[x].ind;
490
491            // if the rest of individuals is not enough to fill
492            // all games remaining for the current individual
493            // (meaning that the current individual has left a
494            // lot of games to play versus players with index
495            // greater than his own), then it should play with
496            // all. In the end, we should check that he finished
497            // all the games he needs. If he did, everything is
498            // ok, otherwise he should play with some other players
499            // with index smaller than his own, but all these games
500            // will count only for his fitness evaluation, and
501            // not for the opponents' (unless allowOverEvaluations is set to true)
502
503            // if true, it means that he has to play against all opponents with greater index
504            if( individuals.length - x - 1 <= groupSize - queue[x].nOpponentsMet )
505                {
506                for( int y = x+1 ; y < queue.length ; y++ )
507                    {
508                    competition[1] = queue[y].ind;
509                    updates[1] = (queue[y].nOpponentsMet < groupSize) || allowOverEvaluation;
510                    prob.evaluate( state, competition, updates, false, subpops, 0 );
511                    queue[x].nOpponentsMet++;
512                    if( updates[1] )
513                        queue[y].nOpponentsMet++;
514                    }
515                }
516            else // here he has to play against a selection of the opponents with greater index
517                {
518                // we can use the queue structure because we'll just rearrange the indexes
519                // but we should make sure we also rearrange the other vectors referring to the individuals
520
521                for( int y = 0 ; groupSize > queue[x].nOpponentsMet ; y++ )
522                    {
523                    // swap to the end and remove from list
524                    int index = state.random[0].nextInt( queue.length - x - 1 - y )+x+1;
525                    competition[1] = queue[index].ind;
526
527                    updates[1] = (queue[index].nOpponentsMet < groupSize) || allowOverEvaluation;
528                    prob.evaluate( state, competition, updates, false, subpops, 0 );
529                    queue[x].nOpponentsMet++;
530                    if( updates[1] )
531                        queue[index].nOpponentsMet++;
532
533                    // swap the players (such that a player will not be considered twice)
534                    EncapsulatedIndividual temp = queue[index];
535                    queue[index] = queue[queue.length - y - 1];
536                    queue[queue.length - y - 1] = temp;
537
538                    }
539
540                }
541
542            // if true, it means that the current player needs to play some games with other players with lower indexes.
543            // this is an unfortunate situation, since all those players have already had their groupSize games for the evaluation
544            if( queue[x].nOpponentsMet < groupSize )
545                {
546                for( int y = queue[x].nOpponentsMet ; y < groupSize ; y++ )
547                    {
548                    // select a random opponent with smaller index (don't even care for duplicates)
549                    int index;
550                    if( x > 0 ) // if x is 0, then there are no players with smaller index, therefore pick a random one
551                        index = state.random[0].nextInt( x );
552                    else
553                        index = state.random[0].nextInt( queue.length-1 )+1;
554                    // use the opponent for the evaluation
555                    competition[1] = queue[index].ind;
556                    updates[1] = (queue[index].nOpponentsMet < groupSize) || allowOverEvaluation;
557                    prob.evaluate( state, competition, updates, false, subpops, 0 );
558                    queue[x].nOpponentsMet++;
559                    if( updates[1] )
560                        queue[index].nOpponentsMet++;
561                   
562                    }
563                }
564
565            }
566        }
567
568    int nextPowerOfTwo( int N )
569        {
570        int i = 1;
571        while( i < N )
572            i *= 2;
573        return i;
574        }
575
576    int whereToPutInformation;
577    void fillPositions( int[] positions, int who, int totalPerDepth, int total )
578        {
579        if(totalPerDepth >= total - 1 )
580            {
581            positions[whereToPutInformation] = who;
582            whereToPutInformation++;
583            }
584        else
585            {
586            fillPositions( positions, who, totalPerDepth*2+1, total );
587            fillPositions( positions, totalPerDepth-who, totalPerDepth*2+1, total );
588            }
589        }
590
591    }
592
593// used by the K-Random-Opponents-One-Way and K-Random-Opponents-Two-Ways evaluations
594class EncapsulatedIndividual
595    {
596    public Individual ind;
597    public int nOpponentsMet;
598    public EncapsulatedIndividual( Individual ind_, int value_ )
599        {
600        ind = ind_;
601        nOpponentsMet = value_;
602        }
603    };
604
605// used by the Single-Elimination-Tournament, (Double-Elimination-Tournament and World-Cup) evaluations
606class IndividualAndVictories
607    {
608    public Individual ind;
609    public int victories;
610    public IndividualAndVictories( Individual ind_, int value_ )
611        {
612        ind = ind_;
613        victories = value_;
614        }
615    };
616
617class IndComparator implements SortComparator
618    {
619    public boolean lt(Object a, Object b)
620        { return ((Individual)a).fitness.betterThan(((Individual)b).fitness); }
621    public boolean gt(Object a, Object b)
622        { return ((Individual)b).fitness.betterThan(((Individual)a).fitness); }
623    }
624
625abstract class CompetitiveEvaluatorThread implements Runnable
626    {
627    public int numinds;
628    public int from;
629    public CompetitiveEvaluator me;
630    public EvolutionState state;
631    public int threadnum;
632    public GroupedProblemForm p;
633    public int subpop;
634    public Individual[] inds;
635    }
636
637class RoundRobinCompetitiveEvaluatorThread extends CompetitiveEvaluatorThread
638    {
639    public synchronized void run()
640        { me.evalRoundRobinPopChunk(state,from,numinds,threadnum,inds, subpop, p); }
641    }
642
643class NRandomOneWayCompetitiveEvaluatorThread extends CompetitiveEvaluatorThread
644    {
645    public synchronized void run()
646        { me.evalNRandomOneWayPopChunk(state,from,numinds,threadnum,inds, subpop, p); }
647    }
648class NRandomTwoWayCompetitiveEvaluatorThread extends CompetitiveEvaluatorThread
649    {
650    public synchronized void run()
651        { me.evalNRandomTwoWayPopChunk(state,from,numinds,threadnum,inds, subpop, p); }
652    }
Note: See TracBrowser for help on using the repository browser.