Extented post-processing

This commit is contained in:
Marcel Kronfeld 2011-05-03 12:26:27 +00:00
parent 88dde22b4b
commit 3ecc1028cd
4 changed files with 206 additions and 27 deletions

View File

@ -35,8 +35,10 @@ import eva2.server.go.problems.AbstractMultiModalProblemKnown;
import eva2.server.go.problems.AbstractOptimizationProblem; import eva2.server.go.problems.AbstractOptimizationProblem;
import eva2.server.go.problems.FM0Problem; import eva2.server.go.problems.FM0Problem;
import eva2.server.go.problems.Interface2DBorderProblem; import eva2.server.go.problems.Interface2DBorderProblem;
import eva2.server.go.problems.InterfaceHasSolutionViewer;
import eva2.server.go.problems.InterfaceInterestingHistogram; import eva2.server.go.problems.InterfaceInterestingHistogram;
import eva2.server.go.problems.InterfaceMultimodalProblemKnown; import eva2.server.go.problems.InterfaceMultimodalProblemKnown;
import eva2.server.go.problems.InterfaceSolutionViewer;
import eva2.server.go.strategies.EvolutionStrategies; import eva2.server.go.strategies.EvolutionStrategies;
import eva2.server.go.strategies.GradientDescentAlgorithm; import eva2.server.go.strategies.GradientDescentAlgorithm;
import eva2.server.go.strategies.HillClimbing; import eva2.server.go.strategies.HillClimbing;
@ -754,13 +756,12 @@ public class PostProcess {
if (indy instanceof InterfaceDataTypeDouble || (indy instanceof InterfaceESIndividual)) { if (indy instanceof InterfaceDataTypeDouble || (indy instanceof InterfaceESIndividual)) {
if (indy instanceof InterfaceESIndividual) ((InterfaceESIndividual)indy).SetDGenotype(data); if (indy instanceof InterfaceESIndividual) ((InterfaceESIndividual)indy).SetDGenotype(data);
else ((InterfaceDataTypeDouble)indy).SetDoubleGenotype(data); else ((InterfaceDataTypeDouble)indy).SetDoubleGenotype(data);
} } else throw new RuntimeException("Error, unable to set double data to individual instance " + indy.getClass() + " in PostProcess.setDoubleData");
} }
/** /**
* Create a population of clones of the given individual in a sub range around the individual. * Create a population of clones of the given individual in a sub range around the individual.
* The given individual must be double compliant. The population size is determined by the range dimension * The given individual must be double compliant.
* using the formula for lambda=4+3*log(dim).
* The individuals are randomly initialized in a box of side length searchBoxLen around indy holding the * The individuals are randomly initialized in a box of side length searchBoxLen around indy holding the
* problem constraints, meaning that the box may be smaller at the brim of the problem-defined search range. * problem constraints, meaning that the box may be smaller at the brim of the problem-defined search range.
* *
@ -769,8 +770,7 @@ public class PostProcess {
* @return * @return
*/ */
public static void createPopInSubRange(Population destPop, double searchBoxLen, public static void createPopInSubRange(Population destPop, double searchBoxLen,
int targetSize, int targetSize, AbstractEAIndividual indy) {
AbstractEAIndividual indy) {
if (isDoubleCompliant(indy)) { if (isDoubleCompliant(indy)) {
double[][] range = getDoubleRange(indy); double[][] range = getDoubleRange(indy);
double[] data = getDoubleData(indy); double[] data = getDoubleData(indy);
@ -783,11 +783,7 @@ public class PostProcess {
// Population pop = new Population(); // Population pop = new Population();
destPop.clear(); destPop.clear();
for (int i=0; i<targetSize; i++) { for (int i=0; i<targetSize; i++) {
AbstractEAIndividual tmpIndy = (AbstractEAIndividual)indy.clone(); destPop.addIndividual(createRandomDoubleClone(indy, newRange));
data = getDoubleData(tmpIndy);
ESIndividualDoubleData.defaultInit(data, newRange);
setDoubleData(tmpIndy, data);
destPop.addIndividual(tmpIndy);
} }
destPop.synchSize(); destPop.synchSize();
} else { } else {
@ -795,6 +791,23 @@ public class PostProcess {
} }
} }
/**
* Clone the given individual and initialize it randomly within the given range.
* The individual must be double compliant.
*
* @param indy
* @param range
* @return
*/
public static AbstractEAIndividual createRandomDoubleClone(AbstractEAIndividual indy, double[][] range) {
AbstractEAIndividual tmpIndy = (AbstractEAIndividual)indy.clone();
double[] data = getDoubleData(tmpIndy);
if (data==null) throw new RuntimeException("Error, given individual must be double compliant in PostProcess.createRandomDoubleClone");
ESIndividualDoubleData.defaultInit(data, range);
setDoubleData(tmpIndy, data);
return tmpIndy;
}
/** /**
* Just execute the runnable. * Just execute the runnable.
*/ */
@ -1064,6 +1077,7 @@ public class PostProcess {
// PostProcessInterim.outputResult((AbstractOptimizationProblem)goParams.getProblem(), outputPop, 0.01, System.out, 0, 2000, 20, goParams.getPostProcessSteps()); // PostProcessInterim.outputResult((AbstractOptimizationProblem)goParams.getProblem(), outputPop, 0.01, System.out, 0, 2000, 20, goParams.getPostProcessSteps());
if (outputPop.size()>1) { if (outputPop.size()>1) {
if (listener != null) listener.println("measures: " + BeanInspector.toString(outputPop.getPopulationMeasures())); if (listener != null) listener.println("measures: " + BeanInspector.toString(outputPop.getPopulationMeasures()));
if (listener != null) listener.println("pop.metric: " + BeanInspector.toString(outputPop.getPopMetric()));
if (listener != null) listener.println("solution histogram: " + solHist + ", score " + solHist.getScore()); if (listener != null) listener.println("solution histogram: " + solHist + ", score " + solHist.getScore());
if ((listener != null) && (problem instanceof InterfaceInterestingHistogram)) { if ((listener != null) && (problem instanceof InterfaceInterestingHistogram)) {
SolutionHistogram pSolHist = ((InterfaceInterestingHistogram)problem).getHistogram(); SolutionHistogram pSolHist = ((InterfaceInterestingHistogram)problem).getHistogram();
@ -1075,17 +1089,118 @@ public class PostProcess {
//////////// multimodal data output //////////// multimodal data output
evaluateMultiModal(outputPop, problem, listener); evaluateMultiModal(outputPop, problem, listener);
Population nBestPop = outputPop.getBestNIndividuals(params.getPrintNBest(), -1); // n individuals are returned and sorted, all of them if n<=0 Population nBestPop = outputPop.getBestNIndividuals(0, -1); // the individuals are returned and sorted, all of them if n<=0
if (listener != null) listener.println("Best after post process:" + ((outputPop.size()>nBestPop.size()) ? ( " (first " + nBestPop.size() + " of " + outputPop.size() + ")") : (" (" + nBestPop.size() + ")") )); if (params.getPrintNBest()!=0) {
int printK = ((params.getPrintNBest()>0) ? params.getPrintNBest() : nBestPop.size());
printK = Math.min(printK, nBestPop.size());
if (listener != null) listener.println("Best after post process:" + " (first " + printK + " of " + outputPop.size() + ")");
// ((outputPop.size()>nBestPop.size())
// ? ( " (first " + nBestPop.size() + " of " + outputPop.size() + ")") :
// (" (" + nBestPop.size() + ")") ));
//////////// output some individual data //////////// output some individual data
if (listener != null) for (int i=0; i<nBestPop.size(); i++) { if (listener != null) for (int i=0; i<printK; i++) {
listener.println(AbstractEAIndividual.getDefaultStringRepresentation(nBestPop.getEAIndividual(i))); listener.println(AbstractEAIndividual.getDefaultStringRepresentation(nBestPop.getEAIndividual(i)));
} }
// for (int i=0; i<printK; i++) {
// System.out.println(AbstractEAIndividual.getDefaultStringRepresentation(nBestPop.getEAIndividual(i)));
// }
}
// System.out.println(nBestPop); // System.out.println(nBestPop);
if (problem instanceof InterfaceHasSolutionViewer) {
InterfaceSolutionViewer viewer = ((InterfaceHasSolutionViewer)problem).getSolutionViewer();
if (viewer!=null) {
viewer.initView(problem);
viewer.updateView(outputPop, true);
}
}
return nBestPop; return nBestPop;
} else return inputPop; } else return inputPop;
} }
/**
* Check the accuracy of a given set of solutions regarding an array of double-valued epsilon thresholds.
* For each epsilon the solutions are post-processed and it is checked if the post-processing moves them
* away from the originally indicated solution by more than epsilon. Post-processing will also pre-cluster
* the solutions if the clustering parameter is positive.
* If solutions are moved by the local search by more than epsilon[k], they are seen as 'inaccurate' by
* epsilon[k] and discarded. The number of solutions
* which are accurate by epsilon[k] are returned in the integer array. Optionally, the refined solutions
* are added to an array of histograms.
*
* For refining, either known optima are used or {@link AbstractOptimizationProblem}.extractPotentialOptima()
* is called.
*
* @see {@link AbstractOptimizationProblem.extractPotentialOptima}
* @param prob
* @param sols
* @param epsilonPhenoSpace
* @param extrOptEpsFitConf
* @param extrOptClustSig clustering distance; if set negative, 0.1 times the respective epsilonPhenoSpace parameter is used for clustering
* @param maxEvals
* @param solHists
* @param treatAsUnknown
* @param listener
* @see AbstractOptimizationProblem.isPotentialOptimumNMS(AbstractEAIndividual, double, double, int)
* @return
*/
public static int[] checkAccuracy(AbstractOptimizationProblem prob, Population sols, double[] epsilonPhenoSpace,
double extrOptEpsFitConf, double extrOptClustSig, int maxEvals, SolutionHistogram[] solHists, boolean treatAsUnknown,
InterfaceTextListener listener) {
int[] foundOpts = new int[epsilonPhenoSpace.length];
Population extrOpts = null;
if (listener!=null) listener.println("Accuracy regarding epsilon thresholds " + BeanInspector.toString(epsilonPhenoSpace) + ", clustering with sigma=" + extrOptClustSig);
// System.out.println("unref: " + sols.getStringRepresentation());
// extract optima (known or estimated) for different epsilon criteria
for (int k=0; k<epsilonPhenoSpace.length; k++) {
if ((prob instanceof InterfaceMultimodalProblemKnown) && !treatAsUnknown) {
extrOpts = PostProcess.getFoundOptima((k==0) ? sols : extrOpts, ((InterfaceMultimodalProblemKnown)prob).getRealOptima(), epsilonPhenoSpace[k], true);
} else {
double clustSig = (extrOptClustSig<0 ? (0.1*epsilonPhenoSpace[k]) : extrOptClustSig);
extrOpts = AbstractOptimizationProblem.extractPotentialOptima(prob, (k==0) ? sols : extrOpts, epsilonPhenoSpace[k], extrOptEpsFitConf, extrOptClustSig/* 2*epsilonPhenoSpace[k]*/, maxEvals);
// TODO should rather depend on the accuracy required than on a static cluster-distance ??
}
// System.out.println("ref "+k+":" + extrOpts.getStringRepresentation());
String prefix = "crit " + epsilonPhenoSpace[k];
if (listener!=null) listener.print(prefix + " found " + extrOpts.size());
foundOpts[k] = extrOpts.size();
if (treatAsUnknown || !(prob instanceof InterfaceMultimodalProblemKnown)) {
SolutionHistogram curHist = null, lastHist = SolutionHistogram.defaultEmptyHistogram(prob);
if (solHists!=null) curHist = solHists[k].cloneEmpty();
else curHist = SolutionHistogram.defaultEmptyHistogram(prob);
lastHist.updateFrom(sols, 0);
curHist.updateFrom(extrOpts, 0);
if (listener!=null) listener.println(", histogram: " + curHist);
if (solHists!=null) {
if (solHists[k]!=null) solHists[k].addHistogram(curHist);
else solHists[k]=curHist;
}
}
if (extrOpts.size() > 0) {
if (listener!=null) listener.print(" measures fit: ");
int critCnt = extrOpts.getEAIndividual(0).getFitness().length;
for (int i=0; i<critCnt; i++) if (listener!=null) listener.print(BeanInspector.toString(extrOpts.getFitnessMeasures(i)) + " ");
if (extrOpts.size()>1) {
if (listener!=null) listener.print("; phen: " + BeanInspector.toString(extrOpts.getPopulationMeasures(new PhenotypeMetric())));
if (listener!=null) listener.print("; eucl: " + BeanInspector.toString(extrOpts.getPopulationMeasures(new EuclideanMetric())));
if (listener!=null) listener.print("; popMetric: " + BeanInspector.toString(extrOpts.getPopulationMeasures()));
}
if (listener!=null) listener.println("");
// listener.println(" correlations of all (min,max,avg,med,var): "+ BeanInspector.toString(extrOpts.getCorrelations()));
for (int i=16; i>2; i/=2) {
Population bestN = extrOpts.getBestNIndividuals(i, -1);
listener.println(" phen. measures of top " + bestN.size() + ": " + BeanInspector.toString(bestN.getPopulationMeasures(new PhenotypeMetric())));
// ClusteringKMeans km = new ClusteringKMeans(); km.setK(2);
// km.initClustering(bestN);
// Population[] clusts = km.cluster(bestN, bestN);
// System.out.println("cluster sizes: " + clusts[0].size() + " " + clusts[1].size() + " " + clusts[2].size());
// listener.println(" correlations of top " + bestN.size() + ": " + BeanInspector.toString(bestN.getCorrelations()));
}
}
}
return foundOpts;
}
/** /**
* Select a local search range for a given method based on the clustering parameter. * Select a local search range for a given method based on the clustering parameter.
* If clustering was deactivated (sigma <= 0), then the default mutation step size is used. * If clustering was deactivated (sigma <= 0), then the default mutation step size is used.

View File

@ -13,6 +13,10 @@ public class PostProcessParams implements InterfacePostProcessParams, Serializab
protected double postProcessClusterSigma = 0.05; protected double postProcessClusterSigma = 0.05;
protected int printNBest = 10; protected int printNBest = 10;
protected PostProcessMethod method = PostProcessMethod.nelderMead; protected PostProcessMethod method = PostProcessMethod.nelderMead;
protected double[] accuracies = new double[]{0.01, 0.001, 0.0001};
protected double accAssumeConv = 1e-8;
protected int accMaxEval = -1;
private boolean withPlot = false; private boolean withPlot = false;
public PostProcessParams() { public PostProcessParams() {
@ -77,6 +81,9 @@ public class PostProcessParams implements InterfacePostProcessParams, Serializab
GenericObjectEditor.setShowProperty(this.getClass(), "printNBest", postProcess); GenericObjectEditor.setShowProperty(this.getClass(), "printNBest", postProcess);
GenericObjectEditor.setShowProperty(this.getClass(), "PPMethod", postProcess); GenericObjectEditor.setShowProperty(this.getClass(), "PPMethod", postProcess);
GenericObjectEditor.setShowProperty(this.getClass(), "withPlot", postProcess); GenericObjectEditor.setShowProperty(this.getClass(), "withPlot", postProcess);
GenericObjectEditor.setShowProperty(this.getClass(), "accuracies", postProcess);
GenericObjectEditor.setShowProperty(this.getClass(), "accAssumeConv", postProcess);
GenericObjectEditor.setShowProperty(this.getClass(), "accMaxEval", postProcess);
} }
public String doPostProcessingTipText() { public String doPostProcessingTipText() {
return "Toggle post processing of the solutions."; return "Toggle post processing of the solutions.";
@ -123,17 +130,16 @@ public class PostProcessParams implements InterfacePostProcessParams, Serializab
} }
public static String globalInfo() { public static String globalInfo() {
return "Combined clustering and hill-climbing for post-processing of solutions."; return "Combined clustering and local search post-processing of solutions. Additionally, accuracy checks can be performed on the " +
"returned solutions with different thresholds.";
} }
public PostProcessMethod getPPMethod() { public PostProcessMethod getPPMethod() {
return method; return method;
} }
public String PPMethodTipText() { public String PPMethodTipText() {
return "The method to use for post-processing."; return "The method to use for post-processing.";
} }
public void setPPMethod(PostProcessMethod meth) { public void setPPMethod(PostProcessMethod meth) {
method=meth; method=meth;
} }
@ -141,8 +147,39 @@ public class PostProcessParams implements InterfacePostProcessParams, Serializab
public boolean isWithPlot() { public boolean isWithPlot() {
return withPlot; return withPlot;
} }
public void setWithPlot(boolean withPlot) { public void setWithPlot(boolean withPlot) {
this.withPlot = withPlot; this.withPlot = withPlot;
} }
public double[] getAccuracies() {
return accuracies;
}
public void setAccuracies(double[] accuracies) {
this.accuracies = accuracies;
}
public String accuraciesTipText() {
return "The accuracy thresholds to be tested";
}
public double getAccAssumeConv() {
return accAssumeConv;
}
public void setAccAssumeConv(double accAssumeConv) {
this.accAssumeConv = accAssumeConv;
}
public String accAssumeConvTipText() {
return "The local search refinement is stopped earlier if the fitness changes less than this value";
}
public int getAccMaxEval() {
return accMaxEval;
}
public void setAccMaxEval(int accMaxEval) {
this.accMaxEval = accMaxEval;
}
public String accMaxEvalTipText() {
return "The maximal number of evaluations (times dimension) for accuracy check or -1 to use the default.";
}
} }

View File

@ -4,6 +4,8 @@ import java.util.Arrays;
import eva2.gui.BeanInspector; import eva2.gui.BeanInspector;
import eva2.server.go.populations.Population; import eva2.server.go.populations.Population;
import eva2.server.go.problems.AbstractOptimizationProblem;
import eva2.server.go.problems.InterfaceInterestingHistogram;
import eva2.tools.math.Mathematics; import eva2.tools.math.Mathematics;
public class SolutionHistogram { public class SolutionHistogram {
@ -100,12 +102,28 @@ public class SolutionHistogram {
*/ */
public static void createFitNormHistogram(Population pop, SolutionHistogram hist, int crit) { public static void createFitNormHistogram(Population pop, SolutionHistogram hist, int crit) {
hist.reset(); hist.reset();
if (pop.size()>0) {
if (pop.getBestFitness()[crit]<hist.getLowerBound()) {
System.err.println("Warning, population contains solution with lower fitness than lower bound of the histogram!");
// System.err.println("Pop was " + pop.getStringRepresentation());
System.err.println("Histogramm was " + hist.toString());
}
for (int i=0; i<hist.getNumBins(); i++) { for (int i=0; i<hist.getNumBins(); i++) {
hist.setEntry(i, PostProcess.filterFitnessIn(pop, hist.lowerBoundOfEntry(i), hist.upperBoundOfEntry(i), crit).size()); hist.setEntry(i, PostProcess.filterFitnessIn(pop, hist.lowerBoundOfEntry(i), hist.upperBoundOfEntry(i), crit).size());
} }
}
hist.setSingularHist(); hist.setSingularHist();
} }
public static SolutionHistogram defaultEmptyHistogram(AbstractOptimizationProblem prob) {
if (prob instanceof InterfaceInterestingHistogram) {
return ((InterfaceInterestingHistogram)prob).getHistogram();
} else {
// System.err.println("Unknown problem to make histogram for, returning default...");
return new SolutionHistogram(0, 100, 10);
}
}
/** /**
* Notify that a single histogram has been created. * Notify that a single histogram has been created.
*/ */

View File

@ -16,6 +16,7 @@ import eva2.server.go.operators.paramcontrol.ConstantParameters;
import eva2.server.go.operators.paramcontrol.InterfaceParameterControl; import eva2.server.go.operators.paramcontrol.InterfaceParameterControl;
import eva2.server.go.operators.postprocess.PostProcess; import eva2.server.go.operators.postprocess.PostProcess;
import eva2.server.go.operators.postprocess.PostProcessParams; import eva2.server.go.operators.postprocess.PostProcessParams;
import eva2.server.go.operators.postprocess.SolutionHistogram;
import eva2.server.go.operators.terminators.EvaluationTerminator; import eva2.server.go.operators.terminators.EvaluationTerminator;
import eva2.server.go.operators.terminators.GenerationTerminator; import eva2.server.go.operators.terminators.GenerationTerminator;
import eva2.server.go.populations.Population; import eva2.server.go.populations.Population;
@ -282,8 +283,9 @@ public class Processor extends Thread implements InterfaceProcessor, InterfacePo
// itself or (ii) sub-instances which have their own parameter controls. On these, the method // itself or (ii) sub-instances which have their own parameter controls. On these, the method
// is called recursively. // is called recursively.
if (controllerOrSubControllable instanceof InterfaceParameterControl) { if (controllerOrSubControllable instanceof InterfaceParameterControl) {
if (!((InterfaceParameterControl)paramCtrlReturn instanceof ConstantParameters)) args[0]=instance;
BeanInspector.callIfAvailable((InterfaceParameterControl)paramCtrlReturn, methodName, args); if (!((InterfaceParameterControl)controllerOrSubControllable instanceof ConstantParameters))
BeanInspector.callIfAvailable((InterfaceParameterControl)controllerOrSubControllable, methodName, args);
} else { } else {
args[0]=controllerOrSubControllable; args[0]=controllerOrSubControllable;
iterateParamCtrl(controllerOrSubControllable, methodName, args); iterateParamCtrl(controllerOrSubControllable, methodName, args);
@ -457,10 +459,17 @@ public class Processor extends Thread implements InterfaceProcessor, InterfacePo
// System.err.println("bad case in Processor::performNewPostProcessing "); // System.err.println("bad case in Processor::performNewPostProcessing ");
resultPop.SetFunctionCalls(goParams.getOptimizer().getPopulation().getFunctionCalls()); resultPop.SetFunctionCalls(goParams.getOptimizer().getPopulation().getFunctionCalls());
} }
if (!resultPop.contains(m_Statistics.getBestSolution())) { // if (!resultPop.contains(m_Statistics.getBestSolution())) {
resultPop.add(m_Statistics.getBestSolution()); // this is a minor cheat but guarantees that the best solution ever found is contained in the final results // resultPop.add(m_Statistics.getBestSolution());
resultPop.synchSize(); // this is a minor cheat but guarantees that the best solution ever found is contained in the final results
} // This was evil in case multiple runs were performed with PP, because the best of an earlier run is added which is confusing.
// the minor cheat should not be necessary anymore anyways, since the getAllSolutions() variant replaced the earlier getPopulation() call
// resultPop.synchSize();
// }
PostProcess.checkAccuracy((AbstractOptimizationProblem)goParams.getProblem(), resultPop, ppp.getAccuracies(), ppp.getAccAssumeConv(),
-1, ppp.getAccMaxEval(), (SolutionHistogram[]) null, true, listener);
resultPop = PostProcess.postProcess(ppp, resultPop, (AbstractOptimizationProblem)goParams.getProblem(), listener); resultPop = PostProcess.postProcess(ppp, resultPop, (AbstractOptimizationProblem)goParams.getProblem(), listener);
resPop = resultPop; resPop = resultPop;
return resultPop; return resultPop;