Files
eva2/src/javaeva/server/go/operators/postprocess/PostProcess.java

546 lines
27 KiB
Java

package javaeva.server.go.operators.postprocess;
import java.util.Collection;
import javaeva.OptimizerFactory;
import javaeva.OptimizerRunnable;
import javaeva.gui.BeanInspector;
import javaeva.server.go.InterfaceTerminator;
import javaeva.server.go.individuals.AbstractEAIndividual;
import javaeva.server.go.operators.cluster.ClusteringDensityBased;
import javaeva.server.go.operators.cluster.InterfaceClustering;
import javaeva.server.go.operators.distancemetric.InterfaceDistanceMetric;
import javaeva.server.go.operators.distancemetric.PhenotypeMetric;
import javaeva.server.go.operators.mutation.InterfaceMutation;
import javaeva.server.go.operators.mutation.MutateESFixedStepSize;
import javaeva.server.go.operators.terminators.EvaluationTerminator;
import javaeva.server.go.populations.Population;
import javaeva.server.go.problems.AbstractOptimizationProblem;
import javaeva.server.go.problems.FM0Problem;
import javaeva.server.go.problems.InterfaceMultimodalProblemKnown;
import javaeva.server.go.strategies.HillClimbing;
import javaeva.server.stat.InterfaceTextListener;
import javaeva.tools.Pair;
/**
* Postprocess a population / list of individuals to find out a set of distinct optima.
*
* @author mkron
*
*/
public class PostProcess {
protected static InterfaceDistanceMetric metric = new PhenotypeMetric();
private static final boolean TRACE = false;
// the default mutation step size for HC post processing
private static double defaultMutationStepSize = 0.01;
// used for hill climbing post processing and only alive during that period
private static OptimizerRunnable hcRunnable = null;
public static final int BEST_ONLY = 1;
public static final int BEST_RAND = 2;
public static final int RAND_ONLY = 3;
public static final int KEEP_LONERS = 11;
public static final int DISCARD_LONERS = 12;
public static final int LONERS_AS_CLUSTERS = 13;
/**
* This method returns a set of individuals corresponding to an optimum in a given list.
* The individuals returned are to be nearer than epsilon to a given optimum. For each optimum, there is
* returned zero or one individual at max.
* If there are several individuals close to an optimum, the fitter one or the closer one
* may be selected, indicated by the boolean flag bTakeFitter. This means, that an individual may be
* returned in more than one copy if the optima are close together and the individual lies in between.
* The returned array may contain null values if an optimum is not considered found at all.
*
* @param pop A population of possible solutions.
* @param optima a set of predefined optima
* @param epsilon the threshold up to which an optimum is considered found.
* @param bTakeFitter if true, the fitter of two close individuals is selected, otherwise the closer one
* @return an array of individuals corresponding to the optimum with the same index
*/
public static AbstractEAIndividual[] getFoundOptimaArray(Population pop, Population optima, double epsilon, boolean bTakeFitter) {
AbstractEAIndividual candidate, opt;
// Population result = new Population(5);
AbstractEAIndividual[] found = new AbstractEAIndividual[optima.size()];
double indDist;
for (int i = 0; i < found.length; i++) found[i] = null;
for (int i = 0; i < pop.size(); i++) {
candidate = (AbstractEAIndividual) pop.get(i);
for (int j = 0; j < optima.size(); j++) {
opt = (AbstractEAIndividual) optima.get(j);
indDist = metric.distance(candidate, opt);
if (found[j] == null) { // current optimum has not been found yet
if (indDist < epsilon) {
found[j] = (AbstractEAIndividual)candidate.clone();
// result.add(found[j]);
}
} else {// there was another one found. set the fitter one or the closer one
if (indDist < epsilon) {
if ((bTakeFitter && (candidate.isDominatingDebConstraints(found[j]))) // flag "fitter" and new one is fitter
|| (!bTakeFitter && (indDist < metric.distance(found[j], opt)))) { // flag "closer" and new one is closer
// int index = result.indexOf(found[j]); // do replacement
found[j] = (AbstractEAIndividual)candidate.clone();
// result.set(index, found[j]);
}
}
}
}
}
return found;
}
/**
* Convenience method for getFoundOptimaArray(), returning the same set of optima in a Population.
*
* @see getFoundOptimaArray(Population pop, Population optima, double epsilon, boolean bTakeFitter)
* @param pop A population of possible solutions.
* @param optima a set of known optima
* @param epsilon the threshold up to which an optimum is considered found.
* @param bTakeFitter if true, the fitter of two close individuals is selected, otherwise the closer one
* @return a Population of individuals corresponding to the given optima
*/
public static Population getFoundOptima(Population pop, Population optima, double epsilon, boolean bTakeFitter) {
Population result = new Population(5);
AbstractEAIndividual[] optsFound = getFoundOptimaArray(pop, optima, epsilon, bTakeFitter);
for (int i=0; i<optsFound.length; i++) {
if (optsFound[i] != null) result.add(optsFound[i]);
}
result.setPopulationSize(result.size());
return result;
}
/**
* Calls clusterBest with a ClusteringDensitiyBased clustering object with the given sigma and a
* minimum group size of 2.
* @see clusterBest(Population pop, InterfaceClustering clustering, double returnQuota, int lonerMode, int takeOverMode)
* @param pop
* @param sigmaCluster
* @param returnQuota
* @param lonerMode
* @param takeOverMode
* @return
*/
public static Population clusterBest(Population pop, double sigmaCluster, double returnQuota, int lonerMode, int takeOverMode) {
return clusterBest(pop, new ClusteringDensityBased(sigmaCluster, 2), returnQuota, lonerMode, takeOverMode);
}
/**
* Cluster the population. Return for every cluster a subset of representatives which are the best individuals
* or a random subset of the cluster or the best and a random subset. Returns shallow copies!
* returnQuota should be in [0,1] and defines, which percentage of individuals of each cluster is kept, however
* if returnQuota is > 0, at least one is kept.
* lonerMode defines whether loners are discarded, kept, or treated as clusters, meaning they are kept if returnQuota > 0.
* takOverMode defines whether, of a cluster with size > 1, which n individuals are kept. Either the n best only,
* or the single best and random n-1, or all n random.
*
* @param pop
* @param clustering
* @param returnQuota
* @param lonerMode
* @param takeOverMode
* @return for every cluster a population of representatives which are the best individuals or a random subset of the cluster
*/
public static Population clusterBest(Population pop, InterfaceClustering clustering, double returnQuota, int lonerMode, int takeOverMode) {
//cluster the undifferentiated population
Population result = new Population(10);
result.setSameParams(pop);
Population[] clusters = clustering.cluster(pop);
if (TRACE) {
System.out.println("found " + clusters.length + " clusters!");
int sum=0;
for (int j=0; j<clusters.length; j++) {
sum += clusters[j].size();
if (TRACE) System.out.print(j + " w " + clusters[j].size() + ", ");
}
System.out.println("\nsum was "+sum);
}
for (int j = 0; j < clusters.length; j++) {
if (j==0) { // cluster 0 contains non-assigned individuals
if (lonerMode == DISCARD_LONERS) continue; // loners are discarded
else if (lonerMode == KEEP_LONERS) {
result.addAll(clusters[j]); // loners are all kept
continue;
} // default case: treat loners just as the rest
if (lonerMode != LONERS_AS_CLUSTERS) System.err.println("invalid loner mode in (), default is treating them like clusters");
}
if (returnQuota >= 1) result.addAll((Collection<AbstractEAIndividual>)clusters[j]); // easy case
else {
int n = Math.max(1, (int)(returnQuota*clusters[j].size())); // return at least one per cluster!
switch (takeOverMode) {
case BEST_ONLY: // another easy case
result.addAll((Collection<AbstractEAIndividual>)(clusters[j].getBestNIndividuals(n)));
break;
case BEST_RAND:
Population exclude = new Population();
exclude.add(clusters[j].getBestEAIndividual());
result.add(exclude.getEAIndividual(0));
result.addAll(clusters[j].getRandNIndividualsExcept(n-1, exclude));
break;
case RAND_ONLY:
result.addAll(clusters[j].getRandNIndividuals(n));
break;
default: System.err.println("Unknown mode in PostProcess:clusterBest!"); break;
}
}
}
result.setPopulationSize(result.size());
return result;
}
public static double[] populationMeasures(Population pop) {
double[] measures = pop.getPopulationMeasures();
return measures;
}
/**
* Filter the individuals of a population which have a fitness norm within given bounds.
* Returns shallow copies!
*
* @param pop
* @param lower
* @param upper
* @return
*/
public static Population filterFitnessIn(Population pop, double lower, double upper) {
Population result = filterFitness(pop, upper, true);
return filterFitness(result, lower, false);
}
/**
* Filter the individuals of a population which have a fitness norm below a given value.
* Returns shallow copies!
*
* @param pop
* @param fitNorm
* @param bSmaller if true, return individuals with lower or equal, else with higher fitness norm only
* @return
*/
public static Population filterFitness(Population pop, double fitNorm, boolean bSmallerEq) {
AbstractEAIndividual indy;
Population result = new Population();
for (int i=0; i<pop.size(); i++) {
indy = pop.getEAIndividual(i);
if (bSmallerEq && (PhenotypeMetric.norm(indy.getFitness())<=fitNorm)) result.add(indy);
else if (!bSmallerEq && (PhenotypeMetric.norm(indy.getFitness())>fitNorm)) result.add(indy);
}
return result;
}
/**
* Create a fitness histogram of an evaluated population within the given interval and nBins number of bins.
* Therefore a bin is of size (upperBound-lowerBound)/nBins, and bin 0 starts at lowerBound.
* Returns an integer array with the number of individuals in each bin.
*
* @param pop the population to scan.
* @param lowerBound lower bound of the fitness interval
* @param upperBound upper bound of the fitness interval
* @param nBins number of bins
* @return an integer array with the number of individuals in each bin
* @see filterFitnessIn()
*/
public static int[] createFitNormHistogram(Population pop, double lowerBound, double upperBound, int nBins) {
int[] res = new int[nBins];
double lower = lowerBound;
double step = (upperBound - lowerBound) / nBins;
for (int i=0; i<nBins; i++) {
if (TRACE) System.out.println("checking between " + lower + " and " + (lower+step));
res[i] = filterFitnessIn(pop, lower, lower+step).size();
if (TRACE) System.out.println("found " + res[i]);
lower += step;
}
return res;
}
// /**
// * This method returns a set of individuals corresponding to an optimum in a given list.
// * The individuals returned are to be nearer than epsilon to a given optimum. It is not
// * guaranteed, however, that the best individuals are returned. For each optimum, there is
// * returned zero or one individual at max.
// * If the optima are close together (e.g. closer than epsilon), or there is no threshold given,
// * it may happen that an individual is not returned if it is second closest to one optimum
// * and closest to another one.
// *
// * @param pop A population of possible solutions.
// * @param optima a set of predefined optima
// * @param epsilon the threshold up to which an optimum is considered found.
// * @return a list of individuals corresponding to an optimum from a list.
// */
// public static List<AbstractEAIndividual> getClosestFoundOptima(Population pop, Population optima, double epsilon) {
// AbstractEAIndividual indy;
// ArrayList<AbstractEAIndividual> result = new ArrayList<AbstractEAIndividual>(5);
// AbstractEAIndividual[] foundIndy = new AbstractEAIndividual[optima.size()];
//
// for (int i = 0; i < pop.size(); i++) {
// indy = (AbstractEAIndividual) pop.get(i);
// IndexFitnessPair bestHit = getClosestIndy(indy, optima);
// if (foundIndy[bestHit.index] == null) { // there has no indy been assigned yet
// // assign the current indy if no epsilon is required, or epsilon-threshold is fulfilled
// if (epsilon < 0 || (bestHit.dist < epsilon)) foundIndy[bestHit.index] = indy;
// } else {
// // assign current indy only if it is better than the earlier assigned one
// // in that case, epsilon is fulfilled automatically
// double oldDist = metric.distance(foundIndy[bestHit.index], (AbstractEAIndividual)optima.get(bestHit.index));
// if (bestHit.dist < oldDist) foundIndy[bestHit.index] = indy;
// }
// }
// return result;
// }
/**
* Search a population and find the closest individual to a given individual. Return the
* best distance and corresponding index in a pair structure.
*
* @param indy
* @param pop
* @return index and distance to the closest individual in the population
*/
public static Pair<Double, Integer> getClosestIndy(AbstractEAIndividual indy, Population pop) {
double bestDist = -1, tmpDist = -1;
int bestIndex = -1;
AbstractEAIndividual opt;
for (int j = 0; j < pop.size(); j++) {
opt = (AbstractEAIndividual) pop.get(j);
tmpDist = metric.distance(indy, opt); // distance current indy to current optimum
if (bestDist < 0 || (tmpDist < bestDist)) { // we have a better hit
bestIndex = j;
bestDist = tmpDist;
}
}
return new Pair<Double, Integer>(bestDist, bestIndex);
}
/**
* Optimize a population with a default hill-climbing heuristic for a number of fitness evaluations.
* As mutation operator, a fixed step size ES mutation is used.
*
* @param pop
* @param problem
* @param maxSteps
* @param fixedStepSize
*/
public static int processWithHC(Population pop, AbstractOptimizationProblem problem, int maxSteps, double fixedStepSize) {
// pop.SetFunctionCalls(0); // or else optimization wont restart on an "old" population
// pop.setGenerationTo(0);
int stepsBef = pop.getFunctionCalls();
processWithHC(pop, problem, new EvaluationTerminator(pop.getFunctionCalls()+maxSteps), new MutateESFixedStepSize(fixedStepSize));
return pop.getFunctionCalls()-stepsBef;
}
public static int processWithHC(Population pop, AbstractOptimizationProblem problem, int maxSteps) {
return processWithHC(pop, problem, maxSteps, defaultMutationStepSize);
}
/**
* Optimize a population with a default hill-climbing heuristic with a given termination criterion and mutation operator.
*
* @param pop
* @param problem
* @param term
* @param mute
*/
public static void processWithHC(Population pop, AbstractOptimizationProblem problem, InterfaceTerminator term, InterfaceMutation mute) {
HillClimbing hc = new HillClimbing();
// HC depends heavily on the selected mutation operator!
hc.SetProblem(problem);
hc.SetMutationOperator(mute);
if (pop.size() != pop.getPopulationSize()) {
System.err.println(pop.size() + " vs. "+ pop.getPopulationSize());
System.err.println("warning: population size and vector size dont match! (PostProcess::processWithHC)");
}
hc.setPopulation(pop);
// hc.initByPopulation(pop, false);
hcRunnable = new OptimizerRunnable(OptimizerFactory.makeParams(hc, pop, problem, 0, term), null, true);
hcRunnable.getGOParams().setDoPostProcessing(false);
hcRunnable.run();
hcRunnable.getGOParams().setDoPostProcessing(true);
hcRunnable = null;
}
/**
* Stop the hill-climbing post processing if its currently running.
*/
public static void stopHC() {
if (hcRunnable != null) synchronized (hcRunnable) {
if (hcRunnable != null) hcRunnable.stopOpt();
}
}
public static void main(String[] args) {
AbstractOptimizationProblem problem = new FM0Problem();
InterfaceMultimodalProblemKnown mmp = (InterfaceMultimodalProblemKnown)problem;
OptimizerRunnable runnable = OptimizerFactory.getOptRunnable(OptimizerFactory.STD_GA, problem, 500, null);
runnable.run();
Population pop = runnable.getGOParams().getOptimizer().getPopulation();
// System.out.println("no optima found: " + mmp.getNumberOfFoundOptima(pop));
Population found = getFoundOptima(pop, mmp.getRealOptima(), 0.05, true);
System.out.println("all found (" + found.size() + "): " + BeanInspector.toString(found));
Pair<Population, Double> popD = new Pair<Population, Double>(pop, 1.);
int i=0;
int evalCnt = 0;
while (popD.tail() > 0.01) {
i++;
// public static PopDoublePair clusterHC(pop, problem, sigmaCluster, funCalls, keepClusterRatio, mute) {
popD = clusterHC(popD.head(), problem, 0.01, 1000 - (evalCnt % 1000), 0.5, new MutateESFixedStepSize(0.02));
evalCnt += popD.head().getFunctionCalls();
}
found = getFoundOptima(popD.head(), mmp.getRealOptima(), 0.05, true);
System.out.println("found at " + i + " (" + found.size() + "): " + BeanInspector.toString(found));
System.out.println("funcalls: " + evalCnt);
// System.out.println(BeanInspector.toString(pop.getMeanFitness()));
// System.out.println("no optima found: " + mmp.getNumberOfFoundOptima(pop));
// System.out.println("best after: " + AbstractEAIndividual.getDefaultStringRepresentation(pop.getBestEAIndividual()));
}
/**
* Cluster a population and reduce it by a certain ratio, then optimize the remaining individuals for a given number of function calls with a HC.
* Return a pair of the optimized population and the improvement in the mean fitness (not normed) that was achieved by the HC run. The returned
* individuals are deep clones, so the given population is not altered. Of a cluster of individuals, the given
* ratio of individuals is kept, more precisely, the best one is kept while the remaining are selected randomly. All loners are kept.
*
* @param pop the population to work on
* @param problem the target problem instance
* @param sigmaCluster minimum clustering distance
* @param funCalls number of function calls for the optimization step
* @param keepClusterRatio of a cluster of individuals, this ratio of individuals is kept for optimization
* @param mute the mutation operator to be used by the hill climber
* @return a pair of the optimized population and the improvement in the mean fitness (not normed) achieved by the HC run
*/
public static Pair<Population, Double> clusterHC(Population pop, AbstractOptimizationProblem problem, double sigmaCluster, int funCalls, double keepClusterRatio, InterfaceMutation mute) {
Population clust = (Population)clusterBest(pop, new ClusteringDensityBased(sigmaCluster, 2), keepClusterRatio, KEEP_LONERS, BEST_RAND).clone();
// System.out.println("keeping " + clust.size() + " for hc....");
double[] meanFit = clust.getMeanFitness();
processWithHC(clust, problem, new EvaluationTerminator(pop.getFunctionCalls()+funCalls), mute);
double improvement = PhenotypeMetric.euclidianDistance(meanFit, clust.getMeanFitness());
if (TRACE) System.out.println("improvement by " + improvement);
return new Pair<Population, Double>(clust, improvement);
}
// /**
// * Do some post processing on a multimodal problem. If the real optima are known, only the number of
// * found optima is printed. Otherwise, the population is clustered and for the population of cluster-representatives,
// * some diversity measures and a fitness histogram are printed.
// *
// * @see Population.getPopulationMeasures()
// * @see createFitNormHistogram
// * @param mmProb the target problem
// * @param pop the solution set
// * @param sigmaCluster the min clustering distance
// * @param out a PrintStream for data output
// * @param minBnd lower fitness bound
// * @param maxBnd upper fitness bound
// * @param bins number of bins for the fitness histogram
// * @return
// */
// public static Population outputResult(AbstractOptimizationProblem mmProb, Population pop, double sigmaCluster, PrintStream out, double minBnd, double maxBnd, int bins, int hcSteps) {
// ClusteringDensityBased clust = new ClusteringDensityBased(sigmaCluster);
// clust.setMinimumGroupSize(2);
// Population clusteredBest = clusterBest(pop, clust, 0, KEEP_LONERS, BEST_ONLY);
// if (hcSteps > 0) { // HC post process
// Population tmpPop = (Population)clusteredBest.clone();
// processWithHC(tmpPop, (AbstractOptimizationProblem)mmProb, hcSteps, 0.001);
// clusteredBest = clusterBest(tmpPop, clust, 0, KEEP_LONERS, BEST_ONLY);
// }
// double[] meas = clusteredBest.getPopulationMeasures();
// int[] sols = createFitNormHistogram(clusteredBest, minBnd, maxBnd, bins);
// out.println("measures: " + BeanInspector.toString(meas));
// out.println("solution hist.: " + BeanInspector.toString(sols));
//
// Object[] bestArr = clusteredBest.toArray();
// for (Object locOpt : bestArr) {
//// out.print((AbstractEAIndividual.getDefaultDataString((IndividualInterface)locOpt)));
// out.println(AbstractEAIndividual.getDefaultStringRepresentation(((AbstractEAIndividual)locOpt)));
// }
// return clusteredBest;
// }
// public static Population outputResultKnown(InterfaceMultimodalProblemKnown mmProb, Population pop, double sigmaCluster, PrintStream out, double minBnd, double maxBnd, int bins) {
// Population found = getFoundOptima(pop, ((InterfaceMultimodalProblemKnown)mmProb).getRealOptima(), ((InterfaceMultimodalProblemKnown)mmProb).getEpsilon(), true);
// for (double epsilon=0.1; epsilon > 0.00000001; epsilon/=10.) {
// //out.println("no optima found: " + ((InterfaceMultimodalProblemKnown)mmProb).getNumberOfFoundOptima(pop));
// out.println("found " + getFoundOptima(pop, ((InterfaceMultimodalProblemKnown)mmProb).getRealOptima(), epsilon, true).size() + " for epsilon = " + epsilon);
// }
// out.println("max peak ratio is " + mmProb.getMaximumPeakRatio(found));
// return found;
// }
/**
* Do some data output for multimodal problems with known optima. The listener may be null, but then the method is
* not really doing much at this state.
*/
public static void procMultiModalKnown(Population solutions, InterfaceMultimodalProblemKnown mmkProb, InterfaceTextListener listener) {
// Population found = getFoundOptima(solutions, mmkProb.getRealOptima(), mmkProb.getEpsilon(), true);
if (listener != null) {
listener.println("default epsilon is " + mmkProb.getEpsilon());
listener.println("max peak ratio is " + mmkProb.getMaximumPeakRatio(getFoundOptima(solutions, mmkProb.getRealOptima(), mmkProb.getEpsilon(), true)));
}
for (double epsilon=0.1; epsilon > 0.00000001; epsilon/=10.) {
// out.println("no optima found: " + ((InterfaceMultimodalProblemKnown)mmProb).getNumberOfFoundOptima(pop));
if (listener != null) listener.println("found " + getFoundOptima(solutions, mmkProb.getRealOptima(), epsilon, true).size() + " for epsilon = " + epsilon);
}
}
/**
* Universal post processing method, receiving parameter instance for specification.
* Optional clustering and HC step, output contains population measures, fitness histogram and
* a list of solutions after post processing.
*
* @param params
* @param inputPop
* @param problem
* @param listener
* @return the clustered, post-processed population
*/
public static Population postProcess(InterfacePostProcessParams params, Population inputPop, AbstractOptimizationProblem problem, InterfaceTextListener listener) {
if (params.isDoPostProcessing()) {
Population clusteredPop, outputPop;
if (params.getPostProcessClusterSigma() > 0) {
clusteredPop = (Population)PostProcess.clusterBest(inputPop, params.getPostProcessClusterSigma(), 0, PostProcess.KEEP_LONERS, PostProcess.BEST_ONLY).clone();
if (clusteredPop.size() < inputPop.size()) {
if (listener != null) listener.println("Initial clustering reduced population size from " + inputPop.size() + " to " + clusteredPop.size());
} else if (listener != null) listener.println("Initial clustering yielded no size reduction.");
} else clusteredPop = inputPop;
if (params.getPostProcessSteps() > 0) {
int stepsDone = processWithHC(clusteredPop, problem, params.getPostProcessSteps());
if (listener != null) listener.println("HC post processing: " + stepsDone + " steps done.");
// some individuals may have now converged again
if (params.getPostProcessClusterSigma() > 0) {
// so if wished, cluster again.
outputPop = (Population)PostProcess.clusterBest(clusteredPop, params.getPostProcessClusterSigma(), 0, PostProcess.KEEP_LONERS, PostProcess.BEST_ONLY).clone();
if (outputPop.size() < clusteredPop.size()) {
if (listener != null) listener.println("Second clustering reduced population size from " + clusteredPop.size() + " to " + outputPop.size());
} else if (listener != null) listener.println("Second clustering yielded no size reduction.");
} else outputPop = clusteredPop;
} else outputPop = clusteredPop;
double upBnd = PhenotypeMetric.norm(outputPop.getWorstEAIndividual().getFitness())*1.1;
upBnd = Math.pow(10,Math.floor(Math.log10(upBnd)+1));
double lowBnd = 0;
int[] sols = PostProcess.createFitNormHistogram(outputPop, lowBnd, upBnd, 20);
// PostProcessInterim.outputResult((AbstractOptimizationProblem)goParams.getProblem(), outputPop, 0.01, System.out, 0, 2000, 20, goParams.getPostProcessSteps());
if (outputPop.size()>1) {
if (listener != null) listener.println("measures: " + BeanInspector.toString(outputPop.getPopulationMeasures()));
if (listener != null) listener.println("solution histogram in [" + lowBnd + "," + upBnd + "]: " + BeanInspector.toString(sols));
}
//////////// multimodal data output?
if (problem instanceof InterfaceMultimodalProblemKnown) procMultiModalKnown(outputPop, (InterfaceMultimodalProblemKnown)problem, listener);
Population nBestPop = outputPop.getBestNIndividuals(params.getPrintNBest()); // n 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() + ")") : ""));
//////////// output some individual data
if (listener != null) for (int i=0; i<nBestPop.size(); i++) {
listener.println(AbstractEAIndividual.getDefaultStringRepresentation(nBestPop.getEAIndividual(i)));
}
return nBestPop;
} else return inputPop;
}
}