EvolutionStatistics.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-4.4.0).
003  * Copyright (c) 2007-2019 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 3.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.getDurations());
122 
123         _killed.accept(result.getKillCount());
124         _invalids.accept(result.getInvalidCount());
125         _altered.accept(result.getAlterCount());
126 
127         result.getPopulation()
128             .forEach(pt -> accept(pt, result.getGeneration()));
129     }
130 
131     void accept(final Phenotype<?, C> pt, final long generation) {
132         _age.accept(pt.getAge(generation));
133     }
134 
135     // Calculate duration statistics
136     private void accept(final EvolutionDurations durations) {
137         final double selection =
138             toSeconds(durations.getOffspringSelectionDuration()) +
139                 toSeconds(durations.getSurvivorsSelectionDuration());
140         final double alter =
141             toSeconds(durations.getOffspringAlterDuration()) +
142                 toSeconds(durations.getOffspringFilterDuration());
143 
144         _selectionDuration.accept(selection);
145         _alterDuration.accept(alter);
146         _evaluationDuration
147             .accept(toSeconds(durations.getEvaluationDuration()));
148         _evolveDuration
149             .accept(toSeconds(durations.getEvolveDuration()));
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 getSelectionDuration() {
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 getAlterDuration() {
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 getEvaluationDuration() {
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 getEvolveDuration() {
198         return _evolveDuration;
199     }
200 
201 
202 
203     /* *************************************************************************
204      * Evolution statistics
205      * ************************************************************************/
206 
207     /**
208      * Return the statistics about the killed individuals during the evolution
209      * process.
210      *
211      @return killed individual statistics
212      */
213     public IntMomentStatistics getKilled() {
214         return _killed;
215     }
216 
217     /**
218      * Return the statistics about the invalid individuals during the evolution
219      * process.
220      *
221      @return invalid individual statistics
222      */
223     public IntMomentStatistics getInvalids() {
224         return _invalids;
225     }
226 
227     /**
228      * Return the statistics about the altered individuals during the evolution
229      * process.
230      *
231      @return altered individual statistics
232      */
233     public IntMomentStatistics getAltered() {
234         return _altered;
235     }
236 
237     /**
238      * Return the statistics about the individuals age.
239      *
240      @return individual age statistics
241      */
242     public LongMomentStatistics getPhenotypeAge() {
243         return _age;
244     }
245 
246     /**
247      * Return the minimal and maximal fitness.
248      *
249      @return minimal and maximal fitness
250      */
251     public FitnessStatistics getFitness() {
252         return _fitness;
253     }
254 
255     final String cpattern = "| %22s %-51s|\n";
256     final String spattern = "| %27s %-46s|\n";
257 
258     @Override
259     public String toString() {
260         return
261             "+---------------------------------------------------------------------------+\n" +
262             "|  Time statistics                                                          |\n" +
263             "+---------------------------------------------------------------------------+\n" +
264             format(cpattern, "Selection:", d(_selectionDuration)) +
265             format(cpattern, "Altering:", d(_alterDuration)) +
266             format(cpattern, "Fitness calculation:", d(_evaluationDuration)) +
267             format(cpattern, "Overall execution:", d(_evolveDuration)) +
268             "+---------------------------------------------------------------------------+\n" +
269             "|  Evolution statistics                                                     |\n" +
270             "+---------------------------------------------------------------------------+\n" +
271             format(cpattern, "Generations:", i(_altered.getCount())) +
272             format(cpattern, "Altered:", i(_altered)) +
273             format(cpattern, "Killed:", i(_killed)) +
274             format(cpattern, "Invalids:", i(_invalids));
275     }
276 
277     private static String d(final DoubleMomentStatistics statistics) {
278         return format(
279             "sum=%3.12f s; mean=%3.12f s",
280             statistics.getSum(), statistics.getMean()
281         );
282     }
283 
284     private static String i(final IntMomentStatistics statistics) {
285         final NumberFormat nf = NumberFormat.getIntegerInstance();
286         return format(
287             "sum=%s; mean=%6.9f",
288             nf.format(statistics.getSum()), statistics.getMean()
289         );
290     }
291 
292     private static String i(final long value) {
293         final NumberFormat nf = NumberFormat.getIntegerInstance();
294         return nf.format(value);
295     }
296 
297     private static String p(final IntMomentStatistics statistics) {
298         final NumberFormat nf = NumberFormat.getIntegerInstance();
299         return format(
300             "max=%s; mean=%6.6f; var=%6.6f",
301             nf.format(statistics.getMax()),
302             statistics.getMean(),
303             statistics.getVariance()
304         );
305     }
306 
307     private static String p(final LongMomentStatistics statistics) {
308         final NumberFormat nf = NumberFormat.getIntegerInstance();
309         return format(
310             "max=%s; mean=%6.6f; var=%6.6f",
311             nf.format(statistics.getMax()),
312             statistics.getMean(),
313             statistics.getVariance()
314         );
315     }
316 
317     private static final class Comp<
318         extends Comparable<? super C>
319         >
320         extends EvolutionStatistics<C, MinMax<C>>
321     {
322         private Comp() {
323             _fitness = MinMax.of();
324         }
325 
326         @Override
327         public void accept(final EvolutionResult<?, C> result) {
328             if (_fitness.getMax() == null) {
329                 _fitness = MinMax.of(result.getOptimize().ascending());
330             }
331 
332             super.accept(result);
333         }
334 
335         @Override
336         void accept(final Phenotype<?, C> pt, final long generation) {
337             super.accept(pt, generation);
338             _fitness.accept(pt.getFitness());
339         }
340 
341         @Override
342         public String toString() {
343             return super.toString() +
344                 "+---------------------------------------------------------------------------+\n" +
345                 "|  Population statistics                                                    |\n" +
346                 "+---------------------------------------------------------------------------+\n" +
347                 format(cpattern, "Age:", p(_age)) +
348                 format(cpattern, "Fitness"""+
349                 format(spattern, "min =", _fitness.getMin()) +
350                 format(spattern, "max =", _fitness.getMax()) +
351                 "+---------------------------------------------------------------------------+";
352         }
353     }
354 
355     private static final class Num<N extends Number & Comparable<? super N>>
356         extends EvolutionStatistics<N, DoubleMomentStatistics>
357     {
358         private Num() {
359             _fitness = new DoubleMomentStatistics();
360         }
361 
362         @Override
363         void accept(final Phenotype<?, N> pt, final long generation) {
364             super.accept(pt, generation);
365             _fitness.accept(pt.getFitness().doubleValue());
366         }
367 
368         @Override
369         public String toString() {
370             return super.toString() +
371                 "+---------------------------------------------------------------------------+\n" +
372                 "|  Population statistics                                                    |\n" +
373                 "+---------------------------------------------------------------------------+\n" +
374                 format(cpattern, "Age:", p(_age)) +
375                 format(cpattern, "Fitness:"""+
376                 format(spattern, "min  =", d(_fitness.getMin())) +
377                 format(spattern, "max  =", d(_fitness.getMax())) +
378                 format(spattern, "mean =", d(_fitness.getMean())) +
379                 format(spattern, "var  =", d(_fitness.getVariance())) +
380                 format(spattern, "std  =", d(sqrt(_fitness.getVariance()))) +
381                 "+---------------------------------------------------------------------------+";
382         }
383 
384         private static String d(final double value) {
385             return format("%3.12f", value);
386         }
387     }
388 
389     public static <C extends Comparable<? super C>>
390     EvolutionStatistics<C, MinMax<C>> ofComparable() {
391         return new Comp<>();
392     }
393 
394     public static <N extends Number & Comparable<? super N>>
395     EvolutionStatistics<N, DoubleMomentStatistics> ofNumber() {
396         return new Num<>();
397     }
398 
399 }