EvolutionStatistics.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-6.2.0).
003  * Copyright (c) 2007-2021 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  */
020 package io.jenetics.engine;
021 
022 import static java.lang.Math.sqrt;
023 import static java.lang.String.format;
024 
025 import java.text.NumberFormat;
026 import java.time.Duration;
027 import java.util.function.Consumer;
028 
029 import io.jenetics.Phenotype;
030 import io.jenetics.stat.DoubleMomentStatistics;
031 import io.jenetics.stat.IntMomentStatistics;
032 import io.jenetics.stat.LongMomentStatistics;
033 import io.jenetics.stat.MinMax;
034 
035 /**
036  * This class can be used to gather additional statistic information of an
037  * evolution process. The additional information can be useful during the
038  * development phase of the GA or while testing the GA's performance. The
039  * following example shows how to integrate the <i>statistics</i> object into
040  * your evolution <i>stream</i>.
041  *
042  <pre>{@code
043  * final Engine<DoubleGene, Double> engine = ...
044  * final EvolutionStatistics<Double, DoubleMomentStatistics> statistics =
045  *     EvolutionStatistics.ofNumber();
046  *
047  * final Phenotype<DoubleGene, Double> result = engine.stream()
048  *     .limit(bySteadyFitness(7))
049  *     .limit(100)
050  *     .peek(statistics)
051  *     .collect(toBestPhenotype());
052  *
053  * System.println(statistics);
054  * }</pre>
055  *
056  <b>Example output</b>
057  *
058  <pre>{@code
059  * +---------------------------------------------------------------------------+
060  * |  Time statistics                                                          |
061  * +---------------------------------------------------------------------------+
062  * |             Selection: sum=0.046538278000 s; mean=0.003878189833 s        |
063  * |              Altering: sum=0.086155457000 s; mean=0.007179621417 s        |
064  * |   Fitness calculation: sum=0.022901606000 s; mean=0.001908467167 s        |
065  * |     Overall execution: sum=0.147298067000 s; mean=0.012274838917 s        |
066  * +---------------------------------------------------------------------------+
067  * |  Evolution statistics                                                     |
068  * +---------------------------------------------------------------------------+
069  * |           Generations: 12                                                 |
070  * |               Altered: sum=7,331; mean=610.916666667                      |
071  * |                Killed: sum=0; mean=0.000000000                            |
072  * |              Invalids: sum=0; mean=0.000000000                            |
073  * +---------------------------------------------------------------------------+
074  * |  Population statistics                                                    |
075  * +---------------------------------------------------------------------------+
076  * |                   Age: max=11; mean=1.951000; var=5.545190                |
077  * |               Fitness:                                                    |
078  * |                      min  = 0.000000000000                                |
079  * |                      max  = 481.748227114537                              |
080  * |                      mean = 384.430345078660                              |
081  * |                      var  = 13006.132537301528                            |
082  * |                      std  = 114.044432                                    |
083  * +---------------------------------------------------------------------------+
084  * }</pre>
085  *
086  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
087  @since 3.0
088  @version 6.0
089  */
090 public abstract class EvolutionStatistics<
091     extends Comparable<? super C>,
092     FitnessStatistics
093 >
094     implements Consumer<EvolutionResult<?, C>>
095 {
096 
097     // The duration statistics values.
098     private final DoubleMomentStatistics
099         _selectionDuration = new DoubleMomentStatistics();
100     private final DoubleMomentStatistics
101         _alterDuration = new DoubleMomentStatistics();
102     private final DoubleMomentStatistics
103         _evaluationDuration = new DoubleMomentStatistics();
104     private final DoubleMomentStatistics
105         _evolveDuration = new DoubleMomentStatistics();
106 
107     // The evolution statistics values.
108     private final IntMomentStatistics _killed = new IntMomentStatistics();
109     private final IntMomentStatistics _invalids = new IntMomentStatistics();
110     private final IntMomentStatistics _altered = new IntMomentStatistics();
111 
112     // The population statistics values.
113     final LongMomentStatistics _age = new LongMomentStatistics();
114     FitnessStatistics _fitness = null;
115 
116     EvolutionStatistics() {
117     }
118 
119     @Override
120     public void accept(final EvolutionResult<?, C> result) {
121         accept(result.durations());
122 
123         _killed.accept(result.killCount());
124         _invalids.accept(result.invalidCount());
125         _altered.accept(result.alterCount());
126 
127         result.population()
128             .forEach(pt -> accept(pt, result.generation()));
129     }
130 
131     void accept(final Phenotype<?, C> pt, final long generation) {
132         _age.accept(pt.age(generation));
133     }
134 
135     // Calculate duration statistics
136     private void accept(final EvolutionDurations durations) {
137         final double selection =
138             toSeconds(durations.offspringSelectionDuration()) +
139                 toSeconds(durations.survivorsSelectionDuration());
140         final double alter =
141             toSeconds(durations.offspringAlterDuration()) +
142                 toSeconds(durations.offspringFilterDuration());
143 
144         _selectionDuration.accept(selection);
145         _alterDuration.accept(alter);
146         _evaluationDuration
147             .accept(toSeconds(durations.evaluationDuration()));
148         _evolveDuration
149             .accept(toSeconds(durations.evolveDuration()));
150     }
151 
152     private static double toSeconds(final Duration duration) {
153         return duration.toNanos()/1_000_000_000.0;
154     }
155 
156     /* *************************************************************************
157      * Evaluation timing statistics
158      * ************************************************************************/
159 
160     /**
161      * Return the duration statistics needed for selecting the population, in
162      * seconds.
163      *
164      @return the duration statistics needed for selecting the population
165      */
166     public DoubleMomentStatistics selectionDuration() {
167         return _selectionDuration;
168     }
169 
170     /**
171      * Return the duration statistics needed for altering the population, in
172      * seconds.
173      *
174      @return the duration statistics needed for altering the population
175      */
176     public DoubleMomentStatistics alterDuration() {
177         return _alterDuration;
178     }
179 
180     /**
181      * Return the duration statistics needed for evaluating the fitness function
182      * of the new individuals, in seconds.
183      *
184      @return the duration statistics needed for evaluating the fitness
185      *         function of the new individuals
186      */
187     public DoubleMomentStatistics evaluationDuration() {
188         return _evaluationDuration;
189     }
190 
191     /**
192      * Return the duration statistics needed for the whole evolve step, in
193      * seconds.
194      *
195      @return the duration statistics needed for the whole evolve step
196      */
197     public DoubleMomentStatistics evolveDuration() {
198         return _evolveDuration;
199     }
200 
201 
202     /* *************************************************************************
203      * Evolution statistics
204      * ************************************************************************/
205 
206     /**
207      * Return the statistics about the killed individuals during the evolution
208      * process.
209      *
210      @return killed individual statistics
211      */
212     public IntMomentStatistics killed() {
213         return _killed;
214     }
215 
216     /**
217      * Return the statistics about the invalid individuals during the evolution
218      * process.
219      *
220      @return invalid individual statistics
221      */
222     public IntMomentStatistics invalids() {
223         return _invalids;
224     }
225 
226     /**
227      * Return the statistics about the altered individuals during the evolution
228      * process.
229      *
230      @return altered individual statistics
231      */
232     public IntMomentStatistics altered() {
233         return _altered;
234     }
235 
236     /**
237      * Return the statistics about the individuals age.
238      *
239      @return individual age statistics
240      */
241     public LongMomentStatistics phenotypeAge() {
242         return _age;
243     }
244 
245     /**
246      * Return the minimal and maximal fitness.
247      *
248      @return minimal and maximal fitness
249      */
250     public FitnessStatistics fitness() {
251         return _fitness;
252     }
253 
254     final String cpattern = "| %22s %-51s|\n";
255     final String spattern = "| %27s %-46s|\n";
256 
257     @Override
258     public String toString() {
259         return
260             "+---------------------------------------------------------------------------+\n" +
261             "|  Time statistics                                                          |\n" +
262             "+---------------------------------------------------------------------------+\n" +
263             format(cpattern, "Selection:", d(_selectionDuration)) +
264             format(cpattern, "Altering:", d(_alterDuration)) +
265             format(cpattern, "Fitness calculation:", d(_evaluationDuration)) +
266             format(cpattern, "Overall execution:", d(_evolveDuration)) +
267             "+---------------------------------------------------------------------------+\n" +
268             "|  Evolution statistics                                                     |\n" +
269             "+---------------------------------------------------------------------------+\n" +
270             format(cpattern, "Generations:", i(_altered.count())) +
271             format(cpattern, "Altered:", i(_altered)) +
272             format(cpattern, "Killed:", i(_killed)) +
273             format(cpattern, "Invalids:", i(_invalids));
274     }
275 
276     private static String d(final DoubleMomentStatistics statistics) {
277         return format(
278             "sum=%3.12f s; mean=%3.12f s",
279             statistics.sum(), statistics.mean()
280         );
281     }
282 
283     private static String i(final IntMomentStatistics statistics) {
284         final NumberFormat nf = NumberFormat.getIntegerInstance();
285         return format(
286             "sum=%s; mean=%6.9f",
287             nf.format(statistics.sum()), statistics.mean()
288         );
289     }
290 
291     private static String i(final long value) {
292         final NumberFormat nf = NumberFormat.getIntegerInstance();
293         return nf.format(value);
294     }
295 
296     private static String p(final IntMomentStatistics statistics) {
297         final NumberFormat nf = NumberFormat.getIntegerInstance();
298         return format(
299             "max=%s; mean=%6.6f; var=%6.6f",
300             nf.format(statistics.max()),
301             statistics.mean(),
302             statistics.variance()
303         );
304     }
305 
306     private static String p(final LongMomentStatistics statistics) {
307         final NumberFormat nf = NumberFormat.getIntegerInstance();
308         return format(
309             "max=%s; mean=%6.6f; var=%6.6f",
310             nf.format(statistics.max()),
311             statistics.mean(),
312             statistics.variance()
313         );
314     }
315 
316     private static final class Comp<
317         extends Comparable<? super C>
318         >
319         extends EvolutionStatistics<C, MinMax<C>>
320     {
321         private Comp() {
322             _fitness = MinMax.of();
323         }
324 
325         @Override
326         public void accept(final EvolutionResult<?, C> result) {
327             if (_fitness.max() == null) {
328                 _fitness = MinMax.of(result.optimize().ascending());
329             }
330 
331             super.accept(result);
332         }
333 
334         @Override
335         void accept(final Phenotype<?, C> pt, final long generation) {
336             super.accept(pt, generation);
337             _fitness.accept(pt.fitness());
338         }
339 
340         @Override
341         public String toString() {
342             return super.toString() +
343                 "+---------------------------------------------------------------------------+\n" +
344                 "|  Population statistics                                                    |\n" +
345                 "+---------------------------------------------------------------------------+\n" +
346                 format(cpattern, "Age:", p(_age)) +
347                 format(cpattern, "Fitness"""+
348                 format(spattern, "min =", _fitness.min()) +
349                 format(spattern, "max =", _fitness.max()) +
350                 "+---------------------------------------------------------------------------+";
351         }
352     }
353 
354     private static final class Num<N extends Number & Comparable<? super N>>
355         extends EvolutionStatistics<N, DoubleMomentStatistics>
356     {
357         private Num() {
358             _fitness = new DoubleMomentStatistics();
359         }
360 
361         @Override
362         void accept(final Phenotype<?, N> pt, final long generation) {
363             super.accept(pt, generation);
364             _fitness.accept(pt.fitness().doubleValue());
365         }
366 
367         @Override
368         public String toString() {
369             return super.toString() +
370                 "+---------------------------------------------------------------------------+\n" +
371                 "|  Population statistics                                                    |\n" +
372                 "+---------------------------------------------------------------------------+\n" +
373                 format(cpattern, "Age:", p(_age)) +
374                 format(cpattern, "Fitness:"""+
375                 format(spattern, "min  =", d(_fitness.min())) +
376                 format(spattern, "max  =", d(_fitness.max())) +
377                 format(spattern, "mean =", d(_fitness.mean())) +
378                 format(spattern, "var  =", d(_fitness.variance())) +
379                 format(spattern, "std  =", d(sqrt(_fitness.variance()))) +
380                 "+---------------------------------------------------------------------------+";
381         }
382 
383         private static String d(final double value) {
384             return format("%3.12f", value);
385         }
386     }
387 
388     public static <C extends Comparable<? super C>>
389     EvolutionStatistics<C, MinMax<C>> ofComparable() {
390         return new Comp<>();
391     }
392 
393     public static <N extends Number & Comparable<? super N>>
394     EvolutionStatistics<N, DoubleMomentStatistics> ofNumber() {
395         return new Num<>();
396     }
397 
398 }