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