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