EvolutionResult.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-7.0.0).
003  * Copyright (c) 2007-2022 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  */
020 package io.jenetics.engine;
021 
022 import static java.util.Objects.requireNonNull;
023 import static java.util.stream.Collectors.toMap;
024 import static io.jenetics.engine.EvolutionInterceptor.ofAfter;
025 import static io.jenetics.internal.util.Hashes.hash;
026 import static io.jenetics.internal.util.SerialIO.readInt;
027 import static io.jenetics.internal.util.SerialIO.readLong;
028 import static io.jenetics.internal.util.SerialIO.writeInt;
029 import static io.jenetics.internal.util.SerialIO.writeLong;
030 
031 import java.io.IOException;
032 import java.io.InvalidObjectException;
033 import java.io.ObjectInput;
034 import java.io.ObjectInputStream;
035 import java.io.ObjectOutput;
036 import java.io.Serial;
037 import java.io.Serializable;
038 import java.util.Map;
039 import java.util.Objects;
040 import java.util.function.Function;
041 import java.util.stream.Collector;
042 import java.util.stream.Stream;
043 
044 import io.jenetics.Gene;
045 import io.jenetics.Genotype;
046 import io.jenetics.Optimize;
047 import io.jenetics.Phenotype;
048 import io.jenetics.internal.util.Lazy;
049 import io.jenetics.stat.MinMax;
050 import io.jenetics.util.Factory;
051 import io.jenetics.util.ISeq;
052 import 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  */
080 public final class EvolutionResult<
081     extends Gene<?, G>,
082     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      * populations 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 create new individuals
575      @param maxRetries the maximal number of genotype creation 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 collectors 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 create 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 creation 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 an 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 an 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 outthrows 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 }