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