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