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