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
EvolutionEngine
s. 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.
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.