Islands in the Watchmaker Framework

The Watchmaker Framework for Evolutionary Computation supports islands models via the IslandEvolution class. Each island is a self-contained EvolutionEngine just like those we have been using previously for single-population evolutionary algorithms. The evolution is divided into epochs. Each epoch consists of a fixed number of generations that each island completes in isolation. At the end of an epoch migration occurs. Then, if the termination conditions are not yet satisfied, a new epoch begins.

The IslandEvolution supports pluggable migration strategies via different implementations of the Migration interface. An island version of the string evolution example from Chapter 2, The Watchmaker Framework might look something like this:

IslandEvolution<String> engine
    = new IslandEvolution<String>(5, // Number of islands.
                                  new RingMigration(),
                                  candidateFactory,
                                  evolutionaryOperator,
                                  fitnessEvaluator,
                                  selectionStrategy,
                                  rng);

engine.evolve(100, // Population size per island.
              5, // Elitism for each island.
              50, // Epoch length (no. generations).
              3, // Migrations from each island at each epoch.
              new TargetFitness(0, false));
      

We can add listeners to an IslandEvolution object, just as we can with individual EvolutionEngines. We use a different interface for this though, IslandEvolutionObserver, which provides two call-backs. The populationUpdate method reports the global state of the combined population of all islands at the end of each epoch. The islandPopulationUpdate method reports the state of individual island populations at the end of each generation.

Advanced Usage

In the example code above we specified how many islands we wanted to use and the IslandEvolution class created one GenerationalEvolutionEngine for each island. Using this approach all of the islands have the same configuration; they use the same candidate factory, evolutionary operator(s) and selection strategy. This is the easiest way to create an island system but it is also possible to construct each island individually for ultimate flexibility.

List<EvolutionEngine<String>> islands
    = new ArrayList<EvolutionEngine<String>>();

// Create individual islands here and add them to the list.
// ...

IslandEvolution<String> engine
    = new IslandEvolution<String>(islands,
                                  new RingMigration(),
                                  false, // Natural fitness?
                                  rng);
        

One reason you might choose to construct the islands explicitly is that it makes it possible to configure individual islands differently. You may choose to have different islands use different parameters for evolutionary operators, or even to use different evolutionary operators all together. Alternatively, you could use the same evolutionary operators and parameters but have different selection strategies so that some islands have stronger selection pressure than others. You should generally use the same fitness function for all islands though, otherwise you might get some strange results.

Another possible reason for creating the islands explicitly is so you don't have to use the standard GenerationalEvolutionEngine for the islands. You can choose to use any implementation of the EvolutionEngine interface, such as the SteadyStateEvolutionEngine class or the EvolutionStrategyEngine class. You can even use a mixture of different island types with the same IslandEvolution object.