MATH-1618: Make "stopping condition" a "BiPredicate".

Second argument is the generation count (as determined by the caller).
This commit is contained in:
Gilles Sadowski 2022-06-12 02:41:56 +02:00
parent f0fe9ab8eb
commit 383256f8cf
3 changed files with 27 additions and 17 deletions

View File

@ -23,7 +23,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.ToDoubleFunction; import java.util.function.ToDoubleFunction;
import java.util.function.Predicate; import java.util.function.BiPredicate;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -100,7 +100,7 @@ public final class MathFunctionOptimizer2 {
}; };
// Stopping condition (not thread-safe). // Stopping condition (not thread-safe).
final Predicate<Population<Chromosome, Coordinates>> stop = final BiPredicate<Population<Chromosome, Coordinates>, Integer> stop =
new UnchangedFitness(UnchangedFitness.Type.BEST, new UnchangedFitness(UnchangedFitness.Type.BEST,
numGeneration); numGeneration);

View File

@ -23,7 +23,7 @@ import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.BiPredicate;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.apache.commons.rng.simple.RandomSource; import org.apache.commons.rng.simple.RandomSource;
@ -43,7 +43,7 @@ public final class GeneticAlgorithmFactory<G, P> implements Callable<Population<
/** Genotype to phenotype converter. */ /** Genotype to phenotype converter. */
private final Function<G, P> decoder; private final Function<G, P> decoder;
/** Criterion for stopping the evolution. */ /** Criterion for stopping the evolution. */
private final Predicate<Population<G, P>> stop; private final BiPredicate<Population<G, P>, Integer> stop;
/** Fitness calculator. */ /** Fitness calculator. */
private final FitnessService<G, P> fitness; private final FitnessService<G, P> fitness;
/** Chromosome selector. */ /** Chromosome selector. */
@ -71,7 +71,7 @@ public final class GeneticAlgorithmFactory<G, P> implements Callable<Population<
*/ */
private GeneticAlgorithmFactory(Collection<G> init, private GeneticAlgorithmFactory(Collection<G> init,
Function<G, P> decoder, Function<G, P> decoder,
Predicate<Population<G, P>> stop, BiPredicate<Population<G, P>, Integer> stop,
FitnessService<G, P> fitness, FitnessService<G, P> fitness,
Selection<G, P> selection, Selection<G, P> selection,
Map<GeneticOperator<G>, ApplicationRate> operators, Map<GeneticOperator<G>, ApplicationRate> operators,
@ -107,7 +107,7 @@ public final class GeneticAlgorithmFactory<G, P> implements Callable<Population<
*/ */
public static <G, P> Callable<Population<G, P>> create(Collection<G> init, public static <G, P> Callable<Population<G, P>> create(Collection<G> init,
Function<G, P> decoder, Function<G, P> decoder,
Predicate<Population<G, P>> stop, BiPredicate<Population<G, P>, Integer> stop,
FitnessService<G, P> fitness, FitnessService<G, P> fitness,
Selection<G, P> selection, Selection<G, P> selection,
Map<GeneticOperator<G>, ApplicationRate> operators, Map<GeneticOperator<G>, ApplicationRate> operators,
@ -147,7 +147,7 @@ public final class GeneticAlgorithmFactory<G, P> implements Callable<Population<
ChromosomeFactory<G> initFactory, ChromosomeFactory<G> initFactory,
int populationSize, int populationSize,
Function<G, P> decoder, Function<G, P> decoder,
Predicate<Population<G, P>> stop, BiPredicate<Population<G, P>, Integer> stop,
FitnessService<G, P> fitness, FitnessService<G, P> fitness,
Selection<G, P> selection, Selection<G, P> selection,
Map<GeneticOperator<G>, ApplicationRate> operators, Map<GeneticOperator<G>, ApplicationRate> operators,
@ -181,8 +181,7 @@ public final class GeneticAlgorithmFactory<G, P> implements Callable<Population<
currentGen.add(init); currentGen.add(init);
final UniformRandomProvider rng = random.create(); final UniformRandomProvider rng = random.create();
while (!stop.test(currentGen)) { while (!stop.test(currentGen, ++generation)) {
++generation;
final Population<G, P> nextGen = new Population<>(popSize, decoder, fitness); final Population<G, P> nextGen = new Population<>(popSize, decoder, fitness);
applyElitism(currentGen, nextGen); applyElitism(currentGen, nextGen);

View File

@ -18,25 +18,30 @@ package org.apache.commons.math4.ga2.stop;
import java.util.Map; import java.util.Map;
import java.util.function.ToDoubleFunction; import java.util.function.ToDoubleFunction;
import java.util.function.Predicate; import java.util.function.BiPredicate;
import org.apache.commons.math4.ga2.Population; import org.apache.commons.math4.ga2.Population;
/** /**
* Criterion for asserting convergence of a population. * Criterion for asserting convergence of a population.
* Note: Class is <em>not</em> thread-safe. * Notes:
* <ul>
* <li>Class is <em>not</em> thread-safe.</li>
* <li>A <em>new</em> instance must created for each GA run (otherwise
* an {@link IllegalStateException} will be thrown).</li>
* </ul>
* *
* @param <G> Genotype. * @param <G> Genotype.
* @param <P> Phenotype. * @param <P> Phenotype.
*/ */
public class UnchangedFitness<G, P> implements Predicate<Population<G, P>> { public class UnchangedFitness<G, P> implements BiPredicate<Population<G, P>, Integer> {
/** Function that computes the reference value. */ /** Function that computes the reference value. */
private final ToDoubleFunction<Population<G, P>> calculator; private final ToDoubleFunction<Population<G, P>> calculator;
/** Number of generations during which no change has happened. */ /** Number of generations during which no change has happened. */
private final int maxGenerations; private final int maxGenerations;
/** Value for previous population. */ /** Value for previous population. */
private double previousFitness = Double.NaN; private double previousFitness = Double.NaN;
/** Number of generations without changes. */ /** Generation at which the last change has occurred. */
private int generations = 0; private int updatedGeneration = 0;
/** What needs to be unchanged. */ /** What needs to be unchanged. */
public enum Type { public enum Type {
@ -71,14 +76,20 @@ public class UnchangedFitness<G, P> implements Predicate<Population<G, P>> {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public boolean test(Population<G, P> population) { public boolean test(Population<G, P> population,
Integer generationCounter) {
final int genDiff = generationCounter - updatedGeneration;
if (genDiff < 0) {
throw new IllegalStateException("Incorrect usage");
}
final double fitness = calculator.applyAsDouble(population); final double fitness = calculator.applyAsDouble(population);
if (fitness == previousFitness) { if (fitness == previousFitness) {
if (++generations > maxGenerations) { if (genDiff > maxGenerations) {
return true; return true;
} }
} else { } else {
generations = 0; updatedGeneration = generationCounter;
previousFitness = fitness; previousFitness = fitness;
} }