ec.steadystate.SteadyStateEvolutionState Maven / Gradle / Ivy
Show all versions of ecj Show documentation
/*
Copyright 2006 by Sean Luke
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package ec.steadystate;
import ec.*;
import ec.util.Parameter;
import ec.util.Checkpoint;
import ec.util.Output;
import ec.simple.*;
//import ec.eval.MasterProblem;
import java.util.*;
/*
* SteadyStateEvolutionState.java
*
*/
/**
* This subclass of EvolutionState implements basic Steady-State Evolution and (in distributed form)
* Asynchronous Evolution. The procedure is as follows. We begin with an empty Population and one by
* one create new Indivdiuals and send them off to be evaluated. In basic Steady-State Evolution the
* individuals are immediately evaluated and we wait for them; but in Asynchronous Evolution the individuals are evaluated
* for however long it takes and we don't wait for them to finish. When individuals return they are
* added to the Population until it is full. No duplicate individuals are allowed.
*
* At this point the system switches to its "steady state": individuals are bred from the population
* one by one, and sent off to be evaluated. Once again, in basic Steady-State Evolution the
* individuals are immediately evaluated and we wait for them; but in Asynchronous Evolution the individuals are evaluated
* for however long it takes and we don't wait for them to finish. When an individual returns, we
* mark an individual in the Population for death, then replace it with the new returning individual.
* Note that during the steady-state, Asynchronous Evolution could be still sending back some "new" individuals
* created during the initialization phase, not "bred" individuals.
*
*
The determination of how an individual is marked for death is done by the SteadyStateBreeder.
*
*
SteadyStateEvolutionState will run either for some N "generations" or for some M evaluations of
* individuals. A "generation" is defined as a Population's worth of evaluations. If you do not
* specify the number of evaluations (the M), then SteadyStateEvolutionState will use the standard
* generations parameter defined in EvolutionState.
*
Parameters
evaluations
int >= 1
(maximal number of evaluations to run.)
steady.replacement-probability
0.0 <= double <= 1.0 (default is 1.0)
(probability that an incoming individual will unilaterally replace the individual marked
for death, as opposed to replacing it only if the incoming individual is superior in fitness)
*
* @author Sean Luke
* @version 1.0
*/
public class SteadyStateEvolutionState extends EvolutionState
{
public static final String P_REPLACEMENT_PROBABILITY = "replacement-probability";
/** Did we just start a new generation? */
public boolean generationBoundary;
/** how big is a generation? Set to the size of subpopulation 0 of the initial population. */
public int generationSize;
/** How many evaluations have we run so far? */
public long evaluations;
/** When a new individual arrives, with what probability should it directly replace the existing
"marked for death" individual, as opposed to only replacing it if it's superior? */
public double replacementProbability;
/** How many individuals have we added to the initial population? */
int[] individualCount;
/** Hash table to check for duplicate individuals */
HashMap[] individualHash;
/** Holds which subpopulation we are currently operating on */
int whichSubpop;
/** First time calling evolve */
protected boolean firstTime;
public void setup(final EvolutionState state, final Parameter base)
{
super.setup(state,base);
// double check that we have valid evaluators and breeders and exchangers
if (!(breeder instanceof SteadyStateBreeder))
state.output.error("You've chosen to use Steady-State Evolution, but your breeder is not of the class SteadyStateBreeder.",base);
if (!(evaluator instanceof SteadyStateEvaluator))
state.output.error("You've chosen to use Steady-State Evolution, but your evaluator is not of the class SteadyStateEvaluator.",base);
if (!(exchanger instanceof SteadyStateExchangerForm))
state.output.error("You've chosen to use Steady-State Evolution, but your exchanger does not implement the SteadyStateExchangerForm.",base);
checkStatistics(state, statistics, new Parameter(P_STATISTICS));
if (parameters.exists(SteadyStateDefaults.base().push(P_REPLACEMENT_PROBABILITY),null))
{
replacementProbability = parameters.getDoubleWithMax(SteadyStateDefaults.base().push(P_REPLACEMENT_PROBABILITY),null,0.0, 1.0);
if (replacementProbability < 0.0) // uh oh
state.output.error("Replacement probability must be between 0.0 and 1.0 inclusive.",
SteadyStateDefaults.base().push(P_REPLACEMENT_PROBABILITY), null);
}
else
{
replacementProbability = 1.0; // always replace
state.output.message("Replacement probability not defined: using 1.0 (always replace)");
}
}
// recursively prints out warnings for all statistics that are not
// of steadystate statistics form
void checkStatistics(final EvolutionState state, Statistics stat, final Parameter base)
{
if (!(stat instanceof SteadyStateStatisticsForm))
state.output.warning("You've chosen to use Steady-State Evolution, but your statistics does not implement the SteadyStateStatisticsForm.",base);
for(int x=0;x UNDEFINED && numEvaluations < generationSize)
output.fatal("Number of evaluations desired is smaller than the initial population of individuals");
// INITIALIZE CONTACTS -- done after initialization to allow
// a hook for the user to do things in Initializer before
// an attempt is made to connect to island models etc.
exchanger.initializeContacts(this);
evaluator.initializeContacts(this);
}
boolean justCalledPostEvaluationStatistics = false;
public int evolve()
{
if (generationBoundary && generation > 0)
{
output.message("Generation " + generation +"\tEvaluations " + evaluations);
statistics.generationBoundaryStatistics(this);
statistics.postEvaluationStatistics(this);
justCalledPostEvaluationStatistics = true;
}
else
{
justCalledPostEvaluationStatistics = false;
}
if (firstTime)
{
if (statistics instanceof SteadyStateStatisticsForm)
((SteadyStateStatisticsForm)statistics).enteringInitialPopulationStatistics(this);
statistics.postInitializationStatistics(this);
((SteadyStateBreeder)breeder).prepareToBreed(this, 0); // unthreaded
((SteadyStateEvaluator)evaluator).prepareToEvaluate(this, 0); // unthreaded
firstTime=false;
}
whichSubpop = (whichSubpop+1)%population.subpops.length; // round robin selection
// is the current subpop full?
boolean partiallyFullSubpop = (individualCount[whichSubpop] < population.subpops[whichSubpop].individuals.length);
// MAIN EVOLVE LOOP
if (((SteadyStateEvaluator) evaluator).canEvaluate()) // are we ready to evaluate?
{
Individual ind=null;
int numDuplicateRetries = population.subpops[whichSubpop].numDuplicateRetries;
for (int tries=0; tries <= numDuplicateRetries; tries++) // see Subpopulation
{
if ( partiallyFullSubpop ) // is population full?
{
ind = population.subpops[whichSubpop].species.newIndividual(this, 0); // unthreaded
}
else
{
ind = ((SteadyStateBreeder)breeder).breedIndividual(this, whichSubpop,0);
statistics.individualsBredStatistics(this, new Individual[]{ind});
}
if (numDuplicateRetries >= 1)
{
Object o = individualHash[whichSubpop].get(ind);
if (o == null)
{
individualHash[whichSubpop].put(ind, ind);
break;
}
}
} // tried to cut down the duplicates
// evaluate the new individual
((SteadyStateEvaluator)evaluator).evaluateIndividual(this, ind, whichSubpop);
}
Individual ind = ((SteadyStateEvaluator)evaluator).getNextEvaluatedIndividual();
if (ind != null) // do we have an evaluated individual?
{
int subpop = ((SteadyStateEvaluator)evaluator).getSubpopulationOfEvaluatedIndividual();
if ( partiallyFullSubpop ) // is subpopulation full?
{
population.subpops[subpop].individuals[individualCount[subpop]++]=ind;
// STATISTICS FOR GENERATION ZERO
if ( individualCount[subpop] == population.subpops[subpop].individuals.length )
if (statistics instanceof SteadyStateStatisticsForm)
((SteadyStateStatisticsForm)statistics).enteringSteadyStateStatistics(subpop, this);
}
else
{
// mark individual for death
int deadIndividual = ((SteadyStateBreeder)breeder).deselectors[subpop].produce(subpop,this,0);
Individual deadInd = population.subpops[subpop].individuals[deadIndividual];
// maybe replace dead individual with new individual
if (ind.fitness.betterThan(deadInd.fitness) || // it's better, we want it
random[0].nextDouble() < replacementProbability) // it's not better but maybe we replace it directly anyway
population.subpops[subpop].individuals[deadIndividual] = ind;
// update duplicate hash table
individualHash[subpop].remove(deadInd);
if (statistics instanceof SteadyStateStatisticsForm)
((SteadyStateStatisticsForm)statistics).individualsEvaluatedStatistics(this,
new Individual[]{ind}, new Individual[]{deadInd}, new int[]{subpop}, new int[]{deadIndividual});
}
// INCREMENT NUMBER OF COMPLETED EVALUATIONS
evaluations++;
// COMPUTE GENERATION BOUNDARY
generationBoundary = (evaluations % generationSize == 0);
}
else
{
generationBoundary = false;
}
// SHOULD WE QUIT?
if (!partiallyFullSubpop && evaluator.runComplete(this) && quitOnRunComplete)
{
output.message("Found Ideal Individual");
return R_SUCCESS;
}
if ((numEvaluations > UNDEFINED && evaluations >= numEvaluations) || // using numEvaluations
(numEvaluations <= UNDEFINED && generationBoundary && generation == numGenerations -1)) // not using numEvaluations
{
// we are not exchanging again, but we might wish to increment the generation
// one last time if we hit a generation boundary
if (generationBoundary)
generation++;
return R_FAILURE;
}
// EXCHANGING
if (generationBoundary)
{
// PRE-BREED EXCHANGE
statistics.prePreBreedingExchangeStatistics(this);
population = exchanger.preBreedingExchangePopulation(this);
statistics.postPreBreedingExchangeStatistics(this);
String exchangerWantsToShutdown = exchanger.runComplete(this);
if (exchangerWantsToShutdown!=null)
{
output.message(exchangerWantsToShutdown);
return R_SUCCESS;
}
// POST BREED EXCHANGE
statistics.prePostBreedingExchangeStatistics(this);
population = exchanger.postBreedingExchangePopulation(this);
statistics.postPostBreedingExchangeStatistics(this);
// INCREMENT GENERATION AND CHECKPOINT
generation++;
if (checkpoint && generation%checkpointModulo == 0)
{
output.message("Checkpointing");
statistics.preCheckpointStatistics(this);
Checkpoint.setCheckpoint(this);
statistics.postCheckpointStatistics(this);
}
}
return R_NOTDONE;
}
/**
* @param result
*/
public void finish(int result)
{
/* finish up -- we completed. */
((SteadyStateBreeder)breeder).finishPipelines(this);
if (!justCalledPostEvaluationStatistics)
{
output.message("Generation " + generation +"\tEvaluations " + evaluations);
statistics.postEvaluationStatistics(this);
}
statistics.finalStatistics(this,result);
finisher.finishPopulation(this,result);
exchanger.closeContacts(this,result);
evaluator.closeContacts(this,result);
}
}