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