/*
Copyright 2006 by Sean Luke
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package ec.simple;
import ec.*;
import ec.util.*;
/*
* SimpleEvaluator.java
*
* Created: Wed Aug 18 21:31:18 1999
* By: Sean Luke
*/
/**
* The SimpleEvaluator is a simple, non-coevolved generational evaluator which
* evaluates every single member of every subpopulation individually in its
* own problem space. One Problem instance is cloned from p_problem for
* each evaluating thread. The Problem must implement SimpleProblemForm.
*
* @author Sean Luke
* @version 2.0
*
* Thanks to Ralf Buschermohle for early versions
* of code which led to this version.
*
*/
public class SimpleEvaluator extends Evaluator
{
public static final String P_CLONE_PROBLEM = "clone-problem";
public static final String P_NUM_TESTS = "num-tests";
public static final String P_MERGE = "merge";
public static final String V_MEAN = "mean";
public static final String V_MEDIAN = "median";
public static final String V_BEST = "best";
public static final String P_CHUNK_SIZE = "chunk-size";
public static final String V_AUTO = "auto";
public static final int MERGE_MEAN = 0;
public static final int MERGE_MEDIAN = 1;
public static final int MERGE_BEST = 2;
public int numTests = 1;
public int mergeForm = MERGE_MEAN;
public boolean cloneProblem;
Object[] lock = new Object[0]; // Arrays are serializable
int individualCounter = 0;
int subPopCounter = 0;
int chunkSize; // a value >= 1, or C_AUTO
public static final int C_AUTO = 0;
public ThreadPool pool = new ThreadPool();
// checks to make sure that the Problem implements SimpleProblemForm
public void setup(final EvolutionState state, final Parameter base)
{
super.setup(state,base);
if (!(p_problem instanceof SimpleProblemForm))
state.output.fatal("" + this.getClass() + " used, but the Problem is not of SimpleProblemForm",
base.push(P_PROBLEM));
cloneProblem =state.parameters.getBoolean(base.push(P_CLONE_PROBLEM), null, true);
if (!cloneProblem && (state.breedthreads > 1)) // uh oh, this can't be right
state.output.fatal("The Evaluator is not cloning its Problem, but you have more than one thread.", base.push(P_CLONE_PROBLEM));
numTests = state.parameters.getInt(base.push(P_NUM_TESTS), null, 1);
if (numTests < 1) numTests = 1;
else if (numTests > 1)
{
String m = state.parameters.getString(base.push(P_MERGE), null);
if (m == null)
state.output.warning("Merge method not provided to SimpleEvaluator. Assuming 'mean'");
else if (m.equals(V_MEAN))
mergeForm = MERGE_MEAN;
else if (m.equals(V_MEDIAN))
mergeForm = MERGE_MEDIAN;
else if (m.equals(V_BEST))
mergeForm = MERGE_BEST;
else
state.output.fatal("Bad merge method: " + m, base.push(P_NUM_TESTS), null);
}
if (!state.parameters.exists(base.push(P_CHUNK_SIZE), null))
{
chunkSize = C_AUTO;
}
else if (state.parameters.getString(base.push(P_CHUNK_SIZE), null).equalsIgnoreCase(V_AUTO))
{
chunkSize = C_AUTO;
}
else
{
chunkSize = (state.parameters.getInt(base.push(P_CHUNK_SIZE), null, 1));
if (chunkSize == 0) // uh oh
state.output.fatal("Chunk Size must be either an integer >= 1 or 'auto'", base.push(P_CHUNK_SIZE), null);
}
}
Population oldpop = null;
// replace the population with one that has some N copies of the original individuals
void expand(EvolutionState state)
{
Population pop = (Population)(state.population.emptyClone());
// populate with clones
for(int i = 0; i < pop.subpops.length; i++)
{
pop.subpops[i].individuals = new Individual[numTests * state.population.subpops[i].individuals.length];
for(int j = 0; j < state.population.subpops[i].individuals.length; j++)
{
for (int k=0; k < numTests; k++)
{
pop.subpops[i].individuals[numTests * j + k] =
(Individual)(state.population.subpops[i].individuals[j].clone());
}
}
}
// swap
oldpop = state.population;
state.population = pop;
}
// Take the N copies of the original individuals and fold their fitnesses back into the original
// individuals, replacing them with the original individuals in the process. See expand(...)
void contract(EvolutionState state)
{
// swap back
Population pop = state.population;
state.population = oldpop;
// merge fitnesses again
for(int i = 0; i < pop.subpops.length; i++)
{
Fitness[] fits = new Fitness[numTests];
for(int j = 0; j < state.population.subpops[i].individuals.length; j++)
{
for (int k=0; k < numTests; k++)
{
fits[k] = pop.subpops[i].individuals[numTests * j + k].fitness;
}
if (mergeForm == MERGE_MEAN)
{
state.population.subpops[i].individuals[j].fitness.setToMeanOf(state, fits);
}
else if (mergeForm == MERGE_MEDIAN)
{
state.population.subpops[i].individuals[j].fitness.setToMedianOf(state, fits);
}
else // MERGE_BEST
{
state.population.subpops[i].individuals[j].fitness.setToBestOf(state, fits);
}
state.population.subpops[i].individuals[j].evaluated = true;
}
}
}
/** A simple evaluator that doesn't do any coevolutionary
evaluation. Basically it applies evaluation pipelines,
one per thread, to various subchunks of a new population. */
public void evaluatePopulation(final EvolutionState state)
{
if (numTests > 1)
expand(state);
// reset counters. Only used in multithreading
individualCounter = 0;
subPopCounter = 0;
// start up if single-threaded?
if (state.evalthreads == 1)
{
int[] numinds = new int[state.population.subpops.length];
int[] from = new int[numinds.length];
for(int i = 0; i < numinds.length; i++)
{
numinds[i] = state.population.subpops[i].individuals.length;
from[i] = 0;
}
SimpleProblemForm prob = null;
if (cloneProblem)
prob = (SimpleProblemForm)(p_problem.clone());
else
prob = (SimpleProblemForm)(p_problem); // just use the prototype
evalPopChunk(state, numinds, from, 0, prob);
}
else
{
ThreadPool.Worker[] threads = new ThreadPool.Worker[state.evalthreads];
for(int i = 0; i < threads.length; i++)
{
SimpleEvaluatorThread run = new SimpleEvaluatorThread();
run.threadnum = i;
run.state = state;
run.prob = (SimpleProblemForm)(p_problem.clone());
threads[i] = pool.start(run, "ECJ Evaluation Thread " + i);
}
// join
pool.joinAll();
}
if (numTests > 1)
contract(state);
}
/** The SimpleEvaluator determines that a run is complete by asking
each individual in each population if he's optimal; if he
finds an individual somewhere that's optimal,
he signals that the run is complete. */
public boolean runComplete(final EvolutionState state)
{
for(int x = 0;x= slop) // beyond the slop
return individualsPerThread;
else return individualsPerThread + 1;
}
/** A helper class for implementing multithreaded evaluation */
class SimpleEvaluatorThread implements Runnable
{
public int threadnum;
public EvolutionState state;
public SimpleProblemForm prob = null;
public void run()
{
Subpopulation[] subpops = state.population.subpops;
int[] numinds = new int[subpops.length];
int[] from = new int[subpops.length];
int count = 1;
int start = 0;
int subpop = 0;
while (true)
{
// We need to grab the information about the next chunk we're responsible for. This stays longer
// in the lock than I'd like :-(
synchronized(lock)
{
// has everyone done all the jobs?
if (subPopCounter >= subpops.length) // all done
return;
// has everyone finished the jobs for this subpopulation?
if (individualCounter >= subpops[subPopCounter].individuals.length) // try again, jump to next subpop
{
individualCounter = 0;
subPopCounter++;
// has everyone done all the jobs? Check again.
if (subPopCounter >= subpops.length) // all done
return;
}
start = individualCounter;
subpop = subPopCounter;
count = chunkSize;
if (count == C_AUTO) // compute automatically for subpopulations
count = computeChunkSizeForSubpopulation(state, subpop, threadnum);
individualCounter += count; // it can be way more than we'll actually do, that's fine
}
// Modify the true count
if (count >= subpops[subpop].individuals.length - start)
count = subpops[subpop].individuals.length - start;
// Load into arrays to reuse evalPopChunk
for(int i = 0; i < from.length; i++)
numinds[i] = 0;
numinds[subpop] = count;
from[subpop] = start;
evalPopChunk(state, numinds, from, threadnum, prob);
}
}
}
}
Ads help maintain this website.