0001 /*
0002 * Java Genetic Algorithm Library (jenetics-8.0.0).
0003 * Copyright (c) 2007-2024 Franz Wilhelmstötter
0004 *
0005 * Licensed under the Apache License, Version 2.0 (the "License");
0006 * you may not use this file except in compliance with the License.
0007 * You may obtain a copy of the License at
0008 *
0009 * http://www.apache.org/licenses/LICENSE-2.0
0010 *
0011 * Unless required by applicable law or agreed to in writing, software
0012 * distributed under the License is distributed on an "AS IS" BASIS,
0013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014 * See the License for the specific language governing permissions and
0015 * limitations under the License.
0016 *
0017 * Author:
0018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
0019 */
0020 package io.jenetics.engine;
0021
0022 import static java.lang.String.format;
0023 import static java.util.Objects.requireNonNull;
0024 import static java.util.concurrent.CompletableFuture.supplyAsync;
0025 import static java.util.concurrent.ForkJoinPool.commonPool;
0026
0027 import java.time.InstantSource;
0028 import java.util.concurrent.CompletableFuture;
0029 import java.util.concurrent.Executor;
0030 import java.util.concurrent.ForkJoinPool;
0031 import java.util.function.Function;
0032 import java.util.function.Supplier;
0033 import java.util.stream.Stream;
0034
0035 import io.jenetics.Alterer;
0036 import io.jenetics.AltererResult;
0037 import io.jenetics.Chromosome;
0038 import io.jenetics.Gene;
0039 import io.jenetics.Genotype;
0040 import io.jenetics.Optimize;
0041 import io.jenetics.Phenotype;
0042 import io.jenetics.Selector;
0043 import io.jenetics.util.BatchExecutor;
0044 import io.jenetics.util.Copyable;
0045 import io.jenetics.util.Factory;
0046 import io.jenetics.util.ISeq;
0047 import io.jenetics.util.MSeq;
0048 import io.jenetics.util.NanoClock;
0049 import io.jenetics.util.Seq;
0050
0051 /**
0052 * Genetic algorithm <em>engine</em> which is the main class. The following
0053 * example shows the main steps in initializing and executing the GA.
0054 *
0055 * {@snippet lang="java":
0056 * public class RealFunction {
0057 * // Definition of the fitness function.
0058 * private static Double eval(final Genotype<DoubleGene> gt) {
0059 * final double x = gt.gene().doubleValue();
0060 * return cos(0.5 + sin(x))*cos(x);
0061 * }
0062 *
0063 * public static void main(String[] args) {
0064 * // Create/configuring the engine via its builder.
0065 * final Engine<DoubleGene, Double> engine = Engine
0066 * .builder(
0067 * RealFunction::eval,
0068 * DoubleChromosome.of(0.0, 2.0*PI))
0069 * .populationSize(500)
0070 * .optimize(Optimize.MINIMUM)
0071 * .alterers(
0072 * new Mutator<>(0.03),
0073 * new MeanAlterer<>(0.6))
0074 * .build();
0075 *
0076 * // Execute the GA (engine).
0077 * final Phenotype<DoubleGene, Double> result = engine.stream()
0078 * // Truncate the evolution stream if no better individual could
0079 * // be found after 5 consecutive generations.
0080 * .limit(bySteadyFitness(5))
0081 * // Terminate the evolution after maximal 100 generations.
0082 * .limit(100)
0083 * .collect(toBestPhenotype());
0084 * }
0085 * }
0086 * }
0087 *
0088 * The architecture allows to decouple the configuration of the engine from the
0089 * execution. The {@code Engine} is configured via the {@code Engine.Builder}
0090 * class and can't be changed after creation. The actual <i>evolution</i> is
0091 * performed by the {@link EvolutionStream}, which is created by the
0092 * {@code Engine}.
0093 *
0094 * <H2>Concurrency</H2>
0095 * By default, the engine uses the {@link ForkJoinPool#commonPool()} for
0096 * executing the evolution steps and evaluating the fitness function concurrently.
0097 * You can change the used execution services with the {@link Builder#executor(Executor)}
0098 * method. If you want to use a different executor for evaluating the fitness
0099 * functions, you have to set the {@link Builder#fitnessExecutor(BatchExecutor)}.
0100 *
0101 * {@snippet lang="java":
0102 * final Engine<DoubleGene, Double> engine = Engine
0103 * .builder(null) // @replace substring='null' replacement="..."
0104 * // Using this execution service for parallelize the evolution steps.
0105 * .executor(Executors.newFixedThreadPool(5))
0106 * // Using one virtual thread for every fitness function evaluation.
0107 * .fitnessExecutor(BatchExecutor.ofVirtualThreads())
0108 * .build();
0109 * }
0110 *
0111 * @implNote
0112 * This class is thread safe: The engine maintains no mutable state.
0113 * Therefore, it is safe to create multiple evolution streams with one
0114 * engine, which may be actually used in different threads.
0115 *
0116 * @see Engine.Builder
0117 * @see EvolutionStart
0118 * @see EvolutionResult
0119 * @see EvolutionStream
0120 * @see EvolutionStatistics
0121 * @see Codec
0122 * @see Constraint
0123 *
0124 * @param <G> the gene type
0125 * @param <C> the fitness result type
0126 *
0127 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
0128 * @since 3.0
0129 * @version 7.0
0130 */
0131 public final class Engine<
0132 G extends Gene<?, G>,
0133 C extends Comparable<? super C>
0134 >
0135 implements
0136 Evolution<G, C>,
0137 EvolutionStreamable<G, C>,
0138 Evaluator<G, C>
0139 {
0140
0141 // Problem definition.
0142 private final Evaluator<G, C> _evaluator;
0143 private final Factory<Genotype<G>> _genotypeFactory;
0144 private final Constraint<G, C> _constraint;
0145 private final Optimize _optimize;
0146
0147 // Evolution parameters.
0148 private final EvolutionParams<G, C> _evolutionParams;
0149
0150 // Execution context for concurrent execution of evolving steps.
0151 private final Executor _executor;
0152 private final InstantSource _clock;
0153 private final EvolutionInterceptor<G, C> _interceptor;
0154
0155
0156 /**
0157 * Create a new GA engine with the given parameters.
0158 *
0159 * @param evaluator the population fitness evaluator
0160 * @param genotypeFactory the genotype factory this GA is working with.
0161 * @param constraint phenotype constraint which can override the default
0162 * implementation the {@link Phenotype#isValid()} method and repairs
0163 * invalid phenotypes when needed.
0164 * @param optimize the kind of optimization (minimize or maximize)
0165 * @param evolutionParams the evolution parameters, which influences the
0166 * evolution process
0167 * @param executor the executor used for executing the single evolved steps
0168 * @param clock the clock used for calculating the timing results
0169 * @param interceptor the evolution interceptor, which gives additional
0170 * possibilities to influence the actual evolution
0171 * @throws NullPointerException if one of the arguments is {@code null}
0172 * @throws IllegalArgumentException if the given integer values are smaller
0173 * than one.
0174 */
0175 Engine(
0176 final Evaluator<G, C> evaluator,
0177 final Factory<Genotype<G>> genotypeFactory,
0178 final Constraint<G, C> constraint,
0179 final Optimize optimize,
0180 final EvolutionParams<G, C> evolutionParams,
0181 final Executor executor,
0182 final InstantSource clock,
0183 final EvolutionInterceptor<G, C> interceptor
0184 ) {
0185 _evaluator = requireNonNull(evaluator);
0186 _genotypeFactory = requireNonNull(genotypeFactory);
0187 _constraint = requireNonNull(constraint);
0188 _optimize = requireNonNull(optimize);
0189 _evolutionParams = requireNonNull(evolutionParams);
0190 _executor = requireNonNull(executor);
0191 _clock = requireNonNull(clock);
0192 _interceptor = requireNonNull(interceptor);
0193 }
0194
0195 @Override
0196 public EvolutionResult<G, C> evolve(final EvolutionStart<G, C> start) {
0197 final EvolutionTiming timing = new EvolutionTiming(_clock);
0198 timing.evolve.start();
0199
0200 final EvolutionStart<G, C> interceptedStart = _interceptor.before(start);
0201
0202 // Create initial population if `start` is empty.
0203 final EvolutionStart<G, C> es = interceptedStart.population().isEmpty()
0204 ? evolutionStart(interceptedStart)
0205 : interceptedStart;
0206
0207 // Initial evaluation of the population.
0208 final ISeq<Phenotype<G, C>> population = es.isDirty()
0209 ? timing.evaluation.timing(() -> eval(es.population()))
0210 : es.population();
0211
0212 // Select the offspring population.
0213 final CompletableFuture<ISeq<Phenotype<G, C>>> offspring =
0214 supplyAsync(() ->
0215 timing.offspringSelection.timing(() ->
0216 selectOffspring(population)
0217 ),
0218 _executor
0219 );
0220
0221 // Select the survivor population.
0222 final CompletableFuture<ISeq<Phenotype<G, C>>> survivors =
0223 supplyAsync(() ->
0224 timing.survivorsSelection.timing(() ->
0225 selectSurvivors(population)
0226 ),
0227 _executor
0228 );
0229
0230 // Altering the offspring population.
0231 final CompletableFuture<AltererResult<G, C>> alteredOffspring =
0232 offspring.thenApplyAsync(off ->
0233 timing.offspringAlter.timing(() ->
0234 _evolutionParams.alterer().alter(off, es.generation())
0235 ),
0236 _executor
0237 );
0238
0239 // Filter and replace invalid and old survivor individuals.
0240 final CompletableFuture<FilterResult<G, C>> filteredSurvivors =
0241 survivors.thenApplyAsync(sur ->
0242 timing.survivorFilter.timing(() ->
0243 filter(sur, es.generation())
0244 ),
0245 _executor
0246 );
0247
0248 // Filter and replace invalid and old offspring individuals.
0249 final CompletableFuture<FilterResult<G, C>> filteredOffspring =
0250 alteredOffspring.thenApplyAsync(off ->
0251 timing.offspringFilter.timing(() ->
0252 filter(off.population(), es.generation())
0253 ),
0254 _executor
0255 );
0256
0257 // Combining survivors and offspring to the new population.
0258 final CompletableFuture<ISeq<Phenotype<G, C>>> nextPopulation =
0259 filteredSurvivors.thenCombineAsync(
0260 filteredOffspring,
0261 (s, o) -> ISeq.of(s.population().append(o.population())),
0262 _executor
0263 );
0264
0265 // Evaluate the fitness-function and wait for a result.
0266 final ISeq<Phenotype<G, C>> pop = nextPopulation.join();
0267 final ISeq<Phenotype<G, C>> result = timing.evaluation.timing(() ->
0268 eval(pop)
0269 );
0270
0271 final int killCount =
0272 filteredOffspring.join().killCount() +
0273 filteredSurvivors.join().killCount();
0274
0275 final int invalidCount =
0276 filteredOffspring.join().invalidCount() +
0277 filteredSurvivors.join().invalidCount();
0278
0279 final int alterationCount = alteredOffspring.join().alterations();
0280
0281 EvolutionResult<G, C> er = EvolutionResult.of(
0282 _optimize,
0283 result,
0284 es.generation(),
0285 timing.toDurations(),
0286 killCount,
0287 invalidCount,
0288 alterationCount
0289 );
0290
0291 final EvolutionResult<G, C> interceptedResult = _interceptor.after(er);
0292 if (er != interceptedResult) {
0293 er = interceptedResult.withPopulation(
0294 timing.evaluation.timing(() ->
0295 eval(interceptedResult.population())
0296 ));
0297 }
0298
0299 timing.evolve.stop();
0300
0301 return er
0302 .withDurations(timing.toDurations())
0303 .clean();
0304 }
0305
0306 // Selects the survivor population. A new population object is returned.
0307 private ISeq<Phenotype<G, C>>
0308 selectSurvivors(final ISeq<Phenotype<G, C>> population) {
0309 return _evolutionParams.survivorsSize() > 0
0310 ? _evolutionParams.survivorsSelector()
0311 .select(population, _evolutionParams.survivorsSize(), _optimize)
0312 : ISeq.empty();
0313 }
0314
0315 // Selects the offspring population. A new population object is returned.
0316 private ISeq<Phenotype<G, C>>
0317 selectOffspring(final ISeq<Phenotype<G, C>> population) {
0318 return _evolutionParams.offspringSize() > 0
0319 ? _evolutionParams.offspringSelector()
0320 .select(population, _evolutionParams.offspringSize(), _optimize)
0321 : ISeq.empty();
0322 }
0323
0324 // Filters out invalid and old individuals. Filtering is done in place.
0325 private FilterResult<G, C> filter(
0326 final Seq<Phenotype<G, C>> population,
0327 final long generation
0328 ) {
0329 int killCount = 0;
0330 int invalidCount = 0;
0331
0332 final MSeq<Phenotype<G, C>> pop = MSeq.of(population);
0333 for (int i = 0, n = pop.size(); i < n; ++i) {
0334 final Phenotype<G, C> individual = pop.get(i);
0335
0336 if (!_constraint.test(individual)) {
0337 pop.set(i, _constraint.repair(individual, generation));
0338 ++invalidCount;
0339 } else if (individual.age(generation) >
0340 _evolutionParams.maximalPhenotypeAge())
0341 {
0342 pop.set(i, Phenotype.of(_genotypeFactory.newInstance(), generation));
0343 ++killCount;
0344 }
0345 }
0346
0347 return new FilterResult<>(pop.toISeq(), killCount, invalidCount);
0348 }
0349
0350
0351 /* *************************************************************************
0352 * Evaluation methods.
0353 **************************************************************************/
0354
0355 /**
0356 * Evaluates the fitness function of the given population with the configured
0357 * {@link Evaluator} of this engine and returns a new population
0358 * with its fitness value assigned.
0359 *
0360 * @since 5.0
0361 *
0362 * @see Evaluator
0363 * @see Evaluator#eval(Seq)
0364 *
0365 * @param population the population to evaluate
0366 * @return a new population with assigned fitness values
0367 * @throws IllegalStateException if the configured fitness function doesn't
0368 * return a population with the same size as the input population.
0369 * This exception is also thrown if one of the populations
0370 * phenotype has no fitness value assigned.
0371 */
0372 @Override
0373 public ISeq<Phenotype<G, C>> eval(final Seq<Phenotype<G, C>> population) {
0374 final ISeq<Phenotype<G, C>> evaluated = _evaluator.eval(population);
0375
0376 if (population.size() != evaluated.size()) {
0377 throw new IllegalStateException(format(
0378 "Expected %d individuals, but got %d. " +
0379 "Check your evaluator function.",
0380 population.size(), evaluated.size()
0381 ));
0382 }
0383 if (!evaluated.forAll(Phenotype::isEvaluated)) {
0384 throw new IllegalStateException(
0385 "Some phenotypes have no assigned fitness value. " +
0386 "Check your evaluator function."
0387 );
0388 }
0389
0390 return evaluated;
0391 }
0392
0393
0394 /* *************************************************************************
0395 * Evolution Stream creation.
0396 **************************************************************************/
0397
0398 @Override
0399 public EvolutionStream<G, C>
0400 stream(final Supplier<EvolutionStart<G, C>> start) {
0401 return EvolutionStream.ofEvolution(
0402 () -> evolutionStart(start.get()),
0403 this
0404 );
0405 }
0406
0407 @Override
0408 public EvolutionStream<G, C> stream(final EvolutionInit<G> init) {
0409 return stream(evolutionStart(init));
0410 }
0411
0412 private EvolutionStart<G, C>
0413 evolutionStart(final EvolutionStart<G, C> start) {
0414 final ISeq<Phenotype<G, C>> population = start.population();
0415 final long gen = start.generation();
0416
0417 final Stream<Phenotype<G, C>> stream = Stream.concat(
0418 population.stream(),
0419 _genotypeFactory.instances()
0420 .map(gt -> Phenotype.of(gt, gen))
0421 );
0422
0423 final ISeq<Phenotype<G, C>> pop = stream
0424 .limit(populationSize())
0425 .collect(ISeq.toISeq());
0426
0427 return EvolutionStart.of(pop, gen);
0428 }
0429
0430 private EvolutionStart<G, C>
0431 evolutionStart(final EvolutionInit<G> init) {
0432 final ISeq<Genotype<G>> pop = init.population();
0433 final long gen = init.generation();
0434
0435 return evolutionStart(
0436 EvolutionStart.of(
0437 pop.map(gt -> Phenotype.of(gt, gen)),
0438 gen
0439 )
0440 );
0441 }
0442
0443 /* *************************************************************************
0444 * Property access methods.
0445 **************************************************************************/
0446
0447 /**
0448 * Return the used genotype {@link Factory} of the GA. The genotype factory
0449 * is used for creating the initial population and new, random individuals
0450 * when needed (as replacement for invalid and/or died genotypes).
0451 *
0452 * @return the used genotype {@link Factory} of the GA.
0453 */
0454 public Factory<Genotype<G>> genotypeFactory() {
0455 return _genotypeFactory;
0456 }
0457
0458 /**
0459 * Return the constraint of the evolution problem.
0460 *
0461 * @since 5.0
0462 *
0463 * @return the constraint of the evolution problem
0464 */
0465 public Constraint<G, C> constraint() {
0466 return _constraint;
0467 }
0468
0469 /**
0470 * Return the used survivor {@link Selector} of the GA.
0471 *
0472 * @return the used survivor {@link Selector} of the GA.
0473 */
0474 public Selector<G, C> survivorsSelector() {
0475 return _evolutionParams.survivorsSelector();
0476 }
0477
0478 /**
0479 * Return the used offspring {@link Selector} of the GA.
0480 *
0481 * @return the used offspring {@link Selector} of the GA.
0482 */
0483 public Selector<G, C> offspringSelector() {
0484 return _evolutionParams.offspringSelector();
0485 }
0486
0487 /**
0488 * Return the used {@link Alterer} of the GA.
0489 *
0490 * @return the used {@link Alterer} of the GA.
0491 */
0492 public Alterer<G, C> alterer() {
0493 return _evolutionParams.alterer();
0494 }
0495
0496 /**
0497 * Return the number of selected offspring.
0498 *
0499 * @return the number of selected offspring
0500 */
0501 public int offspringSize() {
0502 return _evolutionParams.offspringSize();
0503 }
0504
0505 /**
0506 * The number of selected survivors.
0507 *
0508 * @return the number of selected survivors
0509 */
0510 public int survivorsSize() {
0511 return _evolutionParams.survivorsSize();
0512 }
0513
0514 /**
0515 * Return the number of individuals of a population.
0516 *
0517 * @return the number of individuals of a population
0518 */
0519 public int populationSize() {
0520 return _evolutionParams.populationSize();
0521 }
0522
0523 /**
0524 * Return the maximal allowed phenotype age.
0525 *
0526 * @return the maximal allowed phenotype age
0527 */
0528 public long maximalPhenotypeAge() {
0529 return _evolutionParams.maximalPhenotypeAge();
0530 }
0531
0532 /**
0533 * Return the optimization strategy.
0534 *
0535 * @return the optimization strategy
0536 */
0537 public Optimize optimize() {
0538 return _optimize;
0539 }
0540
0541 /**
0542 * Return the {@link InstantSource} the engine is using for measuring the
0543 * execution time.
0544 *
0545 * @return the clock used for measuring the execution time
0546 */
0547 public InstantSource clock() {
0548 return _clock;
0549 }
0550
0551 /**
0552 * Return the {@link Executor} the engine is using for executing the
0553 * evolution steps.
0554 *
0555 * @return the executor used for performing the evolution steps
0556 */
0557 public Executor executor() {
0558 return _executor;
0559 }
0560
0561 /**
0562 * Return the evolution interceptor.
0563 *
0564 * @since 6.0
0565 *
0566 * @return the evolution result mapper
0567 */
0568 public EvolutionInterceptor<G, C> interceptor() {
0569 return _interceptor;
0570 }
0571
0572 /**
0573 * Create a new evolution {@code Engine.Builder} initialized with the values
0574 * of the current evolution {@code Engine}. With this method, the evolution
0575 * engine can serve as a template for a new one.
0576 *
0577 * @return a new engine builder
0578 */
0579 public Builder<G, C> toBuilder() {
0580 return new Builder<>(_evaluator, _genotypeFactory)
0581 .clock(_clock)
0582 .executor(_executor)
0583 .optimize(_optimize)
0584 .constraint(_constraint)
0585 .evolutionParams(_evolutionParams)
0586 .interceptor(_interceptor);
0587 }
0588
0589
0590 /* *************************************************************************
0591 * Static Builder methods.
0592 **************************************************************************/
0593
0594 /**
0595 * Create a new evolution {@code Engine.Builder} with the given fitness
0596 * function and genotype factory.
0597 *
0598 * @param ff the fitness function
0599 * @param gtf the genotype factory
0600 * @param <G> the gene type
0601 * @param <C> the fitness function result type
0602 * @return a new engine builder
0603 * @throws java.lang.NullPointerException if one of the arguments is
0604 * {@code null}.
0605 */
0606 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
0607 Builder<G, C> builder(
0608 final Function<? super Genotype<G>, ? extends C> ff,
0609 final Factory<Genotype<G>> gtf
0610 ) {
0611 return new Builder<>(
0612 new FitnessEvaluator<>(ff, BatchExecutor.of(commonPool())),
0613 gtf
0614 );
0615 }
0616
0617 /**
0618 * Create a new evolution {@code Engine.Builder} with the given fitness
0619 * function and problem {@code codec}.
0620 *
0621 * @since 3.2
0622 *
0623 * @param ff the fitness evaluator
0624 * @param codec the problem codec
0625 * @param <T> the fitness function input type
0626 * @param <C> the fitness function result type
0627 * @param <G> the gene type
0628 * @return a new engine builder
0629 * @throws java.lang.NullPointerException if one of the arguments is
0630 * {@code null}.
0631 */
0632 public static <T, G extends Gene<?, G>, C extends Comparable<? super C>>
0633 Builder<G, C> builder(
0634 final Function<? super T, ? extends C> ff,
0635 final Codec<T, G> codec
0636 ) {
0637 return builder(ff.compose(codec.decoder()), codec.encoding());
0638 }
0639
0640 /**
0641 * Create a new evolution {@code Engine.Builder} for the given
0642 * {@link Problem}.
0643 *
0644 * @since 3.4
0645 *
0646 * @param problem the problem to be solved by the evolution {@code Engine}
0647 * @param <T> the (<i>native</i>) argument type of the problem fitness function
0648 * @param <G> the gene type the evolution engine is working with
0649 * @param <C> the result type of the fitness function
0650 * @return Create a new evolution {@code Engine.Builder}
0651 */
0652 public static <T, G extends Gene<?, G>, C extends Comparable<? super C>>
0653 Builder<G, C> builder(final Problem<T, G, C> problem) {
0654 final var builder = builder(problem.fitness(), problem.codec());
0655 problem.constraint().ifPresent(builder::constraint);
0656 return builder;
0657 }
0658
0659 /**
0660 * Create a new evolution {@code Engine.Builder} with the given fitness
0661 * function and chromosome templates.
0662 *
0663 * @param ff the fitness function
0664 * @param chromosome the first chromosome
0665 * @param chromosomes the chromosome templates
0666 * @param <G> the gene type
0667 * @param <C> the fitness function result type
0668 * @return a new engine builder
0669 * @throws java.lang.NullPointerException if one of the arguments is
0670 * {@code null}.
0671 */
0672 @SafeVarargs
0673 public static <G extends Gene<?, G>, C extends Comparable<? super C>>
0674 Builder<G, C> builder(
0675 final Function<? super Genotype<G>, ? extends C> ff,
0676 final Chromosome<G> chromosome,
0677 final Chromosome<G>... chromosomes
0678 ) {
0679 return builder(ff, Genotype.of(chromosome, chromosomes));
0680 }
0681
0682
0683 /* *************************************************************************
0684 * Engine builder
0685 **************************************************************************/
0686
0687
0688 /**
0689 * Builder class for building GA {@code Engine} instances.
0690 *
0691 * @see Engine
0692 *
0693 * @param <G> the gene type
0694 * @param <C> the fitness function result type
0695 *
0696 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
0697 * @since 3.0
0698 * @version 6.0
0699 */
0700 public static final class Builder<
0701 G extends Gene<?, G>,
0702 C extends Comparable<? super C>
0703 >
0704 implements Copyable<Builder<G, C>>
0705 {
0706
0707 // No default values for this properties.
0708 private final Evaluator<G, C> _evaluator;
0709 private final Factory<Genotype<G>> _genotypeFactory;
0710 private Constraint<G, C> _constraint;
0711 private Optimize _optimize = Optimize.MAXIMUM;
0712
0713 // Evolution parameters.
0714 private final EvolutionParams.Builder<G, C> _evolutionParams =
0715 EvolutionParams.builder();
0716
0717
0718 // Engine execution environment.
0719 private Executor _executor = commonPool();
0720 private BatchExecutor _fitnessExecutor = null;
0721 private InstantSource _clock = NanoClock.systemUTC();
0722
0723 private EvolutionInterceptor<G, C> _interceptor =
0724 EvolutionInterceptor.identity();
0725
0726 /**
0727 * Create a new evolution {@code Engine.Builder} with the given fitness
0728 * evaluator and genotype factory. This is the most general way of
0729 * creating an engine builder.
0730 *
0731 * @since 5.0
0732 *
0733 * @see Engine#builder(Function, Codec)
0734 * @see Engine#builder(Function, Factory)
0735 * @see Engine#builder(Problem)
0736 * @see Engine#builder(Function, Chromosome, Chromosome[])
0737 *
0738 * @param evaluator the fitness evaluator
0739 * @param gtf the genotype factory
0740 * @throws NullPointerException if one of the arguments is {@code null}.
0741 */
0742 public Builder(
0743 final Evaluator<G, C> evaluator,
0744 final Factory<Genotype<G>> gtf
0745 ) {
0746 _genotypeFactory = requireNonNull(gtf);
0747 _evaluator = requireNonNull(evaluator);
0748 }
0749
0750 /**
0751 * Applies the given {@code setup} recipe to {@code this} engine builder.
0752 *
0753 * @since 6.0
0754 *
0755 * @param setup the setup recipe applying to {@code this} builder
0756 * @return {@code this} builder, for command chaining
0757 * @throws NullPointerException if the {@code setup} is {@code null}.
0758 */
0759 public Builder<G, C> setup(final Setup<G, C> setup) {
0760 setup.apply(this);
0761 return this;
0762 }
0763
0764 /**
0765 * Set the evolution parameters used by the engine.
0766 *
0767 * @since 5.2
0768 *
0769 * @param params the evolution parameter
0770 * @return {@code this} builder, for command chaining
0771 * @throws NullPointerException if the {@code params} is {@code null}.
0772 */
0773 public Builder<G, C> evolutionParams(final EvolutionParams<G, C> params) {
0774 _evolutionParams.evolutionParams(params);
0775 return this;
0776 }
0777
0778 /**
0779 * The selector used for selecting the offspring population. <i>Default
0780 * values is set to {@code TournamentSelector<>(3)}.</i>
0781 *
0782 * @param selector used for selecting the offspring population
0783 * @return {@code this} builder, for command chaining
0784 * @throws NullPointerException if one of the {@code selector} is
0785 * {@code null}.
0786 */
0787 public Builder<G, C> offspringSelector(final Selector<G, C> selector) {
0788 _evolutionParams.offspringSelector(selector);
0789 return this;
0790 }
0791
0792 /**
0793 * The selector used for selecting the survivor population. <i>Default
0794 * values is set to {@code TournamentSelector<>(3)}.</i>
0795 *
0796 * @param selector used for selecting survivor population
0797 * @return {@code this} builder, for command chaining
0798 * @throws NullPointerException if one of the {@code selector} is
0799 * {@code null}.
0800 */
0801 public Builder<G, C> survivorsSelector(final Selector<G, C> selector) {
0802 _evolutionParams.survivorsSelector(selector);
0803 return this;
0804 }
0805
0806 /**
0807 * The selector used for selecting the survivors and offspring
0808 * population. <i>Default values is set to
0809 * {@code TournamentSelector<>(3)}.</i>
0810 *
0811 * @param selector used for selecting survivors and offspring population
0812 * @return {@code this} builder, for command chaining
0813 * @throws NullPointerException if one of the {@code selector} is
0814 * {@code null}.
0815 */
0816 public Builder<G, C> selector(final Selector<G, C> selector) {
0817 _evolutionParams.selector(selector);
0818 return this;
0819 }
0820
0821 /**
0822 * The alterers used for alter the offspring population. <i>Default
0823 * values is set to {@code new SinglePointCrossover<>(0.2)} followed by
0824 * {@code new Mutator<>(0.15)}.</i>
0825 *
0826 * @param first the first alterer used for alter the offspring
0827 * population
0828 * @param rest the rest of the alterers used for alter the offspring
0829 * population
0830 * @return {@code this} builder, for command chaining
0831 * @throws NullPointerException if one of the alterers is {@code null}.
0832 */
0833 @SafeVarargs
0834 public final Builder<G, C> alterers(
0835 final Alterer<G, C> first,
0836 final Alterer<G, C>... rest
0837 ) {
0838 _evolutionParams.alterers(first, rest);
0839 return this;
0840 }
0841
0842 /**
0843 * The phenotype constraint is used for detecting invalid individuals
0844 * and repairing them.
0845 *
0846 * <p><i>Default implementation uses {@code Phenotype::isValid} for
0847 * validating the phenotype.</i></p>
0848 *
0849 * @since 5.0
0850 *
0851 * @param constraint phenotype constraint which can override the default
0852 * implementation the {@link Phenotype#isValid()} method and repairs
0853 * invalid phenotypes when needed.
0854 * @return {@code this} builder, for command chaining
0855 * @throws NullPointerException if one of the {@code constraint} is
0856 * {@code null}.
0857 */
0858 public Builder<G, C> constraint(final Constraint<G, C> constraint) {
0859 _constraint = constraint;
0860 return this;
0861 }
0862
0863 /**
0864 * The optimization strategy used by the engine. <i>Default values is
0865 * set to {@code Optimize.MAXIMUM}.</i>
0866 *
0867 * @param optimize the optimization strategy used by the engine
0868 * @return {@code this} builder, for command chaining
0869 * @throws NullPointerException if one of the {@code optimize} is
0870 * {@code null}.
0871 */
0872 public Builder<G, C> optimize(final Optimize optimize) {
0873 _optimize = requireNonNull(optimize);
0874 return this;
0875 }
0876
0877 /**
0878 * Set to a fitness-maximizing strategy.
0879 *
0880 * @since 3.4
0881 *
0882 * @return {@code this} builder, for command chaining
0883 */
0884 public Builder<G, C> maximizing() {
0885 return optimize(Optimize.MAXIMUM);
0886 }
0887
0888 /**
0889 * Set to a fitness minimizing strategy.
0890 *
0891 * @since 3.4
0892 *
0893 * @return {@code this} builder, for command chaining
0894 */
0895 public Builder<G, C> minimizing() {
0896 return optimize(Optimize.MINIMUM);
0897 }
0898
0899 /**
0900 * The offspring fraction. <i>Default values is set to {@code 0.6}.</i>
0901 * This method call is equivalent to
0902 * {@code survivorsFraction(1 - offspringFraction)} and will override
0903 * any previously set survivors-fraction.
0904 *
0905 * @see #survivorsFraction(double)
0906 *
0907 * @param fraction the offspring fraction
0908 * @return {@code this} builder, for command chaining
0909 * @throws java.lang.IllegalArgumentException if the fraction is not
0910 * within the range [0, 1].
0911 */
0912 public Builder<G, C> offspringFraction(final double fraction) {
0913 _evolutionParams.offspringFraction(fraction);
0914 return this;
0915 }
0916
0917 /**
0918 * The survivor fraction. <i>Default values is set to {@code 0.4}.</i>
0919 * This method call is equivalent to
0920 * {@code offspringFraction(1 - survivorsFraction)} and will override
0921 * any previously set offspring-fraction.
0922 *
0923 * @since 3.8
0924 *
0925 * @see #offspringFraction(double)
0926 *
0927 * @param fraction the survivor fraction
0928 * @return {@code this} builder, for command chaining
0929 * @throws java.lang.IllegalArgumentException if the fraction is not
0930 * within the range [0, 1].
0931 */
0932 public Builder<G, C> survivorsFraction(final double fraction) {
0933 return offspringFraction(1 - fraction);
0934 }
0935
0936 /**
0937 * The number of offspring individuals.
0938 *
0939 * @since 3.8
0940 *
0941 * @param size the number of offspring individuals.
0942 * @return {@code this} builder, for command chaining
0943 * @throws java.lang.IllegalArgumentException if the size is not
0944 * within the range [0, population-size].
0945 */
0946 public Builder<G, C> offspringSize(final int size) {
0947 if (size < 0) {
0948 throw new IllegalArgumentException(format(
0949 "Offspring size must be greater or equal zero, but was %s.",
0950 size
0951 ));
0952 }
0953
0954 return offspringFraction(size/(double)_evolutionParams.populationSize());
0955 }
0956
0957 /**
0958 * The number of survivors.
0959 *
0960 * @since 3.8
0961 *
0962 * @param size the number of survivors.
0963 * @return {@code this} builder, for command chaining
0964 * @throws java.lang.IllegalArgumentException if the size is not
0965 * within the range [0, population-size].
0966 */
0967 public Builder<G, C> survivorsSize(final int size) {
0968 if (size < 0) {
0969 throw new IllegalArgumentException(format(
0970 "Survivors must be greater or equal zero, but was %s.",
0971 size
0972 ));
0973 }
0974
0975 return survivorsFraction(size/(double)_evolutionParams.populationSize());
0976 }
0977
0978 /**
0979 * The number of individuals which form the population. <i>Default
0980 * values is set to {@code 50}.</i>
0981 *
0982 * @param size the number of individuals of a population
0983 * @return {@code this} builder, for command chaining
0984 * @throws java.lang.IllegalArgumentException if {@code size < 1}
0985 */
0986 public Builder<G, C> populationSize(final int size) {
0987 _evolutionParams.populationSize(size);
0988 return this;
0989 }
0990
0991 /**
0992 * The maximal allowed age of a phenotype. <i>Default values is set to
0993 * {@code 70}.</i>
0994 *
0995 * @param age the maximal phenotype age
0996 * @return {@code this} builder, for command chaining
0997 * @throws java.lang.IllegalArgumentException if {@code age < 1}
0998 */
0999 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
|