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