summaryrefslogtreecommitdiffstats
path: root/watchmaker/book/src/xml/watchmaker.xml
blob: b18cb3912d99a6639d20d62ec03f8d31c45c77b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
<chapter xmlns="http://docbook.org/ns/docbook"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd"
         id="watchmaker_chapter">
  <title>The Watchmaker Framework</title>
  <indexterm significance="preferred"><primary>Watchmaker Framework</primary></indexterm>
  <para>
    The Watchmaker Framework for Evolutionary Computation is an extensible, high-performance,
    object-oriented framework for implementing platform-independent evolutionary algorithms
    in Java.
    It is freely available under a permissive Open Source licence.  It can be downloaded from
    <link xlink:href="http://watchmaker.uncommons.org">http://watchmaker.uncommons.org</link>.
  </para>
  <para>
    This chapter introduces the core components of the Watchmaker Framework and shows how
    they can be used to implement simple evolutionary algorithms such as the "Hello World"
    example outlined in the previous chapter.
  </para>
  <section>
    <title>The Evolution Engine</title>
    <indexterm><primary><classname>GenerationalEvolutionEngine</classname></primary></indexterm>
    <indexterm><primary><interfacename>EvolutionEngine</interfacename></primary></indexterm>
    <para>
      The central object of an evolutionary program built with the Watchmaker Framework is
      the evolution engine.
    </para>
    <para>
      The framework provides multiple implementations of the
      <interfacename>EvolutionEngine</interfacename> interface, but the one that you will
      usually want to use is <classname>GenerationalEvolutionEngine</classname>.  This is a
      general-purpose implementation of the evolutionary algorithm outline from chapter 1.
    </para>
    <para>
      An <interfacename>EvolutionEngine</interfacename> has a single generic type parameter
      that indicates the type of object that it can evolve.
      For the "Hello World" program we need to be able to evolve Java strings.
      Code that creates an engine that can evolve strings would look something like this:
    </para>
    <informalexample>
      <programlisting language="java">
<![CDATA[EvolutionEngine<String> engine
    = new GenerationalEvolutionEngine<String>(candidateFactory,
                                              evolutionaryOperator,
                                              fitnessEvaluator,
                                              selectionStrategy,
                                              rng);]]>
      </programlisting>
    </informalexample>
    <para>
      Once you have created an <interfacename>EvolutionEngine</interfacename>, your program
      is as simple as calling the <methodname>evolve</methodname> method with appropriate
      arguments.
      However, as you can see from the code snippet above, there is a little bit of work to
      be done first in order to create an <interfacename>EvolutionEngine</interfacename> that
      is configured appropriately for the given problem.
      The constructor of the <classname>GenerationalEvolutionEngine</classname> class requires
      five objects.  These are:
    </para>
    <itemizedlist>
      <listitem>A Candidate Factory</listitem>
      <listitem>An Evolutionary Operator</listitem>
      <listitem>A Fitness Evaluator</listitem>
      <listitem>A Selection Strategy</listitem>
      <listitem>A Random Number Generator</listitem>
    </itemizedlist>
  </section>
  <section>
    <title>The Candidate Factory</title>
    <indexterm significance="preferred"><primary><interfacename>CandidateFactory</interfacename></primary></indexterm>
    <para>
      The first object that needs to be plugged into the evolution engine is a candidate
      factory.  Every evolutionary simulation must start with an initial population of
      candidate solutions and the <interfacename>CandidateFactory</interfacename> interface
      is the mechanism by which the evolution engine creates this population.
    </para>
    <para>
      A candidate factory implementation has an associated type.  It can only create
      objects of that type.  The type must match the type of the evolution engine that
      it is plugged into.
      You can write your own implementation of <interfacename>CandidateFactory</interfacename>
      for your program or, if you are using a common type such as strings, lists or
      arrays, you may be able to use a ready-made factory from the
      <package>org.uncommons.watchmaker.framework.factories</package> package.
    </para>
    <indexterm><primary><classname>StringFactory</classname></primary></indexterm>
    <para>
      For our "Hello World" program, we can use the provided
      <classname>StringFactory</classname>:
    </para>
    <informalexample>
      <programlisting language="java">
<![CDATA[// Define the set of permitted characters (A-Z plus space).
char[] chars = new char[27];
for (char c = 'A'; c <= 'Z'; c++)
{
    chars[c - 'A'] = c;
}
chars[26] = ' ';

// Factory for random 11-character Strings.
CandidateFactory<String> factory = new StringFactory(chars, 11);]]>
      </programlisting>
    </informalexample>
    <tip>
      <indexterm significance="preferred"><primary><classname>AbstractCandidateFactory</classname></primary></indexterm>
      <para>
        When writing your own <interfacename>CandidateFactory</interfacename> implementations,
        it is easiest to extend the provided <classname>AbstractCandidateFactory</classname>
        base class since there is then only a single method that must be implemented.
      </para>
    </tip>
  </section>
  <section>
    <title>Evolutionary Operators</title>
    <indexterm significance="preferred"><primary><interfacename>EvolutionaryOperator</interfacename></primary></indexterm>
    <para>
      Evolutionary operators are the components that perform the actual evolution of a
      population.  Cross-over is an evolutionary operator, as is mutation.
    </para>
    <para>
      In the Watchmaker Framework, evolutionary operators are defined in terms of the
      <interfacename>EvolutionaryOperator</interfacename> interface.  This interface
      declares a single method that takes a list of selected individuals and returns a
      list of evolved individuals.  Some operators (i.e. mutation) will process one
      individual at a time, whereas others will process individuals in groups
      (cross-over processes two individuals at a time).
    </para>
    <indexterm><primary><classname>StringCrossover</classname></primary></indexterm>
    <indexterm><primary><classname>StringMutation</classname></primary></indexterm>
    <para>
      As with candidate factories, evolutionary operators have associated types that
      must be compatible with the type of the evolution engine that they are used with.
      And, as with candidate factories, the framework provides several ready-made operators
      for common types.  These can be found in the
      <package>org.uncommons.watchmaker.framework.operators</package> package.  The
      cross-over and mutation operators that we need for our "Hello World" program are
      provided by the <classname>StringCrossover</classname> and
      <classname>StringMutation</classname> classes.
    </para>
    <section>
      <title>The Evolution Pipeline</title>
      <indexterm significance="preferred"><primary><classname>EvolutionPipeline</classname></primary></indexterm>
      <para>
        Alert readers will have noticed that the evolution engine constructor only accepts
        a single evolutionary operator.  So how can we use both cross-over and mutation?
        The answer is provided by the <classname>EvolutionPipeline</classname> operator.
        This is a compound evolutionary operator that chains together multiple operators of
        the same type.
      </para>
      <informalexample>
        <programlisting language="java">
<![CDATA[List<EvolutionaryOperator<String>> operators
    = new LinkedList<EvolutionaryOperator<String>>();
operators.add(new StringCrossover());
operators.add(new StringMutation(chars, new Probability(0.02)));

EvolutionaryOperator<String> pipeline
    = new EvolutionPipeline<String>(operators);]]>
        </programlisting>
      </informalexample>
      <note>
        <para>
          The evolution pipeline is just one of many useful operators included
          in the <package>org.uncommons.watchmaker.framework.operators</package> package.
          Elaborate evolution schemes can be constructed from combinations of these
          operators.
          Users of the Watchmaker Framework should take a few minutes to browse the API
          documentation and familiarise themselves with the available classes.
        </para>
      </note>
    </section>
  </section>
  <section>
    <title>The Fitness Evaluator</title>
    <indexterm significance="preferred"><primary><interfacename>FitnessEvaluator</interfacename></primary></indexterm>
    <para>
      So far we've been able to build our evolutionary program by simply combining instances
      of classes provided by the framework.  There is one part of the program that we will
      always have to write for ourselves though and that is the fitness function, which is
      necessarily different for every program.
    </para>
    <para>
      In the Watchmaker Framework, a fitness function is written by implementing the
      <interfacename>FitnessEvaluator</interfacename> interface.  The
      <methodname>getFitness</methodname> method of this interface takes a candidate solution
      and returns its fitness score as a Java double.  The method actually takes two
      arguments, but we can ignore the second for now.
    </para>
    <para>
      The listing below is a fitness evaluator for the "Hello World" program.  It
      simply assigns one point for each character in the candidate string that
      matches the corresponding position in the target string.
    </para>
    <informalexample>
      <programlisting language="java">
<![CDATA[public class StringEvaluator implements FitnessEvaluator<String>
{
    private final String targetString = "HELLO WORLD";

    /**
     * Assigns one "fitness point" for every character in the
     * candidate String that matches the corresponding position in
     * the target string.
     */
    public double getFitness(String candidate,
                             List<? extends String> population)
    {
        int matches = 0;
        for (int i = 0; i < candidate.length(); i++)
        {
            if (candidate.charAt(i) == targetString.charAt(i))
            {
                ++matches;
            }
        }
        return matches;
    }

    public boolean isNatural()
    {
        return true;
    }
}]]>        
      </programlisting>
    </informalexample>
    <indexterm><primary>fitness function</primary><secondary>natural</secondary></indexterm>
    <indexterm><primary>natural fitness</primary></indexterm>
    <para>
      By some fitness measures, a higher value indicates a fitter solution.  In other
      cases a lower value is better.  The <methodname>isNatural</methodname> method
      of a fitness evaluator simply specifies which scenario applies.  In Watchmaker
      Framework terminology, a <emphasis>natural</emphasis> fitness function is one that
      returns higher values for fitter individuals.
    </para>
  </section>
  <section>
    <title>Selection Strategy</title>
    <indexterm><primary>selection</primary></indexterm>
    <indexterm><primary>SelectionStrategy</primary></indexterm>
    <para>
      Selection is a key ingredient in any evolutionary algorithm.  It's what determines
      which individuals survive to reproduce and which are discarded.  All we've said about
      selection so far is that it should favour fitter individuals.  This definition permits
      several different implementations.  The Watchmaker Framework includes all of the most
      common selection strategies in the
      <package>org.uncommons.watchmaker.framework.selection</package> package.  These are
      sufficient for most evolutionary algorithms but, if necessary, it is straightforward
      to write your own implementation of the <interfacename>SelectionStrategy</interfacename>
      interface.
    </para>
    <indexterm><primary>RouletteWheelSelection</primary></indexterm>
    <para>
      Some selection strategies work better than others for certain problems.  Often a little
      trial-and-error is required to pick the best option.  We will delve into the details of
      various selection strategies in <xref linkend="selection_chapter" />, but for now we will
      just create an instance of the <classname>RouletteWheelSelection</classname> class and use
      that for our "Hello World" application.
    </para>
    <indexterm><primary>fitness-proportionate selection</primary></indexterm>
    <indexterm><primary>roulette wheel selection</primary></indexterm>
    <indexterm><primary>selection</primary><secondary>fitness-proportionate</secondary></indexterm>
    <indexterm><primary>selection</primary><secondary>roulette wheel</secondary></indexterm>
    <para>
      <emphasis>Roulette wheel selection</emphasis> is the most common type of
      <emphasis>fitness-proportionate selection</emphasis>.
      It gives all individuals a chance of being selected but favours the fitter
      individuals since an individual's selection probability is derived from its
      fitness score.
    </para>
  </section>
  <section>
    <title>Random Number Generator</title>
    <indexterm><primary>random number generator</primary></indexterm>
    <indexterm><primary>Random</primary></indexterm>
    <indexterm><primary>RNG</primary></indexterm>
    <indexterm><primary>SecureRandom</primary></indexterm>
    <para>
      The final dependency that must be satisfied in order to create an evolution engine
      is the random number generator (RNG).  An evolution engine has a single RNG that it
      passes to its candidate factory, evolutionary operator and selection strategy.
      An ideal RNG is both fast and statistically random.  We <emphasis>could</emphasis>
      use the standard Java RNG, <classname>java.util.Random</classname>, but its output
      is not as random as it should be.  The other RNG in the standard library,
      <classname>java.security.SecureRandom</classname> is much better in this respect
      but can be slow.
    </para>
    <indexterm><primary>MersenneTwisterRNG</primary></indexterm>
    <para>
      Fortunately, the Watchmaker Framework provides alternatives.  The
      <classname>org.uncommons.maths.random.MersenneTwisterRNG</classname> random number
      generator is both fast and statistically sound.  It is usually the best choice
      when creating an evolution engine.
    </para>
  </section>
  <section>
    <title>Completing the Jigsaw</title>
    <para>
      We've now got all of the necessary pieces to complete the "Hello World" example
      application.  Assuming that you've already created the
      <classname>StringEvaluator</classname> class (defined above) in a separate file,
      the code needed to create the evolution engine looks like this:
    </para>
    <informalexample>
      <programlisting language="java">
<![CDATA[// 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 StringCrossover())
operators.add(new StringMutation(chars, new Probability(0.02)));
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);]]>
      </programlisting>
    </informalexample>
    <indexterm><primary>evolve method</primary></indexterm>
    <indexterm><primary>population</primary><secondary>size of</secondary></indexterm>
    <para>
      The listing above only creates the evolution engine, it does not perform any
      evolution.  For that we need to call the <methodname>evolve</methodname> method.
      The <methodname>evolve</methodname> 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.
    </para>
    <para>
      The second parameter is concerned with <emphasis>elitism</emphasis>.  Elitism
      is explained in <xref linkend="selection_chapter" />.  For now, just use a value of zero.
      The final varargs parameter specifies one or more termination conditions.
    </para>
    <section>
      <title>Termination Conditions</title>
      <indexterm><primary>TerminationCondition</primary></indexterm>
      <indexterm><primary>TargetFitness</primary></indexterm>
      <para>
        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 <classname>TargetFitness</classname> condition.
      </para>
      <para>
        To complete the evolutionary "Hello World" application, add the following two lines:
      </para>
      <informalexample>
        <programlisting language="java">
<![CDATA[String result = engine.evolve(10, 0, new TargetFitness(11, true));
System.out.println(result);]]>
        </programlisting>
      </informalexample>
      <note>
        <indexterm><primary>ElapsedTime</primary></indexterm>
        <indexterm><primary>GenerationCount</primary></indexterm>
        <indexterm><primary>Stagnation</primary></indexterm>
        <para>
          When we move on to less trivial evolutionary programs, we will rarely be able to
          specify the outcome so precisely.  The
          <package>org.uncommons.watchmaker.framework.termination</package> 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
          <classname>ElapsedTime</classname> and <classname>GenerationCount</classname>
          conditions provide this functionality.  Alternatively, we may want the program to
          continue as long as it is finding progressively better solutions.  The
          <classname>Stagnation</classname> 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.
        </para>
      </note>
    </section>
    <section>
      <title>Evolution Observers</title>
      <para>
        Compile and run the above code and, perhaps after a brief pause, you'll see the
        following output:
      </para>
      <informalexample>
        <programlisting>
<![CDATA[  HELLO WORLD]]>
        </programlisting>
      </informalexample>
      <indexterm><primary>EvolutionObserver</primary></indexterm>
      <para>
        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 <interfacename>EvolutionObserver</interfacename> 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 <methodname>evolve</methodname> method:
      </para>
      <informalexample>
        <programlisting language="java">
<![CDATA[engine.addEvolutionObserver(new EvolutionObserver<String>()
{
    public void populationUpdate(PopulationData<? extends String> data)
    {
        System.out.printf("Generation %d: %s\n",
                          data.getGenerationNumber(),
                          data.getBestCandidate());
    }
});]]>
        </programlisting>
      </informalexample>
      <para>
        Re-compile the program and run it again.  This time you'll see all of the steps
        taken to arrive at the target string:
      </para>
      <informalexample>
        <programlisting>
  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
        </programlisting>
      </informalexample>
    </section>
  </section>
</chapter>