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 */ 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 == 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 out) throws 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}