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.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         * {@snippet lang="java":
402         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
403         * final EvolutionResult<EnumGene<Point>, Double> result = Engine.builder(tsm)
404         *     .optimize(Optimize.MINIMUM).build()
405         *     .stream()
406         *     .limit(100)
407         *     .collect(EvolutionResult.toBestEvolutionResult());
408         * }
409         *
410         * If the collected {@link EvolutionStream} is empty, the collector returns
411         * <b>{@code null}</b>.
412         *
413         * @param <G> the gene type
414         * @param <C> the fitness type
415         * @return a collector which collects the best result of an evolution stream
416         */
417        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
418        Collector<EvolutionResult<G, C>, ?, EvolutionResult<G, C>>
419        toBestEvolutionResult() {
420                return Collector.of(
421                        MinMax::of,
422                        MinMax::accept,
423                        MinMax::combine,
424                        (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null
425                                ? mm.max().withTotalGenerations(mm.count())
426                                : null
427                );
428        }
429
430        /**
431         * Return a collector which collects the best phenotype of an evolution
432         * stream.
433         * {@snippet lang="java":
434         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
435         * final Phenotype<EnumGene<Point>, Double> result = Engine.builder(tsm)
436         *     .optimize(Optimize.MINIMUM).build()
437         *     .stream()
438         *     .limit(100)
439         *     .collect(EvolutionResult.toBestPhenotype());
440         * }
441         *
442         * If the collected {@link EvolutionStream} is empty, the collector returns
443         * <b>{@code null}</b>.
444         *
445         * @param <G> the gene type
446         * @param <C> the fitness type
447         * @return a collector which collects the best phenotype of an evolution
448         *         stream
449         */
450        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
451        Collector<EvolutionResult<G, C>, ?, Phenotype<G, C>>
452        toBestPhenotype() {
453                return Collector.of(
454                        MinMax::of,
455                        MinMax::accept,
456                        MinMax::combine,
457                        (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null
458                                ? mm.max().bestPhenotype()
459                                : null
460                );
461        }
462
463        /**
464         * Return a collector which collects the best genotype of an evolution
465         * stream.
466         * {@snippet lang="java":
467         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
468         * final Genotype<EnumGene<Point>> result = Engine.builder(tsm)
469         *     .optimize(Optimize.MINIMUM).build()
470         *     .stream()
471         *     .limit(100)
472         *     .collect(EvolutionResult.toBestGenotype());
473         * }
474         *
475         * If the collected {@link EvolutionStream} is empty, the collector returns
476         * <b>{@code null}</b>.
477         *
478         * @param <G> the gene type
479         * @param <C> the fitness type
480         * @return a collector which collects the best genotype of an evolution
481         *         stream
482         */
483        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
484        Collector<EvolutionResult<G, C>, ?, Genotype<G>>
485        toBestGenotype() {
486                return Collector.of(
487                        MinMax::of,
488                        MinMax::accept,
489                        MinMax::combine,
490                        (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null
491                                ? mm.max().bestPhenotype() != null
492                                        ? mm.max().bestPhenotype().genotype()
493                                        : null
494                                : null
495                );
496        }
497
498        /**
499         * Return a collector which collects the best <em>result</em> (in the native
500         * problem space).
501         * {@snippet lang="java":
502         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
503         * final ISeq<Point> route = Engine.builder(tsm)
504         *     .optimize(Optimize.MINIMUM).build()
505         *     .stream()
506         *     .limit(100)
507         *     .collect(EvolutionResult.toBestResult(tsm.codec().decoder()));
508         * }
509         *
510         * If the collected {@link EvolutionStream} is empty, the collector returns
511         * <b>{@code null}</b>.
512         *
513         * @since 3.6
514         *
515         * @param decoder the decoder which converts the {@code Genotype} into the
516         *        result of the problem space.
517         * @param <T> the <em>native</em> problem result type
518         * @param <G> the gene type
519         * @param <C> the fitness result type
520         * @return a collector which collects the best result of an evolution stream
521         * @throws NullPointerException if the given {@code decoder} is {@code null}
522         */
523        public static <G extends Gene<?, G>, C extends Comparable<? super C>, T>
524        Collector<EvolutionResult<G, C>, ?, T>
525        toBestResult(final Function<Genotype<G>, T> decoder) {
526                requireNonNull(decoder);
527
528                return Collector.of(
529                        MinMax::of,
530                        MinMax::accept,
531                        MinMax::combine,
532                        (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null
533                                ? mm.max().bestPhenotype() != null
534                                        ? decoder.apply(mm.max().bestPhenotype().genotype())
535                                        : null
536                                : null
537                );
538        }
539
540        /**
541         * Return a collector which collects the best <em>result</em> (in the native
542         * problem space).
543         * {@snippet lang="java":
544         * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..."
545         * final ISeq<Point> route = Engine.builder(tsm)
546         *     .optimize(Optimize.MINIMUM).build()
547         *     .stream()
548         *     .limit(100)
549         *     .collect(EvolutionResult.toBestResult(tsm.codec()));
550         * }
551         *
552         * If the collected {@link EvolutionStream} is empty, the collector returns
553         * <b>{@code null}</b>.
554         *
555         * @since 3.6
556         *
557         * @param codec the problem decoder
558         * @param <T> the <em>native</em> problem result type
559         * @param <G> the gene type
560         * @param <C> the fitness result type
561         * @return a collector which collects the best result of an evolution stream
562         * @throws NullPointerException if the given {@code codec} is {@code null}
563         */
564        public static <G extends Gene<?, G>, C extends Comparable<? super C>, T>
565        Collector<EvolutionResult<G, C>, ?, T>
566        toBestResult(final Codec<T, G> codec) {
567                return toBestResult(codec.decoder());
568        }
569
570        /**
571         * Return a mapping function, which removes duplicate individuals from the
572         * population and replaces it with newly created one by the given genotype
573         * {@code factory}.
574         * {@snippet lang="java":
575         * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..."
576         * final Engine<DoubleGene, Integer> engine = Engine.builder(problem)
577         *     .interceptor(toUniquePopulation(problem.codec().encoding(), 100))
578         *     .build();
579         * final Genotype<DoubleGene> best = engine.stream()
580         *     .limit(100)
581         *     .collect(EvolutionResult.toBestGenotype());
582         * }
583         *
584         * @since 6.0
585         * @see Engine.Builder#interceptor(EvolutionInterceptor)
586         *
587         * @param factory the genotype factory which creates new individuals
588         * @param maxRetries the maximal number of genotype creations tries
589         * @param <G> the gene type
590         * @param <C> the fitness function result type
591         * @return  a mapping function, which removes duplicate individuals from the
592         *          population
593         * @throws NullPointerException if the given genotype {@code factory} is
594         *         {@code null}
595         */
596        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
597        EvolutionInterceptor<G, C>
598        toUniquePopulation(final Factory<Genotype<G>> factory, final int maxRetries) {
599                requireNonNull(factory);
600                return ofAfter(result -> uniquePopulation(factory, maxRetries, result));
601        }
602
603        private static <G extends Gene<?, G>, C extends Comparable<? super C>>
604        EvolutionResult<G, C> uniquePopulation(
605                final Factory<Genotype<G>> factory,
606                final int maxRetries,
607                final EvolutionResult<G, C> result
608        ) {
609                final Seq<Phenotype<G, C>> population = result.population();
610                final Map<Genotype<G>, Phenotype<G, C>> elements =
611                        population.stream()
612                                .collect(toMap(
613                                        Phenotype::genotype,
614                                        Function.identity(),
615                                        (a, b) -> a));
616
617                EvolutionResult<G, C> uniques = result;
618                if (elements.size() < population.size()) {
619                        int retries = 0;
620                        while (elements.size() < population.size() && retries < maxRetries) {
621                                final Genotype<G> gt = factory.newInstance();
622                                final Phenotype<G, C> pt = elements
623                                        .put(gt, Phenotype.of(gt, result.generation()));
624                                if (pt != null) {
625                                        ++retries;
626                                }
627                        }
628                        uniques = result.withPopulation(
629                                Stream.concat(elements.values().stream(), population.stream())
630                                        .limit(population.size())
631                                        .collect(ISeq.toISeq())
632                        );
633                }
634
635                return uniques;
636        }
637
638
639        /* *************************************************************************
640         * Some collector and mapping functions.
641         * ************************************************************************/
642
643
644        /**
645         * Return a mapping function, which removes duplicate individuals from the
646         * population and replaces it with newly created one by the given genotype
647         * {@code factory}.
648         * {@snippet lang="java":
649         * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..."
650         * final Engine<DoubleGene, Integer> engine = Engine.builder(problem)
651         *     .interceptor(toUniquePopulation(problem.codec().encoding()))
652         *     .build();
653         * final Genotype<DoubleGene> best = engine.stream()
654         *     .limit(100)
655         *     .collect(EvolutionResult.toBestGenotype());
656         * }
657         *
658         * @since 6.0
659         * @see Engine.Builder#interceptor(EvolutionInterceptor)
660         *
661         * @param factory the genotype factory which creates new individuals
662         * @param <G> the gene type
663         * @param <C> the fitness function result type
664         * @return  a mapping function, which removes duplicate individuals from the
665         *          population
666         * @throws NullPointerException if the given genotype {@code factory} is
667         *         {@code null}
668         */
669        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
670        EvolutionInterceptor<G, C>
671        toUniquePopulation(final Factory<Genotype<G>> factory) {
672                return toUniquePopulation(factory, 100);
673        }
674
675        /**
676         * Return a mapping function, which removes duplicate individuals from the
677         * population and replaces it with newly created one by the existing
678         * genotype factory.
679         * {@snippet lang="java":
680         * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..."
681         * final Engine<DoubleGene, Integer> engine = Engine.builder(problem)
682         *     .interceptor(toUniquePopulation(10))
683         *     .build();
684         * final Genotype<DoubleGene> best = engine.stream()
685         *     .limit(100)
686         *     .collect(EvolutionResult.toBestGenotype(5));
687         * }
688         *
689         * @since 6.0
690         * @see Engine.Builder#interceptor(EvolutionInterceptor)
691         *
692         * @param maxRetries the maximal number of genotype creations tries
693         * @param <G> the gene type
694         * @param <C> the fitness function result type
695         * @return  a mapping function, which removes duplicate individuals from the
696         *          population
697         * @throws NullPointerException if the given genotype {@code factory} is
698         *         {@code null}
699         */
700        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
701        EvolutionInterceptor<G, C> toUniquePopulation(final int maxRetries) {
702                return ofAfter(result -> uniquePopulation(
703                        result.population().get(0).genotype(),
704                        maxRetries,
705                        result
706                ));
707        }
708
709        /**
710         * Return a mapping function, which removes duplicate individuals from the
711         * population and replaces it with newly created one by the existing
712         * genotype factory.
713         * {@snippet lang="java":
714         * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..."
715         * final Engine<DoubleGene, Integer> engine = Engine.builder(problem)
716         *     .interceptor(EvolutionResult.toUniquePopulation())
717         *     .build();
718         * final Genotype<DoubleGene> best = engine.stream()
719         *     .limit(100)
720         *     .collect(EvolutionResult.toBestGenotype());
721         * }
722         *
723         * @since 6.0
724         * @see Engine.Builder#interceptor(EvolutionInterceptor)
725         *
726         * @param <G> the gene type
727         * @param <C> the fitness function result type
728         * @return  a mapping function, which removes duplicate individuals from the
729         *          population
730         * @throws NullPointerException if the given genotype {@code factory} is
731         *         {@code null}
732         */
733        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
734        EvolutionInterceptor<G, C> toUniquePopulation() {
735                return ofAfter(result -> uniquePopulation(
736                        result.population().get(0).genotype(),
737                        100,
738                        result
739                ));
740        }
741
742        /**
743         * Return a new {@code EvolutionResult} object with the given values.
744         *
745         * @param optimize the optimization strategy used
746         * @param population the population after the evolution step
747         * @param generation the current generation
748         * @param totalGenerations the overall number of generations
749         * @param durations the timing (meta) information
750         * @param killCount the number of individuals which has been killed
751         * @param invalidCount the number of individuals which has been removed as
752         *        invalid
753         * @param alterCount the number of individuals which has been altered
754         * @param <G> the gene type
755         * @param <C> the fitness type
756         * @return an new evolution result object
757         * @throws java.lang.NullPointerException if one of the parameters is
758         *         {@code null}
759         */
760        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
761        EvolutionResult<G, C> of(
762                final Optimize optimize,
763                final ISeq<Phenotype<G, C>> population,
764                final long generation,
765                final long totalGenerations,
766                final EvolutionDurations durations,
767                final int killCount,
768                final int invalidCount,
769                final int alterCount
770        ) {
771                return new EvolutionResult<>(
772                        optimize,
773                        population,
774                        generation,
775                        totalGenerations,
776                        durations,
777                        killCount,
778                        invalidCount,
779                        alterCount,
780                        true
781                );
782        }
783
784        /**
785         * Return a new {@code EvolutionResult} object with the given values.
786         *
787         * @param optimize the optimization strategy used
788         * @param population the population after the evolution step
789         * @param generation the current generation
790         * @param durations the timing (meta) information
791         * @param killCount the number of individuals which has been killed
792         * @param invalidCount the number of individuals which has been removed as
793         *        invalid
794         * @param alterCount the number of individuals which has been altered
795         * @param <G> the gene type
796         * @param <C> the fitness type
797         * @return an new evolution result object
798         * @throws java.lang.NullPointerException if one of the parameters is
799         *         {@code null}
800         */
801        public static <G extends Gene<?, G>, C extends Comparable<? super C>>
802        EvolutionResult<G, C> of(
803                final Optimize optimize,
804                final ISeq<Phenotype<G, C>> population,
805                final long generation,
806                final EvolutionDurations durations,
807                final int killCount,
808                final int invalidCount,
809                final int alterCount
810        ) {
811                return new EvolutionResult<>(
812                        optimize,
813                        population,
814                        generation,
815                        generation,
816                        durations,
817                        killCount,
818                        invalidCount,
819                        alterCount,
820                        true
821                );
822        }
823
824
825        /* *************************************************************************
826         *  Java object serialization
827         * ************************************************************************/
828
829        @Serial
830        private Object writeReplace() {
831                return new SerialProxy(SerialProxy.EVOLUTION_RESULT, this);
832        }
833
834        @Serial
835        private void readObject(final ObjectInputStream stream)
836                throws InvalidObjectException
837        {
838                throw new InvalidObjectException("Serialization proxy required.");
839        }
840
841        void write(final ObjectOutput out) throws IOException {
842                out.writeObject(_optimize);
843                out.writeObject(_population);
844                writeLong(_generation, out);
845                writeLong(_totalGenerations, out);
846                out.writeObject(_durations);
847                writeInt(_killCount, out);
848                writeInt(_invalidCount, out);
849                writeInt(_alterCount, out);
850        }
851
852        @SuppressWarnings({"unchecked", "rawtypes"})
853        static Object read(final ObjectInput in)
854                throws IOException, ClassNotFoundException
855        {
856                return new EvolutionResult<>(
857                        (Optimize)in.readObject(),
858                        (ISeq)in.readObject(),
859                        readLong(in),
860                        readLong(in),
861                        (EvolutionDurations)in.readObject(),
862                        readInt(in),
863                        readInt(in),
864                        readInt(in),
865                        true
866                );
867        }
868
869}