//============================================================================= // Copyright 2006-2010 Daniel W. Dyer // // 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. //============================================================================= package org.uncommons.watchmaker.framework.termination; import org.uncommons.watchmaker.framework.PopulationData; import org.uncommons.watchmaker.framework.TerminationCondition; /** * A {@link TerminationCondition} that halts evolution if no improvement in fitness * is observed within a specified number of generations. * @author Daniel Dyer */ public class Stagnation implements TerminationCondition { private final int generationLimit; private final boolean naturalFitness; private final boolean usePopulationAverage; private double bestFitness; private int fittestGeneration; /** * Creates a {@link TerminationCondition} that will halt evolution after the * specified number of generations passes without any improvement in the population's * fittest individual. * @param generationLimit The number of generations without improvement that * will lead to termination. * @param naturalFitness True if higher fitness scores are better, false otherwise. */ public Stagnation(int generationLimit, boolean naturalFitness) { this(generationLimit, naturalFitness, false); } /** * Creates a {@link TerminationCondition} that will halt evolution after the * specified number of generations passes without any improvement in the population's * fitness (either the fittest individual or the mean fitness of the entire population, * depending on the final parameter). * @param generationLimit The number of generations without improvement that * will lead to termination. * @param naturalFitness True if higher fitness scores are better, false otherwise. * @param usePopulationAverage If true uses the mean fitness of the population as the * criteria, otherwise uses the fittest individual. */ public Stagnation(int generationLimit, boolean naturalFitness, boolean usePopulationAverage) { this.generationLimit = generationLimit; this.naturalFitness = naturalFitness; this.usePopulationAverage = usePopulationAverage; } /** * {@inheritDoc} */ public boolean shouldTerminate(PopulationData<?> populationData) { double fitness = getFitness(populationData); if (populationData.getGenerationNumber() == 0 || hasFitnessImproved(fitness)) { bestFitness = fitness; fittestGeneration = populationData.getGenerationNumber(); } return populationData.getGenerationNumber() - fittestGeneration >= generationLimit; } /** * Determines the fitness of the current population (either best fitness or * mean fitness depending on how the termination condition is configured). * @param populationData Data about the current generation. * @return The fitness measure used to decide whether the evolution has stagnated * or not. */ private double getFitness(PopulationData<?> populationData) { return usePopulationAverage ? populationData.getMeanFitness() : populationData.getBestCandidateFitness(); } /** * Determine whether the population fitness is better than the best seen so far. * @param fitness The fitness of the current population (either best fitness or mean * fitness depending on how the termination condition is configured). * @return True if the fitness has improved in the current generation, false otherwise. */ private boolean hasFitnessImproved(double fitness) { return (naturalFitness && fitness > bestFitness) || (!naturalFitness && fitness < bestFitness); } }