eva2/src/eva2/server/stat/AbstractStatistics.java
2008-06-24 14:39:56 +00:00

423 lines
16 KiB
Java

package eva2.server.stat;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import eva2.gui.BeanInspector;
import eva2.server.go.IndividualInterface;
import eva2.server.go.PopulationInterface;
import eva2.server.go.individuals.AbstractEAIndividual;
import eva2.server.go.problems.InterfaceAdditionalPopulationInformer;
import eva2.tools.Mathematics;
public abstract class AbstractStatistics implements InterfaceTextListener, InterfaceStatistics {
private PrintWriter resultOut;
public final static boolean TRACE = false;
protected InterfaceStatisticsParameter m_StatsParams;
// protected String startDate;
// protected long startTime;
/**
* Keep track of all intermediate fitness values, best, avg. and worst, averaging over all runs
* for final output, "refining" the multi run data.
* If the runs have different lengths, the shortest of all defines the length
* of averaged data to be displayed. This mechanism expects that createNextGenerationPerformed
* comes in regular intervals (in terms of function calls performed). This needs to be emulated
* by dynamic population optimizers, also due to the graph output.
*/
private boolean refineMultiRuns = true;
private ArrayList<double[][]> meanCollection;
// say whether the object should be written to a file every time
private boolean saveParams = true;
private boolean firstPlot = true;
private int runIterCnt = 0;
// show this many iterations of the averaged performance after a full multi-run
private int showAvgIntervals = 9;
// collect data
protected int functionCalls;
protected int functionCallSum;
protected int convergenceCnt;
protected int optRunsPerformed;
protected double[] currentBestFit;
protected double[] meanFitness;
protected double[] currentWorstFit;
protected IndividualInterface bestCurrentIndividual, bestIndivdualAllover;
private ArrayList<InterfaceTextListener> textListeners;
public AbstractStatistics() {
firstPlot = true;
functionCalls = 0;
functionCallSum = 0;
convergenceCnt = 0;
optRunsPerformed = 0;
runIterCnt = 0;
textListeners = new ArrayList<InterfaceTextListener>();
}
public void addTextListener(InterfaceTextListener listener) {
if (!textListeners.contains(listener)) textListeners.add(listener);
}
public boolean removeTextListener(InterfaceTextListener listener) {
return textListeners.remove(listener);
}
/**
* Collect start date and time of the run and if indicated, open a file output stream.
*
* @param infoString
*/
protected void initOutput(String infoString) {
SimpleDateFormat formatter = new SimpleDateFormat("E'_'yyyy.MM.dd'_at_'hh.mm.ss");
String startDate = formatter.format(new Date());
// open the result file:
if ((m_StatsParams.getOutputTo().getSelectedTagID()!=1) // not "text only"
&& (m_StatsParams.getOutputVerbosity().getSelectedTagID() > StatsParameter.VERBOSITY_NONE)) { // verbosity accordingly high
//!resFName.equalsIgnoreCase("none") && !resFName.equals("")) {
String fname = makeOutputFileName(m_StatsParams.getResultFilePrefix(), infoString, startDate);
if (TRACE) System.out.println("FileName =" + fname);
try {
resultOut = new PrintWriter(new FileOutputStream(fname));
} catch (Exception e) {
e.printStackTrace();
System.out.println("Error: " + e);
}
resultOut.println("StartDate:" + startDate);
resultOut.println("On Host:" + getHostName());
} else resultOut = null;
}
private String makeOutputFileName(String prefix, String infoString, String startDate) {
return (prefix + "_" + infoString).replace(' ', '_') + "_" + startDate + ".txt";
}
/**
* If set to true, before every run the parameters will be stored to a file at the start
* of each run. Default is true.
*
* @param doSave
*/
public void setSaveParams(boolean doSave) {
saveParams = doSave;
}
public void startOptPerformed(String infoString, int runNumber, Object params) {
if (TRACE) System.out.println("AbstractStatistics.startOptPerformed " + runNumber);
if (runNumber == 0) {
functionCallSum = 0;
firstPlot = true;
optRunsPerformed = 0;
convergenceCnt = 0;
if (saveParams) m_StatsParams.saveInstance();
initOutput(infoString);
bestCurrentIndividual = null;
bestIndivdualAllover = null;
if (refineMultiRuns) meanCollection = new ArrayList<double[][]>();
else meanCollection = null;
}
runIterCnt = 0;
if (printRunIntroVerbosity()) printToTextListener("\n****** Multirun "+runNumber);
if (params != null) {
if (printRunIntroVerbosity()) printToTextListener("\nModule parameters: ");
if (printRunIntroVerbosity()) printToTextListener(BeanInspector.toString(params));
}
if (printRunIntroVerbosity()) printToTextListener("\nStatistics parameters: ");
if (printRunIntroVerbosity()) printToTextListener(BeanInspector.toString(getStatisticsParameter()) + '\n');
functionCalls = 0;
}
public void stopOptPerformed(boolean normal, String stopMessage) {
if (TRACE) System.out.println("AbstractStatistics.stopOptPerformed");
if (runIterCnt < meanCollection.size()) {
// no good: later run was shorter than the first one. What to do? Discard the longer one:
if (TRACE) System.err.println("Error in AbstractStatistics: later run was shorter than earlier one... discarding rest...");
for (int i=meanCollection.size()-1; i>=runIterCnt; i--) meanCollection.remove(i);
}
optRunsPerformed++;
functionCallSum += functionCalls;
if (printRunStoppedVerbosity() && (stopMessage != null)) printToTextListener(" Termination message: " + stopMessage + "\n");
if (printRunStoppedVerbosity()) printToTextListener(" Function calls run: " + functionCalls + ", sum: " + functionCallSum + "\n");
// check for convergence
if (bestCurrentIndividual != null) {
if (Mathematics.norm(bestCurrentIndividual.getFitness()) < this.m_StatsParams.getConvergenceRateThreshold()) {
convergenceCnt++;
}
if (printRunStoppedVerbosity()) printToTextListener(" Best solution: " + BeanInspector.toString(bestCurrentIndividual) + "\n");
if (printRunStoppedVerbosity()) printToTextListener(AbstractEAIndividual.getDefaultDataString(bestCurrentIndividual) + "\n");
}
if (currentBestFit!= null) {
if (printRunStoppedVerbosity()) printToTextListener(" Best Fitness: " + BeanInspector.toString(currentBestFit) + "\n");
}
if (optRunsPerformed == m_StatsParams.getMultiRuns()) finalizeOutput();
}
protected void finalizeOutput() {
if (printFinalVerbosity()) printToTextListener("*******\n Runs performed: " + optRunsPerformed + ", reached target " + convergenceCnt + " times with threshold " + m_StatsParams.getConvergenceRateThreshold() + ", rate " + convergenceCnt/(double)m_StatsParams.getMultiRuns() + '\n');
if (printFinalVerbosity()) printToTextListener("Best overall individual: " + BeanInspector.toString(bestIndivdualAllover) + '\n');
if (printFinalVerbosity()) printToTextListener(" solution : " + AbstractEAIndividual.getDefaultDataString(bestIndivdualAllover) + '\n');
if (printFinalVerbosity()) printToTextListener(" fitness : " + BeanInspector.toString(bestIndivdualAllover.getFitness()) + '\n');
if (refineMultiRuns && (optRunsPerformed>1) && (meanCollection != null)) {
if (printFinalVerbosity()) printToTextListener("Averaged performance:\n");
for (int i=0; i<meanCollection.size(); i++) divideMean(meanCollection.get(i), optRunsPerformed);
if (printFinalVerbosity()) printToTextListener(refineToText(meanCollection, showAvgIntervals));
}
if (TRACE)
System.out.println("End of run");
if (resultOut != null) {
SimpleDateFormat formatter = new SimpleDateFormat(
"E'_'yyyy.MM.dd'_at_'hh:mm:ss");
String StopDate = formatter.format(new Date());
resultOut.println("StopDate:" + StopDate);
resultOut.close();
}
}
public static String refineToText(ArrayList<double[][]> result, int iterationsToShow) {
double[][] mean;
StringBuffer sbuf = new StringBuffer("Iteration\tFun.Calls\tBest\tMean\tWorst\n");
double step = result.size()/(iterationsToShow-1.);
int printedIteration=0;
for(int i = 1; i < result.size()+1; i++) {
// print the first, last and intermediate iterations requested by the integer parameter
// first one is printed always, as printedIteration=0
if ((i==result.size()) || ((i-1)==Math.round(printedIteration*step))) {
printedIteration++;
mean = result.get(i-1);
sbuf.append(i);
sbuf.append("\t");
sbuf.append(BeanInspector.toString(mean[0]));
sbuf.append("\t");
sbuf.append(BeanInspector.toString(mean[1]));
sbuf.append("\t");
sbuf.append(BeanInspector.toString(mean[2]));
sbuf.append("\t");
sbuf.append(BeanInspector.toString(mean[3]));
sbuf.append("\n");
}
}
return sbuf.toString();
}
public abstract String getHostName();
public void printToTextListener(String s) {
if ((resultOut != null)) resultOut.print(s);
for (InterfaceTextListener l : textListeners) {
if (m_StatsParams.getOutputTo().getSelectedTagID() >= 1) l.print(s);
}
}
////////////// InterfaceTextListener
public void print(String str) {
printToTextListener(str);
}
////////////// InterfaceTextListener
public void println(String str) {
printToTextListener(str);
printToTextListener("\n");
}
public InterfaceStatisticsParameter getStatisticsParameter() {
return m_StatsParams;
}
protected boolean doTextOutput() {
return (resultOut != null) || (textListeners.size()>0);
}
protected String getOutputHeader(InterfaceAdditionalPopulationInformer informer, PopulationInterface pop) {
String headline = "Fit.-calls \t Best \t Mean \t Worst ";
if ((informer == null) || !m_StatsParams.isOutputAdditionalInfo()) {
return headline;
} else return headline + "\t " + informer.getAdditionalFileStringHeader(pop);
}
protected String getOutputLine(InterfaceAdditionalPopulationInformer informer, PopulationInterface pop) {
StringBuffer sbuf = new StringBuffer(Integer.toString(functionCalls));
sbuf.append(" \t ");
sbuf.append(BeanInspector.toString(currentBestFit));
if (meanFitness != null) {
sbuf.append(" \t ");
sbuf.append(BeanInspector.toString(meanFitness));
} else sbuf.append(" \t #");
if (currentWorstFit != null) {
sbuf.append(" \t ");
sbuf.append(BeanInspector.toString(currentWorstFit));
} else sbuf.append(" # \t");
if (informer != null && m_StatsParams.isOutputAdditionalInfo()) {
sbuf.append(" \t ");
sbuf.append(informer.getAdditionalFileStringValue(pop));
}
return sbuf.toString();
}
/**
*
*/
public synchronized void createNextGenerationPerformed(double[] bestfit,
double[] worstfit, int calls) {
functionCalls = calls;
currentBestFit = bestfit;
currentWorstFit = worstfit;
meanFitness = null;
if (firstPlot) {
initPlots(m_StatsParams.getPlotDescriptions());
// if (doTextOutput()) printToTextListener(getOutputHeader(null, null)+'\n');
firstPlot = false;
}
if ((runIterCnt == 0) && printHeaderByVerbosity()) printToTextListener(getOutputHeader(null, null)+'\n');
if (doTextOutput() && printLineByVerbosity(calls)) printToTextListener(getOutputLine(null, null)+'\n');
plotCurrentResults();
runIterCnt++;
}
/**
* If the population returns a specific data array, this method is called instead of doing standard output
* @param pop
* @param informer
*/
public abstract void plotSpecificData(PopulationInterface pop, InterfaceAdditionalPopulationInformer informer);
protected abstract void plotCurrentResults();
/**
* Called at the very first (multirun mode) plot of a fitness curve.
*/
protected abstract void initPlots(List<String[]> description);
/**
* Do some data collection on the population. The informer parameter will not be handled by this method.
*
*/
public synchronized void createNextGenerationPerformed(PopulationInterface
pop, InterfaceAdditionalPopulationInformer informer) {
if (firstPlot) {
initPlots(m_StatsParams.getPlotDescriptions());
// if (doTextOutput()) printToTextListener(getOutputHeader(informer, pop)+'\n');
firstPlot = false;
}
if ((runIterCnt==0) && printHeaderByVerbosity()) printToTextListener(getOutputHeader(informer, pop)+'\n');
if (pop.getSpecificData() != null) {
plotSpecificData(pop, informer);
return;
}
// by default plotting only the best
bestCurrentIndividual = pop.getBestIndividual().getClone();
if ((bestIndivdualAllover == null) || (secondIsBetter(bestIndivdualAllover, bestCurrentIndividual))) {
bestIndivdualAllover = bestCurrentIndividual;
// printToTextListener("new best found!, last was " + BeanInspector.toString(bestIndivdualAllover) + "\n");
}
// IndividualInterface WorstInd = Pop.getWorstIndividual();
if (bestCurrentIndividual == null) {
System.err.println("createNextGenerationPerformed BestInd==null");
}
currentBestFit = bestCurrentIndividual.getFitness().clone();
if (currentBestFit == null) {
System.err.println("BestFitness==null !");
}
meanFitness = pop.getMeanFitness().clone();
currentWorstFit = pop.getWorstIndividual().getFitness().clone();
functionCalls = pop.getFunctionCalls();
if (meanCollection != null) {
// Collect average data
double[][] means = null;
if ((optRunsPerformed==0) && (meanCollection.size()<=runIterCnt)) {
// in the first run, newly allocate the arrays
means = new double[4][currentBestFit.length];
meanCollection.add(means);
} else {
if (meanCollection.size()<=runIterCnt) {// bad case!
// may happen for dynamic pop-sizes, e.g. in Tribe, when runs do not necessarily send the
// "generation performed" event the same number of times.
// thus: dont do an update for events that are "too late"
means = null;
} else means = meanCollection.get(runIterCnt);
}
if (means != null) updateMeans(means, functionCalls, currentBestFit, meanFitness, currentWorstFit);
}
// meanCollection.set(pop.getGenerations()-1, means);
if (doTextOutput() && printLineByVerbosity(runIterCnt)) printToTextListener(getOutputLine(informer, pop)+'\n');
plotCurrentResults();
runIterCnt++;
}
private boolean printLineByVerbosity(int iteration) {
return (m_StatsParams.getOutputVerbosity().getSelectedTagID() > StatsParameter.VERBOSITY_KTH_IT)
|| ((m_StatsParams.getOutputVerbosity().getSelectedTagID() == StatsParameter.VERBOSITY_KTH_IT)
&& (isKthRun(iteration, m_StatsParams.getOutputVerbosityK())));
}
private boolean printRunIntroVerbosity() {
return (m_StatsParams.getOutputVerbosity().getSelectedTagID() >= StatsParameter.VERBOSITY_KTH_IT);
}
private boolean printRunStoppedVerbosity() {
return (m_StatsParams.getOutputVerbosity().getSelectedTagID() >= StatsParameter.VERBOSITY_KTH_IT);
}
private boolean printFinalVerbosity() {
return (m_StatsParams.getOutputVerbosity().getSelectedTagID() > StatsParameter.VERBOSITY_NONE);
}
private boolean isKthRun(int i, int k) {
return (i % k) == 0;
}
private boolean printHeaderByVerbosity() {
return (m_StatsParams.getOutputVerbosity().getSelectedTagID() >= StatsParameter.VERBOSITY_KTH_IT);
}
private void updateMeans(double[][] means, double funCalls, double[] bestFit, double[] meanFit, double[] worstFit) {
means[0][0]+=funCalls;
addMean(means[1], bestFit);
addMean(means[2], meanFit);
addMean(means[3], worstFit);
}
private static void divideMean(double[][] mean, double d) {
for (int i=0; i<mean.length; i++) {
for (int j=0; j<mean[i].length; j++) mean[i][j] /= d;
}
}
private void addMean(double[] mean, double[] fit) {
for (int i=0; i<mean.length; i++) mean[i] += fit[i];
}
private boolean secondIsBetter(IndividualInterface indy1, IndividualInterface indy2) {
if (indy1 == null) return true;
if (indy2 == null) return false;
if (indy1 instanceof AbstractEAIndividual) return ((AbstractEAIndividual)indy2).isDominatingDebConstraints((AbstractEAIndividual)indy1);
return (indy1.isDominant(indy2));
}
public double[] getBestFitness() {
return currentBestFit;
}
public IndividualInterface getBestSolution() {
return bestIndivdualAllover;
}
public int getFitnessCalls() {
return functionCalls;
}
}