001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.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 * {@snippet lang="java": 402 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..." 403 * final EvolutionResult<EnumGene<Point>, Double> result = Engine.builder(tsm) 404 * .optimize(Optimize.MINIMUM).build() 405 * .stream() 406 * .limit(100) 407 * .collect(EvolutionResult.toBestEvolutionResult()); 408 * } 409 * 410 * If the collected {@link EvolutionStream} is empty, the collector returns 411 * <b>{@code null}</b>. 412 * 413 * @param <G> the gene type 414 * @param <C> the fitness type 415 * @return a collector which collects the best result of an evolution stream 416 */ 417 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 418 Collector<EvolutionResult<G, C>, ?, EvolutionResult<G, C>> 419 toBestEvolutionResult() { 420 return Collector.of( 421 MinMax::of, 422 MinMax::accept, 423 MinMax::combine, 424 (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null 425 ? mm.max().withTotalGenerations(mm.count()) 426 : null 427 ); 428 } 429 430 /** 431 * Return a collector which collects the best phenotype of an evolution 432 * stream. 433 * {@snippet lang="java": 434 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..." 435 * final Phenotype<EnumGene<Point>, Double> result = Engine.builder(tsm) 436 * .optimize(Optimize.MINIMUM).build() 437 * .stream() 438 * .limit(100) 439 * .collect(EvolutionResult.toBestPhenotype()); 440 * } 441 * 442 * If the collected {@link EvolutionStream} is empty, the collector returns 443 * <b>{@code null}</b>. 444 * 445 * @param <G> the gene type 446 * @param <C> the fitness type 447 * @return a collector which collects the best phenotype of an evolution 448 * stream 449 */ 450 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 451 Collector<EvolutionResult<G, C>, ?, Phenotype<G, C>> 452 toBestPhenotype() { 453 return Collector.of( 454 MinMax::of, 455 MinMax::accept, 456 MinMax::combine, 457 (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null 458 ? mm.max().bestPhenotype() 459 : null 460 ); 461 } 462 463 /** 464 * Return a collector which collects the best genotype of an evolution 465 * stream. 466 * {@snippet lang="java": 467 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..." 468 * final Genotype<EnumGene<Point>> result = Engine.builder(tsm) 469 * .optimize(Optimize.MINIMUM).build() 470 * .stream() 471 * .limit(100) 472 * .collect(EvolutionResult.toBestGenotype()); 473 * } 474 * 475 * If the collected {@link EvolutionStream} is empty, the collector returns 476 * <b>{@code null}</b>. 477 * 478 * @param <G> the gene type 479 * @param <C> the fitness type 480 * @return a collector which collects the best genotype of an evolution 481 * stream 482 */ 483 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 484 Collector<EvolutionResult<G, C>, ?, Genotype<G>> 485 toBestGenotype() { 486 return Collector.of( 487 MinMax::of, 488 MinMax::accept, 489 MinMax::combine, 490 (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null 491 ? mm.max().bestPhenotype() != null 492 ? mm.max().bestPhenotype().genotype() 493 : null 494 : null 495 ); 496 } 497 498 /** 499 * Return a collector which collects the best <em>result</em> (in the native 500 * problem space). 501 * {@snippet lang="java": 502 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..." 503 * final ISeq<Point> route = Engine.builder(tsm) 504 * .optimize(Optimize.MINIMUM).build() 505 * .stream() 506 * .limit(100) 507 * .collect(EvolutionResult.toBestResult(tsm.codec().decoder())); 508 * } 509 * 510 * If the collected {@link EvolutionStream} is empty, the collector returns 511 * <b>{@code null}</b>. 512 * 513 * @since 3.6 514 * 515 * @param decoder the decoder which converts the {@code Genotype} into the 516 * result of the problem space. 517 * @param <T> the <em>native</em> problem result type 518 * @param <G> the gene type 519 * @param <C> the fitness result type 520 * @return a collector which collects the best result of an evolution stream 521 * @throws NullPointerException if the given {@code decoder} is {@code null} 522 */ 523 public static <G extends Gene<?, G>, C extends Comparable<? super C>, T> 524 Collector<EvolutionResult<G, C>, ?, T> 525 toBestResult(final Function<Genotype<G>, T> decoder) { 526 requireNonNull(decoder); 527 528 return Collector.of( 529 MinMax::of, 530 MinMax::accept, 531 MinMax::combine, 532 (MinMax<EvolutionResult<G, C>> mm) -> mm.max() != null 533 ? mm.max().bestPhenotype() != null 534 ? decoder.apply(mm.max().bestPhenotype().genotype()) 535 : null 536 : null 537 ); 538 } 539 540 /** 541 * Return a collector which collects the best <em>result</em> (in the native 542 * problem space). 543 * {@snippet lang="java": 544 * final Problem<ISeq<Point>, EnumGene<Point>, Double> tsm = null; // @replace substring='null' replacement="..." 545 * final ISeq<Point> route = Engine.builder(tsm) 546 * .optimize(Optimize.MINIMUM).build() 547 * .stream() 548 * .limit(100) 549 * .collect(EvolutionResult.toBestResult(tsm.codec())); 550 * } 551 * 552 * If the collected {@link EvolutionStream} is empty, the collector returns 553 * <b>{@code null}</b>. 554 * 555 * @since 3.6 556 * 557 * @param codec the problem decoder 558 * @param <T> the <em>native</em> problem result type 559 * @param <G> the gene type 560 * @param <C> the fitness result type 561 * @return a collector which collects the best result of an evolution stream 562 * @throws NullPointerException if the given {@code codec} is {@code null} 563 */ 564 public static <G extends Gene<?, G>, C extends Comparable<? super C>, T> 565 Collector<EvolutionResult<G, C>, ?, T> 566 toBestResult(final Codec<T, G> codec) { 567 return toBestResult(codec.decoder()); 568 } 569 570 /** 571 * Return a mapping function, which removes duplicate individuals from the 572 * population and replaces it with newly created one by the given genotype 573 * {@code factory}. 574 * {@snippet lang="java": 575 * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..." 576 * final Engine<DoubleGene, Integer> engine = Engine.builder(problem) 577 * .interceptor(toUniquePopulation(problem.codec().encoding(), 100)) 578 * .build(); 579 * final Genotype<DoubleGene> best = engine.stream() 580 * .limit(100) 581 * .collect(EvolutionResult.toBestGenotype()); 582 * } 583 * 584 * @since 6.0 585 * @see Engine.Builder#interceptor(EvolutionInterceptor) 586 * 587 * @param factory the genotype factory which creates new individuals 588 * @param maxRetries the maximal number of genotype creations tries 589 * @param <G> the gene type 590 * @param <C> the fitness function result type 591 * @return a mapping function, which removes duplicate individuals from the 592 * population 593 * @throws NullPointerException if the given genotype {@code factory} is 594 * {@code null} 595 */ 596 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 597 EvolutionInterceptor<G, C> 598 toUniquePopulation(final Factory<Genotype<G>> factory, final int maxRetries) { 599 requireNonNull(factory); 600 return ofAfter(result -> uniquePopulation(factory, maxRetries, result)); 601 } 602 603 private static <G extends Gene<?, G>, C extends Comparable<? super C>> 604 EvolutionResult<G, C> uniquePopulation( 605 final Factory<Genotype<G>> factory, 606 final int maxRetries, 607 final EvolutionResult<G, C> result 608 ) { 609 final Seq<Phenotype<G, C>> population = result.population(); 610 final Map<Genotype<G>, Phenotype<G, C>> elements = 611 population.stream() 612 .collect(toMap( 613 Phenotype::genotype, 614 Function.identity(), 615 (a, b) -> a)); 616 617 EvolutionResult<G, C> uniques = result; 618 if (elements.size() < population.size()) { 619 int retries = 0; 620 while (elements.size() < population.size() && retries < maxRetries) { 621 final Genotype<G> gt = factory.newInstance(); 622 final Phenotype<G, C> pt = elements 623 .put(gt, Phenotype.of(gt, result.generation())); 624 if (pt != null) { 625 ++retries; 626 } 627 } 628 uniques = result.withPopulation( 629 Stream.concat(elements.values().stream(), population.stream()) 630 .limit(population.size()) 631 .collect(ISeq.toISeq()) 632 ); 633 } 634 635 return uniques; 636 } 637 638 639 /* ************************************************************************* 640 * Some collector and mapping functions. 641 * ************************************************************************/ 642 643 644 /** 645 * Return a mapping function, which removes duplicate individuals from the 646 * population and replaces it with newly created one by the given genotype 647 * {@code factory}. 648 * {@snippet lang="java": 649 * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..." 650 * final Engine<DoubleGene, Integer> engine = Engine.builder(problem) 651 * .interceptor(toUniquePopulation(problem.codec().encoding())) 652 * .build(); 653 * final Genotype<DoubleGene> best = engine.stream() 654 * .limit(100) 655 * .collect(EvolutionResult.toBestGenotype()); 656 * } 657 * 658 * @since 6.0 659 * @see Engine.Builder#interceptor(EvolutionInterceptor) 660 * 661 * @param factory the genotype factory which creates new individuals 662 * @param <G> the gene type 663 * @param <C> the fitness function result type 664 * @return a mapping function, which removes duplicate individuals from the 665 * population 666 * @throws NullPointerException if the given genotype {@code factory} is 667 * {@code null} 668 */ 669 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 670 EvolutionInterceptor<G, C> 671 toUniquePopulation(final Factory<Genotype<G>> factory) { 672 return toUniquePopulation(factory, 100); 673 } 674 675 /** 676 * Return a mapping function, which removes duplicate individuals from the 677 * population and replaces it with newly created one by the existing 678 * genotype factory. 679 * {@snippet lang="java": 680 * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..." 681 * final Engine<DoubleGene, Integer> engine = Engine.builder(problem) 682 * .interceptor(toUniquePopulation(10)) 683 * .build(); 684 * final Genotype<DoubleGene> best = engine.stream() 685 * .limit(100) 686 * .collect(EvolutionResult.toBestGenotype(5)); 687 * } 688 * 689 * @since 6.0 690 * @see Engine.Builder#interceptor(EvolutionInterceptor) 691 * 692 * @param maxRetries the maximal number of genotype creations tries 693 * @param <G> the gene type 694 * @param <C> the fitness function result type 695 * @return a mapping function, which removes duplicate individuals from the 696 * population 697 * @throws NullPointerException if the given genotype {@code factory} is 698 * {@code null} 699 */ 700 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 701 EvolutionInterceptor<G, C> toUniquePopulation(final int maxRetries) { 702 return ofAfter(result -> uniquePopulation( 703 result.population().get(0).genotype(), 704 maxRetries, 705 result 706 )); 707 } 708 709 /** 710 * Return a mapping function, which removes duplicate individuals from the 711 * population and replaces it with newly created one by the existing 712 * genotype factory. 713 * {@snippet lang="java": 714 * final Problem<Double, DoubleGene, Integer> problem = null; // @replace substring='null' replacement="..." 715 * final Engine<DoubleGene, Integer> engine = Engine.builder(problem) 716 * .interceptor(EvolutionResult.toUniquePopulation()) 717 * .build(); 718 * final Genotype<DoubleGene> best = engine.stream() 719 * .limit(100) 720 * .collect(EvolutionResult.toBestGenotype()); 721 * } 722 * 723 * @since 6.0 724 * @see Engine.Builder#interceptor(EvolutionInterceptor) 725 * 726 * @param <G> the gene type 727 * @param <C> the fitness function result type 728 * @return a mapping function, which removes duplicate individuals from the 729 * population 730 * @throws NullPointerException if the given genotype {@code factory} is 731 * {@code null} 732 */ 733 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 734 EvolutionInterceptor<G, C> toUniquePopulation() { 735 return ofAfter(result -> uniquePopulation( 736 result.population().get(0).genotype(), 737 100, 738 result 739 )); 740 } 741 742 /** 743 * Return a new {@code EvolutionResult} object with the given values. 744 * 745 * @param optimize the optimization strategy used 746 * @param population the population after the evolution step 747 * @param generation the current generation 748 * @param totalGenerations the overall number of generations 749 * @param durations the timing (meta) information 750 * @param killCount the number of individuals which has been killed 751 * @param invalidCount the number of individuals which has been removed as 752 * invalid 753 * @param alterCount the number of individuals which has been altered 754 * @param <G> the gene type 755 * @param <C> the fitness type 756 * @return an new evolution result object 757 * @throws java.lang.NullPointerException if one of the parameters is 758 * {@code null} 759 */ 760 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 761 EvolutionResult<G, C> of( 762 final Optimize optimize, 763 final ISeq<Phenotype<G, C>> population, 764 final long generation, 765 final long totalGenerations, 766 final EvolutionDurations durations, 767 final int killCount, 768 final int invalidCount, 769 final int alterCount 770 ) { 771 return new EvolutionResult<>( 772 optimize, 773 population, 774 generation, 775 totalGenerations, 776 durations, 777 killCount, 778 invalidCount, 779 alterCount, 780 true 781 ); 782 } 783 784 /** 785 * Return a new {@code EvolutionResult} object with the given values. 786 * 787 * @param optimize the optimization strategy used 788 * @param population the population after the evolution step 789 * @param generation the current generation 790 * @param durations the timing (meta) information 791 * @param killCount the number of individuals which has been killed 792 * @param invalidCount the number of individuals which has been removed as 793 * invalid 794 * @param alterCount the number of individuals which has been altered 795 * @param <G> the gene type 796 * @param <C> the fitness type 797 * @return an new evolution result object 798 * @throws java.lang.NullPointerException if one of the parameters is 799 * {@code null} 800 */ 801 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 802 EvolutionResult<G, C> of( 803 final Optimize optimize, 804 final ISeq<Phenotype<G, C>> population, 805 final long generation, 806 final EvolutionDurations durations, 807 final int killCount, 808 final int invalidCount, 809 final int alterCount 810 ) { 811 return new EvolutionResult<>( 812 optimize, 813 population, 814 generation, 815 generation, 816 durations, 817 killCount, 818 invalidCount, 819 alterCount, 820 true 821 ); 822 } 823 824 825 /* ************************************************************************* 826 * Java object serialization 827 * ************************************************************************/ 828 829 @Serial 830 private Object writeReplace() { 831 return new SerialProxy(SerialProxy.EVOLUTION_RESULT, this); 832 } 833 834 @Serial 835 private void readObject(final ObjectInputStream stream) 836 throws InvalidObjectException 837 { 838 throw new InvalidObjectException("Serialization proxy required."); 839 } 840 841 void write(final ObjectOutput out) throws IOException { 842 out.writeObject(_optimize); 843 out.writeObject(_population); 844 writeLong(_generation, out); 845 writeLong(_totalGenerations, out); 846 out.writeObject(_durations); 847 writeInt(_killCount, out); 848 writeInt(_invalidCount, out); 849 writeInt(_alterCount, out); 850 } 851 852 @SuppressWarnings({"unchecked", "rawtypes"}) 853 static Object read(final ObjectInput in) 854 throws IOException, ClassNotFoundException 855 { 856 return new EvolutionResult<>( 857 (Optimize)in.readObject(), 858 (ISeq)in.readObject(), 859 readLong(in), 860 readLong(in), 861 (EvolutionDurations)in.readObject(), 862 readInt(in), 863 readInt(in), 864 readInt(in), 865 true 866 ); 867 } 868 869}