diff --git a/src/eva2/server/go/individuals/AbstractEAIndividualComparator.java b/src/eva2/server/go/individuals/AbstractEAIndividualComparator.java index a153f1d8..5a8316f8 100644 --- a/src/eva2/server/go/individuals/AbstractEAIndividualComparator.java +++ b/src/eva2/server/go/individuals/AbstractEAIndividualComparator.java @@ -21,7 +21,6 @@ import java.util.Comparator; * */ public class AbstractEAIndividualComparator implements Comparator, Serializable { - // flag whether a data field should be used. private String indyDataKey = ""; private int fitCriterion = -1; @@ -51,7 +50,7 @@ public class AbstractEAIndividualComparator implements Comparator, Seria } /** - * Constructor for a specific fitness criterion in the multiobjective case. + * Constructor for a specific fitness criterion in the multi-objective case. * For comparison, only the given fitness criterion is used if it is >= 0. * * @param fitnessCriterion @@ -60,10 +59,34 @@ public class AbstractEAIndividualComparator implements Comparator, Seria this("", fitnessCriterion, true); } + /** + * Constructor for a specific fitness criterion in the multi-objective case. + * For comparison, only the given fitness criterion is used if it is >= 0. + * If preferFeasible is true, feasible individuals will always be prefered. + * + * @param fitIndex + * @param preferFeasible + */ public AbstractEAIndividualComparator(int fitIndex, boolean preferFeasible) { this("", fitIndex, preferFeasible); } + @Override + public boolean equals(Object other) { + if (other instanceof AbstractEAIndividualComparator) { + AbstractEAIndividualComparator o = (AbstractEAIndividualComparator)other; + if ((indyDataKey==o.indyDataKey) || (indyDataKey!=null && (indyDataKey.equals(o.indyDataKey)))) { + if ((fitCriterion == o.fitCriterion) && (preferFeasible == o.preferFeasible)) return true; + } + } + return false; + } + + @Override + public int hashCode() { + return indyDataKey.hashCode()+100+fitCriterion+(preferFeasible ? 7 : 13); + } + /** * Generic constructor. * diff --git a/src/eva2/server/go/individuals/IndividualWeightedFitnessComparator.java b/src/eva2/server/go/individuals/IndividualWeightedFitnessComparator.java new file mode 100644 index 00000000..2e39ff4f --- /dev/null +++ b/src/eva2/server/go/individuals/IndividualWeightedFitnessComparator.java @@ -0,0 +1,110 @@ +package eva2.server.go.individuals; + +import java.io.Serializable; +import java.util.Comparator; + +import eva2.tools.EVAERROR; + +/** + * Compare two individuals based on a linear combination of the fitness values. + * + * @author mkron + * + */ +public class IndividualWeightedFitnessComparator implements Comparator, Serializable { + private double [] fitWeights = null; + + public IndividualWeightedFitnessComparator(double[] weights) { + setFitWeights(weights); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof IndividualWeightedFitnessComparator) { + IndividualWeightedFitnessComparator o = (IndividualWeightedFitnessComparator)obj; + if (fitWeights==null && (o.fitWeights==null)) return true; + if (fitWeights==null || o.fitWeights==null) return false; + // now both are non null: + if (fitWeights.length==o.fitWeights.length) { + for (int i=0; i score2) return 1; + else return 0; + } + + private double calcScore(double[] f) { + if (f==null || fitWeights==null) throw new RuntimeException("Error, missing information in " + this.getClass()); + if (f.length!=fitWeights.length) { + if (f.length)(clusters[j].getBestNIndividuals(n))); + result.addAll((Collection)(clusters[j].getBestNIndividuals(n, -1))); break; case BEST_RAND: Population exclude = new Population(); @@ -1075,7 +1075,7 @@ public class PostProcess { //////////// multimodal data output evaluateMultiModal(outputPop, problem, listener); - Population nBestPop = outputPop.getBestNIndividuals(params.getPrintNBest()); // n individuals are returned and sorted, all of them if n<=0 + Population nBestPop = outputPop.getBestNIndividuals(params.getPrintNBest(), -1); // 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() + ")") : (" (" + nBestPop.size() + ")") )); //////////// output some individual data if (listener != null) for (int i=0; i= m_stagTime) + " after " + (pop.getFunctionCalls() - popFitCalls)); - return (pop.getFunctionCalls() - popFitCalls) >= m_stagTime; - } else {// by generation -// System.out.println("stagnationTimeHasPassed returns " + ((pop.getFunctionCalls() - popGens) >= m_stagTime) + " after " + (pop.getFunctionCalls() - popGens)); - return (pop.getGeneration() - popGens) >= m_stagTime; - } - } - - /** - * - */ - public void setConvergenceThreshold(double x) { - convThresh = x; - } - - /** - * - */ - public double getConvergenceThreshold() { - return convThresh; - } - - public String convergenceThresholdTipText() { - return "Terminate if the fitness has not improved by this percentage / absolute value for a whole stagnation time period"; - } - - /** - * - */ - public void setStagnationTime(int k) { - m_stagTime = k; - } - - /** - * - */ - public int getStagnationTime() { - return m_stagTime; - } - - public String stagnationTimeTipText() { - return "Terminate if the population has not improved for this time"; - } - - - /** - * @return the stagnationTimeIn - */ - public SelectedTag getStagnationMeasure() { - return stagnationMeasure; - } - - /** - * @param stagnationTimeIn the stagnationTimeIn to set - */ - public void setStagnationMeasure(SelectedTag stagnationTimeIn) { - this.stagnationMeasure = stagnationTimeIn; - } - - public String stagnationMeasureTipText() { - return "Stagnation time is measured in fitness calls or generations, to be selected here."; - } - - /** - * @return the convergenceCondition - */ - public SelectedTag getConvergenceCondition() { - return convergenceCondition; - } - - /** - * @param convergenceCondition the convergenceCondition to set - */ - public void setConvergenceCondition(SelectedTag convergenceCondition) { - this.convergenceCondition = convergenceCondition; - } - - public String convergenceConditionTipText() { - return "Select between absolute and relative convergence condition"; + @Override + protected String getMeasureName() { + return "Fitness"; } } \ No newline at end of file diff --git a/src/eva2/server/go/operators/terminators/ParetoMetricTerminator.java b/src/eva2/server/go/operators/terminators/ParetoMetricTerminator.java new file mode 100644 index 00000000..23487264 --- /dev/null +++ b/src/eva2/server/go/operators/terminators/ParetoMetricTerminator.java @@ -0,0 +1,102 @@ +package eva2.server.go.operators.terminators; + +import java.io.Serializable; + +import eva2.gui.BeanInspector; +import eva2.server.go.PopulationInterface; +import eva2.server.go.operators.paretofrontmetrics.InterfaceParetoFrontMetric; +import eva2.server.go.operators.paretofrontmetrics.MetricS; +import eva2.server.go.populations.Population; +import eva2.server.go.problems.AbstractMultiObjectiveOptimizationProblem; +import eva2.server.go.problems.InterfaceOptimizationProblem; +import eva2.tools.EVAERROR; + +/** + * Employ a pareto metric to determine convergence of a population. Requires to be run + * with a AbstractMultiObjectiveOptimizationProblem instance since the metric depend + * on the fitness range. + * The metric may be employed on the current population or the current pareto front + * maintained by the problem instance. + * + * @author mkron + * + */ +public class ParetoMetricTerminator extends PopulationMeasureTerminator implements Serializable { + private InterfaceParetoFrontMetric pMetric = new MetricS(); + AbstractMultiObjectiveOptimizationProblem moProb=null; + private boolean useCurrentPop = false; + + public ParetoMetricTerminator() { + moProb=null; + } + + //public PopulationMeasureTerminator(double convergenceThreshold, int stagnationTime, boolean bFitCallBased, ChangeTypeEnum detectChangeType, boolean bImprovement) { + + public ParetoMetricTerminator(InterfaceParetoFrontMetric metric, boolean useCurrentPop, double convergenceThreshold, int stagnationTime, boolean bFitCallBased, ChangeTypeEnum detectChangeType, boolean bImprovement) { + super(convergenceThreshold, stagnationTime, bFitCallBased, detectChangeType, bImprovement); + this.pMetric = metric; + this.useCurrentPop = useCurrentPop; + } + + public ParetoMetricTerminator(ParetoMetricTerminator o) { + super(o); + this.pMetric = (InterfaceParetoFrontMetric)o.pMetric.clone(); + this.moProb = o.moProb; + this.useCurrentPop = o.useCurrentPop; + } + + @Override + public void init(InterfaceOptimizationProblem prob) { + super.init(prob); + if (prob instanceof AbstractMultiObjectiveOptimizationProblem) moProb = (AbstractMultiObjectiveOptimizationProblem)prob; + else { + moProb = null; + EVAERROR.errorMsgOnce("Error, " + this.getClass() + " works only with problems inheriting from " + AbstractMultiObjectiveOptimizationProblem.class + "!"); + } + } + + @Override + protected double calcInitialMeasure(PopulationInterface pop) { + if (moProb==null) return Double.MAX_VALUE; + else { + if (isUseCurrentPop()) return getParetoMetric().calculateMetricOn((Population)pop, moProb); + else return getParetoMetric().calculateMetricOn(moProb.getLocalParetoFront(), moProb); + } + } + + @Override + protected double calcPopulationMeasure(PopulationInterface pop) { + return calcInitialMeasure(pop); + } + + @Override + protected String getMeasureName() { + String metricName=null; + try { + metricName = (String)BeanInspector.callIfAvailable(getParetoMetric(), "getName", null); + } catch(ClassCastException e) {metricName=null;} + + if (metricName==null) return "ParetoMetric"; + else return metricName; + } + + public void setParetoMetric(InterfaceParetoFrontMetric pMetric) { + this.pMetric = pMetric; + } + public InterfaceParetoFrontMetric getParetoMetric() { + return pMetric; + } + public String paretoMetricTipText() { + return "The pareto metric to use"; + } + + public void setUseCurrentPop(boolean useCurrentPop) { + this.useCurrentPop = useCurrentPop; + } + public boolean isUseCurrentPop() { + return useCurrentPop; + } + public String useCurrentPopTipText() { + return "If true, the current population is used, otherwise the pareto front of the multi-objective problem instance is used"; + } +} diff --git a/src/eva2/server/go/operators/terminators/PhenotypeConvergenceTerminator.java b/src/eva2/server/go/operators/terminators/PhenotypeConvergenceTerminator.java index 1e188d4d..f102aaba 100644 --- a/src/eva2/server/go/operators/terminators/PhenotypeConvergenceTerminator.java +++ b/src/eva2/server/go/operators/terminators/PhenotypeConvergenceTerminator.java @@ -1,53 +1,64 @@ package eva2.server.go.operators.terminators; -import eva2.gui.BeanInspector; -import eva2.server.go.IndividualInterface; import eva2.server.go.InterfaceTerminator; import eva2.server.go.PopulationInterface; import eva2.server.go.individuals.AbstractEAIndividual; import eva2.server.go.operators.distancemetric.PhenotypeMetric; import eva2.server.go.problems.InterfaceOptimizationProblem; -public class PhenotypeConvergenceTerminator extends FitnessConvergenceTerminator implements InterfaceTerminator { +public class PhenotypeConvergenceTerminator extends PopulationMeasureTerminator implements InterfaceTerminator { AbstractEAIndividual oldIndy = null; - double oldPhenNorm = 0; + private PhenotypeMetric pMetric = null; +// double oldPhenNorm = 0; public PhenotypeConvergenceTerminator() { super(); - tagString = "Phenotype converged"; + pMetric = new PhenotypeMetric(); + } + + public PhenotypeConvergenceTerminator(double thresh, int stagnTime, boolean bFitCallBased, ChangeTypeEnum changeType, boolean bImprovement) { + super(thresh, stagnTime, bFitCallBased, changeType, bImprovement); + pMetric = new PhenotypeMetric(); } - public PhenotypeConvergenceTerminator(double thresh, int stagnTime, boolean bFitCallBased, boolean bAbsolute) { - super(thresh, stagnTime, bFitCallBased, bAbsolute); + public PhenotypeConvergenceTerminator(PhenotypeConvergenceTerminator o) { + super(o); + oldIndy = (AbstractEAIndividual)o.oldIndy.clone(); + pMetric = (PhenotypeMetric)o.pMetric.clone(); +// oldPhenNorm = o.oldPhenNorm; } public void init(InterfaceOptimizationProblem prob) { super.init(prob); - tagString = "Phenotype converged"; +// oldPhenNorm = 0; + oldIndy = null; } - - /** - * Return true if |oldPhen - curPhen| < |oldPhen| * thresh (relative case) - * and if |oldFit - curFit| < thresh (absolute case). - * - * @param curFit - * @return - */ - protected boolean isStillConverged(PopulationInterface pop) { - double dist = pMetric.distance(oldIndy, (AbstractEAIndividual)pop.getBestIndividual()); - boolean ret; - if (getConvergenceCondition().isSelectedString("Relative")) { - ret = (dist < (oldPhenNorm * convThresh)); - } else { - ret = (dist < convThresh); - } - if (TRACE) System.out.println("isStillConverged returns " + ret + ", dist " + dist + ", old indy " + BeanInspector.toString(oldIndy) + ", cur indy" + BeanInspector.toString(pop.getBestIndividual())); - return ret; + + @Override + protected double calcInitialMeasure(PopulationInterface pop) { + oldIndy = (AbstractEAIndividual)((AbstractEAIndividual)pop.getBestIndividual()).clone(); +// oldPhenNorm = PhenotypeMetric.norm(oldIndy); + return Double.MAX_VALUE; } + @Override + protected double calcPopulationMeasure(PopulationInterface pop) { + return pMetric.distance(oldIndy, (AbstractEAIndividual)pop.getBestIndividual()); + } + + @Override protected void saveState(PopulationInterface Pop) { super.saveState(Pop); oldIndy = (AbstractEAIndividual)((AbstractEAIndividual)Pop.getBestIndividual()).clone(); - oldPhenNorm = PhenotypeMetric.norm(oldIndy); +// oldPhenNorm = PhenotypeMetric.norm(oldIndy); + } + + @Override + protected String getMeasureName() { + return "Phenotype"; + } + + public static String globalInfo() { + return "Terminate if the best individual of the current population moved less than a threshold within phenotypic space."; } } diff --git a/src/eva2/server/go/operators/terminators/PopulationArchiveTerminator.java b/src/eva2/server/go/operators/terminators/PopulationArchiveTerminator.java new file mode 100644 index 00000000..b56798c9 --- /dev/null +++ b/src/eva2/server/go/operators/terminators/PopulationArchiveTerminator.java @@ -0,0 +1,64 @@ +package eva2.server.go.operators.terminators; + +import eva2.server.go.PopulationInterface; +import eva2.server.go.individuals.IndividualWeightedFitnessComparator; +import eva2.server.go.populations.Population; + +/** + * Terminate if a score based on the archive of the population converges. + * Note that this only works if the archive is filled with sensible data. + * + * @author mkron + * + */ +public class PopulationArchiveTerminator extends PopulationMeasureTerminator { + IndividualWeightedFitnessComparator wfComp = new IndividualWeightedFitnessComparator(null); + +// private boolean isStillConverged(PopulationInterface pop) { +// Population archive = ((Population)pop).getArchive(); +// if (archive==null || (archive.size()<1)) { +// System.err.println("Error, population had no archive in " + this.getClass()); +// return false; +// } else { +// double bestScore = Population.getScore(archive.getEAIndividual(0), fitWeights); +// for (int i=1; i=oldScore) return true; +// else { +// oldScore=bestScore; +// return false; +// } +// } +// } + + @Override + protected double calcInitialMeasure(PopulationInterface pop) { + Population archive = ((Population)pop).getArchive(); + if (archive==null || (archive.size()<1)) return Double.MAX_VALUE; + else return wfComp.calcScore(archive.getBestEAIndividual(wfComp)); + } + + @Override + protected double calcPopulationMeasure(PopulationInterface pop) { + Population archive = ((Population)pop).getArchive(); + if (archive==null || (archive.size()<1)) return Double.MAX_VALUE; + else return wfComp.calcScore(archive.getBestEAIndividual(wfComp)); + } + + @Override + protected String getMeasureName() { + return "Archive Weighted Score"; + } + + public double[] getFitWeights() { + return wfComp.getFitWeights(); + } + public void setFitWeights(double[] fWeights) { + wfComp.setFitWeights(fWeights); + } + public String fitWeightsTipText() { + return wfComp.fitWeightsTipText(); + } +} diff --git a/src/eva2/server/go/operators/terminators/PopulationMeasureTerminator.java b/src/eva2/server/go/operators/terminators/PopulationMeasureTerminator.java new file mode 100644 index 00000000..d64d50f1 --- /dev/null +++ b/src/eva2/server/go/operators/terminators/PopulationMeasureTerminator.java @@ -0,0 +1,326 @@ +package eva2.server.go.operators.terminators; + +import java.io.Serializable; + +import eva2.gui.BeanInspector; +import eva2.server.go.InterfaceTerminator; +import eva2.server.go.PopulationInterface; +import eva2.server.go.populations.InterfaceSolutionSet; +import eva2.server.go.problems.InterfaceOptimizationProblem; +import eva2.tools.SelectedTag; + +/** + * Abstract class giving the framework for a terminator that is based on + * a population measure converging for a given time (number of evaluations or + * generations). + * The class detects changes of a population measure over time and may signal convergence + * if the measure m(P) behaved in a certain way for a given time. Convergence may + * be signaled + * - if the measure reached absolute values below convThresh (absolute value), + * - if the measure remained within m(P)+/-convThresh (absolute change), + * - if the measure remained above m(P)-convThresh (absolute change and regard improvement only), + * - if the measure remained within m(P)*[1-convThresh, 1+convThresh] (relative change), + * - if the measure remained above m(P)*(1-convThresh) (relative change and regard improvement only). + * + * @author mkron + * + */ +public abstract class PopulationMeasureTerminator implements InterfaceTerminator, +Serializable { + public enum ChangeTypeEnum {relativeChange, absoluteChange, absoluteValue}; + + protected static boolean TRACE = false; + private double convThresh = 0.01; //, convThreshLower=0.02; + private double oldMeasure = -1; + private int stagTime = 1000; + private int oldPopFitCalls = 1000; + private int oldPopGens = 1000; + private boolean firstTime = true; + private SelectedTag stagnationMeasure = new SelectedTag("Fitness calls", "Generations"); +// private SelectedTag convCondition = new SelectedTag("Relative change", "Absolute change", "Absolute value"); + private ChangeTypeEnum changeType = ChangeTypeEnum.relativeChange; + private SelectedTag condImprovementOrChange = new SelectedTag("Improvement", "Improvement and Deterioration"); + protected String msg="Not terminated."; + + public PopulationMeasureTerminator() {} + + public PopulationMeasureTerminator(double convergenceThreshold, int stagnationTime, boolean bFitCallBased, ChangeTypeEnum detectChangeType, boolean bImprovement) { + convThresh = convergenceThreshold; + stagTime = stagnationTime; + stagnationMeasure.setSelectedTag(bFitCallBased ? 0 : 1); +// convergenceCondition.setSelectedTag(bAbsolute ? 1 : 0); + changeType = detectChangeType; + condImprovementOrChange.setSelectedTag(bImprovement ? 0 : 1); + } + + public PopulationMeasureTerminator(PopulationMeasureTerminator o) { + convThresh = o.convThresh; + stagTime = o.stagTime; + oldPopFitCalls = o.oldPopFitCalls; + oldPopGens = o.oldPopGens; + firstTime = o.firstTime; +// oldFit = o.oldFit.clone(); +// oldNorm = o.oldNorm; + msg = o.msg; + this.stagnationMeasure.setSelectedTag(o.stagnationMeasure.getSelectedTagID()); +// this.convergenceCondition.setSelectedTag(o.convergenceCondition.getSelectedTagID()); + this.changeType = o.changeType; + this.condImprovementOrChange.setSelectedTag(o.condImprovementOrChange.getSelectedTagID()); + } + +// public void hideHideable() { +// setConvergenceCondition(getConvergenceCondition()); +// } + +// public PopulationMeasureTerminator() { +// pMetric = new PhenotypeMetric(); +// } +// +// public PopulationMeasureTerminator(double thresh, int stagnPeriod, boolean bFitCallBased, boolean bAbsolute) { +// pMetric = new PhenotypeMetric(); +// convThresh = thresh; +// this.m_stagTime = stagnPeriod; +// if (bFitCallBased) stagnationMeasure.setSelectedTag("Fitness calls"); +// else stagnationMeasure.setSelectedTag("Generations"); +// if (bAbsolute) convergenceCondition.setSelectedTag("Absolute"); +// else convergenceCondition.setSelectedTag("Relative"); +// } +// +// public PopulationMeasureTerminator(PopulationMeasureTerminator other) { +// pMetric = new PhenotypeMetric(); +// convThresh = other.convThresh; +// this.m_stagTime = other.m_stagTime; +// stagnationMeasure.setSelectedTag(other.getStagnationMeasure().getSelectedTagID()); +// convergenceCondition.setSelectedTag(other.getConvergenceCondition().getSelectedTagID()); +// } + + public static String globalInfo() { + return "Stop if a convergence criterion has been met."; + } + + public void init(InterfaceOptimizationProblem prob) { + firstTime = true; + msg = "Not terminated."; +// oldFit = null; +// oldNorm=-1; + oldPopFitCalls=-1; + oldPopGens=-1; + } + + public boolean isTerminated(InterfaceSolutionSet solSet) { + return isTerminated(solSet.getCurrentPopulation()); + } + + public boolean isTerminated(PopulationInterface pop) { + if (!firstTime && isStillConverged(pop)) { + if (TRACE) System.out.println("Converged at " + pop.getGeneration() + "/" + pop.getFunctionCalls() + ", measure " + calcPopulationMeasure(pop)); + if (stagnationTimeHasPassed(pop)) { + // population hasnt changed much for max time, criterion is met + msg = getTerminationMessage(); + return true; + } else { + // population hasnt changed much for i= allowedLower); + // Old Version: +// if (isRelativeConvergence()) { +// double delta = oldMeasure*convThresh; +// if (doCheckImprovement()) ret = (measure >= (oldMeasure - delta)); +// else ret = ((measure >= (oldMeasure-delta)) && (measure <= (oldMeasure+delta))); // check for rel. change which must be within +/- thresh +// } else { // check absolute values +// if (doCheckImprovement()) ret = (measure < oldMeasure+convThresh); // absolute improvement below fixed number +// else ret = ((measure < oldMeasure+convThresh) && (measure > oldMeasure-convThresh)); // absolute change within fixed range +// } + if (TRACE) System.out.println("isStillConverged returns " + ret + ", measure " + measure + ", old measure " + BeanInspector.toString(oldMeasure) + ", bounds: [" + allowedLower + " , " + allowedUpper + "]"); + return ret; + } + + public boolean doCheckImprovement() { + return condImprovementOrChange.isSelectedString("Improvement"); + } + + public boolean isRelativeConvergence() { + return changeType==ChangeTypeEnum.relativeChange; + } + + /** + * Return true if the defined stagnation time (function calls or generations) has passed + * since the last noteable change. + * + * @param pop + * @return + */ + private boolean stagnationTimeHasPassed(PopulationInterface pop) { + if (stagnationMeasure.isSelectedString("Fitness calls")) { // by fitness calls +// System.out.println("stagnationTimeHasPassed returns " + ((pop.getFunctionCalls() - popFitCalls) >= m_stagTime) + " after " + (pop.getFunctionCalls() - popFitCalls)); + return (pop.getFunctionCalls() - oldPopFitCalls) >= stagTime; + } else {// by generation +// System.out.println("stagnationTimeHasPassed returns " + ((pop.getFunctionCalls() - popGens) >= m_stagTime) + " after " + (pop.getFunctionCalls() - popGens)); + return (pop.getGeneration() - oldPopGens) >= stagTime; + } + } + + public void setConvergenceThreshold(double x) { + convThresh = x; + } + public double getConvergenceThreshold() { + return convThresh; + } + public String convergenceThresholdTipText() { + return "Ratio of improvement or absolute value of improvement or change to determine convergence."; + } + +// public void setConvergenceThresholdLower(double x) { +// convThreshLower = x; +// } +// public double getConvergenceThresholdLower() { +// return convThreshLower; +// } +// public String convergenceThresholdUpperTipText() { +// return "Lower threshold value in case of detecting absolute change, meaning the bounds [measure-convThresh,measure+convThresh] must be kept to assume convergence."; +// } + + public void setStagnationTime(int k) { + stagTime = k; + } + public int getStagnationTime() { + return stagTime; + } + public String stagnationTimeTipText() { + return "Terminate if the population has not improved for this time"; + } + + public SelectedTag getStagnationMeasure() { + return stagnationMeasure; + } + public void setStagnationMeasure(SelectedTag stagnationTimeIn) { + this.stagnationMeasure = stagnationTimeIn; + } + public String stagnationMeasureTipText() { + return "Stagnation time is measured in fitness calls or generations, to be selected here."; + } + + public ChangeTypeEnum getConvergenceCondition() { + return changeType; + } + public void setConvergenceCondition(ChangeTypeEnum convergenceCondition) { + this.changeType = convergenceCondition; +// GenericObjectEditor.setHideProperty(this.getClass(), "convergenceThresholdUpper", isRelativeConvergence() || doCheckImprovement()); + } + public String convergenceConditionTipText() { + return "Select between absolute and relative convergence condition"; + } + + public SelectedTag getCheckType() { + return condImprovementOrChange; + } + public void setCheckType(SelectedTag ct) { + this.condImprovementOrChange = ct; +// GenericObjectEditor.setHideProperty(this.getClass(), "convergenceThresholdUpper", isRelativeConvergence() || doCheckImprovement()); + } + public String checkTypeTipText() { + return "Detect improvement only (one-directional change) or improvement and deterioration (two-directional change)."; + } +} \ No newline at end of file diff --git a/src/eva2/server/go/populations/Population.java b/src/eva2/server/go/populations/Population.java index 672ba722..9cdd32e5 100644 --- a/src/eva2/server/go/populations/Population.java +++ b/src/eva2/server/go/populations/Population.java @@ -70,7 +70,7 @@ public class Population extends ArrayList implements PopulationInterface, Clonea private int lastQModCount = -1; // a sorted queue (for efficiency) transient private ArrayList sortedArr = null; - private int lastFitCrit = -1; + private Comparator lastSortingComparator = null; // private AbstractEAIndividualComparator historyComparator = null; public static final String funCallIntervalReached = "FunCallIntervalReached"; @@ -807,21 +807,39 @@ public class Population extends ArrayList implements PopulationInterface, Clonea /** * This method will return the index of the current best (worst) individual from the - * population. If indicated, only those are regarded which do not violate the constraints. - * If all violate the constraints, the smallest (largest) violation is selected. - * Comparisons are done multicriterial, but note that for incomparable sets (pareto fronts) - * this selection will not be fair (always the lowest index of incomparable sets will be returned). + * population. A given comparator is employed for individual comparisons. * - * @param bBest if true, smallest fitness (regarded best) index is returned, else the highest one - * @param indicate whether constraints should be regarded + * @param bBest if true, the best (first) index is returned, else the worst (last) one + * @param comparator indicate whether constraints should be regarded * @return The index of the best (worst) individual. */ - public int getIndexOfBestOrWorstIndividual(boolean bBest, AbstractEAIndividualComparator comparator) { - ArrayList sorted = getSorted(comparator); + public int getIndexOfBestOrWorstIndividual(boolean bBest, Comparator comparator) { + ArrayList sorted = sortBy(comparator); if (bBest) return indexOf(sorted.get(0)); else return indexOfInstance(sorted.get(sorted.size()-1)); } + public int getIndexOfBestEAIndividual(AbstractEAIndividualComparator comparator) { + return getIndexOfBestOrWorstIndividual(true, comparator); + } + + public AbstractEAIndividual getBestEAIndividual(Comparator comparator) { + int index = getIndexOfBestOrWorstIndividual(true, comparator); + return getEAIndividual(index); + } + + /** + * Return the index of the best (or worst) indy using an AbstractEAIndividualComparator + * that checks the constraints first and then for the given fitness criterion (or + * a pareto criterion if it is -1). + * + * @param bBest if true, the best (first) index is returned, else the worst (last) one + * @param checkConstraints + * @param fitIndex + * @see #getIndexOfBestOrWorstIndividual(boolean, Comparator) + * @see AbstractEAIndividualComparator + * @return + */ public int getIndexOfBestOrWorstIndy(boolean bBest, boolean checkConstraints, int fitIndex) { return getIndexOfBestOrWorstIndividual(bBest, new AbstractEAIndividualComparator(fitIndex, checkConstraints)); } @@ -981,20 +999,22 @@ public class Population extends ArrayList implements PopulationInterface, Clonea * @return The m best individuals, where m <= n * */ - public Population getBestNIndividuals(int n) { - return getSortedNIndividuals(n, true); + public Population getBestNIndividuals(int n, int fitIndex) { + Population pop = new Population(n); + getSortedNIndividuals(n, true, pop, new AbstractEAIndividualComparator(fitIndex)); + return pop; } /** * This method returns a clone of the population instance with sorted individuals, where - * the sorting criterion is delivered by an AbstractEAIndividualComparator. - * @see #getSortedNIndividuals(int, boolean, Population) + * the sorting criterion is delivered by a Comparator. + * @see #getSortedNIndividuals(int, boolean, Population, Comparator) * * @return a clone of the population instance with sorted individuals, best fitness first */ - public Population getSortedBestFirst() { + public Population getSortedBestFirst(Comparator comp) { Population result = this.cloneWithoutInds(); - getSortedNIndividuals(size(), true, result); + getSortedNIndividuals(size(), true, result, comp); result.synchSize(); return result; } @@ -1009,25 +1029,26 @@ public class Population extends ArrayList implements PopulationInterface, Clonea * @return The m sorted best or worst individuals, where m <= n * */ - public Population getSortedNIndividuals(int n, boolean bBestOrWorst) { - Population result = new Population((n > 0) ? n : this.size()); - getSortedNIndividuals(n, bBestOrWorst, result); - return result; - } +// public Population getSortedNIndividuals(int n, boolean bBestOrWorst) { +// Population result = new Population((n > 0) ? n : this.size()); +// getSortedNIndividuals(n, bBestOrWorst, result); +// return result; +// } /** * This method returns the n current best individuals from the population, where - * the sorting criterion is delivered by an AbstractEAIndividualComparator. + * the sorting criterion is delivered by a Comparator instance. * There are less than n individuals returned if the population is smaller than n. * This does not check constraints! * * @param n number of individuals to look out for * @param bBestOrWorst if true, the best n are returned, else the worst n individuals * @param res sorted result population, will be cleared + * @param comparator the Comparator to use with individuals * @return The m sorted best or worst individuals, where m <= n * */ - public void getSortedNIndividuals(int n, boolean bBestOrWorst, Population res) { + public void getSortedNIndividuals(int n, boolean bBestOrWorst, Population res, Comparator comp) { if ((n < 0) || (n>super.size())) { // this may happen, treat it gracefully //System.err.println("invalid request to getSortedNIndividuals: n="+n + ", size is " + super.size()); @@ -1036,7 +1057,8 @@ public class Population extends ArrayList implements PopulationInterface, Clonea int skip = 0; if (!bBestOrWorst) skip = super.size()-n; - ArrayList sorted = getSorted(lastFitCrit); +// hier getSorted aufrufen + ArrayList sorted = getSorted(comp); res.clear(); for (int i = skip; i < skip+n; i++) { res.add(sorted.get(i)); @@ -1044,6 +1066,18 @@ public class Population extends ArrayList implements PopulationInterface, Clonea res.synchSize(); } +// /** +// * Get the n best (or worst) individuals from the population. The last comparator +// * is reused, or if none has been employed yet, a standard comparator is used. +// * +// * @param n +// * @param bBestOrWorst +// * @param res +// */ +// public void getSortedNIndividuals(int n, boolean bBestOrWorst, Population res) { +// getSortedNIndividuals(n, bBestOrWorst, res, lastSortingComparator); +// } + /** * From the given list, remove all but the first n elements. * @param n @@ -1076,11 +1110,13 @@ public class Population extends ArrayList implements PopulationInterface, Clonea } /** - * Set a fitness criterion for sorting procedures. This also affects getBest + * Set a fitness criterion for sorting procedures. This sorts the + * population once and influences further getBest* methods if no + * specific comparator is given. * @param fitIndex */ public void setSortingFitnessCriterion(int fitIndex) { - getSorted(fitIndex); + getSorted(new AbstractEAIndividualComparator(fitIndex)); } // /** @@ -1100,7 +1136,7 @@ public class Population extends ArrayList implements PopulationInterface, Clonea * @param comp A comparator by which sorting is performed - it should work on AbstractEAIndividual instances. * @return */ - public ArrayList getSorted(Comparator comp) { + protected ArrayList sortBy(Comparator comp) { if (super.size()==0) return new ArrayList(); PriorityQueue sQueue = new PriorityQueue(super.size(), comp); for (int i = 0; i < super.size(); i++) { @@ -1114,26 +1150,43 @@ public class Population extends ArrayList implements PopulationInterface, Clonea } /** - * Avoids having to sort again in several calls without modifications in between. - * The returned array should not be modified! + * Return a sorted list of individuals. The order is based on the + * given comparator. Repeated calls do not resort the population every + * time as long as an equal comparator is used (implement the equals() method!) + * and the population has not been modified. + * The returned array must not be altered! * * @param fitIndex the fitness criterion to be used or -1 for pareto dominance * @return */ - protected ArrayList getSorted(int fitIndex) { - if ((fitIndex != lastFitCrit) || (sortedArr == null) || (super.modCount != lastQModCount)) { - lastFitCrit=fitIndex; - ArrayList sArr = getSorted(new AbstractEAIndividualComparator(fitIndex)); + public ArrayList getSorted(Comparator comp) { +// Comparator comp = new AbstractEAIndividualComparator(fitIndex); +// if (!comp.equals(lastSortingComparator)) return sortedArr; + if (!comp.equals(lastSortingComparator) || (sortedArr == null) || (super.modCount != lastQModCount)) { +// lastFitCrit=fitIndex;lastSortingComparator + ArrayList sArr = sortBy(comp); if (sortedArr==null) sortedArr = sArr; else { sortedArr.clear(); sortedArr.addAll(sArr); - } + } + lastSortingComparator = (Comparator) Serializer.deepClone(comp); lastQModCount = super.modCount; } return sortedArr; } + /** + * Returns the sorted population as a new population instance. + * @see getSorted(Comparator) + */ + public Population getSortedPop(Comparator comp) { + Population pop = this.cloneWithoutInds(); + ArrayList sortedIndies = getSorted(comp); + pop.addAll(sortedIndies); + return pop; + } + /** * This method retrieves n random individuals from the population and * returns them within a new population. diff --git a/src/eva2/server/go/problems/AbstractMultiObjectiveOptimizationProblem.java b/src/eva2/server/go/problems/AbstractMultiObjectiveOptimizationProblem.java index 7913cb9f..8d7e3206 100644 --- a/src/eva2/server/go/problems/AbstractMultiObjectiveOptimizationProblem.java +++ b/src/eva2/server/go/problems/AbstractMultiObjectiveOptimizationProblem.java @@ -3,8 +3,6 @@ package eva2.server.go.problems; import java.awt.Color; import java.util.ArrayList; import java.util.Vector; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import javax.swing.JFrame; @@ -21,7 +19,8 @@ import eva2.server.go.operators.moso.MOSONoConvert; import eva2.server.go.operators.paretofrontmetrics.InterfaceParetoFrontMetric; import eva2.server.go.operators.paretofrontmetrics.MetricS; import eva2.server.go.populations.Population; -import eva2.server.go.problems.AbstractOptimizationProblem.EvalThread; +import eva2.server.go.strategies.InterfaceOptimizer; +import eva2.tools.ToolBox; import eva2.tools.chart2d.Chart2DDPointIconCircle; import eva2.tools.chart2d.Chart2DDPointIconText; import eva2.tools.chart2d.DPoint; @@ -352,65 +351,6 @@ public abstract class AbstractMultiObjectiveOptimizationProblem extends Abstract moProblem.drawAdditionalData(plot, p, 10); } } - -// /** This method will draw the current state of the optimization process -// * @param p The current population -// */ -// public static void drawProblem(Population p, Plot plot, AbstractMultiObjectiveOptimizationProblem moProblem) { -// ArchivingAllDominating tmpArch = new ArchivingAllDominating(); -// Population tmpPop = null; -// -// if (p.getGeneration() > 2) { -//// m_Plot = new eva2.gui.Plot("Multiobjective Optimization", "Y1", "Y2"); -// // i want to plot the pareto front for MOEA and other strategies -// // but i have to differentiate between the case where -// // there is a true MOEA at work and where the -// // MOOpt was converted into a SOOpt -// if (AbstractMultiObjectiveOptimizationProblem.isPopulationMultiObjective(p)) { -// // in this case i have to use my local archive -// tmpPop = moProblem.m_ParetoFront; -// } else { -// // in this case i use the population of the optimizer -// // and eventually the pop.archive if there is one -// tmpPop = new Population(); -// tmpPop.addPopulation(p); -// if (p.getArchive() != null) tmpPop.addPopulation(p.getArchive()); -// tmpArch.addElementsToArchive(tmpPop); -// tmpPop = tmpPop.getArchive(); -// } -// if (tmpPop != null) { -// // i got either a multiobjective population or a multiobjective local population -// plot.clearAll(); -// tmpArch.plotParetoFront(tmpPop, plot); -// if ((true) && (p.getArchive() != null)) { -// GraphPointSet mySet = new GraphPointSet(10, plot.getFunctionArea()); -// DPoint myPoint; -// Chart2DDPointIconCircle icon; -// double[] tmpD; -// mySet.setConnectedMode(false); -// tmpPop = p.getArchive(); -// for (int i = 0; i < tmpPop.size(); i++) { -// icon = new Chart2DDPointIconCircle(); -// tmpD = ((AbstractEAIndividual)tmpPop.get(i)).getFitness(); -// myPoint = new DPoint(tmpD[0], tmpD[1]); -// if (((AbstractEAIndividual)tmpPop.get(i)).getConstraintViolation() > 0) { -// icon.setBorderColor(Color.RED); -// icon.setFillColor(Color.RED); -// } else { -// icon.setBorderColor(Color.BLACK); -// icon.setFillColor(Color.BLACK); -// } -// myPoint.setIcon(icon); -// mySet.addDPoint(myPoint); -// } -// } -// } else { -//// // in this case i got a single objective optimization problem -// } -// // draw additional data -// moProblem.drawAdditionalData(plot, p, 10); -// } -// } /** * This method will plot a reference solutions or something like it @@ -536,25 +476,33 @@ public abstract class AbstractMultiObjectiveOptimizationProblem extends Abstract @Override public String[] getAdditionalFileStringHeader(PopulationInterface pop) { - String[] result = new String[1]; - if (AbstractMultiObjectiveOptimizationProblem.isPopulationMultiObjective((Population)pop)) - result[0] = "SMetric"; - else - result[0] = "BestFitness"; - return result; + String[] superHd = super.getAdditionalFileStringHeader(pop); + return ToolBox.appendArrays(new String[]{"paretoMetricCurrent","paretoMetricFront"}, superHd); } @Override public Object[] getAdditionalFileStringValue(PopulationInterface pop) { - Object[] result = new Object[1]; - if (AbstractMultiObjectiveOptimizationProblem.isPopulationMultiObjective((Population)pop)) - result[0] = this.calculateMetric((Population)pop); - else - result[0] = ((Population)pop).getBestEAIndividual().getFitness()[0]; - return result; + Object[] result = new Object[2]; + result[0] = this.calculateMetric((Population)pop); + result[1] = this.calculateMetric(getLocalParetoFront()); + return ToolBox.appendArrays(result, super.getAdditionalFileStringValue(pop)); + } + + @Override + public String[] getAdditionalFileStringInfo(PopulationInterface pop) { + String[] superInfo = super.getAdditionalFileStringInfo(pop); + return ToolBox.appendArrays(new String[]{"Pareto metric on the current population (per generation)", + "Pareto metric on the collected pareto front"}, superInfo); } - public double calculateMetric(Population pop) { + @Override + public String getStringRepresentationForProblem(InterfaceOptimizer opt) { + // TODO Auto-generated method stub + return null; + } + + public double calculateMetric(Population pop) { + if (pop==null) return Double.NaN; return this.m_Metric.calculateMetricOn(pop, this); } diff --git a/src/eva2/server/go/problems/AbstractOptimizationProblem.java b/src/eva2/server/go/problems/AbstractOptimizationProblem.java index a912204a..d79dee73 100644 --- a/src/eva2/server/go/problems/AbstractOptimizationProblem.java +++ b/src/eva2/server/go/problems/AbstractOptimizationProblem.java @@ -30,6 +30,7 @@ import eva2.server.go.operators.postprocess.SolutionHistogram; import eva2.server.go.operators.terminators.CombinedTerminator; import eva2.server.go.operators.terminators.EvaluationTerminator; import eva2.server.go.operators.terminators.PhenotypeConvergenceTerminator; +import eva2.server.go.operators.terminators.PopulationMeasureTerminator.ChangeTypeEnum; import eva2.server.go.populations.Population; import eva2.server.go.strategies.InterfaceOptimizer; @@ -483,7 +484,7 @@ implements InterfaceOptimizationProblem /*, InterfaceParamControllable*/, Serial Population pop = new Population(1); pop.add(orig); InterfaceTerminator term = new EvaluationTerminator(maxEvaluations); - if (epsilonFitConv > 0) term = new CombinedTerminator(new PhenotypeConvergenceTerminator(epsilonFitConv, 100*dim, true, true), term, false); + if (epsilonFitConv > 0) term = new CombinedTerminator(new PhenotypeConvergenceTerminator(epsilonFitConv, 100*dim, true, ChangeTypeEnum.absoluteChange, true), term, false); int evalsPerf = PostProcess.processSingleCandidatesNMCMA(PostProcessMethod.nelderMead, pop, term, initRelPerturb, prob); overallDist = metric.distance(indy, pop.getBestEAIndividual()); //System.out.println(System.currentTimeMillis() + " in " + evalsPerf + " evals moved by "+ overallDist); diff --git a/src/eva2/server/go/problems/AbstractProblemDouble.java b/src/eva2/server/go/problems/AbstractProblemDouble.java index ce896b84..08e63365 100644 --- a/src/eva2/server/go/problems/AbstractProblemDouble.java +++ b/src/eva2/server/go/problems/AbstractProblemDouble.java @@ -396,7 +396,7 @@ public abstract class AbstractProblemDouble extends AbstractOptimizationProblem tmpIndy.SetDoubleGenotype(pos); ((AbstractEAIndividual)tmpIndy).SetFitness(prob.eval(pos)); pop.add(tmpIndy); - FitnessConvergenceTerminator convTerm = new FitnessConvergenceTerminator(1e-25, 10, false, true); + FitnessConvergenceTerminator convTerm = new FitnessConvergenceTerminator(1e-25, 10, false, true, true); int calls = PostProcess.processSingleCandidatesNMCMA(PostProcessMethod.nelderMead, pop, convTerm, 0.001, prob); return ((InterfaceDataTypeDouble)pop.getBestEAIndividual()).getDoubleData(); } diff --git a/src/eva2/server/go/strategies/EvolutionStrategyIPOP.java b/src/eva2/server/go/strategies/EvolutionStrategyIPOP.java index 8cd98748..a310b552 100644 --- a/src/eva2/server/go/strategies/EvolutionStrategyIPOP.java +++ b/src/eva2/server/go/strategies/EvolutionStrategyIPOP.java @@ -156,8 +156,7 @@ public class EvolutionStrategyIPOP extends EvolutionStrategies implements Interf bestList = new LinkedList(); best = getPopulation().getBestEAIndividual(); dim = AbstractEAIndividual.getDoublePositionShallow(getPopulation().getEAIndividual(0)).length; - - fitConvTerm = new FitnessConvergenceTerminator(stagThreshold, (isStagnationTimeUserDef()) ? stagTimeArbitrary : calcDefaultStagnationTime(), false, true); // gen. based, absolute + fitConvTerm = new FitnessConvergenceTerminator(stagThreshold, (isStagnationTimeUserDef()) ? stagTimeArbitrary : calcDefaultStagnationTime(), false, true, true); // gen. based, absolute getPopulation().addPopulationChangedEventListener(this); getPopulation().setNotifyEvalInterval(initialLambda); } diff --git a/src/eva2/server/go/strategies/NelderMeadSimplex.java b/src/eva2/server/go/strategies/NelderMeadSimplex.java index d5008ce9..cfb98da8 100644 --- a/src/eva2/server/go/strategies/NelderMeadSimplex.java +++ b/src/eva2/server/go/strategies/NelderMeadSimplex.java @@ -126,7 +126,7 @@ public class NelderMeadSimplex implements InterfaceOptimizer, Serializable, Inte // hole die n-1 besten individuen der fitness dimension fitIndex subpop.setSortingFitnessCriterion(fitIndex); - Population bestpop = subpop.getBestNIndividuals(subpop.size()-1); + Population bestpop = subpop.getBestNIndividuals(subpop.size()-1, fitIndex); // und das schlechteste AbstractEAIndividual worst = subpop.getWorstEAIndividual(fitIndex); AbstractEAIndividual best=subpop.getBestEAIndividual(fitIndex);