org.jenetics.engine.Engine Maven / Gradle / Ivy
Show all versions of jenetics Show documentation
/*
* Java Genetic Algorithm Library (jenetics-3.9.0).
* Copyright (c) 2007-2017 Franz Wilhelmstötter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author:
* Franz Wilhelmstötter ([email protected])
*/
package org.jenetics.engine;
import static java.lang.Math.round;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static org.jenetics.Population.toPopulation;
import static org.jenetics.internal.util.require.probability;
import java.time.Clock;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.jenetics.internal.util.Concurrency;
import org.jenetics.internal.util.require;
import org.jenetics.Alterer;
import org.jenetics.Chromosome;
import org.jenetics.Gene;
import org.jenetics.Genotype;
import org.jenetics.Mutator;
import org.jenetics.Optimize;
import org.jenetics.Phenotype;
import org.jenetics.Population;
import org.jenetics.Selector;
import org.jenetics.SinglePointCrossover;
import org.jenetics.TournamentSelector;
import org.jenetics.util.Copyable;
import org.jenetics.util.Factory;
import org.jenetics.util.NanoClock;
/**
* Genetic algorithm engine which is the main class. The following
* example shows the main steps in initializing and executing the GA.
*
* {@code
* public class RealFunction {
* // Definition of the fitness function.
* private static Double eval(final Genotype gt) {
* final double x = gt.getGene().doubleValue();
* return cos(0.5 + sin(x))*cos(x);
* }
*
* public static void main(String[] args) {
* // Create/configuring the engine via its builder.
* final Engine engine = Engine
* .builder(
* RealFunction::eval,
* DoubleChromosome.of(0.0, 2.0*PI))
* .populationSize(500)
* .optimize(Optimize.MINIMUM)
* .alterers(
* new Mutator<>(0.03),
* new MeanAlterer<>(0.6))
* .build();
*
* // Execute the GA (engine).
* final Phenotype result = engine.stream()
* // Truncate the evolution stream if no better individual could
* // be found after 5 consecutive generations.
* .limit(bySteadyFitness(5))
* // Terminate the evolution after maximal 100 generations.
* .limit(100)
* .collect(toBestPhenotype());
* }
* }
* }
*
* The architecture allows to decouple the configuration of the engine from the
* execution. The {@code Engine} is configured via the {@code Engine.Builder}
* class and can't be changed after creation. The actual evolution is
* performed by the {@link EvolutionStream}, which is created by the
* {@code Engine}.
*
*
* This class is thread safe:
* No mutable state is maintained by the engine. Therefore it is save to
* create multiple evolution streams with one engine, which may be actually
* used in different threads.
*
*
* @see Engine.Builder
* @see EvolutionStart
* @see EvolutionResult
* @see EvolutionStream
* @see EvolutionStatistics
* @see Codec
*
* @author Franz Wilhelmstötter
* @since 3.0
* @version 3.7
*/
public final class Engine<
G extends Gene, G>,
C extends Comparable super C>
>
implements Function, EvolutionResult>
{
// Problem definition.
private final Function super Genotype, ? extends C> _fitnessFunction;
private final Factory> _genotypeFactory;
// Evolution parameters.
private final Function super C, ? extends C> _fitnessScaler;
private final Selector _survivorsSelector;
private final Selector _offspringSelector;
private final Alterer _alterer;
private final Predicate super Phenotype> _validator;
private final Optimize _optimize;
private final int _offspringCount;
private final int _survivorsCount;
private final long _maximalPhenotypeAge;
// Execution context for concurrent execution of evolving steps.
private final TimedExecutor _executor;
private final Clock _clock;
// Additional parameters.
private final int _individualCreationRetries;
/**
* Create a new GA engine with the given parameters.
*
* @param fitnessFunction the fitness function this GA is using.
* @param genotypeFactory the genotype factory this GA is working with.
* @param fitnessScaler the fitness scaler this GA is using.
* @param survivorsSelector the selector used for selecting the survivors
* @param offspringSelector the selector used for selecting the offspring
* @param alterer the alterer used for altering the offspring
* @param validator phenotype validator which can override the default
* implementation the {@link Phenotype#isValid()} method.
* @param optimize the kind of optimization (minimize or maximize)
* @param offspringCount the number of the offspring individuals
* @param survivorsCount the number of the survivor individuals
* @param maximalPhenotypeAge the maximal age of an individual
* @param executor the executor used for executing the single evolve steps
* @param clock the clock used for calculating the timing results
* @param individualCreationRetries the maximal number of attempts for
* creating a valid individual.
* @throws NullPointerException if one of the arguments is {@code null}
* @throws IllegalArgumentException if the given integer values are smaller
* than one.
*/
Engine(
final Function super Genotype, ? extends C> fitnessFunction,
final Factory> genotypeFactory,
final Function super C, ? extends C> fitnessScaler,
final Selector survivorsSelector,
final Selector offspringSelector,
final Alterer alterer,
final Predicate super Phenotype> validator,
final Optimize optimize,
final int offspringCount,
final int survivorsCount,
final long maximalPhenotypeAge,
final Executor executor,
final Clock clock,
final int individualCreationRetries
) {
_fitnessFunction = requireNonNull(fitnessFunction);
_fitnessScaler = requireNonNull(fitnessScaler);
_genotypeFactory = requireNonNull(genotypeFactory);
_survivorsSelector = requireNonNull(survivorsSelector);
_offspringSelector = requireNonNull(offspringSelector);
_alterer = requireNonNull(alterer);
_validator = requireNonNull(validator);
_optimize = requireNonNull(optimize);
_offspringCount = require.nonNegative(offspringCount);
_survivorsCount = require.nonNegative(survivorsCount);
_maximalPhenotypeAge = require.positive(maximalPhenotypeAge);
_executor = new TimedExecutor(requireNonNull(executor));
_clock = requireNonNull(clock);
if (individualCreationRetries < 0) {
throw new IllegalArgumentException(format(
"Retry count must not be negative: %d",
individualCreationRetries
));
}
_individualCreationRetries = individualCreationRetries;
}
/**
* Perform one evolution step with the given {@code population} and
* {@code generation}. New phenotypes are created with the fitness function
* and fitness scaler defined by this engine
*
* This method is thread-safe.
*
* @see #evolve(EvolutionStart)
*
* @param population the population to evolve
* @param generation the current generation; used for calculating the
* phenotype age.
* @return the evolution result
* @throws java.lang.NullPointerException if the given {@code population} is
* {@code null}
* @throws IllegalArgumentException if the given {@code generation} is
* smaller then one
*/
public EvolutionResult evolve(
final Population population,
final long generation
) {
return evolve(EvolutionStart.of(population, generation));
}
/**
* Perform one evolution step with the given evolution {@code start} object
* New phenotypes are created with the fitness function and fitness scaler
* defined by this engine
*
* This method is thread-safe.
*
* @since 3.1
* @see #evolve(org.jenetics.Population, long)
*
* @param start the evolution start object
* @return the evolution result
* @throws java.lang.NullPointerException if the given evolution
* {@code start} is {@code null}
*/
public EvolutionResult evolve(final EvolutionStart start) {
final Timer timer = Timer.of(_clock).start();
final Population startPopulation = start.getPopulation();
// Initial evaluation of the population.
final Timer evaluateTimer = Timer.of(_clock).start();
evaluate(startPopulation);
evaluateTimer.stop();
// Select the offspring population.
final CompletableFuture>> offspring =
_executor.async(() ->
selectOffspring(startPopulation),
_clock
);
// Select the survivor population.
final CompletableFuture>> survivors =
_executor.async(() ->
selectSurvivors(startPopulation),
_clock
);
// Altering the offspring population.
final CompletableFuture>> alteredOffspring =
_executor.thenApply(offspring, p ->
alter(p.result, start.getGeneration()),
_clock
);
// Filter and replace invalid and old survivor individuals.
final CompletableFuture>> filteredSurvivors =
_executor.thenApply(survivors, pop ->
filter(pop.result, start.getGeneration()),
_clock
);
// Filter and replace invalid and old offspring individuals.
final CompletableFuture>> filteredOffspring =
_executor.thenApply(alteredOffspring, pop ->
filter(pop.result.population, start.getGeneration()),
_clock
);
// Combining survivors and offspring to the new population.
final CompletableFuture> population =
filteredSurvivors.thenCombineAsync(filteredOffspring, (s, o) -> {
final Population pop = new Population<>(
s.result.population.size() +
o.result.population.size()
);
pop.addAll(s.result.population);
pop.addAll(o.result.population);
return pop;
},
_executor.get()
);
// Evaluate the fitness-function and wait for result.
final Population pop = population.join();
final TimedResult> result = TimedResult
.of(() -> evaluate(pop), _clock)
.get();
final EvolutionDurations durations = EvolutionDurations.of(
offspring.join().duration,
survivors.join().duration,
alteredOffspring.join().duration,
filteredOffspring.join().duration,
filteredSurvivors.join().duration,
result.duration.plus(evaluateTimer.getTime()),
timer.stop().getTime()
);
final int killCount =
filteredOffspring.join().result.killCount +
filteredSurvivors.join().result.killCount;
final int invalidCount =
filteredOffspring.join().result.invalidCount +
filteredSurvivors.join().result.invalidCount;
return EvolutionResult.of(
_optimize,
result.result,
start.getGeneration(),
durations,
killCount,
invalidCount,
alteredOffspring.join().result.alterCount
);
}
/**
* This method is an alias for the {@link #evolve(EvolutionStart)}
* method.
*
* @since 3.1
*/
@Override
public EvolutionResult apply(final EvolutionStart start) {
return evolve(start);
}
// Selects the survivors population. A new population object is returned.
private Population selectSurvivors(final Population population) {
return _survivorsCount > 0
?_survivorsSelector.select(population, _survivorsCount, _optimize)
: Population.empty();
}
// Selects the offspring population. A new population object is returned.
private Population selectOffspring(final Population population) {
return _offspringCount > 0
? _offspringSelector.select(population, _offspringCount, _optimize)
: Population.empty();
}
// Filters out invalid and old individuals. Filtering is done in place.
private FilterResult filter(
final Population population,
final long generation
) {
int killCount = 0;
int invalidCount = 0;
for (int i = 0, n = population.size(); i < n; ++i) {
final Phenotype individual = population.get(i);
if (!_validator.test(individual)) {
population.set(i, newPhenotype(generation));
++invalidCount;
} else if (individual.getAge(generation) > _maximalPhenotypeAge) {
population.set(i, newPhenotype(generation));
++killCount;
}
}
return new FilterResult<>(population, killCount, invalidCount);
}
// Create a new and valid phenotype
private Phenotype newPhenotype(final long generation) {
int count = 0;
Phenotype phenotype;
do {
phenotype = Phenotype.of(
_genotypeFactory.newInstance(),
generation,
_fitnessFunction,
_fitnessScaler
);
} while (++count < _individualCreationRetries &&
!_validator.test(phenotype));
return phenotype;
}
// Alters the given population. The altering is done in place.
private AlterResult alter(
final Population population,
final long generation
) {
return new AlterResult<>(
population,
_alterer.alter(population, generation)
);
}
// Evaluates the fitness function of the give population concurrently.
private Population evaluate(final Population population) {
try (Concurrency c = Concurrency.with(_executor.get())) {
c.execute(population);
}
return population;
}
/* *************************************************************************
* Evolution Stream/Iterator creation.
**************************************************************************/
/**
* Create a new infinite evolution iterator with a newly created
* population. This is an alternative way for evolution. It lets the user
* start, stop and resume the evolution process whenever desired.
*
* @return a new infinite evolution iterator
*/
public Iterator> iterator() {
return new EvolutionIterator<>(
this::evolutionStart,
this::evolve
);
}
/**
* Create a new infinite evolution stream with a newly created
* population.
*
* @return a new evolution stream.
*/
public EvolutionStream stream() {
return EvolutionStream.of(this::evolutionStart, this::evolve);
}
private EvolutionStart evolutionStart() {
final int generation = 1;
final int size = _offspringCount + _survivorsCount;
final Population population = new Population(size)
.fill(() -> newPhenotype(generation), size);
return EvolutionStart.of(population, generation);
}
/**
* Create a new infinite evolution iterator with the given initial
* individuals. If an empty {@code Iterable} is given, the engines genotype
* factory is used for creating the population.
*
* @param genotypes the initial individuals used for the evolution iterator.
* Missing individuals are created and individuals not needed are
* skipped.
* @return a new infinite evolution iterator
* @throws java.lang.NullPointerException if the given {@code genotypes} is
* {@code null}.
*/
public Iterator> iterator(
final Iterable> genotypes
) {
requireNonNull(genotypes);
return new EvolutionIterator<>(
() -> evolutionStart(genotypes, 1),
this::evolve
);
}
/**
* Create a new infinite evolution stream with the given initial
* individuals. If an empty {@code Iterable} is given, the engines genotype
* factory is used for creating the population.
*
* @since 3.7
*
* @param genotypes the initial individuals used for the evolution stream.
* Missing individuals are created and individuals not needed are
* skipped.
* @return a new evolution stream.
* @throws java.lang.NullPointerException if the given {@code genotypes} is
* {@code null}.
*/
public EvolutionStream stream(final Iterable> genotypes) {
requireNonNull(genotypes);
return EvolutionStream.of(
() -> evolutionStart(genotypes, 1),
this::evolve
);
}
private EvolutionStart evolutionStart(
final Iterable> genotypes,
final long generation
) {
final Stream> stream = Stream.concat(
StreamSupport.stream(genotypes.spliterator(), false)
.map(gt -> Phenotype.of(
gt, generation, _fitnessFunction, _fitnessScaler)),
Stream.generate(() -> newPhenotype(generation))
);
final Population population = stream
.limit(getPopulationSize())
.collect(toPopulation());
return EvolutionStart.of(population, generation);
}
/**
* Create a new infinite evolution iterator with the given initial
* individuals. If an empty {@code Iterable} is given, the engines genotype
* factory is used for creating the population.
*
* @since 3.7
*
* @param genotypes the initial individuals used for the evolution iterator.
* Missing individuals are created and individuals not needed are
* skipped.
* @param generation the generation the stream starts from; must be greater
* than zero.
* @return a new infinite evolution iterator
* @throws java.lang.NullPointerException if the given {@code genotypes} is
* {@code null}.
* @throws IllegalArgumentException if the given {@code generation} is
* smaller then one
*/
public Iterator> iterator(
final Iterable> genotypes,
final long generation
) {
requireNonNull(genotypes);
require.positive(generation);
return new EvolutionIterator<>(
() -> evolutionStart(genotypes, generation),
this::evolve
);
}
/**
* Create a new infinite evolution stream with the given initial
* individuals. If an empty {@code Iterable} is given, the engines genotype
* factory is used for creating the population.
*
* @since 3.7
*
* @param genotypes the initial individuals used for the evolution stream.
* Missing individuals are created and individuals not needed are
* skipped.
* @param generation the generation the stream starts from; must be greater
* than zero.
* @return a new evolution stream.
* @throws java.lang.NullPointerException if the given {@code genotypes} is
* {@code null}.
* @throws IllegalArgumentException if the given {@code generation} is
* smaller then one
*/
public EvolutionStream stream(
final Iterable> genotypes,
final long generation
) {
requireNonNull(genotypes);
return EvolutionStream.of(
() -> evolutionStart(genotypes, generation),
this::evolve
);
}
/**
* Create a new infinite evolution iterator with the given initial
* population. If an empty {@code Population} is given, the engines genotype
* factory is used for creating the population. The given population might
* be the result of an other engine and this method allows to start the
* evolution with the outcome of an different engine. The fitness function
* and the fitness scaler are replaced by the one defined for this engine.
*
* @since 3.7
*
* @param population the initial individuals used for the evolution iterator.
* Missing individuals are created and individuals not needed are
* skipped.
* @return a new infinite evolution iterator
* @throws java.lang.NullPointerException if the given {@code population} is
* {@code null}.
*/
public Iterator> iterator(
final Population population
) {
requireNonNull(population);
return new EvolutionIterator<>(
() -> evolutionStart(population, 1),
this::evolve
);
}
/**
* Create a new infinite evolution stream with the given initial
* population. If an empty {@code Population} is given, the engines genotype
* factory is used for creating the population. The given population might
* be the result of an other engine and this method allows to start the
* evolution with the outcome of an different engine. The fitness function
* and the fitness scaler are replaced by the one defined for this engine.
*
* @param population the initial individuals used for the evolution stream.
* Missing individuals are created and individuals not needed are
* skipped.
* @return a new evolution stream.
* @throws java.lang.NullPointerException if the given {@code population} is
* {@code null}.
*/
public EvolutionStream stream(
final Population population
) {
requireNonNull(population);
return EvolutionStream.of(
() -> evolutionStart(population, 1),
this::evolve
);
}
private EvolutionStart evolutionStart(
final Population population,
final long generation
) {
final Stream> stream = Stream.concat(
population.stream()
.map(p -> p.newInstance(
p.getGeneration(),
_fitnessFunction,
_fitnessScaler)),
Stream.generate(() -> newPhenotype(generation))
);
final Population pop = stream
.limit(getPopulationSize())
.collect(toPopulation());
return EvolutionStart.of(pop, generation);
}
/**
* Create a new infinite evolution iterator with the given initial
* population. If an empty {@code Population} is given, the engines genotype
* factory is used for creating the population. The given population might
* be the result of an other engine and this method allows to start the
* evolution with the outcome of an different engine. The fitness function
* and the fitness scaler are replaced by the one defined for this engine.
*
* @param population the initial individuals used for the evolution iterator.
* Missing individuals are created and individuals not needed are
* skipped.
* @param generation the generation the iterator starts from; must be greater
* than zero.
* @return a new infinite evolution iterator
* @throws java.lang.NullPointerException if the given {@code population} is
* {@code null}.
* @throws IllegalArgumentException if the given {@code generation} is smaller
* then one
*/
public Iterator> iterator(
final Population population,
final long generation
) {
requireNonNull(population);
require.positive(generation);
return new EvolutionIterator<>(
() -> evolutionStart(population, generation),
this::evolve
);
}
/**
* Create a new infinite evolution stream with the given initial
* population. If an empty {@code Population} is given, the engines genotype
* factory is used for creating the population. The given population might
* be the result of an other engine and this method allows to start the
* evolution with the outcome of an different engine. The fitness function
* and the fitness scaler are replaced by the one defined for this engine.
*
* @param population the initial individuals used for the evolution stream.
* Missing individuals are created and individuals not needed are
* skipped.
* @param generation the generation the stream starts from; must be greater
* than zero.
* @return a new evolution stream.
* @throws java.lang.NullPointerException if the given {@code population} is
* {@code null}.
* @throws IllegalArgumentException if the given {@code generation} is
* smaller then one
*/
public EvolutionStream stream(
final Population population,
final long generation
) {
requireNonNull(population);
require.positive(generation);
return EvolutionStream.of(
() -> evolutionStart(population, generation),
this::evolve
);
}
/**
* Create a new infinite evolution iterator starting with a
* previously evolved {@link EvolutionResult}. The iterator is initialized
* with the population of the given {@code result} and its total generation
* {@link EvolutionResult#getTotalGenerations()}.
*
* @since 3.7
*
* @param result the previously evolved {@code EvolutionResult}
* @return a new evolution stream, which continues a previous one
* @throws NullPointerException if the given evolution {@code result} is
* {@code null}
*/
public Iterator> iterator(
final EvolutionResult result
) {
return iterator(result.getPopulation(), result.getTotalGenerations());
}
/**
* Create a new {@code EvolutionStream} starting with a previously evolved
* {@link EvolutionResult}. The stream is initialized with the population
* of the given {@code result} and its total generation
* {@link EvolutionResult#getTotalGenerations()}.
*
* {@code
* private static final Problem
* PROBLEM = Problem.of(
* x -> cos(0.5 + sin(x))*cos(x),
* codecs.ofScalar(DoubleRange.of(0.0, 2.0*PI))
* );
*
* private static final Engine
* ENGINE = Engine.builder(PROBLEM)
* .optimize(Optimize.MINIMUM)
* .offspringSelector(new RouletteWheelSelector<>())
* .build();
*
* public static void main(final String[] args) throws IOException {
* // Result of the first evolution run.
* final EvolutionResult rescue = ENGINE.stream()
* .limit(limit.bySteadyFitness(10))
* .collect(EvolutionResult.toBestEvolutionResult());
*
* // Save the result of the first run into a file.
* final Path path = Paths.get("result.bin");
* IO.object.write(rescue, path);
*
* // Load the previous result and continue evolution.
* \@SuppressWarnings("unchecked")
* final EvolutionResult result = ENGINE
* .stream((EvolutionResult)IO.object.read(path))
* .limit(limit.bySteadyFitness(20))
* .collect(EvolutionResult.toBestEvolutionResult());
*
* System.out.println(result.getBestPhenotype());
* }
* }
*
* The example above shows how to save an {@link EvolutionResult} from a
* first run, save it to disk and continue the evolution.
*
* @since 3.7
*
* @param result the previously evolved {@code EvolutionResult}
* @return a new evolution stream, which continues a previous one
* @throws NullPointerException if the given evolution {@code result} is
* {@code null}
*/
public EvolutionStream stream(final EvolutionResult result) {
return stream(result.getPopulation(), result.getTotalGenerations());
}
/* *************************************************************************
* Property access methods.
**************************************************************************/
/**
* Return the fitness function of the GA engine.
*
* @return the fitness function
*/
public Function super Genotype, ? extends C> getFitnessFunction() {
return _fitnessFunction;
}
/**
* Return the fitness scaler of the GA engine.
*
* @return the fitness scaler
*/
public Function super C, ? extends C> getFitnessScaler() {
return _fitnessScaler;
}
/**
* Return the used genotype {@link Factory} of the GA. The genotype factory
* is used for creating the initial population and new, random individuals
* when needed (as replacement for invalid and/or died genotypes).
*
* @return the used genotype {@link Factory} of the GA.
*/
public Factory> getGenotypeFactory() {
return _genotypeFactory;
}
/**
* Return the used survivor {@link Selector} of the GA.
*
* @return the used survivor {@link Selector} of the GA.
*/
public Selector getSurvivorsSelector() {
return _survivorsSelector;
}
/**
* Return the used offspring {@link Selector} of the GA.
*
* @return the used offspring {@link Selector} of the GA.
*/
public Selector getOffspringSelector() {
return _offspringSelector;
}
/**
* Return the used {@link Alterer} of the GA.
*
* @return the used {@link Alterer} of the GA.
*/
public Alterer getAlterer() {
return _alterer;
}
/**
* Return the number of selected offsprings.
*
* @return the number of selected offsprings
*/
public int getOffspringCount() {
return _offspringCount;
}
/**
* The number of selected survivors.
*
* @return the number of selected survivors
*/
public int getSurvivorsCount() {
return _survivorsCount;
}
/**
* Return the number of individuals of a population.
*
* @return the number of individuals of a population
*/
public int getPopulationSize() {
return _offspringCount + _survivorsCount;
}
/**
* Return the maximal allowed phenotype age.
*
* @return the maximal allowed phenotype age
*/
public long getMaximalPhenotypeAge() {
return _maximalPhenotypeAge;
}
/**
* Return the optimization strategy.
*
* @return the optimization strategy
*/
public Optimize getOptimize() {
return _optimize;
}
/**
* Return the {@link Clock} the engine is using for measuring the execution
* time.
*
* @return the clock used for measuring the execution time
*/
public Clock getClock() {
return _clock;
}
/**
* Return the {@link Executor} the engine is using for executing the
* evolution steps.
*
* @return the executor used for performing the evolution steps
*/
public Executor getExecutor() {
return _executor.get();
}
/* *************************************************************************
* Builder methods.
**************************************************************************/
/**
* Create a new evolution {@code Engine.Builder} initialized with the values
* of the current evolution {@code Engine}. With this method, the evolution
* engine can serve as a template for a new one.
*
* @return a new engine builder
*/
public Builder builder() {
return new Builder(_genotypeFactory, _fitnessFunction)
.alterers(_alterer)
.clock(_clock)
.executor(_executor.get())
.fitnessScaler(_fitnessScaler)
.maximalPhenotypeAge(_maximalPhenotypeAge)
.offspringFraction((double)_offspringCount/(double)getPopulationSize())
.offspringSelector(_offspringSelector)
.optimize(_optimize)
.phenotypeValidator(_validator)
.populationSize(getPopulationSize())
.survivorsSelector(_survivorsSelector)
.individualCreationRetries(_individualCreationRetries);
}
/**
* Create a new evolution {@code Engine.Builder} for the given
* {@link Problem}.
*
* @since 3.4
*
* @param problem the problem to be solved by the evolution {@code Engine}
* @param the (native) argument type of the problem fitness function
* @param the gene type the evolution engine is working with
* @param the result type of the fitness function
* @return Create a new evolution {@code Engine.Builder}
*/
public static , C extends Comparable super C>>
Builder builder(final Problem problem) {
return builder(problem.fitness(), problem.codec());
}
/**
* Create a new evolution {@code Engine.Builder} with the given fitness
* function and genotype factory.
*
* @param ff the fitness function
* @param genotypeFactory the genotype factory
* @param the gene type
* @param the fitness function result type
* @return a new engine builder
* @throws java.lang.NullPointerException if one of the arguments is
* {@code null}.
*/
public static , C extends Comparable super C>>
Builder builder(
final Function super Genotype, ? extends C> ff,
final Factory> genotypeFactory
) {
return new Builder<>(genotypeFactory, ff);
}
/**
* Create a new evolution {@code Engine.Builder} with the given fitness
* function and chromosome templates.
*
* @param ff the fitness function
* @param chromosome the first chromosome
* @param chromosomes the chromosome templates
* @param the gene type
* @param the fitness function result type
* @return a new engine builder
* @throws java.lang.NullPointerException if one of the arguments is
* {@code null}.
*/
@SafeVarargs
public static , C extends Comparable super C>>
Builder builder(
final Function super Genotype, ? extends C> ff,
final Chromosome chromosome,
final Chromosome... chromosomes
) {
return new Builder<>(Genotype.of(chromosome, chromosomes), ff);
}
/**
* Create a new evolution {@code Engine.Builder} with the given fitness
* function and problem {@code codec}.
*
* @since 3.2
*
* @param ff the fitness function
* @param codec the problem codec
* @param the fitness function input type
* @param the fitness function result type
* @param the gene type
* @return a new engine builder
* @throws java.lang.NullPointerException if one of the arguments is
* {@code null}.
*/
public static , C extends Comparable super C>>
Builder builder(
final Function super T, ? extends C> ff,
final Codec codec
) {
return builder(ff.compose(codec.decoder()), codec.encoding());
}
/* *************************************************************************
* Inner classes
**************************************************************************/
/**
* Builder class for building GA {@code Engine} instances.
*
* @see Engine
*
* @author Franz Wilhelmstötter
* @since 3.0
* @version 3.8
*/
public static final class Builder<
G extends Gene, G>,
C extends Comparable super C>
>
implements Copyable>
{
// No default values for this properties.
private Function super Genotype, ? extends C> _fitnessFunction;
private Factory> _genotypeFactory;
// This are the properties which default values.
private Function super C, ? extends C> _fitnessScaler = a -> a;
private Selector _survivorsSelector = new TournamentSelector<>(3);
private Selector _offspringSelector = new TournamentSelector<>(3);
private Alterer _alterer = Alterer.of(
new SinglePointCrossover(0.2),
new Mutator<>(0.15)
);
private Predicate super Phenotype> _validator = Phenotype::isValid;
private Optimize _optimize = Optimize.MAXIMUM;
private double _offspringFraction = 0.6;
private int _populationSize = 50;
private long _maximalPhenotypeAge = 70;
// Engine execution environment.
private Executor _executor = ForkJoinPool.commonPool();
private Clock _clock = NanoClock.systemUTC();
private int _individualCreationRetries = 10;
private Builder(
final Factory> genotypeFactory,
final Function super Genotype, ? extends C> fitnessFunction
) {
_genotypeFactory = requireNonNull(genotypeFactory);
_fitnessFunction = requireNonNull(fitnessFunction);
}
/**
* Set the fitness function of the evolution {@code Engine}.
*
* @param function the fitness function to use in the GA {@code Engine}
* @return {@code this} builder, for command chaining
*/
public Builder fitnessFunction(
Function super Genotype, ? extends C> function
) {
_fitnessFunction = requireNonNull(function);
return this;
}
/**
* Set the fitness scaler of the evolution {@code Engine}. Default
* value is set to the identity function.
*
* @param scaler the fitness scale to use in the GA {@code Engine}
* @return {@code this} builder, for command chaining
*/
public Builder fitnessScaler(
final Function super C, ? extends C> scaler
) {
_fitnessScaler = requireNonNull(scaler);
return this;
}
/**
* The genotype factory used for creating new individuals.
*
* @param genotypeFactory the genotype factory for creating new
* individuals.
* @return {@code this} builder, for command chaining
*/
public Builder genotypeFactory(
final Factory> genotypeFactory
) {
_genotypeFactory = requireNonNull(genotypeFactory);
return this;
}
/**
* The selector used for selecting the offspring population. Default
* values is set to {@code TournamentSelector<>(3)}.
*
* @param selector used for selecting the offspring population
* @return {@code this} builder, for command chaining
*/
public Builder offspringSelector(
final Selector selector
) {
_offspringSelector = requireNonNull(selector);
return this;
}
/**
* The selector used for selecting the survivors population. Default
* values is set to {@code TournamentSelector<>(3)}.
*
* @param selector used for selecting survivors population
* @return {@code this} builder, for command chaining
*/
public Builder survivorsSelector(
final Selector selector
) {
_survivorsSelector = requireNonNull(selector);
return this;
}
/**
* The selector used for selecting the survivors and offspring
* population. Default values is set to
* {@code TournamentSelector<>(3)}.
*
* @param selector used for selecting survivors and offspring population
* @return {@code this} builder, for command chaining
*/
public Builder selector(final Selector selector) {
_offspringSelector = requireNonNull(selector);
_survivorsSelector = requireNonNull(selector);
return this;
}
/**
* The alterers used for alter the offspring population. Default
* values is set to {@code new SinglePointCrossover<>(0.2)} followed by
* {@code new Mutator<>(0.15)}.
*
* @param first the first alterer used for alter the offspring
* population
* @param rest the rest of the alterers used for alter the offspring
* population
* @return {@code this} builder, for command chaining
* @throws java.lang.NullPointerException if one of the alterers is
* {@code null}.
*/
@SafeVarargs
public final Builder alterers(
final Alterer first,
final Alterer... rest
) {
requireNonNull(first);
Stream.of(rest).forEach(Objects::requireNonNull);
_alterer = rest.length == 0 ?
first :
Alterer.of(rest).compose(first);
return this;
}
/**
* The phenotype validator used for detecting invalid individuals.
* Alternatively it is also possible to set the genotype validator with
* {@link #genotypeFactory(Factory)}, which will replace any
* previously set phenotype validators.
*
* Default value is set to {@code Phenotype::isValid}.
*
* @since 3.1
*
* @see #genotypeValidator(Predicate)
*
* @param validator the {@code validator} used for validating the
* individuals (phenotypes).
* @return {@code this} builder, for command chaining
* @throws java.lang.NullPointerException if the {@code validator} is
* {@code null}.
*/
public Builder phenotypeValidator(
final Predicate super Phenotype> validator
) {
_validator = requireNonNull(validator);
return this;
}
/**
* The genotype validator used for detecting invalid individuals.
* Alternatively it is also possible to set the phenotype validator with
* {@link #phenotypeValidator(Predicate)}, which will replace any
* previously set genotype validators.
*
* Default value is set to {@code Genotype::isValid}.
*
* @since 3.1
*
* @see #phenotypeValidator(Predicate)
*
* @param validator the {@code validator} used for validating the
* individuals (genotypes).
* @return {@code this} builder, for command chaining
* @throws java.lang.NullPointerException if the {@code validator} is
* {@code null}.
*/
public Builder genotypeValidator(
final Predicate super Genotype> validator
) {
requireNonNull(validator);
_validator = pt -> validator.test(pt.getGenotype());
return this;
}
/**
* The optimization strategy used by the engine. Default values is
* set to {@code Optimize.MAXIMUM}.
*
* @param optimize the optimization strategy used by the engine
* @return {@code this} builder, for command chaining
*/
public Builder optimize(final Optimize optimize) {
_optimize = requireNonNull(optimize);
return this;
}
/**
* Set to a fitness maximizing strategy.
*
* @since 3.4
*
* @return {@code this} builder, for command chaining
*/
public Builder maximizing() {
return optimize(Optimize.MAXIMUM);
}
/**
* Set to a fitness minimizing strategy.
*
* @since 3.4
*
* @return {@code this} builder, for command chaining
*/
public Builder minimizing() {
return optimize(Optimize.MINIMUM);
}
/**
* The offspring fraction. Default values is set to {@code 0.6}.
* This method call is equivalent to
* {@code survivorsFraction(1 - offspringFraction)} and will override
* any previously set survivors-fraction.
*
* @see #survivorsFraction(double)
*
* @param fraction the offspring fraction
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if the fraction is not
* within the range [0, 1].
*/
public Builder offspringFraction(final double fraction) {
_offspringFraction = probability(fraction);
return this;
}
/**
* The survivors fraction. Default values is set to {@code 0.4}.
* This method call is equivalent to
* {@code offspringFraction(1 - survivorsFraction)} and will override
* any previously set offspring-fraction.
*
* @since 3.8
*
* @see #offspringFraction(double)
*
* @param fraction the survivors fraction
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if the fraction is not
* within the range [0, 1].
*/
public Builder survivorsFraction(final double fraction) {
_offspringFraction = 1.0 - probability(fraction);
return this;
}
/**
* The number of offspring individuals.
*
* @since 3.8
*
* @param size the number of offspring individuals.
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if the size is not
* within the range [0, population-size].
*/
public Builder offspringSize(final int size) {
if (size < 0) {
throw new IllegalArgumentException(format(
"Offspring size must be greater or equal zero, but was %s.",
size
));
}
return offspringFraction((double)size/(double)_populationSize);
}
/**
* The number of survivors.
*
* @since 3.8
*
* @param size the number of survivors.
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if the size is not
* within the range [0, population-size].
*/
public Builder survivorsSize(final int size) {
if (size < 0) {
throw new IllegalArgumentException(format(
"Survivors must be greater or equal zero, but was %s.",
size
));
}
return survivorsFraction((double)size/(double)_populationSize);
}
/**
* The number of individuals which form the population. Default
* values is set to {@code 50}.
*
* @param size the number of individuals of a population
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if {@code size < 1}
*/
public Builder populationSize(final int size) {
if (size < 1) {
throw new IllegalArgumentException(format(
"Population size must be greater than zero, but was %s.",
size
));
}
_populationSize = size;
return this;
}
/**
* The maximal allowed age of a phenotype. Default values is set to
* {@code 70}.
*
* @param age the maximal phenotype age
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if {@code age < 1}
*/
public Builder maximalPhenotypeAge(final long age) {
if (age < 1) {
throw new IllegalArgumentException(format(
"Phenotype age must be greater than one, but was %s.", age
));
}
_maximalPhenotypeAge = age;
return this;
}
/**
* The executor used by the engine.
*
* @param executor the executor used by the engine
* @return {@code this} builder, for command chaining
*/
public Builder executor(final Executor executor) {
_executor = requireNonNull(executor);
return this;
}
/**
* The clock used for calculating the execution durations.
*
* @param clock the clock used for calculating the execution durations
* @return {@code this} builder, for command chaining
*/
public Builder clock(final Clock clock) {
_clock = requireNonNull(clock);
return this;
}
/**
* The maximal number of attempt before the {@code Engine} gives up
* creating a valid individual ({@code Phenotype}). Default values is
* set to {@code 10}.
*
* @since 3.1
*
* @param retries the maximal retry count
* @throws IllegalArgumentException if the given retry {@code count} is
* smaller than zero.
* @return {@code this} builder, for command chaining
*/
public Builder individualCreationRetries(final int retries) {
if (retries < 0) {
throw new IllegalArgumentException(format(
"Retry count must not be negative: %d",
retries
));
}
_individualCreationRetries = retries;
return this;
}
/**
* Builds an new {@code Engine} instance from the set properties.
*
* @return an new {@code Engine} instance from the set properties
*/
public Engine build() {
return new Engine<>(
_fitnessFunction,
_genotypeFactory,
_fitnessScaler,
_survivorsSelector,
_offspringSelector,
_alterer,
_validator,
_optimize,
getOffspringCount(),
getSurvivorsCount(),
_maximalPhenotypeAge,
_executor,
_clock,
_individualCreationRetries
);
}
private int getSurvivorsCount() {
return _populationSize - getOffspringCount();
}
private int getOffspringCount() {
return (int)round(_offspringFraction*_populationSize);
}
/**
* Return the used {@link Alterer} of the GA.
*
* @return the used {@link Alterer} of the GA.
*/
public Alterer getAlterers() {
return _alterer;
}
/**
* Return the {@link Clock} the engine is using for measuring the execution
* time.
*
* @since 3.1
*
* @return the clock used for measuring the execution time
*/
public Clock getClock() {
return _clock;
}
/**
* Return the {@link Executor} the engine is using for executing the
* evolution steps.
*
* @since 3.1
*
* @return the executor used for performing the evolution steps
*/
public Executor getExecutor() {
return _executor;
}
/**
* Return the fitness function of the GA engine.
*
* @since 3.1
*
* @return the fitness function
*/
public Function super Genotype, ? extends C> getFitnessFunction() {
return _fitnessFunction;
}
/**
* Return the fitness scaler of the GA engine.
*
* @since 3.1
*
* @return the fitness scaler
*/
public Function super C, ? extends C> getFitnessScaler() {
return _fitnessScaler;
}
/**
* Return the used genotype {@link Factory} of the GA. The genotype factory
* is used for creating the initial population and new, random individuals
* when needed (as replacement for invalid and/or died genotypes).
*
* @since 3.1
*
* @return the used genotype {@link Factory} of the GA.
*/
public Factory> getGenotypeFactory() {
return _genotypeFactory;
}
/**
* Return the maximal allowed phenotype age.
*
* @since 3.1
*
* @return the maximal allowed phenotype age
*/
public long getMaximalPhenotypeAge() {
return _maximalPhenotypeAge;
}
/**
* Return the offspring fraction.
*
* @return the offspring fraction.
*/
public double getOffspringFraction() {
return _offspringFraction;
}
/**
* Return the used offspring {@link Selector} of the GA.
*
* @since 3.1
*
* @return the used offspring {@link Selector} of the GA.
*/
public Selector getOffspringSelector() {
return _offspringSelector;
}
/**
* Return the used survivor {@link Selector} of the GA.
*
* @since 3.1
*
* @return the used survivor {@link Selector} of the GA.
*/
public Selector getSurvivorsSelector() {
return _survivorsSelector;
}
/**
* Return the optimization strategy.
*
* @since 3.1
*
* @return the optimization strategy
*/
public Optimize getOptimize() {
return _optimize;
}
/**
* Return the number of individuals of a population.
*
* @since 3.1
*
* @return the number of individuals of a population
*/
public int getPopulationSize() {
return _populationSize;
}
/**
* Return the maximal number of attempt before the {@code Engine} gives
* up creating a valid individual ({@code Phenotype}).
*
* @since 3.1
*
* @return the maximal number of {@code Phenotype} creation attempts
*/
public int getIndividualCreationRetries() {
return _individualCreationRetries;
}
/**
* Create a new builder, with the current configuration.
*
* @since 3.1
*
* @return a new builder, with the current configuration
*/
@Override
public Builder copy() {
return new Builder(_genotypeFactory, _fitnessFunction)
.alterers(_alterer)
.clock(_clock)
.executor(_executor)
.fitnessScaler(_fitnessScaler)
.maximalPhenotypeAge(_maximalPhenotypeAge)
.offspringFraction(_offspringFraction)
.offspringSelector(_offspringSelector)
.phenotypeValidator(_validator)
.optimize(_optimize)
.populationSize(_populationSize)
.survivorsSelector(_survivorsSelector)
.individualCreationRetries(_individualCreationRetries);
}
}
}