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