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.util.Objects.requireNonNull;
023import static java.util.stream.Collectors.toMap;
024import static io.jenetics.engine.EvolutionInterceptor.ofAfter;
025import static io.jenetics.internal.util.Hashes.hash;
026import static io.jenetics.internal.util.SerialIO.readInt;
027import static io.jenetics.internal.util.SerialIO.readLong;
028import static io.jenetics.internal.util.SerialIO.writeInt;
029import static io.jenetics.internal.util.SerialIO.writeLong;
030
031import java.io.IOException;
032import java.io.InvalidObjectException;
033import java.io.ObjectInput;
034import java.io.ObjectInputStream;
035import java.io.ObjectOutput;
036import java.io.Serial;
037import java.io.Serializable;
038import java.util.Map;
039import java.util.Objects;
040import java.util.function.Function;
041import java.util.stream.Collector;
042import java.util.stream.Stream;
043
044import io.jenetics.Gene;
045import io.jenetics.Genotype;
046import io.jenetics.Optimize;
047import io.jenetics.Phenotype;
048import io.jenetics.internal.util.Lazy;
049import io.jenetics.stat.MinMax;
050import io.jenetics.util.Factory;
051import io.jenetics.util.ISeq;
052import io.jenetics.util.Seq;
053
054/**
055 * Represents a state of the GA after an evolution step. It also represents the
056 * final state of an evolution process and can be created with an appropriate
057 * collector:
058 * {@snippet lang="java":
059 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
060 * final EvolutionResult<EnumGene<Point>, Double> result = Engine.builder(tsm)
061 *     .optimize(Optimize.MINIMUM)
062 *     .build()
063 *     .stream()
064 *     .limit(100)
065 *     .collect(EvolutionResult.toBestEvolutionResult());
066 * }
067 *
068 * @implSpec
069 * This class implements the {@link Comparable} interface, which compares two
070 * {@link EvolutionResult} objects according its <em>optimal</em> fitness value.
071 * This means that the better evolution result is always <em>greater</em>, no
072 * matter if the fitness function is minimized or maximized.
073 * {@snippet lang="java":
074 * final EvolutionResult<DoubleGene, Double> result1 = null; // @replace substring='null' replacement="..."
075 * final EvolutionResult<DoubleGene, Double> result2 = null; // @replace substring='null' replacement="..."
076 *
077 * if (result1.compareTo(result2) > 0) {
078 *      // Holds for maximizing evolution results.
079 *     assert result1.bestFitness() > result2.bestFitness();
080 *
081 *     // Holds for minimizing evolution results.
082  *    assert result1.bestFitness() < result2.bestFitness();
083 * }
084 * }
085 *
086 * @see EvolutionStart
087 * @see Engine
088 *
089 * @param <G> the gene type
090 * @param <C> the fitness type
091 *
092 * @implNote
093 * This class is immutable and thread-safe.
094 *
095 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
096 * @since 3.0
097 * @version 6.0
098 */
099public final class EvolutionResult<
100        G extends Gene<?, G>,
101        C extends Comparable<? super C>
102>
103        implements Comparable<EvolutionResult<G, C>>, Serializable
104{
105        @Serial
106        private static final long serialVersionUID = 2L;
107
108        private final Optimize _optimize;
109        private final ISeq<Phenotype<G, C>> _population;
110        private final long _generation;
111        private final long _totalGenerations;
112
113        private final EvolutionDurations _durations;
114        private final int _killCount;
115        private final int _invalidCount;
116        private final int _alterCount;
117
118        private final boolean _dirty;
119
120        private final Lazy<Phenotype<G, C>> _best;
121        private final Lazy<Phenotype<G, C>> _worst;
122
123        private EvolutionResult(
124                final Optimize optimize,
125                final ISeq<Phenotype<G, C>> population,
126                final long generation,
127                final long totalGenerations,
128                final EvolutionDurations durations,
129                final int killCount,
130                final int invalidCount,
131                final int alterCount,
132                final boolean dirty
133        ) {
134                _optimize = requireNonNull(optimize);
135                _population = requireNonNull(population);
136                _generation = generation;
137                _totalGenerations = totalGenerations;
138                _durations = requireNonNull(durations);
139                _killCount = killCount;
140                _invalidCount = invalidCount;
141                _alterCount = alterCount;
142                _dirty = dirty;
143
144                _best = Lazy.of(() -> _population.stream()
145                        .max(_optimize.ascending())
146                        .orElse(null)
147                );
148
149                _worst = Lazy.of(() -> _population.stream()
150                        .min(_optimize.ascending())
151                        .orElse(null)
152                );
153        }
154
155        /**
156         * Return the optimization strategy used.
157         *
158         * @return the optimization strategy used
159         */
160        public Optimize optimize() {
161                return _optimize;
162        }
163
164        /**
165         * Return the population after the evolution step.
166         *
167         * @return the population after the evolution step
168         */
169        public ISeq<Phenotype<G, C>> population() {
170                return _population;
171        }
172
173        /**
174         * Return the current list of genotypes of this evolution result.
175         *
176         * @since 5.2
177         *
178         * @return the list of genotypes of this evolution result.
179         */
180        public ISeq<Genotype<G>> genotypes() {
181                return _population.map(Phenotype::genotype);
182        }
183
184        /**
185         * The current generation.
186         *
187         * @return the current generation
188         */
189        public long generation() {
190                return _generation;
191        }
192
193        /**
194         * Return the generation count evaluated so far.
195         *
196         * @return the total number of generations evaluated so far
197         */
198        public long totalGenerations() {
199                return _totalGenerations;
200        }
201
202        /**
203         * Return the timing (meta) information of the evolution step.
204         *
205         * @return the timing (meta) information of the evolution step
206         */
207        public EvolutionDurations durations() {
208                return _durations;
209        }
210
211        /**
212         * Return the number of killed individuals.
213         *
214         * @return the number of killed individuals
215         */
216        public int killCount() {
217                return _killCount;
218        }
219
220        /**
221         * Return the number of invalid individuals.
222         *
223         * @return the number of invalid individuals
224         */
225        public int invalidCount() {
226                return _invalidCount;
227        }
228
229        /**
230         * The number of altered individuals.
231         *
232         * @return the number of altered individuals
233         */
234        public int alterCount() {
235                return _alterCount;
236        }
237
238        /**
239         * Return the best {@code Phenotype} of the result population.
240         *
241         * @return the best {@code Phenotype} of the result population
242         */
243        public Phenotype<G, C> bestPhenotype() {
244                return _best.get();
245        }
246
247        /**
248         * Return the worst {@code Phenotype} of the result population.
249         *
250         * @return the worst {@code Phenotype} of the result population
251         */
252        public Phenotype<G, C> worstPhenotype() {
253                return _worst.get();
254        }
255
256        /**
257         * Return the best population fitness.
258         *
259         * @return The best population fitness.
260         */
261        public C bestFitness() {
262                return _best.get() != null
263                        ? _best.get().fitness()
264                        : null;
265        }
266
267        /**
268         * Return the worst population fitness.
269         *
270         * @return The worst population fitness.
271         */
272        public C worstFitness() {
273                return _worst.get() != null ? _worst.get().fitness() : null;
274        }
275
276        /**
277         * Return the next evolution start object with the current population and
278         * the incremented generation.
279         *
280         * @since 4.1
281         *
282         * @return the next evolution start object
283         */
284        public EvolutionStart<G, C> next() {
285                return new EvolutionStart<>(_population, _totalGenerations + 1, _dirty);
286        }
287
288        /**
289         * Return the current evolution result object as an {@code EvolutionStart}
290         * object with the current population and current total generation.
291         *
292         * @since 4.1
293         *
294         * @return the current result as evolution start
295         */
296        public EvolutionStart<G, C> toEvolutionStart() {
297                return new EvolutionStart<>(_population, _totalGenerations, _dirty);
298        }
299
300        /**
301         * Compare {@code this} evolution result with another one, according the
302         * population's best individual.
303         *
304         * @param other the other evolution result to compare
305         * @return  a negative integer, zero, or a positive integer as this result
306         *          is less than, equal to, or greater than the specified result.
307         */
308        @Override
309        public int compareTo(final EvolutionResult<G, C> other) {
310                return _optimize.compare(_best.get(), other._best.get());
311        }
312
313        private EvolutionResult<G, C> withTotalGenerations(final long total) {
314                return EvolutionResult.of(
315                        _optimize,
316                        _population,
317                        _generation,
318                        total,
319                        _durations,
320                        _killCount,
321                        _invalidCount,
322                        _alterCount
323                );
324        }
325
326        EvolutionResult<G, C> withPopulation(final ISeq<Phenotype<G, C>> population) {
327                return EvolutionResult.of(
328                        optimize(),
329                        population,
330                        generation(),
331                        totalGenerations(),
332                        durations(),
333                        killCount(),
334                        invalidCount(),
335                        alterCount()
336                );
337        }
338
339        EvolutionResult<G, C> withDurations(final EvolutionDurations durations) {
340                return EvolutionResult.of(
341                        optimize(),
342                        population(),
343                        generation(),
344                        totalGenerations(),
345                        durations,
346                        killCount(),
347                        invalidCount(),
348                        alterCount()
349                );
350        }
351
352        EvolutionResult<G, C> clean() {
353                return new EvolutionResult<>(
354                        optimize(),
355                        population(),
356                        generation(),
357                        totalGenerations(),
358                        durations(),
359                        killCount(),
360                        invalidCount(),
361                        alterCount(),
362                        false
363                );
364        }
365
366        @Override
367        public int hashCode() {
368                return
369                        hash(_optimize,
370                        hash(_population,
371                        hash(_generation,
372                        hash(_totalGenerations,
373                        hash(_durations,
374                        hash(_killCount,
375                        hash(_invalidCount,
376                        hash(_alterCount))))))));
377        }
378
379        @Override
380        public boolean equals(final Object obj) {
381                return obj == this ||
382                        obj instanceof EvolutionResult<?, ?> other &&
383                        Objects.equals(_optimize, other._optimize) &&
384                        Objects.equals(_population, other._population) &&
385                        Objects.equals(_generation, other._generation) &&
386                        Objects.equals(_totalGenerations, other._totalGenerations) &&
387                        Objects.equals(_durations, other._durations) &&
388                        Objects.equals(_killCount, other._killCount) &&
389                        Objects.equals(_invalidCount, other._invalidCount) &&
390                        Objects.equals(_alterCount, other._alterCount);
391        }
392
393
394        /* *************************************************************************
395         *  Some static collector/factory methods.
396         * ************************************************************************/
397
398
399        /**
400         * Return a collector which collects the best result of an evolution stream.
401         *
402         * {@snippet lang="java":
403         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
404         * final EvolutionResult<EnumGene<Point>, Double> result = Engine.builder(tsm)
405         *     .optimize(Optimize.MINIMUM).build()
406         *     .stream()
407         *     .limit(100)
408         *     .collect(EvolutionResult.toBestEvolutionResult());
409         * }
410         *
411         * If the collected {@link EvolutionStream} is empty, the collector returns
412         * <b>{@code null}</b>.
413         *
414         * @param <G> the gene type
415         * @param <C> the fitness type
416         * @return a collector which collects the best result of an evolution stream
417         */
418        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
419        Collector<EvolutionResult<G, C>, ?, EvolutionResult<G, C>>
420        toBestEvolutionResult() {
421                return Collector.of(
422                        MinMax::of,
423                        MinMax::accept,
424                        MinMax::combine,
425                        (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null
426                                ? mm.max().withTotalGenerations(mm.count())
427                                : null
428                );
429        }
430
431        /**
432         * Return a collector which collects the best phenotype of an evolution
433         * stream.
434         *
435         * {@snippet lang="java":
436         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
437         * final Phenotype<EnumGene<Point>, Double> result = Engine.builder(tsm)
438         *     .optimize(Optimize.MINIMUM).build()
439         *     .stream()
440         *     .limit(100)
441         *     .collect(EvolutionResult.toBestPhenotype());
442         * }
443         *
444         * If the collected {@link EvolutionStream} is empty, the collector returns
445         * <b>{@code null}</b>.
446         *
447         * @param <G> the gene type
448         * @param <C> the fitness type
449         * @return a collector which collects the best phenotype of an evolution
450         *         stream
451         */
452        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
453        Collector<EvolutionResult<G, C>, ?, Phenotype<G, C>>
454        toBestPhenotype() {
455                return Collector.of(
456                        MinMax::of,
457                        MinMax::accept,
458                        MinMax::combine,
459                        (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null
460                                ? mm.max().bestPhenotype()
461                                : null
462                );
463        }
464
465        /**
466         * Return a collector which collects the best genotype of an evolution
467         * stream.
468         *
469         * {@snippet lang="java":
470         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
471         * final Genotype<EnumGene<Point>> result = Engine.builder(tsm)
472         *     .optimize(Optimize.MINIMUM).build()
473         *     .stream()
474         *     .limit(100)
475         *     .collect(EvolutionResult.toBestGenotype());
476         * }
477         *
478         * If the collected {@link EvolutionStream} is empty, the collector returns
479         * <b>{@code null}</b>.
480         *
481         * @param <G> the gene type
482         * @param <C> the fitness type
483         * @return a collector which collects the best genotype of an evolution
484         *         stream
485         */
486        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
487        Collector<EvolutionResult<G, C>, ?, Genotype<G>>
488        toBestGenotype() {
489                return Collector.of(
490                        MinMax::of,
491                        MinMax::accept,
492                        MinMax::combine,
493                        (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null
494                                ? mm.max().bestPhenotype() != null
495                                        ? mm.max().bestPhenotype().genotype()
496                                        : null
497                                : null
498                );
499        }
500
501        /**
502         * Return a collector which collects the best <em>result</em> (in the native
503         * problem space).
504         *
505         * {@snippet lang="java":
506         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
507         * final ISeq<Point> route = Engine.builder(tsm)
508         *     .optimize(Optimize.MINIMUM).build()
509         *     .stream()
510         *     .limit(100)
511         *     .collect(EvolutionResult.toBestResult(tsm.codec().decoder()));
512         * }
513         *
514         * If the collected {@link EvolutionStream} is empty, the collector returns
515         * <b>{@code null}</b>.
516         *
517         * @since 3.6
518         *
519         * @param decoder the decoder which converts the {@code Genotype} into the
520         *        result of the problem space.
521         * @param <T> the <em>native</em> problem result type
522         * @param <G> the gene type
523         * @param <C> the fitness result type
524         * @return a collector which collects the best result of an evolution stream
525         * @throws NullPointerException if the given {@code decoder} is {@code null}
526         */
527        public static <G extends Gene<?, G>, C extends Comparable<? super C>, T>
528        Collector<EvolutionResult<G, C>, ?, T>
529        toBestResult(final Function<Genotype<G>, T> decoder) {
530                requireNonNull(decoder);
531
532                return Collector.of(
533                        MinMax::of,
534                        MinMax::accept,
535                        MinMax::combine,
536                        (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null
537                                ? mm.max().bestPhenotype() != null
538                                        ? decoder.apply(mm.max().bestPhenotype().genotype())
539                                        : null
540                                : null
541                );
542        }
543
544        /**
545         * Return a collector which collects the best <em>result</em> (in the native
546         * problem space).
547         *
548         * {@snippet lang="java":
549         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
550         * final ISeq<Point> route = Engine.builder(tsm)
551         *     .optimize(Optimize.MINIMUM).build()
552         *     .stream()
553         *     .limit(100)
554         *     .collect(EvolutionResult.toBestResult(tsm.codec()));
555         * }
556         *
557         * If the collected {@link EvolutionStream} is empty, the collector returns
558         * <b>{@code null}</b>.
559         *
560         * @since 3.6
561         *
562         * @param codec the problem decoder
563         * @param <T> the <em>native</em> problem result type
564         * @param <G> the gene type
565         * @param <C> the fitness result type
566         * @return a collector which collects the best result of an evolution stream
567         * @throws NullPointerException if the given {@code codec} is {@code null}
568         */
569        public static <G extends Gene<?, G>, C extends Comparable<? super C>, T>
570        Collector<EvolutionResult<G, C>, ?, T>
571        toBestResult(final Codec<T, G> codec) {
572                return toBestResult(codec.decoder());
573        }
574
575        /**
576         * Return a mapping function, which removes duplicate individuals from the
577         * population and replaces it with newly created one by the given genotype
578         * {@code factory}.
579         *
580         * {@snippet lang="java":
581         * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..."
582         * final Engine<DoubleGene, Integer> engine = Engine.builder(problem)
583         *     .interceptor(toUniquePopulation(problem.codec().encoding(), 100))
584         *     .build();
585         * final Genotype<DoubleGene> best = engine.stream()
586         *     .limit(100)
587         *     .collect(EvolutionResult.toBestGenotype());
588         * }
589         *
590         * @since 6.0
591         * @see Engine.Builder#interceptor(EvolutionInterceptor)
592         *
593         * @param factory the genotype factory which creates new individuals
594         * @param maxRetries the maximal number of genotype creations tries
595         * @param <G> the gene type
596         * @param <C> the fitness function result type
597         * @return  a mapping function, which removes duplicate individuals from the
598         *          population
599         * @throws NullPointerException if the given genotype {@code factory} is
600         *         {@code null}
601         */
602        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
603        EvolutionInterceptor<G, C>
604        toUniquePopulation(final Factory<Genotype<G>> factory, final int maxRetries) {
605                requireNonNull(factory);
606                return ofAfter(result -> uniquePopulation(factory, maxRetries, result));
607        }
608
609        private static <G extends Gene<?, G>, C extends Comparable<? super C>>
610        EvolutionResult<G, C> uniquePopulation(
611                final Factory<Genotype<G>> factory,
612                final int maxRetries,
613                final EvolutionResult<G, C> result
614        ) {
615                final Seq<Phenotype<G, C>> population = result.population();
616                final Map<Genotype<G>, Phenotype<G, C>> elements =
617                        population.stream()
618                                .collect(toMap(
619                                        Phenotype::genotype,
620                                        Function.identity(),
621                                        (a, b) -> a));
622
623                EvolutionResult<G, C> uniques = result;
624                if (elements.size() < population.size()) {
625                        int retries = 0;
626                        while (elements.size() < population.size() && retries < maxRetries) {
627                                final Genotype<G> gt = factory.newInstance();
628                                final Phenotype<G, C> pt = elements
629                                        .put(gt, Phenotype.of(gt, result.generation()));
630                                if (pt != null) {
631                                        ++retries;
632                                }
633                        }
634                        uniques = result.withPopulation(
635                                Stream.concat(elements.values().stream(), population.stream())
636                                        .limit(population.size())
637                                        .collect(ISeq.toISeq())
638                        );
639                }
640
641                return uniques;
642        }
643
644
645        /* *************************************************************************
646         * Some collector and mapping functions.
647         * ************************************************************************/
648
649
650        /**
651         * Return a mapping function, which removes duplicate individuals from the
652         * population and replaces it with newly created one by the given genotype
653         * {@code factory}.
654         *
655         * {@snippet lang="java":
656         * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..."
657         * final Engine<DoubleGene, Integer> engine = Engine.builder(problem)
658         *     .interceptor(toUniquePopulation(problem.codec().encoding()))
659         *     .build();
660         * final Genotype<DoubleGene> best = engine.stream()
661         *     .limit(100)
662         *     .collect(EvolutionResult.toBestGenotype());
663         * }
664         *
665         * @since 6.0
666         * @see Engine.Builder#interceptor(EvolutionInterceptor)
667         *
668         * @param factory the genotype factory which creates new individuals
669         * @param <G> the gene type
670         * @param <C> the fitness function result type
671         * @return  a mapping function, which removes duplicate individuals from the
672         *          population
673         * @throws NullPointerException if the given genotype {@code factory} is
674         *         {@code null}
675         */
676        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
677        EvolutionInterceptor<G, C>
678        toUniquePopulation(final Factory<Genotype<G>> factory) {
679                return toUniquePopulation(factory, 100);
680        }
681
682        /**
683         * Return a mapping function, which removes duplicate individuals from the
684         * population and replaces it with newly created one by the existing
685         * genotype factory.
686         *
687         * {@snippet lang="java":
688         * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..."
689         * final Engine<DoubleGene, Integer> engine = Engine.builder(problem)
690         *     .interceptor(toUniquePopulation(10))
691         *     .build();
692         * final Genotype<DoubleGene> best = engine.stream()
693         *     .limit(100)
694         *     .collect(EvolutionResult.toBestGenotype(5));
695         * }
696         *
697         * @since 6.0
698         * @see Engine.Builder#interceptor(EvolutionInterceptor)
699         *
700         * @param maxRetries the maximal number of genotype creations tries
701         * @param <G> the gene type
702         * @param <C> the fitness function result type
703         * @return  a mapping function, which removes duplicate individuals from the
704         *          population
705         * @throws NullPointerException if the given genotype {@code factory} is
706         *         {@code null}
707         */
708        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
709        EvolutionInterceptor<G, C> toUniquePopulation(final int maxRetries) {
710                return ofAfter(result -> uniquePopulation(
711                        result.population().get(0).genotype(),
712                        maxRetries,
713                        result
714                ));
715        }
716
717        /**
718         * Return a mapping function, which removes duplicate individuals from the
719         * population and replaces it with newly created one by the existing
720         * genotype factory.
721         *
722         * {@snippet lang="java":
723         * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..."
724         * final Engine<DoubleGene, Integer> engine = Engine.builder(problem)
725         *     .interceptor(EvolutionResult.toUniquePopulation())
726         *     .build();
727         * final Genotype<DoubleGene> best = engine.stream()
728         *     .limit(100)
729         *     .collect(EvolutionResult.toBestGenotype());
730         * }
731         *
732         * @since 6.0
733         * @see Engine.Builder#interceptor(EvolutionInterceptor)
734         *
735         * @param <G> the gene type
736         * @param <C> the fitness function result type
737         * @return  a mapping function, which removes duplicate individuals from the
738         *          population
739         * @throws NullPointerException if the given genotype {@code factory} is
740         *         {@code null}
741         */
742        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
743        EvolutionInterceptor<G, C> toUniquePopulation() {
744                return ofAfter(result -> uniquePopulation(
745                        result.population().get(0).genotype(),
746                        100,
747                        result
748                ));
749        }
750
751        /**
752         * Return a new {@code EvolutionResult} object with the given values.
753         *
754         * @param optimize the optimization strategy used
755         * @param population the population after the evolution step
756         * @param generation the current generation
757         * @param totalGenerations the overall number of generations
758         * @param durations the timing (meta) information
759         * @param killCount the number of individuals which has been killed
760         * @param invalidCount the number of individuals which has been removed as
761         *        invalid
762         * @param alterCount the number of individuals which has been altered
763         * @param <G> the gene type
764         * @param <C> the fitness type
765         * @return an new evolution result object
766         * @throws java.lang.NullPointerException if one of the parameters is
767         *         {@code null}
768         */
769        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
770        EvolutionResult<G, C> of(
771                final Optimize optimize,
772                final ISeq<Phenotype<G, C>> population,
773                final long generation,
774                final long totalGenerations,
775                final EvolutionDurations durations,
776                final int killCount,
777                final int invalidCount,
778                final int alterCount
779        ) {
780                return new EvolutionResult<>(
781                        optimize,
782                        population,
783                        generation,
784                        totalGenerations,
785                        durations,
786                        killCount,
787                        invalidCount,
788                        alterCount,
789                        true
790                );
791        }
792
793        /**
794         * Return a new {@code EvolutionResult} object with the given values.
795         *
796         * @param optimize the optimization strategy used
797         * @param population the population after the evolution step
798         * @param generation the current generation
799         * @param durations the timing (meta) information
800         * @param killCount the number of individuals which has been killed
801         * @param invalidCount the number of individuals which has been removed as
802         *        invalid
803         * @param alterCount the number of individuals which has been altered
804         * @param <G> the gene type
805         * @param <C> the fitness type
806         * @return an new evolution result object
807         * @throws java.lang.NullPointerException if one of the parameters is
808         *         {@code null}
809         */
810        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
811        EvolutionResult<G, C> of(
812                final Optimize optimize,
813                final ISeq<Phenotype<G, C>> population,
814                final long generation,
815                final EvolutionDurations durations,
816                final int killCount,
817                final int invalidCount,
818                final int alterCount
819        ) {
820                return new EvolutionResult<>(
821                        optimize,
822                        population,
823                        generation,
824                        generation,
825                        durations,
826                        killCount,
827                        invalidCount,
828                        alterCount,
829                        true
830                );
831        }
832
833
834        /* *************************************************************************
835         *  Java object serialization
836         * ************************************************************************/
837
838        @Serial
839        private Object writeReplace() {
840                return new SerialProxy(SerialProxy.EVOLUTION_RESULT, this);
841        }
842
843        @Serial
844        private void readObject(final ObjectInputStream stream)
845                throws InvalidObjectException
846        {
847                throw new InvalidObjectException("Serialization proxy required.");
848        }
849
850        void write(final ObjectOutput out) throws IOException {
851                out.writeObject(_optimize);
852                out.writeObject(_population);
853                writeLong(_generation, out);
854                writeLong(_totalGenerations, out);
855                out.writeObject(_durations);
856                writeInt(_killCount, out);
857                writeInt(_invalidCount, out);
858                writeInt(_alterCount, out);
859        }
860
861        @SuppressWarnings({"unchecked", "rawtypes"})
862        static Object read(final ObjectInput in)
863                throws IOException, ClassNotFoundException
864        {
865                return new EvolutionResult<>(
866                        (Optimize)in.readObject(),
867                        (ISeq)in.readObject(),
868                        readLong(in),
869                        readLong(in),
870                        (EvolutionDurations)in.readObject(),
871                        readInt(in),
872                        readInt(in),
873                        readInt(in),
874                        true
875                );
876        }
877
878}