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