001/* 002 * Java Genetic Algorithm Library (jenetics-9.1.0). 003 * Copyright (c) 2007-2026 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.lang.String.format; 023import static java.util.Objects.requireNonNull; 024import static java.util.concurrent.CompletableFuture.supplyAsync; 025import static java.util.concurrent.ForkJoinPool.commonPool; 026 027import java.time.InstantSource; 028import java.util.concurrent.CompletableFuture; 029import java.util.concurrent.Executor; 030import java.util.concurrent.ForkJoinPool; 031import java.util.function.Function; 032import java.util.function.Supplier; 033import java.util.stream.Stream; 034 035import io.jenetics.Alterer; 036import io.jenetics.AltererResult; 037import io.jenetics.Chromosome; 038import io.jenetics.Gene; 039import io.jenetics.Genotype; 040import io.jenetics.Optimize; 041import io.jenetics.Phenotype; 042import io.jenetics.Selector; 043import io.jenetics.util.BatchExecutor; 044import io.jenetics.util.Copyable; 045import io.jenetics.util.Factory; 046import io.jenetics.util.ISeq; 047import io.jenetics.util.MSeq; 048import io.jenetics.util.NanoClock; 049import io.jenetics.util.Seq; 050 051/** 052 * Genetic algorithm <em>engine</em> which is the main class. The following 053 * example shows the main steps in initializing and executing the GA. 054 * {@snippet lang="java": 055 * public class RealFunction { 056 * // Definition of the fitness function. 057 * private static Double eval(final Genotype<DoubleGene> gt) { 058 * final double x = gt.gene().doubleValue(); 059 * return cos(0.5 + sin(x))*cos(x); 060 * } 061 * 062 * void main() { 063 * // Create/configuring the engine via its builder. 064 * final Engine<DoubleGene, Double> engine = Engine 065 * .builder( 066 * RealFunction::eval, 067 * DoubleChromosome.of(0.0, 2.0*PI)) 068 * .populationSize(500) 069 * .optimize(Optimize.MINIMUM) 070 * .alterers( 071 * new Mutator<>(0.03), 072 * new MeanAlterer<>(0.6)) 073 * .build(); 074 * 075 * // Execute the GA (engine). 076 * final Phenotype<DoubleGene, Double> result = engine.stream() 077 * // Truncate the evolution stream if no better individual could 078 * // be found after 5 consecutive generations. 079 * .limit(bySteadyFitness(5)) 080 * // Terminate the evolution after maximal 100 generations. 081 * .limit(100) 082 * .collect(toBestPhenotype()); 083 * } 084 * } 085 * } 086 * 087 * The architecture allows to decouple the configuration of the engine from the 088 * execution. The {@code Engine} is configured via the {@code Engine.Builder} 089 * class and can't be changed after creation. The actual <i>evolution</i> is 090 * performed by the {@link EvolutionStream}, which is created by the 091 * {@code Engine}. 092 * 093 * <H2>Concurrency</H2> 094 * By default, the engine uses the {@link ForkJoinPool#commonPool()} for 095 * executing the evolution steps and evaluating the fitness function concurrently. 096 * You can change the used execution services with the {@link Builder#executor(Executor)} 097 * method. If you want to use a different executor for evaluating the fitness 098 * functions, you have to set the {@link Builder#fitnessExecutor(BatchExecutor)}. 099 * 100 * {@snippet lang="java": 101 * final Engine<DoubleGene, Double> engine = Engine 102 * .builder(null) // @replace substring='null' replacement="..." 103 * // Using this execution service for parallelize the evolution steps. 104 * .executor(Executors.newFixedThreadPool(5)) 105 * // Using one virtual thread for every fitness function evaluation. 106 * .fitnessExecutor(BatchExecutor.ofVirtualThreads()) 107 * .build(); 108 * } 109 * 110 * @implNote 111 * This class is thread safe: The engine maintains no mutable state. 112 * Therefore, it is safe to create multiple evolution streams with one 113 * engine, which may be actually used in different threads. 114 * 115 * @see Engine.Builder 116 * @see EvolutionStart 117 * @see EvolutionResult 118 * @see EvolutionStream 119 * @see EvolutionStatistics 120 * @see Codec 121 * @see Constraint 122 * 123 * @param <G> the gene type 124 * @param <C> the fitness result type 125 * 126 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 127 * @since 3.0 128 * @version 7.0 129 */ 130public final class Engine< 131 G extends Gene<?, G>, 132 C extends Comparable<? super C> 133> 134 implements 135 Evolution<G, C>, 136 EvolutionStreamable<G, C>, 137 Evaluator<G, C> 138{ 139 140 // Problem definition. 141 private final Evaluator<G, C> _evaluator; 142 private final Factory<Genotype<G>> _genotypeFactory; 143 private final Constraint<G, C> _constraint; 144 private final Optimize _optimize; 145 146 // Evolution parameters. 147 private final EvolutionParams<G, C> _evolutionParams; 148 149 // Execution context for concurrent execution of evolving steps. 150 private final Executor _executor; 151 private final InstantSource _clock; 152 private final EvolutionInterceptor<G, C> _interceptor; 153 154 155 /** 156 * Create a new GA engine with the given parameters. 157 * 158 * @param evaluator the population fitness evaluator 159 * @param genotypeFactory the genotype factory this GA is working with. 160 * @param constraint phenotype constraint which can override the default 161 * implementation the {@link Phenotype#isValid()} method and repairs 162 * invalid phenotypes when needed. 163 * @param optimize the kind of optimization (minimize or maximize) 164 * @param evolutionParams the evolution parameters, which influences the 165 * evolution process 166 * @param executor the executor used for executing the single evolved steps 167 * @param clock the clock used for calculating the timing results 168 * @param interceptor the evolution interceptor, which gives additional 169 * possibilities to influence the actual evolution 170 * @throws NullPointerException if one of the arguments is {@code null} 171 */ 172 Engine( 173 final Evaluator<G, C> evaluator, 174 final Factory<Genotype<G>> genotypeFactory, 175 final Constraint<G, C> constraint, 176 final Optimize optimize, 177 final EvolutionParams<G, C> evolutionParams, 178 final Executor executor, 179 final InstantSource clock, 180 final EvolutionInterceptor<G, C> interceptor 181 ) { 182 _evaluator = requireNonNull(evaluator); 183 _genotypeFactory = requireNonNull(genotypeFactory); 184 _constraint = requireNonNull(constraint); 185 _optimize = requireNonNull(optimize); 186 _evolutionParams = requireNonNull(evolutionParams); 187 _executor = requireNonNull(executor); 188 _clock = requireNonNull(clock); 189 _interceptor = requireNonNull(interceptor); 190 } 191 192 @Override 193 public EvolutionResult<G, C> evolve(final EvolutionStart<G, C> start) { 194 final EvolutionTiming timing = new EvolutionTiming(_clock); 195 timing.evolve.start(); 196 197 final EvolutionStart<G, C> interceptedStart = _interceptor.before(start); 198 199 // Create an initial population if `start` is empty. 200 final EvolutionStart<G, C> es = interceptedStart.population().isEmpty() 201 ? evolutionStart(interceptedStart) 202 : interceptedStart; 203 204 // Initial evaluation of the population. 205 final ISeq<Phenotype<G, C>> population = es.isDirty() 206 ? timing.evaluation.timing(() -> eval(es.population())) 207 : es.population(); 208 209 // Select the offspring population. 210 final CompletableFuture<ISeq<Phenotype<G, C>>> offspring = 211 supplyAsync(() -> 212 timing.offspringSelection.timing(() -> 213 selectOffspring(population) 214 ), 215 _executor 216 ); 217 218 // Select the survivor population. 219 final CompletableFuture<ISeq<Phenotype<G, C>>> survivors = 220 supplyAsync(() -> 221 timing.survivorsSelection.timing(() -> 222 selectSurvivors(population) 223 ), 224 _executor 225 ); 226 227 // Altering the offspring population. 228 final CompletableFuture<AltererResult<G, C>> alteredOffspring = 229 offspring.thenApplyAsync(off -> 230 timing.offspringAlter.timing(() -> 231 _evolutionParams.alterer().alter(off, es.generation()) 232 ), 233 _executor 234 ); 235 236 // Filter and replace invalid and old survivor individuals. 237 final CompletableFuture<FilterResult<G, C>> filteredSurvivors = 238 survivors.thenApplyAsync(sur -> 239 timing.survivorFilter.timing(() -> 240 filter(sur, es.generation()) 241 ), 242 _executor 243 ); 244 245 // Filter and replace invalid and old offspring individuals. 246 final CompletableFuture<FilterResult<G, C>> filteredOffspring = 247 alteredOffspring.thenApplyAsync(off -> 248 timing.offspringFilter.timing(() -> 249 filter(off.population(), es.generation()) 250 ), 251 _executor 252 ); 253 254 // Combining survivors and offspring to the new population. 255 final CompletableFuture<ISeq<Phenotype<G, C>>> nextPopulation = 256 filteredSurvivors.thenCombineAsync( 257 filteredOffspring, 258 (s, o) -> ISeq.of(s.population().append(o.population())), 259 _executor 260 ); 261 262 // Evaluate the fitness-function and wait for a result. 263 final ISeq<Phenotype<G, C>> pop = nextPopulation.join(); 264 final ISeq<Phenotype<G, C>> result = timing.evaluation.timing(() -> 265 eval(pop) 266 ); 267 268 final int killCount = 269 filteredOffspring.join().killCount() + 270 filteredSurvivors.join().killCount(); 271 272 final int invalidCount = 273 filteredOffspring.join().invalidCount() + 274 filteredSurvivors.join().invalidCount(); 275 276 final int alterationCount = alteredOffspring.join().alterations(); 277 278 EvolutionResult<G, C> er = EvolutionResult.of( 279 _optimize, 280 result, 281 es.generation(), 282 timing.toDurations(), 283 killCount, 284 invalidCount, 285 alterationCount 286 ); 287 288 final EvolutionResult<G, C> interceptedResult = _interceptor.after(er); 289 if (er != interceptedResult) { 290 er = interceptedResult.withPopulation( 291 timing.evaluation.timing(() -> 292 eval(interceptedResult.population()) 293 )); 294 } 295 296 timing.evolve.stop(); 297 298 return er 299 .withDurations(timing.toDurations()) 300 .clean(); 301 } 302 303 // Selects the survivor population. A new population object is returned. 304 private ISeq<Phenotype<G, C>> 305 selectSurvivors(final ISeq<Phenotype<G, C>> population) { 306 return _evolutionParams.survivorsSize() > 0 307 ? _evolutionParams.survivorsSelector() 308 .select(population, _evolutionParams.survivorsSize(), _optimize) 309 : ISeq.empty(); 310 } 311 312 // Selects the offspring population. A new population object is returned. 313 private ISeq<Phenotype<G, C>> 314 selectOffspring(final ISeq<Phenotype<G, C>> population) { 315 return _evolutionParams.offspringSize() > 0 316 ? _evolutionParams.offspringSelector() 317 .select(population, _evolutionParams.offspringSize(), _optimize) 318 : ISeq.empty(); 319 } 320 321 // Filters out invalid and old individuals. Filtering is done in place. 322 private FilterResult<G, C> filter( 323 final Seq<Phenotype<G, C>> population, 324 final long generation 325 ) { 326 int killCount = 0; 327 int invalidCount = 0; 328 329 final MSeq<Phenotype<G, C>> pop = MSeq.of(population); 330 for (int i = 0, n = pop.size(); i < n; ++i) { 331 final Phenotype<G, C> individual = pop.get(i); 332 333 if (!_constraint.test(individual)) { 334 pop.set(i, _constraint.repair(individual, generation)); 335 ++invalidCount; 336 } else if (individual.age(generation) > 337 _evolutionParams.maximalPhenotypeAge()) 338 { 339 pop.set(i, Phenotype.of(_genotypeFactory.newInstance(), generation)); 340 ++killCount; 341 } 342 } 343 344 return new FilterResult<>(pop.toISeq(), killCount, invalidCount); 345 } 346 347 348 /* ************************************************************************* 349 * Evaluation methods. 350 **************************************************************************/ 351 352 /** 353 * Evaluates the fitness function of the given population with the configured 354 * {@link Evaluator} of this engine and returns a new population 355 * with its fitness value assigned. 356 * 357 * @since 5.0 358 * 359 * @see Evaluator 360 * @see Evaluator#eval(Seq) 361 * 362 * @param population the population to evaluate 363 * @return a new population with assigned fitness values 364 * @throws IllegalStateException if the configured fitness function doesn't 365 * return a population with the same size as the input population. 366 * This exception is also thrown if one of the populations 367 * phenotype has no fitness value assigned. 368 */ 369 @Override 370 public ISeq<Phenotype<G, C>> eval(final Seq<Phenotype<G, C>> population) { 371 final ISeq<Phenotype<G, C>> evaluated = _evaluator.eval(population); 372 373 if (population.size() != evaluated.size()) { 374 throw new IllegalStateException(format( 375 "Expected %d individuals, but got %d. " + 376 "Check your evaluator function.", 377 population.size(), evaluated.size() 378 )); 379 } 380 if (!evaluated.forAll(Phenotype::isEvaluated)) { 381 throw new IllegalStateException( 382 "Some phenotypes have no assigned fitness value. " + 383 "Check your evaluator function." 384 ); 385 } 386 387 return evaluated; 388 } 389 390 391 /* ************************************************************************* 392 * Evolution Stream creation. 393 **************************************************************************/ 394 395 @Override 396 public EvolutionStream<G, C> 397 stream(final Supplier<EvolutionStart<G, C>> start) { 398 return EvolutionStream.ofEvolution( 399 () -> evolutionStart(start.get()), 400 this 401 ); 402 } 403 404 @Override 405 public EvolutionStream<G, C> stream(final EvolutionInit<G> init) { 406 return stream(evolutionStart(init)); 407 } 408 409 private EvolutionStart<G, C> 410 evolutionStart(final EvolutionStart<G, C> start) { 411 final ISeq<Phenotype<G, C>> population = start.population(); 412 final long gen = start.generation(); 413 414 final Stream<Phenotype<G, C>> stream = Stream.concat( 415 population.stream(), 416 _genotypeFactory.instances() 417 .map(gt -> Phenotype.of(gt, gen)) 418 ); 419 420 final ISeq<Phenotype<G, C>> pop = stream 421 .limit(populationSize()) 422 .collect(ISeq.toISeq()); 423 424 return EvolutionStart.of(pop, gen); 425 } 426 427 private EvolutionStart<G, C> 428 evolutionStart(final EvolutionInit<G> init) { 429 final ISeq<Genotype<G>> pop = init.population(); 430 final long gen = init.generation(); 431 432 return evolutionStart( 433 EvolutionStart.of( 434 pop.map(gt -> Phenotype.of(gt, gen)), 435 gen 436 ) 437 ); 438 } 439 440 /* ************************************************************************* 441 * Property access methods. 442 **************************************************************************/ 443 444 /** 445 * Return the used genotype {@link Factory} of the GA. The genotype factory 446 * is used for creating the initial population and new, random individuals 447 * when needed (as replacement for invalid and/or died genotypes). 448 * 449 * @return the used genotype {@link Factory} of the GA. 450 */ 451 public Factory<Genotype<G>> genotypeFactory() { 452 return _genotypeFactory; 453 } 454 455 /** 456 * Return the constraint of the evolution problem. 457 * 458 * @since 5.0 459 * 460 * @return the constraint of the evolution problem 461 */ 462 public Constraint<G, C> constraint() { 463 return _constraint; 464 } 465 466 /** 467 * Return the used survivor {@link Selector} of the GA. 468 * 469 * @return the used survivor {@link Selector} of the GA. 470 */ 471 public Selector<G, C> survivorsSelector() { 472 return _evolutionParams.survivorsSelector(); 473 } 474 475 /** 476 * Return the used offspring {@link Selector} of the GA. 477 * 478 * @return the used offspring {@link Selector} of the GA. 479 */ 480 public Selector<G, C> offspringSelector() { 481 return _evolutionParams.offspringSelector(); 482 } 483 484 /** 485 * Return the used {@link Alterer} of the GA. 486 * 487 * @return the used {@link Alterer} of the GA. 488 */ 489 public Alterer<G, C> alterer() { 490 return _evolutionParams.alterer(); 491 } 492 493 /** 494 * Return the number of selected offspring. 495 * 496 * @return the number of selected offspring 497 */ 498 public int offspringSize() { 499 return _evolutionParams.offspringSize(); 500 } 501 502 /** 503 * The number of selected survivors. 504 * 505 * @return the number of selected survivors 506 */ 507 public int survivorsSize() { 508 return _evolutionParams.survivorsSize(); 509 } 510 511 /** 512 * Return the number of individuals of a population. 513 * 514 * @return the number of individuals of a population 515 */ 516 public int populationSize() { 517 return _evolutionParams.populationSize(); 518 } 519 520 /** 521 * Return the maximal allowed phenotype age. 522 * 523 * @return the maximal allowed phenotype age 524 */ 525 public long maximalPhenotypeAge() { 526 return _evolutionParams.maximalPhenotypeAge(); 527 } 528 529 /** 530 * Return the optimization strategy. 531 * 532 * @return the optimization strategy 533 */ 534 public Optimize optimize() { 535 return _optimize; 536 } 537 538 /** 539 * Return the {@link InstantSource} the engine is using for measuring the 540 * execution time. 541 * 542 * @return the clock used for measuring the execution time 543 */ 544 public InstantSource clock() { 545 return _clock; 546 } 547 548 /** 549 * Return the {@link Executor} the engine is using for executing the 550 * evolution steps. 551 * 552 * @return the executor used for performing the evolution steps 553 */ 554 public Executor executor() { 555 return _executor; 556 } 557 558 /** 559 * Return the evolution interceptor. 560 * 561 * @since 6.0 562 * 563 * @return the evolution result mapper 564 */ 565 public EvolutionInterceptor<G, C> interceptor() { 566 return _interceptor; 567 } 568 569 /** 570 * Create a new evolution {@code Engine.Builder} initialized with the values 571 * of the current evolution {@code Engine}. With this method, the evolution 572 * engine can serve as a template for a new one. 573 * 574 * @apiNote 575 * If this engine was created from a fitness function and a dedicated 576 * fitness executor, the returned builder preserves the fitness function, 577 * but not the originally configured fitness executor. Calling 578 * {@link Builder#fitnessExecutor(BatchExecutor)} on the returned builder 579 * creates a new fitness evaluator with the given executor. 580 * 581 * @return a new engine builder 582 */ 583 public Builder<G, C> toBuilder() { 584 return new Builder<>(_evaluator, _genotypeFactory) 585 .clock(_clock) 586 .executor(_executor) 587 .optimize(_optimize) 588 .constraint(_constraint) 589 .evolutionParams(_evolutionParams) 590 .interceptor(_interceptor); 591 } 592 593 594 /* ************************************************************************* 595 * Static Builder methods. 596 **************************************************************************/ 597 598 /** 599 * Create a new evolution {@code Engine.Builder} with the given fitness 600 * function and genotype factory. 601 * 602 * @param ff the fitness function 603 * @param gtf the genotype factory 604 * @param <G> the gene type 605 * @param <C> the fitness function result type 606 * @return a new engine builder 607 * @throws java.lang.NullPointerException if one of the arguments is 608 * {@code null}. 609 */ 610 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 611 Builder<G, C> builder( 612 final Function<? super Genotype<G>, ? extends C> ff, 613 final Factory<Genotype<G>> gtf 614 ) { 615 return new Builder<>( 616 new FitnessEvaluator<>(ff, BatchExecutor.of(commonPool())), 617 gtf 618 ); 619 } 620 621 /** 622 * Create a new evolution {@code Engine.Builder} with the given fitness 623 * function and problem {@code codec}. 624 * 625 * @since 3.2 626 * 627 * @param ff the fitness evaluator 628 * @param codec the problem codec 629 * @param <T> the fitness function input type 630 * @param <C> the fitness function result type 631 * @param <G> the gene type 632 * @return a new engine builder 633 * @throws java.lang.NullPointerException if one of the arguments is 634 * {@code null}. 635 */ 636 public static <T, G extends Gene<?, G>, C extends Comparable<? super C>> 637 Builder<G, C> builder( 638 final Function<? super T, ? extends C> ff, 639 final Codec<T, G> codec 640 ) { 641 return builder(ff.compose(codec.decoder()), codec.encoding()); 642 } 643 644 /** 645 * Create a new evolution {@code Engine.Builder} for the given 646 * {@link Problem}. 647 * 648 * @since 3.4 649 * 650 * @param problem the problem to be solved by the evolution {@code Engine} 651 * @param <T> the (<i>native</i>) argument type of the problem fitness function 652 * @param <G> the gene type the evolution engine is working with 653 * @param <C> the result type of the fitness function 654 * @return Create a new evolution {@code Engine.Builder} 655 */ 656 public static <T, G extends Gene<?, G>, C extends Comparable<? super C>> 657 Builder<G, C> builder(final Problem<T, G, C> problem) { 658 final var builder = builder(problem.fitness(), problem.codec()); 659 problem.constraint().ifPresent(builder::constraint); 660 return builder; 661 } 662 663 /** 664 * Create a new evolution {@code Engine.Builder} with the given fitness 665 * function and chromosome templates. 666 * 667 * @param ff the fitness function 668 * @param chromosome the first chromosome 669 * @param chromosomes the chromosome templates 670 * @param <G> the gene type 671 * @param <C> the fitness function result type 672 * @return a new engine builder 673 * @throws java.lang.NullPointerException if one of the arguments is 674 * {@code null}. 675 */ 676 @SafeVarargs 677 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 678 Builder<G, C> builder( 679 final Function<? super Genotype<G>, ? extends C> ff, 680 final Chromosome<G> chromosome, 681 final Chromosome<G>... chromosomes 682 ) { 683 return builder(ff, Genotype.of(chromosome, chromosomes)); 684 } 685 686 687 /* ************************************************************************* 688 * Engine builder 689 **************************************************************************/ 690 691 692 /** 693 * Builder class for building GA {@code Engine} instances. 694 * 695 * @see Engine 696 * 697 * @param <G> the gene type 698 * @param <C> the fitness function result type 699 * 700 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 701 * @since 3.0 702 * @version 6.0 703 */ 704 public static final class Builder< 705 G extends Gene<?, G>, 706 C extends Comparable<? super C> 707 > 708 implements Copyable<Builder<G, C>> 709 { 710 711 // No default values for this properties. 712 private final Evaluator<G, C> _evaluator; 713 private final Factory<Genotype<G>> _genotypeFactory; 714 private Constraint<G, C> _constraint; 715 private Optimize _optimize = Optimize.MAXIMUM; 716 717 // Evolution parameters. 718 private final EvolutionParams.Builder<G, C> _evolutionParams = 719 EvolutionParams.builder(); 720 721 722 // Engine execution environment. 723 private Executor _executor = commonPool(); 724 private BatchExecutor _fitnessExecutor = null; 725 private InstantSource _clock = NanoClock.systemUTC(); 726 727 private EvolutionInterceptor<G, C> _interceptor = 728 EvolutionInterceptor.identity(); 729 730 /** 731 * Create a new evolution {@code Engine.Builder} with the given fitness 732 * evaluator and genotype factory. This is the most general way of 733 * creating an engine builder. 734 * 735 * @since 5.0 736 * 737 * @see Engine#builder(Function, Codec) 738 * @see Engine#builder(Function, Factory) 739 * @see Engine#builder(Problem) 740 * @see Engine#builder(Function, Chromosome, Chromosome[]) 741 * 742 * @param evaluator the fitness evaluator 743 * @param gtf the genotype factory 744 * @throws NullPointerException if one of the arguments is {@code null}. 745 */ 746 public Builder( 747 final Evaluator<G, C> evaluator, 748 final Factory<Genotype<G>> gtf 749 ) { 750 _genotypeFactory = requireNonNull(gtf); 751 _evaluator = requireNonNull(evaluator); 752 } 753 754 /** 755 * Applies the given {@code setup} recipe to {@code this} engine builder. 756 * 757 * @since 6.0 758 * 759 * @param setup the setup recipe applying to {@code this} builder 760 * @return {@code this} builder, for command chaining 761 * @throws NullPointerException if the {@code setup} is {@code null}. 762 */ 763 public Builder<G, C> setup(final Setup<G, C> setup) { 764 setup.apply(this); 765 return this; 766 } 767 768 /** 769 * Set the evolution parameters used by the engine. 770 * 771 * @since 5.2 772 * 773 * @param params the evolution parameter 774 * @return {@code this} builder, for command chaining 775 * @throws NullPointerException if the {@code params} is {@code null}. 776 */ 777 public Builder<G, C> evolutionParams(final EvolutionParams<G, C> params) { 778 _evolutionParams.evolutionParams(params); 779 return this; 780 } 781 782 /** 783 * The selector used for selecting the offspring population. <i>Default 784 * values is set to {@code TournamentSelector<>(3)}.</i> 785 * 786 * @param selector used for selecting the offspring population 787 * @return {@code this} builder, for command chaining 788 * @throws NullPointerException if one of the {@code selector} is 789 * {@code null}. 790 */ 791 public Builder<G, C> offspringSelector(final Selector<G, C> selector) { 792 _evolutionParams.offspringSelector(selector); 793 return this; 794 } 795 796 /** 797 * The selector used for selecting the survivor population. <i>Default 798 * values is set to {@code TournamentSelector<>(3)}.</i> 799 * 800 * @param selector used for selecting survivor population 801 * @return {@code this} builder, for command chaining 802 * @throws NullPointerException if one of the {@code selector} is 803 * {@code null}. 804 */ 805 public Builder<G, C> survivorsSelector(final Selector<G, C> selector) { 806 _evolutionParams.survivorsSelector(selector); 807 return this; 808 } 809 810 /** 811 * The selector used for selecting the survivors and offspring 812 * population. <i>Default values is set to 813 * {@code TournamentSelector<>(3)}.</i> 814 * 815 * @param selector used for selecting survivors and offspring population 816 * @return {@code this} builder, for command chaining 817 * @throws NullPointerException if one of the {@code selector} is 818 * {@code null}. 819 */ 820 public Builder<G, C> selector(final Selector<G, C> selector) { 821 _evolutionParams.selector(selector); 822 return this; 823 } 824 825 /** 826 * The alterers used for alter the offspring population. <i>Default 827 * values is set to {@code new SinglePointCrossover<>(0.2)} followed by 828 * {@code new Mutator<>(0.15)}.</i> 829 * 830 * @param first the first alterer used for alter the offspring 831 * population 832 * @param rest the rest of the alterers used for alter the offspring 833 * population 834 * @return {@code this} builder, for command chaining 835 * @throws NullPointerException if one of the alterers is {@code null}. 836 */ 837 @SafeVarargs 838 public final Builder<G, C> alterers( 839 final Alterer<G, C> first, 840 final Alterer<G, C>... rest 841 ) { 842 _evolutionParams.alterers(first, rest); 843 return this; 844 } 845 846 /** 847 * The phenotype constraint is used for detecting invalid individuals 848 * and repairing them. 849 * 850 * <p><i>Default implementation uses {@code Phenotype::isValid} for 851 * validating the phenotype.</i> Calling this method with {@code null} 852 * resets the builder to the default constraint.</p> 853 * 854 * @since 5.0 855 * 856 * @param constraint phenotype constraint which can override the default 857 * implementation the {@link Phenotype#isValid()} method and repairs 858 * invalid phenotypes when needed, or {@code null} for the default 859 * constraint. 860 * @return {@code this} builder, for command chaining 861 */ 862 public Builder<G, C> constraint(final Constraint<G, C> constraint) { 863 _constraint = constraint; 864 return this; 865 } 866 867 /** 868 * The optimization strategy used by the engine. <i>Default values is 869 * set to {@code Optimize.MAXIMUM}.</i> 870 * 871 * @param optimize the optimization strategy used by the engine 872 * @return {@code this} builder, for command chaining 873 * @throws NullPointerException if one of the {@code optimize} is 874 * {@code null}. 875 */ 876 public Builder<G, C> optimize(final Optimize optimize) { 877 _optimize = requireNonNull(optimize); 878 return this; 879 } 880 881 /** 882 * Set to a fitness-maximizing strategy. 883 * 884 * @since 3.4 885 * 886 * @return {@code this} builder, for command chaining 887 */ 888 public Builder<G, C> maximizing() { 889 return optimize(Optimize.MAXIMUM); 890 } 891 892 /** 893 * Set to a fitness minimizing strategy. 894 * 895 * @since 3.4 896 * 897 * @return {@code this} builder, for command chaining 898 */ 899 public Builder<G, C> minimizing() { 900 return optimize(Optimize.MINIMUM); 901 } 902 903 /** 904 * The offspring fraction. <i>Default values is set to {@code 0.6}.</i> 905 * This method call is equivalent to 906 * {@code survivorsFraction(1 - offspringFraction)} and will override 907 * any previously set survivors-fraction. 908 * 909 * @see #survivorsFraction(double) 910 * 911 * @param fraction the offspring fraction 912 * @return {@code this} builder, for command chaining 913 * @throws java.lang.IllegalArgumentException if the fraction is not 914 * within the range [0, 1]. 915 */ 916 public Builder<G, C> offspringFraction(final double fraction) { 917 _evolutionParams.offspringFraction(fraction); 918 return this; 919 } 920 921 /** 922 * The survivor fraction. <i>Default values is set to {@code 0.4}.</i> 923 * This method call is equivalent to 924 * {@code offspringFraction(1 - survivorsFraction)} and will override 925 * any previously set offspring-fraction. 926 * 927 * @since 3.8 928 * 929 * @see #offspringFraction(double) 930 * 931 * @param fraction the survivor fraction 932 * @return {@code this} builder, for command chaining 933 * @throws java.lang.IllegalArgumentException if the fraction is not 934 * within the range [0, 1]. 935 */ 936 public Builder<G, C> survivorsFraction(final double fraction) { 937 return offspringFraction(1 - fraction); 938 } 939 940 /** 941 * The number of offspring individuals. 942 * 943 * @since 3.8 944 * 945 * @apiNote 946 * The offspring size is stored as a fraction of the current population 947 * size. Changing the population size afterwards keeps this fraction, not 948 * the absolute offspring size. 949 * 950 * @param size the number of offspring individuals. 951 * @return {@code this} builder, for command chaining 952 * @throws java.lang.IllegalArgumentException if the size is not 953 * within the range [0, population-size]. 954 */ 955 public Builder<G, C> offspringSize(final int size) { 956 if (size < 0) { 957 throw new IllegalArgumentException(format( 958 "Offspring size must be greater or equal zero, but was %s.", 959 size 960 )); 961 } 962 963 return offspringFraction(size/(double)_evolutionParams.populationSize()); 964 } 965 966 /** 967 * The number of survivors. 968 * 969 * @since 3.8 970 * 971 * @apiNote 972 * The survivor size is stored as a fraction of the current population 973 * size. Changing the population size afterwards keeps this fraction, not 974 * the absolute survivor size. 975 * 976 * @param size the number of survivors. 977 * @return {@code this} builder, for command chaining 978 * @throws java.lang.IllegalArgumentException if the size is not 979 * within the range [0, population-size]. 980 */ 981 public Builder<G, C> survivorsSize(final int size) { 982 if (size < 0) { 983 throw new IllegalArgumentException(format( 984 "Survivors must be greater or equal zero, but was %s.", 985 size 986 )); 987 } 988 989 return survivorsFraction(size/(double)_evolutionParams.populationSize()); 990 } 991 992 /** 993 * The number of individuals which form the population. <i>Default 994 * values is set to {@code 50}.</i> 995 * 996 * @param size the number of individuals of a population 997 * @return {@code this} builder, for command chaining 998 * @throws java.lang.IllegalArgumentException if {@code size < 1} 999 */ 1000 public Builder<G, C> populationSize(final int size) { 1001 _evolutionParams.populationSize(size); 1002 return this; 1003 } 1004 1005 /** 1006 * The maximal allowed age of a phenotype. <i>Default value is set to 1007 * {@code 70}.</i> 1008 * 1009 * @param age the maximal phenotype age 1010 * @return {@code this} builder, for command chaining 1011 * @throws java.lang.IllegalArgumentException if {@code age < 1} 1012 */ 1013 public Builder<G, C> maximalPhenotypeAge(final long age) { 1014 _evolutionParams.maximalPhenotypeAge(age); 1015 return this; 1016 } 1017 1018 /** 1019 * The executor used by the engine. 1020 * 1021 * @apiNote 1022 * If no dedicated {@link Evaluator} is defined, this is also the 1023 * executor, used for evaluating the fitness functions. 1024 * 1025 * @param executor the executor used by the engine 1026 * @return {@code this} builder, for command chaining 1027 */ 1028 public Builder<G, C> executor(final Executor executor) { 1029 _executor = requireNonNull(executor); 1030 return this; 1031 } 1032 1033 /** 1034 * This executor is used for evaluating the fitness functions. 1035 * 1036 * @apiNote 1037 * If a dedicated {@link Evaluator} is defined, this executor is not 1038 * used. 1039 * 1040 * @since 8.0 1041 * 1042 * @param executor the executor used for evaluating the fitness functions 1043 * @return {@code this} builder, for command chaining 1044 */ 1045 public Builder<G, C> fitnessExecutor(final BatchExecutor executor) { 1046 _fitnessExecutor = requireNonNull(executor); 1047 return this; 1048 } 1049 1050 /** 1051 * The clock used for calculating the execution durations. 1052 * 1053 * @param clock the clock used for calculating the execution durations 1054 * @return {@code this} builder, for command chaining 1055 */ 1056 public Builder<G, C> clock(final InstantSource clock) { 1057 _clock = requireNonNull(clock); 1058 return this; 1059 } 1060 1061 /** 1062 * The evolution interceptor, which allows changing the evolution start 1063 * and result. 1064 * 1065 * @since 6.0 1066 * @see EvolutionResult#toUniquePopulation() 1067 * 1068 * @param interceptor the evolution interceptor 1069 * @return {@code this} builder, for command chaining 1070 * @throws NullPointerException if the given {@code interceptor} is 1071 * {@code null} 1072 */ 1073 public Builder<G, C> 1074 interceptor(final EvolutionInterceptor<G, C> interceptor) { 1075 _interceptor = requireNonNull(interceptor); 1076 return this; 1077 } 1078 1079 /** 1080 * Builds a new {@code Engine} instance from the set properties. 1081 * 1082 * @return a new {@code Engine} instance from the set properties 1083 */ 1084 public Engine<G, C> build() { 1085 return new Engine<>( 1086 __evaluator(), 1087 _genotypeFactory, 1088 __constraint(), 1089 _optimize, 1090 _evolutionParams.build(), 1091 _executor, 1092 _clock, 1093 _interceptor 1094 ); 1095 } 1096 1097 private Evaluator<G, C> __evaluator() { 1098 return _evaluator instanceof FitnessEvaluator<G, C> fe 1099 ? new FitnessEvaluator<>(fe.function(), fitnessExecutor()) 1100 : _evaluator; 1101 } 1102 1103 private Constraint<G, C> __constraint() { 1104 return _constraint == null 1105 ? RetryConstraint.of(_genotypeFactory) 1106 : _constraint; 1107 } 1108 1109 /* ********************************************************************* 1110 * Current properties 1111 ***********************************************************************/ 1112 1113 /** 1114 * Return the used {@link Alterer} of the GA. 1115 * 1116 * @return the used {@link Alterer} of the GA. 1117 */ 1118 public Alterer<G, C> alterer() { 1119 return _evolutionParams.alterer(); 1120 } 1121 1122 /** 1123 * Return the {@link InstantSource} the engine is using for measuring 1124 * the execution time. 1125 * 1126 * @since 3.1 1127 * 1128 * @return the clock used for measuring the execution time 1129 */ 1130 public InstantSource clock() { 1131 return _clock; 1132 } 1133 1134 /** 1135 * Return the {@link Executor} the engine is using for executing the 1136 * evolution steps. 1137 * 1138 * @since 3.1 1139 * 1140 * @return the executor used for performing the evolution steps 1141 */ 1142 public Executor executor() { 1143 return _executor; 1144 } 1145 1146 /** 1147 * Return the batch executor, used for evaluating the fitness functions. 1148 * 1149 * @since 8.0 1150 * 1151 * @return the batch executor, used for evaluating the fitness functions 1152 */ 1153 public BatchExecutor fitnessExecutor() { 1154 return _fitnessExecutor != null 1155 ? _fitnessExecutor 1156 : BatchExecutor.of(executor()); 1157 } 1158 1159 /** 1160 * Return the used genotype {@link Factory} of the GA. The genotype factory 1161 * is used for creating the initial population and new, random individuals 1162 * when needed (as replacement for invalid and/or died genotypes). 1163 * 1164 * @since 3.1 1165 * 1166 * @return the used genotype {@link Factory} of the GA. 1167 */ 1168 public Factory<Genotype<G>> genotypeFactory() { 1169 return _genotypeFactory; 1170 } 1171 1172 /** 1173 * Return the constraint of the evolution problem. 1174 * 1175 * @since 5.0 1176 * 1177 * @return the constraint of the evolution problem 1178 */ 1179 public Constraint<G, C> constraint() { 1180 return _constraint; 1181 } 1182 1183 /** 1184 * Return the currently set evolution parameters. 1185 * 1186 * @since 5.2 1187 * 1188 * @return the currently set evolution parameters 1189 */ 1190 public EvolutionParams<G, C> evolutionParams() { 1191 return _evolutionParams.build(); 1192 } 1193 1194 /** 1195 * Return the maximal allowed phenotype age. 1196 * 1197 * @since 3.1 1198 * 1199 * @return the maximal allowed phenotype age 1200 */ 1201 public long maximalPhenotypeAge() { 1202 return _evolutionParams.maximalPhenotypeAge(); 1203 } 1204 1205 /** 1206 * Return the offspring fraction. 1207 * 1208 * @return the offspring fraction. 1209 */ 1210 public double offspringFraction() { 1211 return _evolutionParams.offspringFraction(); 1212 } 1213 1214 /** 1215 * Return the used offspring {@link Selector} of the GA. 1216 * 1217 * @since 3.1 1218 * 1219 * @return the used offspring {@link Selector} of the GA. 1220 */ 1221 public Selector<G, C> offspringSelector() { 1222 return _evolutionParams.offspringSelector(); 1223 } 1224 1225 /** 1226 * Return the used survivor {@link Selector} of the GA. 1227 * 1228 * @since 3.1 1229 * 1230 * @return the used survivor {@link Selector} of the GA. 1231 */ 1232 public Selector<G, C> survivorsSelector() { 1233 return _evolutionParams.survivorsSelector(); 1234 } 1235 1236 /** 1237 * Return the optimization strategy. 1238 * 1239 * @since 3.1 1240 * 1241 * @return the optimization strategy 1242 */ 1243 public Optimize optimize() { 1244 return _optimize; 1245 } 1246 1247 /** 1248 * Return the number of individuals of a population. 1249 * 1250 * @since 3.1 1251 * 1252 * @return the number of individuals of a population 1253 */ 1254 public int populationSize() { 1255 return _evolutionParams.populationSize(); 1256 } 1257 1258 /** 1259 * Return the evolution interceptor. 1260 * 1261 * @since 6.0 1262 * 1263 * @return the evolution interceptor 1264 */ 1265 public EvolutionInterceptor<G, C> interceptor() { 1266 return _interceptor; 1267 } 1268 1269 /** 1270 * Create a new builder, with the current configuration. 1271 * 1272 * @apiNote 1273 * For builders created from a fitness function, a configured fitness 1274 * executor is not copied. Calling {@link #fitnessExecutor(BatchExecutor)} 1275 * on the copied builder creates a new fitness evaluator with the given 1276 * executor. 1277 * 1278 * @since 3.1 1279 * 1280 * @return a new builder, with the current configuration 1281 */ 1282 @Override 1283 public Builder<G, C> copy() { 1284 return new Builder<>(_evaluator, _genotypeFactory) 1285 .clock(_clock) 1286 .executor(_executor) 1287 .constraint(_constraint) 1288 .optimize(_optimize) 1289 .evolutionParams(_evolutionParams.build()) 1290 .interceptor(_interceptor); 1291 } 1292 1293 } 1294 1295 1296 /* ************************************************************************* 1297 * Engine setup 1298 **************************************************************************/ 1299 1300 1301 /** 1302 * This interface represents a recipe for configuring (setup) a given 1303 * {@link Builder}. It is mainly used for grouping mutually dependent 1304 * engine configurations. The following code snippet shows a possible usage 1305 * example. 1306 * {@snippet lang="java": 1307 * final Engine<CharacterGene, Integer> engine = Engine.builder(problem) 1308 * .setup(new WeaselProgram<>()) 1309 * .build(); 1310 * } 1311 * 1312 * @see Builder#setup(Setup) 1313 * 1314 * @param <G> the gene type 1315 * @param <C> the fitness result type 1316 * 1317 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 1318 * @version 6.0 1319 * @since 6.0 1320 */ 1321 @FunctionalInterface 1322 public interface Setup< 1323 G extends Gene<?, G>, 1324 C extends Comparable<? super C> 1325 > { 1326 1327 /** 1328 * Applies {@code this} setup to the given engine {@code builder}. 1329 * 1330 * @param builder the engine builder to set up (configure) 1331 */ 1332 void apply(final Builder<G, C> builder); 1333 1334 } 1335}