001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.0).
003 * Copyright (c) 2007-2024 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 */
020package io.jenetics.engine;
021
022import static java.lang.Math.sqrt;
023import static java.lang.String.format;
024
025import java.text.NumberFormat;
026import java.time.Duration;
027import java.util.function.Consumer;
028
029import io.jenetics.Phenotype;
030import io.jenetics.stat.DoubleMomentStatistics;
031import io.jenetics.stat.IntMomentStatistics;
032import io.jenetics.stat.LongMomentStatistics;
033import 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 * {@snippet lang="java":
043 * final Engine<DoubleGene, Double> engine = null; // @replace substring='null' replacement="..."
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 * }
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 */
090public abstract class EvolutionStatistics<
091        C 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 evolved 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 individual's 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                C 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}