ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/TSPExchangerTest.java
(Generate patch)

Comparing jsr166/src/test/loops/TSPExchangerTest.java (file contents):
Revision 1.1 by dl, Sun Aug 7 19:25:55 2005 UTC vs.
Revision 1.2 by dl, Sat Dec 10 20:08:51 2005 UTC

# Line 1 | Line 1
1   /*
2 < * Written by Bill Scherer and Doug Lea with assistance from members
3 < * of JCP JSR-166 Expert Group and released to the public domain. Use,
4 < * modify, and redistribute this code in any way without
5 < * acknowledgement.
2 > * Written by Doug Lea and Bill Scherer with assistance from members
3 > * of JCP JSR-166 Expert Group and released to the public domain, as
4 > * explained at http://creativecommons.org/licenses/publicdomain
5   */
6  
7 + import java.util.*;
8   import java.util.concurrent.*;
9   import java.util.concurrent.atomic.*;
10   import java.util.concurrent.locks.*;
11  
12 + /**
13 + * A parallel Traveling Salesperson Problem (TSP) program based on a
14 + * genetic algorithm using an Exchanger.  A population of chromosomes
15 + * is distributed among "pools".  The chromosomes represent tours, and
16 + * their fitness is the total tour length. Each chromosome is
17 + * initialized as a random tour.  A Task is associated with each pool.
18 + * Each task repeatedly does, for a fixed number of iterations
19 + * (generations):
20 + *
21 + * <ol>
22 + *   <li> Select a breeder b from the pool
23 + *   <li> Create a strand of its tour with a random starting point and length
24 + *   <li> Offer the strand to the exchanger, receiving a strand from
25 + *        another pool
26 + *   <li> Combine b and the received strand using crossing function to
27 + *        create new chromosome c.
28 + *   <li> Replace a chromosome in the pool with c.
29 + * </ol>
30 + *
31 + * See below for more details.
32 + * <p>
33 + *
34 + */
35   public class TSPExchangerTest {
36 <    // Set SLS true to use as default the settings in Scherer, Lea, and
37 <    // Scott paper. Otherwise smaller values are used to speed up testing
38 <    static final boolean SLS = false;
39 <
40 <    static final int DEFAULT_THREADS      = SLS?    32:     8;
41 <    static final int DEFAULT_CITIES       = SLS?   100:    50;
42 <    static final int DEFAULT_POPULATION   = SLS?  1000:   500;
43 <    static final int DEFAULT_BREEDERS     = SLS?   200:   100;
44 <    static final int DEFAULT_GENERATIONS  = SLS? 20000: 10000;
36 >    static final int DEFAULT_MAX_THREADS  =
37 >        (Runtime.getRuntime().availableProcessors() + 2);
38 >
39 >    /**
40 >     * The problem size. Each city is a random point. The goal is to
41 >     * find a tour among them with smallest total Euclidean distance.
42 >     */
43 >    static final int DEFAULT_CITIES = 144;
44 >
45 >    // Tuning parameters.
46 >
47 >    /**
48 >     * The number of chromosomes per pool. Must be a power of two.
49 >     *
50 >     * Smaller values lead to faster iterations but poorer quality
51 >     * results
52 >     */
53 >    static final int DEFAULT_POOL_SIZE = 32;
54  
55 +    /**
56 +     * The number of iterations per task. Convergence appears
57 +     * to be roughly proportional to #cities-squared
58 +     */
59 +    static final int DEFAULT_GENERATIONS = DEFAULT_CITIES * DEFAULT_CITIES;
60 +
61 +    /**
62 +     * The number of pools. The total population is #pools * poolSize,
63 +     * which should be roughly on the order of #cities-squared
64 +     *
65 +     * Smaller values lead to faster total runs but poorer quality
66 +     * results
67 +     */
68 +    static final int DEFAULT_NPOOLS = DEFAULT_GENERATIONS / DEFAULT_POOL_SIZE;
69 +
70 +    /**
71 +     * The minimum length for a random chromosome strand.
72 +     * Must be at least 1.
73 +     */
74 +    static final int MIN_STRAND_LENGTH = 3;
75 +
76 +    /**
77 +     * The probablility mask value for creating random strands,
78 +     * that have lengths at least MIN_STRAND_LENGTH, and grow
79 +     * with exposnential decay 2^(-(1/(RANDOM_STRAND_MASK + 1)
80 +     * Must be 1 less than a power of two.
81 +     */
82 +    static final int RANDOM_STRAND_MASK = 7;
83 +
84 +    /**
85 +     * Probablility control for selecting breeders.
86 +     * Breeders are selected starting at the best-fitness chromosome,
87 +     * with exponentially decaying probablility
88 +     * 1 / (poolSize >>> BREEDER_DECAY).
89 +     *
90 +     * Larger values usually cause faster convergence but poorer
91 +     * quality results
92 +     */
93 +    static final int BREEDER_DECAY = 1;
94 +
95 +    /**
96 +     * Probablility control for selecting dyers.
97 +     * Dyers are selected starting at the worst-fitness chromosome,
98 +     * with exponentially decaying probablility
99 +     * 1 / (poolSize >>> DYER_DECAY)
100 +     *
101 +     * Larger values usually cause faster convergence but poorer
102 +     * quality results
103 +     */
104 +    static final int DYER_DECAY = 1;
105 +
106 +    /**
107 +     * The probability mask for a task to give up running and
108 +     * resubmit itself. On each iteration, a task stops iterating
109 +     * and resubmits itself with probability 1 / (RESUBMIT_MASK+1).
110 +     * This avoids some tasks running to completion before others
111 +     * even start when there are more pools than threads.
112 +     *
113 +     * Must be 1 less than a power of two.
114 +     */
115 +    static final int RESUBMIT_MASK = 63;
116 +
117 +    static final boolean verbose = true;
118 +    static final long SNAPSHOT_RATE = 10000; // in milliseconds
119 +
120 +    /**
121 +     * The set of cities. Created once per program run, to
122 +     * make it easier to compare solutions across different runs.
123 +     */
124 +    static CitySet cities;
125  
126      public static void main(String[] args) throws Exception {
127 <        int maxThreads = DEFAULT_THREADS;
127 >        int maxThreads = DEFAULT_MAX_THREADS;
128          int nCities = DEFAULT_CITIES;
129 <        int pSize = DEFAULT_POPULATION;
130 <        int nBreeders = DEFAULT_BREEDERS;
131 <        int numGenerations = DEFAULT_GENERATIONS;
129 >        int poolSize = DEFAULT_POOL_SIZE;
130 >        int nGen = nCities * nCities;
131 >        int nPools = nCities * nCities / poolSize;
132  
31        // Parse and check args
32        int argc = 0;
133          try {
134 +            int argc = 0;
135              while (argc < args.length) {
136                  String option = args[argc++];
137 <                if (option.equals("-b"))
37 <                    nBreeders = Integer.parseInt(args[argc]);
38 <                else if (option.equals("-c"))
137 >                if (option.equals("-c")) {
138                      nCities = Integer.parseInt(args[argc]);
139 +                    nGen = nCities * nCities;
140 +                    nPools = nCities * nCities / poolSize;
141 +                }
142 +                else if (option.equals("-p"))
143 +                    poolSize = Integer.parseInt(args[argc]);
144                  else if (option.equals("-g"))
145 <                    numGenerations = Integer.parseInt(args[argc]);
146 <                else if (option.equals("-p"))
147 <                    pSize = Integer.parseInt(args[argc]);
148 <                else
145 >                    nGen = Integer.parseInt(args[argc]);
146 >                else if (option.equals("-n"))
147 >                    nPools = Integer.parseInt(args[argc]);
148 >                else
149                      maxThreads = Integer.parseInt(option);
150                  argc++;
151              }
152          }
49        catch (NumberFormatException e) {
50            reportUsageErrorAndDie();
51            System.exit(0);
52        }
153          catch (Exception e) {
154              reportUsageErrorAndDie();
155          }
156  
157 <        // Display runtime parameters
158 <        System.out.print("TSPExchangerTest -b " + nBreeders);
159 <        System.out.print(" -c " + nCities);
160 <        System.out.print(" -g " + numGenerations);
161 <        System.out.print(" -p " + pSize);
162 <        System.out.print(" max threads " + maxThreads);
163 <        System.out.println();
64 <
65 <        // warmup
66 <        System.out.print("Threads: " + 2 + "\t");
67 <        oneRun(2,
68 <               nCities,
69 <               pSize,
70 <               nBreeders,
71 <               numGenerations);
72 <        Thread.sleep(100);
157 >        System.out.print("TSPExchangerTest");
158 >        System.out.print(" -c " + nCities);
159 >        System.out.print(" -g " + nGen);
160 >        System.out.print(" -p " + poolSize);
161 >        System.out.print(" -n " + nPools);
162 >        System.out.print(" max threads " + maxThreads);
163 >        System.out.println();
164  
165 <        int k = 4;
166 <        for (int i = 2; i <= maxThreads;) {
167 <            System.out.print("Threads: " + i + "\t");
168 <            oneRun(i,
78 <                   nCities,
79 <                   pSize,
80 <                   nBreeders,
81 <                   numGenerations);
82 <            Thread.sleep(100);
83 <            if (i == k) {
84 <                k = i << 1;
85 <                i = i + (i >>> 1);
86 <            }
87 <            else
88 <                i = k;
89 <        }
165 >        cities = new CitySet(nCities);
166 >
167 >        for (int i = 2; i <= maxThreads; i += 2)
168 >            oneRun(i, nPools, poolSize, nGen);
169      }
170  
171 <    private static void reportUsageErrorAndDie() {
172 <        System.out.print("usage: TSPExchangerTest [-b #breeders] [-c #cities]");
173 <        System.out.println(" [-g #generations]");
174 <        System.out.println(" [-p population size] [ #threads]");
171 >    static void reportUsageErrorAndDie() {
172 >        System.out.print("usage: TSPExchangerTest");
173 >        System.out.print(" [-c #cities]");
174 >        System.out.print(" [-p #poolSize]");
175 >        System.out.print(" [-g #generations]");
176 >        System.out.print(" [-n #pools]");
177 >        System.out.print(" #threads]");
178 >        System.out.println();
179          System.exit(0);
180      }
181  
182 <    static void oneRun(int nThreads,
183 <                       int nCities,
184 <                       int pSize,
185 <                       int nBreeders,
186 <                       int numGenerations)
187 <        throws Exception {
188 <        CyclicBarrier runBarrier = new CyclicBarrier(nThreads + 1);
189 <        Population p = new Population(nCities, pSize, nBreeders, nThreads,
190 <                                      numGenerations, runBarrier);
191 <
192 <        // Run the test
193 <        long startTime = System.currentTimeMillis();
194 <        runBarrier.await(); // start 'em off
195 <        runBarrier.await(); // wait 'til they're done
196 <        long stopTime = System.currentTimeMillis();
197 <        long elapsed = stopTime - startTime;
198 <        long rate = (numGenerations * 1000) / elapsed;
199 <        double secs = (double)elapsed / 1000.0;
182 >    /**
183 >     * Perform one run with the given parameters.  Each run completes
184 >     * when all but one of the tasks has finished.  The last remaining
185 >     * task may have no one left to exchange with, so the pool is
186 >     * abruptly terminated.
187 >     */
188 >    static void oneRun(int nThreads, int nPools, int poolSize, int nGen)
189 >        throws InterruptedException {
190 >        Population p = new Population(nThreads, nPools, poolSize, nGen);
191 >        ProgressMonitor mon = null;
192 >        if (verbose) {
193 >            mon = new ProgressMonitor(p);
194 >            mon.start();
195 >        }
196 >        p.printSnapshot(0);
197 >        long startTime = System.nanoTime();
198 >        p.start();
199 >        p.awaitTasks();
200 >        long stopTime = System.nanoTime();
201 >        if (mon != null)
202 >            mon.interrupt();
203 >        p.shutdown();
204 >        Thread.sleep(100);
205  
206 <        // Display results
207 <        System.out.print(LoopHelpers.rightJustify((int)p.bestFitness()) +
208 <                         " fitness");
209 <        System.out.print(LoopHelpers.rightJustify(rate) + " gen/s \t");
210 <        System.out.print(secs + "s elapsed");
123 <        System.out.println();
206 >        long elapsed = stopTime - startTime;
207 >        long rate = elapsed / (nPools * nGen);
208 >        double secs = (double)elapsed / 1000000000.0;
209 >        p.printSnapshot(secs);
210 >        System.out.printf("%10d ns per transfer\n", rate);
211      }
212  
213 +
214 +    /**
215 +     * A Population creates the pools, tasks, and threads for a run
216 +     * and has control methods to start, stop, and report progress.
217 +     */
218      static final class Population {
219 <        final Chromosome[] individuals;
220 <        final Exchanger<Chromosome> x;
221 <        final CitySet cities;
222 <        final int[] dyers;
223 <        final int[] breeders;
224 <        final CyclicBarrier generationBarrier;
133 <        final Thread[] threads;
134 <        final boolean[] doneMating;
135 <        final ReentrantLock matingBarrierLock;
136 <        final Condition matingBarrier;
137 <        final LoopHelpers.SimpleRandom[] rngs;
219 >        final Task[] tasks;
220 >        final Exchanger<Strand> exchanger;
221 >        final ThreadPoolExecutor exec;
222 >        final CountDownLatch done;
223 >        final int nGen;
224 >        final int poolSize;
225          final int nThreads;
139        volatile int matingBarrierCount;
226  
227 <        // action to run between each generation
142 <        class BarrierAction implements Runnable {
143 <            public void run() {
144 <                prepareToBreed();
145 <                resetMatingBarrier();
146 <            }
147 <        }
148 <
149 <        Population(int nCities,
150 <                   int pSize,
151 <                   int nBreeders,
152 <                   int nThreads,
153 <                   int nGen,
154 <                   CyclicBarrier runBarrier) {
227 >        Population(int nThreads, int nPools, int poolSize, int nGen) {
228              this.nThreads = nThreads;
229 <            // rngs[nThreads] is for global actions; others are per-thread
230 <            this.rngs = new LoopHelpers.SimpleRandom[nThreads+1];
231 <            for (int i = 0; i < rngs.length; ++i)
232 <                rngs[i] = new LoopHelpers.SimpleRandom();
233 <            this.cities = new CitySet(nCities, rngs[nThreads]);
234 <            this.individuals = new Chromosome[pSize];
235 <            for (int i = 0; i < individuals.length; i++)
236 <                individuals[i] = new Chromosome(cities, nCities,
237 <                                                rngs[nThreads]);
238 <            this.doneMating = new boolean[nThreads];
239 <            this.dyers = new int[nBreeders];
240 <            this.breeders = new int[nBreeders];
241 <
242 <            this.x = new Exchanger();
243 <
244 <            this.matingBarrierLock = new ReentrantLock();
245 <            this.matingBarrier = matingBarrierLock.newCondition();
246 <
247 <            BarrierAction ba = new BarrierAction();
248 <            this.generationBarrier = new CyclicBarrier(nThreads, ba);
249 <            ba.run(); // prepare for first generation
250 <
251 <            this.threads = new Thread[nThreads];
252 <            for (int i = 0; i < nThreads; i++) {
253 <                Runner r = new Runner(i, this, runBarrier, nGen);
254 <                threads[i] = new Thread(r);
255 <                threads[i].start();
229 >            this.nGen = nGen;
230 >            this.poolSize = poolSize;
231 >            this.exchanger = new Exchanger<Strand>();
232 >            this.done = new CountDownLatch(nPools-1);
233 >            this.tasks = new Task[nPools];
234 >            for (int i = 0; i < nPools; i++)
235 >                tasks[i] = new Task(this);
236 >            BlockingQueue<Runnable> tq =
237 >                new LinkedBlockingQueue<Runnable>();
238 >            this.exec = new ThreadPoolExecutor(nThreads, nThreads,
239 >                                               0L, TimeUnit.MILLISECONDS,
240 >                                               tq);
241 >            exec.prestartAllCoreThreads();
242 >        }
243 >
244 >        /** Start the tasks */
245 >        void start() {
246 >            for (int i = 0; i < tasks.length; i++)
247 >                exec.execute(tasks[i]);
248 >        }
249 >
250 >        /** Stop the tasks */
251 >        void shutdown() {
252 >            exec.shutdownNow();
253 >        }
254 >
255 >        /** Called by task upon terminations */
256 >        void taskDone() {
257 >            done.countDown();
258 >        }
259 >
260 >        /** Wait for (all but one) task to complete */
261 >        void awaitTasks() throws InterruptedException {
262 >            done.await();
263 >        }
264 >
265 >        /**
266 >         * Called by a task to resubmit itself after completing
267 >         * fewer than nGen iterations.
268 >         */
269 >        void resubmit(Task task) {
270 >            exec.execute(task);
271 >        }
272 >
273 >        void printSnapshot(double secs) {
274 >            int gens = 0;
275 >            double best = Double.MAX_VALUE;
276 >            double worst = 0;
277 >            for (int k = 0; k < tasks.length; ++k) {
278 >                gens += tasks[k].gen;
279 >                Chromosome[] cs = tasks[k].chromosomes;
280 >                float b = cs[0].fitness;
281 >                if (b < best)
282 >                    best = b;
283 >                float w = cs[cs.length-1].fitness;
284 >                if (w > worst)
285 >                    worst = w;
286 >            }
287 >            int avegen = (done.getCount() == 0)? nGen : gens / tasks.length;
288 >            System.out.printf("Time:%9.3f Best:%9.3f Worst:%9.3f Gen:%6d Threads:%4d\n",
289 >                              secs, best, worst, avegen, nThreads);
290 >        }
291 >        
292 >        float averageFitness() { // currently unused
293 >            float total = 0;
294 >            int count = 0;
295 >            for (int k = 0; k < tasks.length; ++k) {
296 >                Chromosome[] cs = tasks[k].chromosomes;
297 >                for (int i = 0; i < cs.length; i++)
298 >                    total += cs[i].fitness;
299 >                count += cs.length;
300              }
301 +            return total/(float)count;
302          }
303 <        
186 <        double averageFitness() {
187 <            double total = 0;
188 <            for (int i = 0; i < individuals.length; i++)
189 <                total += individuals[i].fitness;
190 <            return total/(double)individuals.length;
191 <        }
192 <
193 <        double bestFitness() {
194 <            double best = individuals[0].fitness;
195 <            for (int i = 0; i < individuals.length; i++)
196 <                if (individuals[i].fitness < best)
197 <                    best = individuals[i].fitness;
198 <            return best;
199 <        }
303 >    }
304  
305 <        void resetMatingBarrier() {
306 <            matingBarrierCount = nThreads - 1;
307 <        }
305 >    /**
306 >     * A Task updates its pool of chromosomes..
307 >     */
308 >    static final class Task implements Runnable {
309 >        /** The pool of chromosomes, kept in sorted order */
310 >        final Chromosome[] chromosomes;
311 >        final Population pop;
312 >        /** The common exchanger, same for all tasks */
313 >        final Exchanger<Strand> exchanger;
314 >        /** The current strand being exchanged */
315 >        Strand strand;
316 >        /** Bitset used in cross */
317 >        final int[] inTour;
318 >        final RNG rng;
319 >        final int poolSize;
320 >        final int nGen;
321 >        int gen;
322  
323 <        void awaitMatingBarrier(int tid) {
324 <            doneMating[tid] = true; // heuristically set before lock
325 <            matingBarrierLock.lock();
323 >        Task(Population pop) {
324 >            this.pop = pop;
325 >            this.nGen = pop.nGen;
326 >            this.gen = 0;
327 >            this.poolSize = pop.poolSize;
328 >            this.exchanger = pop.exchanger;
329 >            this.rng = new RNG();
330 >            int length = cities.length;
331 >            this.strand = new Strand(length);
332 >            this.inTour = new int[(length >>> 5) + 1];
333 >            this.chromosomes = new Chromosome[poolSize];
334 >            for (int j = 0; j < poolSize; ++j)
335 >                chromosomes[j] = new Chromosome(length, rng);
336 >            Arrays.sort(chromosomes);
337 >        }
338 >
339 >        /**
340 >         * Run one or more update cycles.
341 >         */
342 >        public void run() {
343              try {
344 <                int m = matingBarrierCount--;
345 <                if (m < 1) {
346 <                    for (int i = 0; i < doneMating.length; ++i)
347 <                        doneMating[i] = false;
348 <                    Thread.interrupted(); // clear
214 <                    matingBarrier.signalAll();
215 <                } else {
216 <                    doneMating[tid] = true;
217 <                    if (m == 1 && nThreads > 2) {
218 <                        for (int j = 0; j < doneMating.length; ++j) {
219 <                            if (!doneMating[j]) {
220 <                                threads[j].interrupt();
221 <                                break;
222 <                            }
223 <                        }
344 >                for (;;) {
345 >                    update();
346 >                    if (++gen >= nGen) {
347 >                        pop.taskDone();
348 >                        return;
349                      }
350 <                    try {
351 <                        do {
352 <                            matingBarrier.await();
353 <                        } while (matingBarrierCount >= 0);
229 <                    } catch(InterruptedException ie) {}
350 >                    if ((rng.next() & RESUBMIT_MASK) == 1) {
351 >                        pop.resubmit(this);
352 >                        return;
353 >                    }
354                  }
355 <            } finally {
356 <                matingBarrierLock.unlock();
355 >            } catch (InterruptedException ie) {
356 >                pop.taskDone();
357              }
358          }
359  
360 <        void prepareToBreed() {
361 <
362 <            // Calculate statistics
363 <            double totalFitness = 0;
364 <            double worstFitness = 0;
365 <            double bestFitness = individuals[0].fitness;
366 <
367 <            for (int i = 0; i < individuals.length; i++) {
368 <                totalFitness += individuals[i].fitness;
369 <                if (individuals[i].fitness > worstFitness)
370 <                    worstFitness = individuals[i].fitness;
371 <                if (individuals[i].fitness < bestFitness)
372 <                    bestFitness = individuals[i].fitness;
373 <            }
374 <            
375 <            double[] lifeNorm = new double[individuals.length];
376 <            double lifeNormTotal = 0;
377 <            double[] deathNorm = new double[individuals.length];
378 <            double deathNormTotal = 0;
379 <            for (int i = 0; i < individuals.length; i++) {
380 <                deathNorm[i] = (individuals[i].fitness - bestFitness)
381 <                    / (worstFitness - bestFitness + 1) + .05;
382 <                deathNorm[i] = (deathNorm[i] * deathNorm[i]);
383 <                lifeNorm[i] = 1.0 - deathNorm[i];
384 <                lifeNormTotal += lifeNorm[i];
385 <                deathNormTotal += deathNorm[i];
386 <            }
387 <            
388 <            double deathScale = deathNormTotal / (double)0x7FFFFFFF;
389 <            double lifeScale = lifeNormTotal / (double)0x7FFFFFFF;
390 <            
391 <            int nSub = breeders.length / nThreads;
392 <            LoopHelpers.SimpleRandom random = rngs[nThreads];
393 <
394 <            // Select breeders (need to be distinct)
395 <            for (int i = 0; i < nSub; i++) {
396 <                boolean newBreeder;
397 <                int lucky;
398 <                do {
399 <                    newBreeder = true;
400 <                    double choice = lifeScale * (double)random.next();
401 <                    for (lucky = 0; lucky < individuals.length; lucky++) {
402 <                        choice -= lifeNorm[lucky];
403 <                        if (choice <= 0)
404 <                            break;
405 <                    }
406 <                    for (int j = 0; j < i; j++)
407 <                        if (breeders[j] == lucky)
408 <                            newBreeder = false;
409 <                } while (!newBreeder);
410 <                breeders[i] = lucky;
411 <            }
412 <            
413 <            // Select dead guys (need to be distinct)
414 <            for (int i = 0; i < nSub; i++) {
415 <                boolean newDead;
416 <                int victim;
417 <                do {
418 <                    newDead = true;
419 <                    double choice = deathScale * (double)random.next();
420 <                    for (victim = 0; victim < individuals.length; victim++) {
421 <                        choice -= deathNorm[victim];
422 <                        if (choice <= 0)
423 <                            break;
424 <                    }
425 <                    for (int j = 0; j < i; j++)
426 <                        if (dyers[j] == victim)
427 <                            newDead = false;
428 <                } while (!newDead);
429 <                dyers[i] = victim;
430 <            }
360 >        /**
361 >         * Choose a breeder, exchange strand with another pool, and
362 >         * cross them to create new chromosome to replace a chosen
363 >         * dyer.
364 >         */
365 >        void update() throws InterruptedException {
366 >            int b = chooseBreeder();
367 >            int d = chooseDyer(b);
368 >            Chromosome breeder = chromosomes[b];
369 >            Chromosome child = chromosomes[d];
370 >            chooseStrand(breeder);
371 >            strand = exchanger.exchange(strand);
372 >            cross(breeder, child);
373 >            fixOrder(child, d);
374 >        }
375 >
376 >        /**
377 >         * Choose a breeder, with exponentially decreasing probability
378 >         * starting at best.
379 >         * @return index of selected breeder
380 >         */
381 >        int chooseBreeder() {
382 >            int mask = (poolSize >>> BREEDER_DECAY) - 1;
383 >            int b = 0;
384 >            while ((rng.next() & mask) != mask) {
385 >                if (++b >= poolSize)
386 >                    b = 0;
387 >            }
388 >            return b;
389 >        }
390 >
391 >        /**
392 >         * Choose a chromosome that will be replaced, with
393 >         * exponentially decreasing probablility starting at
394 >         * worst, ignoring the excluded index
395 >         * @param exclude index to ignore; use -1 to not exclude any
396 >         * @return index of selected dyer
397 >         */
398 >        int chooseDyer(int exclude) {
399 >            int mask = (poolSize >>> DYER_DECAY)  - 1;
400 >            int d = poolSize - 1;
401 >            while (d == exclude || (rng.next() & mask) != mask) {
402 >                if (--d < 0)
403 >                    d = poolSize - 1;
404 >            }
405 >            return d;
406 >        }
407 >
408 >        /**
409 >         * Select a random strand of b's.
410 >         * @param breeder the breeder
411 >         */
412 >        void chooseStrand(Chromosome breeder) {
413 >            int[] bs = breeder.alleles;
414 >            int length = bs.length;
415 >            int strandLength = MIN_STRAND_LENGTH;
416 >            while (strandLength < length &&
417 >                   (rng.next() & RANDOM_STRAND_MASK) != RANDOM_STRAND_MASK)
418 >                strandLength++;
419 >            strand.strandLength = strandLength;
420 >            int[] ss = strand.alleles;
421 >            int k = (rng.next() & 0x7FFFFFFF) % length;
422 >            for (int i = 0; i < strandLength; ++i) {
423 >                ss[i] = bs[k];
424 >                if (++k >= length) k = 0;
425 >            }
426 >        }
427 >
428 >        /**
429 >         * Copy current strand to start of c's, and then append all
430 >         * remaining b's that aren't in the strand.
431 >         * @param breeder the breeder
432 >         * @param child the child
433 >         */
434 >        void cross(Chromosome breeder, Chromosome child) {
435 >            for (int k = 0; k < inTour.length; ++k) // clear bitset
436 >                inTour[k] = 0;
437 >
438 >            // Copy current strand to c
439 >            int[] cs = child.alleles;
440 >            int ssize = strand.strandLength;
441 >            int[] ss = strand.alleles;
442 >            int i;
443 >            for (i = 0; i < ssize; ++i) {
444 >                int x = ss[i];
445 >                cs[i] = x;
446 >                inTour[x >>> 5] |= 1 << (x & 31); // record in bit set
447 >            }
448 >
449 >            // Find index of matching origin in b
450 >            int first = cs[0];
451 >            int j = 0;
452 >            int[] bs = breeder.alleles;
453 >            while (bs[j] != first)
454 >                ++j;
455 >
456 >            // Append remaining b's that aren't already in tour
457 >            while (i < cs.length) {
458 >                if (++j >= bs.length) j = 0;
459 >                int x = bs[j];
460 >                if ((inTour[x >>> 5] & (1 << (x & 31))) == 0)
461 >                    cs[i++] = x;
462 >            }
463  
464          }
465  
466 <        
467 <        void nextGeneration(int tid, int matings)
468 <            throws InterruptedException, BrokenBarrierException {
469 <
470 <            int firstChild = ((individuals.length * tid) / nThreads);
471 <            int lastChild = ((individuals.length * (tid + 1)) / nThreads);
472 <            int nChildren =  lastChild - firstChild;
473 <            
474 <            int firstSub = ((breeders.length * tid) / nThreads);
475 <            int lastSub = ((breeders.length * (tid + 1)) / nThreads);
476 <            int nSub = lastSub - firstSub;
477 <            
478 <            Chromosome[] children = new Chromosome[nChildren];
479 <
480 <            LoopHelpers.SimpleRandom random = rngs[tid];
481 <
326 <            for (int i = 0; i < nSub; i++) {
327 <                Chromosome parent = individuals[breeders[firstSub + i]];
328 <                Chromosome offspring = new Chromosome(parent);
329 <                int k = 0;
330 <                while (k < matings && matingBarrierCount > 0) {
331 <                    try {
332 <                        Chromosome other = x.exchange(offspring);
333 <                        offspring = offspring.reproduceWith(other, random);
334 <                        ++k;
335 <                    } catch (InterruptedException to) {
336 <                        break;
337 <                    }
466 >        /**
467 >         * Fix the sort order of a changed Chromosome c at position k
468 >         * @param c the chromosome
469 >         * @param k the index
470 >         */
471 >        void fixOrder(Chromosome c, int k) {
472 >            Chromosome[] cs = chromosomes;
473 >            float oldFitness = c.fitness;
474 >            c.recalcFitness();
475 >            float newFitness = c.fitness;
476 >            if (newFitness < oldFitness) {
477 >                int j = k;
478 >                int p = j - 1;
479 >                while (p >= 0 && cs[p].fitness > newFitness) {
480 >                    cs[j] = cs[p];
481 >                    j = p--;
482                  }
483 <                if (k != 0)
484 <                    children[i] = offspring;
485 <                else {
486 <                    // No peers, so we mate with ourselves
487 <                    for ( ; i < nSub - 1; i += 2) {
488 <                        int cur = firstSub + i;
489 <                        Chromosome bro = individuals[breeders[cur]];
346 <                        Chromosome sis = individuals[breeders[cur + 1]];
347 <                        
348 <                        children[i] = bro.breedWith(sis, matings, random);
349 <                        children[i+1] = sis.breedWith(bro, matings, random);
350 <                    }
351 <                    
352 <                    // Not even a sibling, so we go to asexual reproduction
353 <                    if (i < nSub)
354 <                        children[i] = individuals[breeders[firstSub + 1]];
355 <                    break;
483 >                cs[j] = c;
484 >            } else if (newFitness > oldFitness) {
485 >                int j = k;
486 >                int n = j + 1;
487 >                while (n < cs.length && cs[n].fitness < newFitness) {
488 >                    cs[j] = cs[n];
489 >                    j = n++;
490                  }
491 <
358 <            }
359 <
360 <            awaitMatingBarrier(tid);
361 <
362 <            // Kill off dead guys
363 <            for (int i = 0; i < nSub; i++) {
364 <                individuals[dyers[firstSub + 1]] = children[i];
491 >                cs[j] = c;
492              }
366
367            generationBarrier.await();
493          }
494      }
495  
496 <    static final class Chromosome {
497 <        private final CitySet cities;
498 <        private final int[] alleles;  
499 <        private final int length;    
500 <        public  double fitness; // immutable after publication
501 <        
502 <        // Basic constructor - gets randomized genetic code
503 <        Chromosome(CitySet cities, int length,
504 <                   LoopHelpers.SimpleRandom random) {
505 <            this.length = length;
506 <            this.cities = cities;
507 <            // Initialize alleles to a random shuffle
496 >    /**
497 >     * A Chromosome is a candidate TSP tour.
498 >     */
499 >    static final class Chromosome implements Comparable {
500 >        /** Index of cities in tour order */
501 >        final int[] alleles;
502 >        /** Total tour length */
503 >        float fitness;
504 >
505 >        /**
506 >         * Initialize to random tour
507 >         */
508 >        Chromosome(int length, RNG random) {
509              alleles = new int[length];
510              for (int i = 0; i < length; i++)
511                  alleles[i] = i;
512              for (int i = length - 1; i > 0; i--) {
513 +                int idx = (random.next() & 0x7FFFFFFF) % alleles.length;
514                  int tmp = alleles[i];
388                int idx = random.next() % length;
515                  alleles[i] = alleles[idx];
516                  alleles[idx] = tmp;
517              }
518              recalcFitness();
519          }
520 <        
521 <        // Copy constructor - clones parent's genetic code
522 <        Chromosome(Chromosome clone) {
523 <            length = clone.length;
524 <            cities = clone.cities;
399 <            fitness = clone.fitness;
400 <            alleles = new int[length];
401 <            System.arraycopy(clone.alleles, 0, alleles, 0, length);
402 <        }
403 <        
404 <        int getAllele(int offset) {
405 <            return alleles[offset % length];
406 <        }
407 <        void setAllele(int offset, int v) {
408 <            alleles[offset % length] = v;
520 >
521 >        public int compareTo(Object x) { // to enable sorting
522 >            float xf = ((Chromosome)x).fitness;
523 >            float f = fitness;
524 >            return ((f == xf)? 0 :((f < xf)? -1 : 1));
525          }
526 <        
526 >
527          void recalcFitness() {
528 <            fitness = cities.distanceBetween(alleles[0], alleles[length-1]);
529 <            for (int i = 1; i < length; i++) {
530 <                fitness += cities.distanceBetween(alleles[i-1], alleles[i]);
531 <            }
532 <        }
533 <        
534 <        Chromosome breedWith(Chromosome partner, int n,
535 <                             LoopHelpers.SimpleRandom random) {
536 <            Chromosome offspring = new Chromosome(this);
537 <            for (int i = 0; i < n; i++)
538 <                offspring = offspring.reproduceWith(partner, random);
539 <            return offspring;
528 >            int[] a = alleles;
529 >            int len = a.length;
530 >            int p = a[0];
531 >            float f = cities.distanceBetween(a[len-1], p);
532 >            for (int i = 1; i < len; i++) {
533 >                int n = a[i];
534 >                f += cities.distanceBetween(p, n);
535 >                p = n;
536 >            }
537 >            fitness = f;
538 >        }
539 >
540 >        void validate() { // Ensure that this is a valid tour.
541 >            int len = alleles.length;
542 >            boolean[] used = new boolean[len];
543 >            for (int i = 0; i < len; ++i)
544 >                used[alleles[i]] = true;
545 >            for (int i = 0; i < len; ++i)
546 >                if (!used[i])
547 >                    throw new Error("Bad tour");
548          }
549 <        
426 <        Chromosome reproduceWith(Chromosome other,
427 <                                 LoopHelpers.SimpleRandom random) {
428 <            Chromosome child = new Chromosome(this);
429 <            int coStart = random.next() % length;
430 <            int coLen = 3;
431 <            while (1 == (random.next() & 1) && (coLen < length))
432 <                coLen++;
433 <            int cPos, pPos;
434 <            
435 <            int join = other.getAllele(coStart);
436 <            child.alleles[0] = join;
437 <            
438 <            for (pPos = 0; alleles[pPos] != join; pPos++)
439 <                ;
440 <            
441 <            for (cPos = 1; cPos < coLen; cPos++)
442 <                child.setAllele(cPos, other.getAllele(coStart + cPos));
443 <            
444 <            for (int i = 0; i < length; i++) {
445 <                boolean found = false;
446 <                int allele = getAllele(pPos++);
447 <                for (int j = 0; j < coLen; j++) {
448 <                    if (found = (child.getAllele(j) == allele))
449 <                        break;
450 <                }
451 <                if (!found)
452 <                    child.setAllele(cPos++, allele);
453 <            }
454 <            
455 <            child.recalcFitness();
456 <            return child;
457 <        }
458 <        
549 >
550      }
551 +
552      /**
553 <     * A collection of (x,y) points that represent cities
553 >     * A Strand is a random sub-sequence of a Chromosome.  Each task
554 >     * creates only one strand, and then trades it with others,
555 >     * refilling it on each iteration.
556 >     */
557 >    static final class Strand {
558 >        final int[] alleles;
559 >        int strandLength;
560 >        Strand(int length) { alleles = new int[length]; }
561 >    }
562 >
563 >    /**
564 >     * A collection of (x,y) points that represent cities.  Distances
565 >     * are scaled in [0,1) to simply checking results.  The expected
566 >     * optimal TSP for random points is believed to be around 0.76 *
567 >     * sqrt(N). For papers discussing this, see
568 >     * http://www.densis.fee.unicamp.br/~moscato/TSPBIB_home.html for
569       */
570      static final class CitySet {
571 <        final int XMAX = 1000;
572 <        final int YMAX = 1000;
571 >        // Scale ints to doubles in [0,1)
572 >        static final double PSCALE = (double)0x80000000L;
573 >
574          final int length;
575 <        final int xPts[];
576 <        final int yPts[];
577 <        final double distances[];
578 <        
579 <        CitySet(int n, LoopHelpers.SimpleRandom random) {
575 >        final int[] xPts;
576 >        final int[] yPts;
577 >        final float[][] distances;
578 >
579 >        CitySet(int n) {
580              this.length = n;
581 <            xPts = new int[n];
582 <            yPts = new int [n];
581 >            this.xPts = new int[n];
582 >            this.yPts = new int[n];
583 >            this.distances = new float[n][n];
584 >
585 >            RNG random = new RNG();
586              for (int i = 0; i < n; i++) {
587 <                xPts[i] = random.next() % XMAX;
588 <                yPts[i] = random.next() % YMAX;
587 >                xPts[i] = (random.next() & 0x7FFFFFFF);
588 >                yPts[i] = (random.next() & 0x7FFFFFFF);
589              }
590 <            distances = new double[n * n];
590 >
591              for (int i = 0; i < n; i++) {
592                  for (int j = 0; j < n; j++) {
593 <                    double dX = (double)(xPts[i] - xPts[j]);
594 <                    double dY = (double)(yPts[i] - yPts[j]);
595 <                    distances[i + j * n] = Math.hypot(dX, dY);
593 >                    double dX = (xPts[i] - xPts[j]) / PSCALE;
594 >                    double dY = (yPts[i] - yPts[j]) / PSCALE;
595 >                    distances[i][j] = (float)Math.hypot(dX, dY);
596                  }
597              }
598          }
599 <        
599 >
600          // Retrieve the cached distance between a pair of cities
601 <        double distanceBetween(int idx1, int idx2) {
602 <            return distances[idx1 + idx2 * length];
601 >        float distanceBetween(int i, int j) {
602 >            return distances[i][j];
603          }
604      }
605  
606 <    static final class Runner implements Runnable {
607 <        final Population pop;
608 <        final CyclicBarrier b;
609 <        final int nGen;
610 <        final int tid;
611 <        static final boolean verbose = false;
612 <        
613 <        Runner(int tid, Population p, CyclicBarrier b, int n) {
614 <            this.tid = tid;
615 <            this.pop = p;
616 <            this.b = b;
617 <            this.nGen = n;
606 >    /**
607 >     * Cheap XorShift random number generator
608 >     */
609 >    static final class RNG {
610 >        /** Seed generator for XorShift RNGs */
611 >        static final Random seedGenerator = new Random();
612 >
613 >        int seed;
614 >        RNG(int seed) { this.seed = seed; }
615 >        RNG()         { this.seed = seedGenerator.nextInt(); }
616 >
617 >        int next() {
618 >            int x = seed;
619 >            x ^= x << 6;
620 >            x ^= x >>> 21;
621 >            x ^= x << 7;
622 >            seed = x;
623 >            return x;
624          }
625 <        
625 >    }
626 >
627 >    static final class ProgressMonitor extends Thread {
628 >        final Population pop;
629 >        ProgressMonitor(Population p) { pop = p; }
630          public void run() {
631 +            double time = 0;
632              try {
633 <                b.await();
634 <                for (int i = 0; i < nGen; i++) {
635 <                    if (verbose && 0 == tid && 0 == i % 1000) {
636 <                        System.out.print("Gen " + i + " fitness:");
515 <                        System.out.print(" best=" + (int)pop.bestFitness());
516 <                        System.out.println("; avg=" + (int)pop.averageFitness());
517 <                    }
518 <                    int matings = (((nGen - i) * 2) / (nGen)) + 1;
519 <                    pop.nextGeneration(tid, matings);
633 >                while (!Thread.interrupted()) {
634 >                    sleep(SNAPSHOT_RATE);
635 >                    time += SNAPSHOT_RATE;
636 >                    pop.printSnapshot(time / 1000.0);
637                  }
638 <                b.await();
522 <            }
523 <            catch (InterruptedException e) {
524 <                e.printStackTrace(System.out);
525 <                System.exit(0);
526 <            }
527 <            catch (BrokenBarrierException e) {
528 <                e.printStackTrace(System.out);
529 <                System.exit(0);
530 <            }
638 >            } catch (InterruptedException ie) {}
639          }
640      }
641   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines