/*
Copyright 2006 by Sean Luke
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package ec.select;
import ec.util.*;
import ec.*;
/*
* SUSSelection.java
*
* Created: Thu Feb 12 16:19:52 EST 2009
* By: Sean Luke
*/
/**
* Picks individuals in a population using the Stochastic Universal Selection (SUS) process, using
* fitnesses as returned by their fitness() methods. This is expensive to
* set up and bring down, so it's not appropriate for steady-state evolution.
* If you're not familiar with the relative advantages of
* selection methods and just want a good one,
* use TournamentSelection instead. Not appropriate for
* multiobjective fitnesses.
*
* By default this implementation of SUS shuffles the order of the individuals
* in the distribution before performing selection. This isn't always present in classic
* implementations of the algorithm but it can't hurt anything and certainly can avoid
* certain pathological situations. If you'd prefer not to preshuffle, set shuffle=false
* Note that we don't actually change the order of the individuals in the population -- instead
* we maintain our own internal array of indices and shuffle that.
*
*
Like truncation selection,
* SUS samples N individuals (with replacement) up front from the population,
* Then returns those individuals one by one.
* ECJ's implementation assumes that N is the size of the population -- that is, you're
* going to ultimately request a whole population out of this one selection method.
* This could be a false assumption: for example, if you only sometimes call this
* selection method, and sometimes TournamentSelection; or if you've got multiple
* pipelines. In these cases, SUS is probably a bad choice anyway.
*
*
If you ask for more than a population's worth of individuals, SUS tries
* to handle this gracefully by reshuffling its array and starting to select over
* again. But again that might suggest you are doing something wrong.
*
*
* Note: Fitnesses must be non-negative. 0 is assumed to be the worst fitness.
*
Typical Number of Individuals Produced Per produce(...) call
Always 1.
Parameters
base.shuffle
bool = true (default) or false
(should we preshuffle the array before doing selection?)
Default Base
select.sus
*
* @author Sean Luke
* @version 1.0
*/
public class SUSSelection extends SelectionMethod
{
/** Default base */
public static final String P_SUS = "sus";
public static final String P_SHUFFLE = "shuffle";
/** An array of pointers to individuals in the population, shuffled along with the fitnesses array. */
public int[] indices;
/** The distribution of fitnesses. */
public double[] fitnesses;
/** Should we shuffle first? */
public boolean shuffle = true;
/** The floating point value to consider for the next selected individual. */
public double offset = 0.0;
/** The index in the array of the last individual selected. */
public int lastIndex;
/** How many samples have been done? */
public int steps;
public Parameter defaultBase()
{
return SelectDefaults.base().push(P_SUS);
}
public void setup(final EvolutionState state, final Parameter base)
{
super.setup(state,base);
Parameter def = defaultBase();
shuffle = state.parameters.getBoolean(base.push(P_SHUFFLE),def.push(P_SHUFFLE),true);
}
/* Largely stolen from sim.util.Bag. Shuffles both the indices and the doubles */
void shuffle(MersenneTwisterFast random, double[] fitnesses, int[] indices)
{
int numObjs = fitnesses.length;
//double[] fitnesses = this.fitnesses;
//int[] indices = this.indices;
double f;
int i;
int rand;
for(int x=numObjs-1; x >= 1 ; x--)
{
rand = random.nextInt(x+1);
f = fitnesses[x];
fitnesses[x] = fitnesses[rand];
fitnesses[rand] = f;
i = indices[x];
indices[x] = indices[rand];
indices[rand] = i;
}
}
// don't need clone etc.
public void prepareToProduce(final EvolutionState s,
final int subpopulation,
final int thread)
{
lastIndex = 0;
steps = 0;
fitnesses = new double[s.population.subpops[subpopulation].individuals.length];
// compute offset
offset = (double)(s.random[thread].nextDouble() / fitnesses.length);
// load fitnesses but don't build distribution yet
for(int x=0;x= fitnesses.length) // we've gone too far, clearly an error
{
state.output.warning("SUSSelection was asked for too many individuals, so we're re-shuffling. This will give you proper results, but it might suggest an error in your code.");
boolean s = shuffle;
shuffle = true;
prepareToProduce(state, subpopulation, thread); // rebuild
shuffle = s; // just in case
}
// find the next index
for( /* empty */ ; lastIndex < fitnesses.length - 1; lastIndex++)
if ((lastIndex == 0 || offset >= fitnesses[lastIndex - 1]) && offset < fitnesses[lastIndex])
break;
offset += (double)(1.0 / fitnesses.length); // update for next time
steps++;
return indices[lastIndex];
}
}
Ads help maintain this website.