EvolutionStatistics.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-5.2.0).
003  * Copyright (c) 2007-2020 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 5.2
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 selecting the population, in
172      * seconds.
173      *
174      @return the duration statistics needed for selecting the population
175      @deprecated Use {@link #selectionDuration()} instead
176      */
177     @Deprecated
178     public DoubleMomentStatistics getSelectionDuration() {
179         return _selectionDuration;
180     }
181 
182     /**
183      * Return the duration statistics needed for altering the population, in
184      * seconds.
185      *
186      @return the duration statistics needed for altering the population
187      */
188     public DoubleMomentStatistics alterDuration() {
189         return _alterDuration;
190     }
191 
192     /**
193      * Return the duration statistics needed for altering the population, in
194      * seconds.
195      *
196      @return the duration statistics needed for altering the population
197      @deprecated Use {@link #alterDuration()} instead
198      */
199     @Deprecated
200     public DoubleMomentStatistics getAlterDuration() {
201         return _alterDuration;
202     }
203 
204     /**
205      * Return the duration statistics needed for evaluating the fitness function
206      * of the new individuals, in seconds.
207      *
208      @return the duration statistics needed for evaluating the fitness
209      *         function of the new individuals
210      */
211     public DoubleMomentStatistics evaluationDuration() {
212         return _evaluationDuration;
213     }
214 
215     /**
216      * Return the duration statistics needed for evaluating the fitness function
217      * of the new individuals, in seconds.
218      *
219      @return the duration statistics needed for evaluating the fitness
220      *         function of the new individuals
221      @deprecated Use {@link #evaluationDuration()} instead
222      */
223     @Deprecated
224     public DoubleMomentStatistics getEvaluationDuration() {
225         return _evaluationDuration;
226     }
227 
228     /**
229      * Return the duration statistics needed for the whole evolve step, in
230      * seconds.
231      *
232      @return the duration statistics needed for the whole evolve step
233      */
234     public DoubleMomentStatistics evolveDuration() {
235         return _evolveDuration;
236     }
237 
238     /**
239      * Return the duration statistics needed for the whole evolve step, in
240      * seconds.
241      *
242      @return the duration statistics needed for the whole evolve step
243      @deprecated Use {@link #evaluationDuration()} instead
244      */
245     @Deprecated
246     public DoubleMomentStatistics getEvolveDuration() {
247         return _evolveDuration;
248     }
249 
250 
251     /* *************************************************************************
252      * Evolution statistics
253      * ************************************************************************/
254 
255     /**
256      * Return the statistics about the killed individuals during the evolution
257      * process.
258      *
259      @return killed individual statistics
260      */
261     public IntMomentStatistics killed() {
262         return _killed;
263     }
264 
265     /**
266      * Return the statistics about the killed individuals during the evolution
267      * process.
268      *
269      @return killed individual statistics
270      @deprecated Use {@link #killed()} instead
271      */
272     @Deprecated
273     public IntMomentStatistics getKilled() {
274         return _killed;
275     }
276 
277     /**
278      * Return the statistics about the invalid individuals during the evolution
279      * process.
280      *
281      @return invalid individual statistics
282      */
283     public IntMomentStatistics invalids() {
284         return _invalids;
285     }
286 
287     /**
288      * Return the statistics about the invalid individuals during the evolution
289      * process.
290      *
291      @return invalid individual statistics
292      @deprecated Use {@link #invalids()} instead
293      */
294     @Deprecated
295     public IntMomentStatistics getInvalids() {
296         return _invalids;
297     }
298 
299     /**
300      * Return the statistics about the altered individuals during the evolution
301      * process.
302      *
303      @return altered individual statistics
304      */
305     public IntMomentStatistics altered() {
306         return _altered;
307     }
308 
309     /**
310      * Return the statistics about the altered individuals during the evolution
311      * process.
312      *
313      @return altered individual statistics
314      @deprecated Use {@link #altered()} instead
315      */
316     @Deprecated
317     public IntMomentStatistics getAltered() {
318         return _altered;
319     }
320 
321     /**
322      * Return the statistics about the individuals age.
323      *
324      @return individual age statistics
325      */
326     public LongMomentStatistics phenotypeAge() {
327         return _age;
328     }
329 
330     /**
331      * Return the statistics about the individuals age.
332      *
333      @return individual age statistics
334      @deprecated Use {@link #phenotypeAge()} instead
335      */
336     @Deprecated
337     public LongMomentStatistics getPhenotypeAge() {
338         return _age;
339     }
340 
341     /**
342      * Return the minimal and maximal fitness.
343      *
344      @return minimal and maximal fitness
345      */
346     public FitnessStatistics fitness() {
347         return _fitness;
348     }
349 
350     /**
351      * Return the minimal and maximal fitness.
352      *
353      @return minimal and maximal fitness
354      @deprecated Use {@link #fitness()} instead
355      */
356     @Deprecated
357     public FitnessStatistics getFitness() {
358         return _fitness;
359     }
360 
361     final String cpattern = "| %22s %-51s|\n";
362     final String spattern = "| %27s %-46s|\n";
363 
364     @Override
365     public String toString() {
366         return
367             "+---------------------------------------------------------------------------+\n" +
368             "|  Time statistics                                                          |\n" +
369             "+---------------------------------------------------------------------------+\n" +
370             format(cpattern, "Selection:", d(_selectionDuration)) +
371             format(cpattern, "Altering:", d(_alterDuration)) +
372             format(cpattern, "Fitness calculation:", d(_evaluationDuration)) +
373             format(cpattern, "Overall execution:", d(_evolveDuration)) +
374             "+---------------------------------------------------------------------------+\n" +
375             "|  Evolution statistics                                                     |\n" +
376             "+---------------------------------------------------------------------------+\n" +
377             format(cpattern, "Generations:", i(_altered.count())) +
378             format(cpattern, "Altered:", i(_altered)) +
379             format(cpattern, "Killed:", i(_killed)) +
380             format(cpattern, "Invalids:", i(_invalids));
381     }
382 
383     private static String d(final DoubleMomentStatistics statistics) {
384         return format(
385             "sum=%3.12f s; mean=%3.12f s",
386             statistics.sum(), statistics.mean()
387         );
388     }
389 
390     private static String i(final IntMomentStatistics statistics) {
391         final NumberFormat nf = NumberFormat.getIntegerInstance();
392         return format(
393             "sum=%s; mean=%6.9f",
394             nf.format(statistics.sum()), statistics.mean()
395         );
396     }
397 
398     private static String i(final long value) {
399         final NumberFormat nf = NumberFormat.getIntegerInstance();
400         return nf.format(value);
401     }
402 
403     private static String p(final IntMomentStatistics statistics) {
404         final NumberFormat nf = NumberFormat.getIntegerInstance();
405         return format(
406             "max=%s; mean=%6.6f; var=%6.6f",
407             nf.format(statistics.max()),
408             statistics.mean(),
409             statistics.variance()
410         );
411     }
412 
413     private static String p(final LongMomentStatistics statistics) {
414         final NumberFormat nf = NumberFormat.getIntegerInstance();
415         return format(
416             "max=%s; mean=%6.6f; var=%6.6f",
417             nf.format(statistics.max()),
418             statistics.mean(),
419             statistics.variance()
420         );
421     }
422 
423     private static final class Comp<
424         extends Comparable<? super C>
425         >
426         extends EvolutionStatistics<C, MinMax<C>>
427     {
428         private Comp() {
429             _fitness = MinMax.of();
430         }
431 
432         @Override
433         public void accept(final EvolutionResult<?, C> result) {
434             if (_fitness.max() == null) {
435                 _fitness = MinMax.of(result.optimize().ascending());
436             }
437 
438             super.accept(result);
439         }
440 
441         @Override
442         void accept(final Phenotype<?, C> pt, final long generation) {
443             super.accept(pt, generation);
444             _fitness.accept(pt.fitness());
445         }
446 
447         @Override
448         public String toString() {
449             return super.toString() +
450                 "+---------------------------------------------------------------------------+\n" +
451                 "|  Population statistics                                                    |\n" +
452                 "+---------------------------------------------------------------------------+\n" +
453                 format(cpattern, "Age:", p(_age)) +
454                 format(cpattern, "Fitness"""+
455                 format(spattern, "min =", _fitness.min()) +
456                 format(spattern, "max =", _fitness.max()) +
457                 "+---------------------------------------------------------------------------+";
458         }
459     }
460 
461     private static final class Num<N extends Number & Comparable<? super N>>
462         extends EvolutionStatistics<N, DoubleMomentStatistics>
463     {
464         private Num() {
465             _fitness = new DoubleMomentStatistics();
466         }
467 
468         @Override
469         void accept(final Phenotype<?, N> pt, final long generation) {
470             super.accept(pt, generation);
471             _fitness.accept(pt.fitness().doubleValue());
472         }
473 
474         @Override
475         public String toString() {
476             return super.toString() +
477                 "+---------------------------------------------------------------------------+\n" +
478                 "|  Population statistics                                                    |\n" +
479                 "+---------------------------------------------------------------------------+\n" +
480                 format(cpattern, "Age:", p(_age)) +
481                 format(cpattern, "Fitness:"""+
482                 format(spattern, "min  =", d(_fitness.min())) +
483                 format(spattern, "max  =", d(_fitness.max())) +
484                 format(spattern, "mean =", d(_fitness.mean())) +
485                 format(spattern, "var  =", d(_fitness.variance())) +
486                 format(spattern, "std  =", d(sqrt(_fitness.variance()))) +
487                 "+---------------------------------------------------------------------------+";
488         }
489 
490         private static String d(final double value) {
491             return format("%3.12f", value);
492         }
493     }
494 
495     public static <C extends Comparable<? super C>>
496     EvolutionStatistics<C, MinMax<C>> ofComparable() {
497         return new Comp<>();
498     }
499 
500     public static <N extends Number & Comparable<? super N>>
501     EvolutionStatistics<N, DoubleMomentStatistics> ofNumber() {
502         return new Num<>();
503     }
504 
505 }