We've now got all of the necessary pieces to complete the "Hello World" example
application. Assuming that you've already created the
StringEvaluator class (defined above) in a separate file,
the code needed to create the evolution engine looks like this:
// Create a factory to generate random 11-character Strings. char[] chars = new char[27]; for (char c = 'A'; c <= 'Z'; c++) { chars[c - 'A'] = c; } chars[26] = ' '; CandidateFactory<String> factory = new StringFactory(chars, 11); // Create a pipeline that applies cross-over then mutation. List<EvolutionaryOperator<String>> operators = new LinkedList<EvolutionaryOperator<String>>(); operators.add(new StringMutation(chars, new Probability(0.02))); operators.add(new StringCrossover()) EvolutionaryOperator<String> pipeline = new EvolutionPipeline<String>(operators); FitnessEvaluator<String> fitnessEvaluator = new StringEvaluator(); SelectionStrategy<Object> selection = new RouletteWheelSelection(); Random rng = new MersenneTwisterRNG(); EvolutionEngine<String> engine = new GenerationalEvolutionEngine<String>(factory, pipeline, fitnessEvaluator, selection, rng);
The listing above only creates the evolution engine, it does not perform any
evolution. For that we need to call the evolve method.
The evolve method takes three parameters. The first
is the size of the population. This is the number of candidate solutions that
exist at any time. A bigger population will often result in a satisfactory
solution being found in fewer generations. On the other hand, the processing
of each generation will take longer because there are more individuals to deal
with. For the "Hello World" program, a population size of 10 is fine.
The second parameter is concerned with elitism. Elitism is explained in Chapter 3, Selection Strategies & Elitism. For now, just use a value of zero. The final varargs parameter specifies one or more termination conditions.
Termination conditions make the evolution stop. There are a few reasons why
we would like the evolution to stop. The most obvious is because we have found the
solution that we are looking for. In the case of the "Hello World" program, that
is when we have found the target string. The target string has a fitness score of
11 so we use the TargetFitness condition.
To complete the evolutionary "Hello World" application, add the following two lines:
String result = engine.evolve(10, 0, new TargetFitness(11)); System.out.println(result);
When we move on to less trivial evolutionary programs, we will rarely be able to
specify the outcome so precisely. The
org.uncommons.watchmaker.framework.termination package includes
other termination conditions that can be used. For example, we may want the program
to run for a certain period of time, or a certain number of generations, and then
return the best solution it has found up until that point. The
ElapsedTime and GenerationCount
conditions provide this functionality. Alternatively, we may want the program to
continue as long as it is finding progressively better solutions. The
Stagnation condition will terminate the evolution after a
set number of generations pass without any improvement in the fitness of the fittest
candidate.
If multiple termination conditions are specified, the evolution will stop as soon
as any one of them is satisfied.
Compile and run the above code and, perhaps after a brief pause, you'll see the following output:
HELLO WORLD
This is quite probably the most convoluted "Hello World" program you'll ever write.
It also gives no hints as to its evolutionary nature. We can make the program more
interesting by adding an EvolutionObserver to report
on the progress of the evolution at the end of each generation. Add the following
code to your program before the call to the evolve method:
engine.addEvolutionObserver(new EvolutionObserver<String>()
{
public void populationUpdate(PopulationData<? extends String> data)
{
System.out.printf("Generation %d: %s\n",
data.getGenerationNumber(),
data.getBestCandidate());
}
});
Re-compile the program and run it again. This time you'll see all of the steps taken to arrive at the target string:
Generation 0: JIKDORHOQZJ
Generation 1: ULLLFQWZPXG
Generation 2: UEULKFVFZLS
Generation 3: KLLLFKZGRLS
Generation 4: HLLLFKZGRLS
Generation 5: HEDPOYWOZLS
Generation 6: HEULKIWWZLD
Generation 7: HPRLOYWOZLS
Generation 8: HEULOYWOZLS
Generation 9: HEULOYWORLS
Generation 10: HEULOYWORLS
Generation 11: HPLLK WQRLH
Generation 12: HEBLOYWQRLS
Generation 13: HEULOYWOBLA
Generation 14: HEBLOIWMRLD
Generation 15: HEBLOIWMRLD
Generation 16: HEYLFNWQRLD
Generation 17: HEBLOIWORLS
Generation 18: HEBLOIWORLT
Generation 19: HEBLOKWGRLD
Generation 20: HELLAYWORLS
Generation 21: HELHOIWORLT
Generation 22: HEWLOIWORLS
Generation 23: HEBLOYCORLD
Generation 24: HELLKQWORLD
Generation 25: HELLOIWORLT
Generation 26: HELLOIWORLS
Generation 27: HELLKQWORLD
Generation 28: HELLFYWORLD
Generation 29: HELLOIWORLD
Generation 30: HELLOIWORLD
Generation 31: HELLOIWORLD
Generation 32: HELLOIWORLD
Generation 33: HELLOIWORLD
Generation 34: HELLOIWORLD
Generation 35: HELLOIWDRLD
Generation 36: HELLOIWORLD
Generation 37: HELLOIWORLD
Generation 38: HELLOPWORLD
Generation 39: HELLOIWORLD
Generation 40: HELLO WORLD
HELLO WORLD