diff --git a/src/eva2/server/go/strategies/CBNPSO.java b/src/eva2/server/go/strategies/CBNPSO.java new file mode 100644 index 00000000..6f60907c --- /dev/null +++ b/src/eva2/server/go/strategies/CBNPSO.java @@ -0,0 +1,224 @@ +package eva2.server.go.strategies; + +import java.io.Serializable; + +import eva2.server.go.PopulationInterface; +import eva2.server.go.enums.PSOTopologyEnum; +import eva2.server.go.operators.cluster.ClusteringDensityBased; +import eva2.server.go.operators.distancemetric.IndividualDataMetric; +import eva2.server.go.operators.paramcontrol.CbpsoFitnessThresholdBasedAdaption; +import eva2.server.go.operators.paramcontrol.LinearParamAdaption; +import eva2.server.go.operators.paramcontrol.ParamAdaption; +import eva2.server.go.operators.paramcontrol.SinusoidalParamAdaption; +import eva2.server.go.populations.Population; +import eva2.server.go.populations.SolutionSet; +import eva2.server.go.problems.AbstractProblemDouble; +import eva2.server.go.problems.InterfaceInterestingHistogram; +import eva2.server.go.problems.InterfaceOptimizationProblem; +import eva2.server.go.problems.InterfaceProblemDouble; +import eva2.tools.EVAERROR; +import eva2.tools.ToolBox; + +public class CBNPSO extends ClusterBasedNichingEA implements Serializable { + private boolean forceUpperClustDist = true; + + /** + * Standard constructur with fixed frequency (no fitness threshold based frequency adaption) + */ + public CBNPSO() { + this(false); + } + + public CBNPSO(boolean threshAdaption) { + this(10, 15, 0.001, 1e-10, 15, 100, threshAdaption); +// super(); +// setDifferentiationCA(new ClusteringDensityBased(getClusterDiffDist(), 10, new IndividualDataMetric(ParticleSwarmOptimization.partBestPosKey))); +// setMergingCA(new ClusteringDensityBased(0.001, 3, new IndividualDataMetric(ParticleSwarmOptimization.partBestPosKey))); +// setEpsilonBound(1e-10); +// setHaltingWindow(15); +// setMaxSpeciesSize(15); +// setOptimizer(new ParticleSwarmOptimization(100, 2.05, 2.05, PSOTopologyEnum.grid, 2)); +// ParamAdaption[] defAdpt = new ParamAdaption[]{getDefaultSinusoidalAdaption()}; +// setParameterControl(defAdpt); +// if (threshAdaption) addParameterControl(getDefaultThreshAdaption()); +// setPopulationSize(100); + } + + public CBNPSO(int minSpecSize, int maxSpecSize, double sigmaMerge, double epsilonConv, int haltingWindow, int popSize, boolean threshAdaption) { + super(); + setDifferentiationCA(new ClusteringDensityBased(getClusterDiffDist(), minSpecSize, new IndividualDataMetric(ParticleSwarmOptimization.partBestPosKey))); + setMergingCA(new ClusteringDensityBased(sigmaMerge, 3, new IndividualDataMetric(ParticleSwarmOptimization.partBestPosKey))); + setEpsilonBound(epsilonConv); + setHaltingWindow(haltingWindow); + setMaxSpeciesSize(maxSpecSize); + setOptimizer(new ParticleSwarmOptimization(popSize, 2.05, 2.05, PSOTopologyEnum.grid, 2)); + ParamAdaption[] defAdpt = new ParamAdaption[]{getDefaultSinusoidalAdaption()}; + setParameterControl(defAdpt); + if (threshAdaption) addParameterControl(getDefaultThreshAdaption()); + setPopulationSize(popSize); + } + + private ParamAdaption getDefaultSinusoidalAdaption() { + return new SinusoidalParamAdaption(0.1, 1., 10000, 0, "clusterDiffDist"); + } + + private ParamAdaption getDefaultThreshAdaption() { + return new CbpsoFitnessThresholdBasedAdaption(); + } + + @Override + public void SetProblem(InterfaceOptimizationProblem problem) { + super.SetProblem(problem); + if (problem instanceof AbstractProblemDouble) { + AbstractProblemDouble dblProb = ((AbstractProblemDouble)problem); + adaptMinMaxSwarmSizeByDim(dblProb); + } + } + + /** + * Return the period of the sinusoidal sigma adaption or -1 if not applicable. + * + * @param p + */ + public int getSigmaAdaptionPeriod() { + ParamAdaption[] prmAd = getParameterControl(); + for (int i=0; i0) { + String addInfo="adpt"; + if (getParameterControl()[0] instanceof SinusoidalParamAdaption) addInfo="SinT"+((SinusoidalParamAdaption)getParameterControl()[0]).getIterationPeriod(); + else if (getParameterControl()[0] instanceof LinearParamAdaption) addInfo="Lin"+((LinearParamAdaption)getParameterControl()[0]).getStartV()+"-"+((LinearParamAdaption)getParameterControl()[0]).getEndV(); + return "CBN-PSO-"+addInfo; + } else return "CBN-PSO"; + } + + public static String globalInfo() { + return "A CBN-EA variant employing PSO and dynamic variation of the clustering parameter by default."; + } + + public void setForceUpperClustDist(boolean forceUpperClustDist) { + this.forceUpperClustDist = forceUpperClustDist; + } + public boolean isForceUpperClustDist() { + return forceUpperClustDist; + } + public String forceUpperClustDistTipText() { + return "Activate to force cluster distance to be maximal corresponding to the CBN-PSO settings."; + } + + /** + * Return the ratio of interesting solutions per archived solutions, which + * is in [0,1] if any solutions have been identified, or -1 if the archive + * is empty. + * + * @param cbpso + * @param pop + * @param iteration + * @param maxIteration + * @return + */ + public double getInterestingSolutionRatio() { + InterfaceOptimizationProblem prob = getProblem(); + double fitThres = 100; + if (prob instanceof InterfaceInterestingHistogram) { + fitThres = ((InterfaceInterestingHistogram)prob).getHistogram().getUpperBound(); + } else EVAERROR.errorMsgOnce("Warning, problem does not define a fitness threshold!"); + + SolutionSet solSet = getAllSolutions(); + Population archived = solSet.getSolutions(); + Population interesting = archived.filterByFitness(fitThres, 0); + +// Population archived = getArchivedSolutions(); +// Population interesting = archived.filterByFitness(fitThres, 0); + + if (archived.size()>0) { + return ((double)interesting.size())/((double)archived.size()); + } else return -1; + } + + @Override + public String[] getAdditionalDataHeader() { + String[] addVals = {"interestingRatio"}; + if (getCurrentPeriod()>=0) addVals = new String[]{"interestingRatio", "adaptPeriod"}; + return ToolBox.appendArrays(super.getAdditionalDataHeader(), addVals); + } + + /** + * Retrieve the current period of the sinusoidal sigma adaption (in case it is controlled by a threshold adaption) + * or -1 if this does not apply. + * + * @return + */ + private int getCurrentPeriod() { + ParamAdaption[] adaptors = super.getParameterControl(); + SinusoidalParamAdaption sinA=null; + CbpsoFitnessThresholdBasedAdaption ftA=null; + if (adaptors!=null) for (int i=0; i=0) addVals = new Object[]{getInterestingSolutionRatio(), freq}; + else addVals = new Object[]{getInterestingSolutionRatio()}; + return ToolBox.appendArrays(super.getAdditionalDataValue(pop), addVals); + } +} diff --git a/src/eva2/server/go/strategies/ClusterBasedNichingEA.java b/src/eva2/server/go/strategies/ClusterBasedNichingEA.java index 82de30df..664936f4 100644 --- a/src/eva2/server/go/strategies/ClusterBasedNichingEA.java +++ b/src/eva2/server/go/strategies/ClusterBasedNichingEA.java @@ -12,20 +12,29 @@ import eva2.gui.GraphPointSet; import eva2.gui.Plot; import eva2.gui.TopoPlot; import eva2.server.go.InterfacePopulationChangedEventListener; +import eva2.server.go.InterfaceTerminator; import eva2.server.go.PopulationInterface; import eva2.server.go.individuals.AbstractEAIndividual; import eva2.server.go.individuals.AbstractEAIndividualComparator; import eva2.server.go.individuals.InterfaceDataTypeDouble; import eva2.server.go.operators.cluster.ClusteringDensityBased; import eva2.server.go.operators.cluster.InterfaceClustering; +import eva2.server.go.operators.cluster.InterfaceClusteringDistanceParam; +import eva2.server.go.operators.cluster.InterfaceClusteringMetricBased; import eva2.server.go.operators.distancemetric.ObjectiveSpaceMetric; +import eva2.server.go.operators.paramcontrol.InterfaceHasUpperDoubleBound; +import eva2.server.go.operators.paramcontrol.ParamAdaption; +import eva2.server.go.operators.paramcontrol.ParameterControlManager; +import eva2.server.go.operators.terminators.HistoryConvergenceTerminator; import eva2.server.go.populations.Population; import eva2.server.go.populations.SolutionSet; import eva2.server.go.problems.B1Problem; import eva2.server.go.problems.Interface2DBorderProblem; import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; import eva2.server.go.problems.InterfaceOptimizationProblem; +import eva2.server.go.problems.InterfaceProblemDouble; import eva2.server.go.problems.TF1Problem; +import eva2.tools.EVAERROR; import eva2.tools.chart2d.Chart2DDPointIconCircle; import eva2.tools.chart2d.Chart2DDPointIconText; import eva2.tools.chart2d.DPoint; @@ -37,12 +46,18 @@ import eva2.tools.math.Mathematics; * It should be able to identify and track multiple global/local optima * at the same time. * - * Copyright: Copyright (c) 2003 + * Notes: For std. GA, the mutation rate may have to reduced, because the + * initial step size tends to be rel. large and easily disperse clustered + * species (so that they fall below the minimum swarm size and the local + * optimum is lost). + * + * For the CBN-PSO remember to use the IndividualDataMetric so that the + * remembered positions are used for clustering (which are rel. stable - + * so that species clustering actually makes sense). + * + * Copyright: Copyright (c) 2010 * Company: University of Tuebingen, Computer Architecture - * @author Felix Streichert - * @version: $Revision: 322 $ - * $Date: 2007-12-11 17:24:07 +0100 (Tue, 11 Dec 2007) $ - * $Author: mkron $ + * @author Felix Streichert, Marcel Kronfeld */ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventListener, InterfaceAdditionalPopulationInformer, InterfaceOptimizer, java.io.Serializable { @@ -57,6 +72,9 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis private InterfaceOptimizer m_Optimizer = new GeneticAlgorithm(); private InterfaceClustering m_CAForSpeciesDifferentation = new ClusteringDensityBased(); private InterfaceClustering m_CAForSpeciesMerging = new ClusteringDensityBased(); + + private double clusterDiffDist = 0.05; +// private double clusterMergeDist = 0.0001; // private Distraction distraction = null; private boolean useDistraction = false; // private double distrDefaultStrength = .7; @@ -75,8 +93,9 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis private boolean m_mergeSpecies = true; private int m_PopulationSize = 50; private int convergedCnt = 0; + private int collisions = 0; - private static boolean TRACE = false, TRACE_STATE=false; + private static boolean TRACE = false, TRACE_STATE=false, TRACE_EVTS=false; private int m_ShowCycle = 0; transient private TopoPlot m_Topology; private int haltingWindow = 15; @@ -84,14 +103,52 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis private int sleepTime = 0; private int m_maxSpeciesSize = 15; private AbstractEAIndividualComparator reduceSizeComparator = new AbstractEAIndividualComparator(); + private AbstractEAIndividualComparator histComparator = new AbstractEAIndividualComparator("", -1, true); + protected ParameterControlManager paramControl = new ParameterControlManager(); + private double avgDistForConvergence = 0.1; // Upper bound for average indy distance in a species in the test for convergence + public ClusterBasedNichingEA() { this.m_CAForSpeciesMerging = new ClusteringDensityBased(); - ((ClusteringDensityBased)this.m_CAForSpeciesMerging).setMinimumGroupSize(m_minGroupSize); +// ((ClusteringDensityBased)this.m_CAForSpeciesMerging).setMinimumGroupSize(m_minGroupSize); // if (useDistraction) distraction = new Distraction(distrDefaultStrength, Distraction.METH_BEST); } - public ClusterBasedNichingEA(ClusterBasedNichingEA a) { + /********************************************************************************************************************** + * These are for InterfaceParamControllable + */ + + public Object[] getParamControl() { + List ctrlbls = ParameterControlManager.listOfControllables(this); + ctrlbls.add(paramControl); + return ctrlbls.toArray(); + // this works - however differently than when returning a ParameterControlManager + // Only instances are returned which + } + + /** + * This method is necessary to allow access from the Processor. + * @return + */ +// public ParameterControlManager getParamControl() { +// return paramControl; +// } + + public ParamAdaption[] getParameterControl() { + return paramControl.getSingleAdapters(); + } + public void setParameterControl(ParamAdaption[] paramControl) { + this.paramControl.setSingleAdapters(paramControl); + } + public String parameterControlTipText() { + return "You may define dynamic paramter control strategies using the parameter name."; + } + + public void addParameterControl(ParamAdaption pa) { + this.paramControl.addSingleAdapter(pa); + } + + public ClusterBasedNichingEA(ClusterBasedNichingEA a) { this.epsilonBound = a.epsilonBound; this.m_Population = (Population)a.m_Population.clone(); this.m_Archive = (Population)a.m_Archive.clone(); @@ -112,6 +169,11 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis this.muLambdaRatio = a.muLambdaRatio; this.sleepTime = a.sleepTime; this.convergedCnt = a.convergedCnt; + this.collisions = a.collisions; + this.clusterDiffDist = a.clusterDiffDist; + this.useDistraction = a.useDistraction; + this.m_ShowCycle = a.m_ShowCycle; + this.m_maxSpeciesSize = a.m_maxSpeciesSize; } public Object clone() { @@ -119,18 +181,24 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis } public void init() { - this.m_Undifferentiated = new Population(m_PopulationSize); + if (m_Undifferentiated==null) this.m_Undifferentiated = new Population(m_PopulationSize); + else { + m_Undifferentiated.resetProperties(); + m_Undifferentiated.setTargetSize(m_PopulationSize); + } + this.m_Undifferentiated.setUseHistory(true); this.m_Problem.initPopulation(this.m_Undifferentiated); - this.m_Optimizer.setPopulation(m_Undifferentiated); + this.m_Optimizer.initByPopulation(m_Undifferentiated, true); + m_Undifferentiated=this.m_Optimizer.getPopulation(); // some optimizers clone the given one. if (m_Optimizer instanceof EvolutionStrategies) { EvolutionStrategies es = (EvolutionStrategies)m_Optimizer; es.setLambda(getPopulationSize()); es.setMu((int)(muLambdaRatio*(double)getPopulationSize())); } - this.m_Optimizer.init(); - m_doomedPop = new Population(); +// this.m_Optimizer.init(); + m_doomedPop = m_Undifferentiated.cloneWithoutInds(); if (m_Undifferentiated.getFunctionCalls()!=m_PopulationSize) { System.err.println("Whats that in CBN!?"); } @@ -152,12 +220,14 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis this.m_Optimizer.addPopulationChangedEventListener(this); this.m_Undifferentiated.setTargetSize(this.m_PopulationSize); this.m_Species = new ArrayList(); - this.m_Archive = new Population(); + this.m_Archive = m_Undifferentiated.cloneWithoutInds(); // if (useDistraction) distraction = new Distraction(distrDefaultStrength, Distraction.METH_BEST); convergedCnt = 0; + collisions=0; if (evalPop) this.evaluatePopulation(this.m_Undifferentiated); this.m_Optimizer.initByPopulation(m_Undifferentiated, false); this.m_Undifferentiated = m_Optimizer.getPopulation(); // required for changes to the population by the optimizer + m_Population = m_Undifferentiated; this.firePropertyChangedEvent("FirstGenerationPerformed"); } @@ -223,7 +293,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis Population pop; this.m_Topology = new TopoPlot("CBN-Species at gen. " + gen,"x","y",a,a); - this.m_Topology.setParams(60, 60); + this.m_Topology.setParams(50, 50); this.m_Topology.setTopology((Interface2DBorderProblem)this.m_Problem); //draw the undifferentiated for (int i = 0; i < this.m_Undifferentiated.size(); i++) { @@ -238,7 +308,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis } if (!useDistraction) { for (int i = 0; i < this.m_Archive.size(); i++) { - plotIndy('x',(InterfaceDataTypeDouble)m_Archive.get(i)); + plotIndy(m_Topology, 'x',(InterfaceDataTypeDouble)m_Archive.get(i)); } } else { // for (int i = 0; i < this.distraction.getDistractorSetSize(); i++) { @@ -246,16 +316,9 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis // } } } - if (sleepTime > 0) { - try { - Thread.sleep(sleepTime); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } } - private void plotPopConnected(TopoPlot tp, Population pop) { + public static void plotPopConnected(TopoPlot tp, Population pop) { DPointSet popRep; InterfaceDataTypeDouble tmpIndy1; if (pop.size()>1) { @@ -269,31 +332,26 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis } } else { // this is an inactive species - plotIndy('+',(InterfaceDataTypeDouble)pop.get(0)); + plotIndy(tp, '+',(InterfaceDataTypeDouble)pop.get(0)); } } - private void plotLine(TopoPlot tp, AbstractEAIndividual indy1, + public static void plotLine(TopoPlot tp, AbstractEAIndividual indy1, AbstractEAIndividual indy2) { - DPointSet popRep; +// DPointSet popRep; double[] pos1, pos2; if (indy1 instanceof InterfaceDataTypeDouble) pos1=((InterfaceDataTypeDouble)indy1).getDoubleData(); else pos1=(indy1).getDoublePosition(); if (indy2 instanceof InterfaceDataTypeDouble) pos2=((InterfaceDataTypeDouble)indy2).getDoubleData(); else pos2 =(indy2).getDoublePosition(); - - popRep = new DPointSet(); - popRep.setConnected(true); - popRep.addDPoint(new DPoint(pos1[0], pos1[1])); - popRep.addDPoint(new DPoint(pos2[0], pos2[1])); - tp.getFunctionArea().addDElement(popRep); + tp.getFunctionArea().drawLine(pos1, pos2); } - private void plotIndy(char c, InterfaceDataTypeDouble tmpIndy) { - plotPosFit(c, tmpIndy.getDoubleData(), ((AbstractEAIndividual)tmpIndy).getFitness(0)); + public static void plotIndy(Plot p, char c, InterfaceDataTypeDouble tmpIndy) { + plotPosFit(p, c, tmpIndy.getDoubleData(), ((AbstractEAIndividual)tmpIndy).getFitness(0)); } - private void plotPosFit(char c, double[] position, double fitness) { + public static void plotPosFit(Plot p, char c, double[] position, double fitness) { DPointSet popRep; popRep = new DPointSet(); popRep.addDPoint(new DPoint(position[0], position[1])); @@ -301,7 +359,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis DPointIcon icon = new Chart2DDPointIconText(c+""+d); ((Chart2DDPointIconText)icon).setIcon(new Chart2DDPointIconCircle()); popRep.setIcon(icon); - this.m_Topology.getFunctionArea().addDElement(popRep); + p.getFunctionArea().addDElement(popRep); } /** This method is called to generate n freshly initialized individuals @@ -309,7 +367,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis * @return A population of new individuals */ private Population initializeIndividuals(int n) { - Population result = new Population(); + Population result = m_Undifferentiated.cloneWithoutInds(); result.setUseHistory(true); result.setTargetSize(n); //@todo: crossover between species is to be implemented @@ -328,37 +386,56 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis * @return True if converged. */ private boolean testSpeciesForConvergence(Population pop) { - List speciesHistory = pop.getHistory(); - int histLen = speciesHistory.size(); + int histLen = pop.getHistoryLength(); - if (histLen <= haltingWindow) { + if (histLen < haltingWindow) { // System.out.println("not long enough... gen " + pop.getGeneration()); return false; } else { - AbstractEAIndividual historicHWAgo = speciesHistory.get(histLen-haltingWindow); - for (int i = 1; i < haltingWindow; i++) { - // if historic[-hW] is worse than historic[-hW+i] return false - AbstractEAIndividual historicIter = speciesHistory.get(histLen-haltingWindow+i); - // if the iterated indy (the later one in history) has improved, there is no convergence. - if (testSecondForImprovement(historicHWAgo, historicIter)) return false; +// TODO: undo: InterfaceTerminator convergenceTerminator = new DiversityTerminator(epsilonBound,new PhenotypeMetric(),0); +// boolean term = convergenceTerminator.isTerminated(pop); + + InterfaceTerminator convergenceTerminator = new HistoryConvergenceTerminator(haltingWindow, epsilonBound, 0, false); + boolean term = convergenceTerminator.isTerminated(pop); +// if (term) { +// System.out.println("Conv spec. aged " + pop.getGeneration() + " with meas. " + BeanInspector.toString(pop.getPopulationMeasures())); +// } + if (term) { + // this case is especially relevant if sequential niching is "faked" using the CBN approach, + // because in this case, the niching parameter is very large and the single (sequentially build) species + // may be still spread throughout the search space and still not improve for the given halting window. + // Omitting this causes high numbers of phantom solutions which are far from converged optima + double[] specMeas = pop.getPopulationMeasures(); + if (specMeas[0]>avgDistForConvergence ) { +// if (getClusterDiffDist()<=0.5) +// System.err.println("ALTERNATIVE BREAK, FORBIDDING CONVERGENCE! sig=" + getClusterDiffDist() + " / avD="+ specMeas[0]); + InterfaceTerminator convTerm2 = new HistoryConvergenceTerminator(2*haltingWindow, epsilonBound, 0, false); + term = convTerm2.isTerminated(pop); + if (term) { +// System.out.println("Twice the halting window passed without improvement and still no phenotypic convergence!!!"); + return true; + } else return false; + } + } + + if (term) { +// System.out.println(); + } + // TODO something like this may be used as additional convergence criterion. + // it influences the number of local optima archived and seems to increase the score, but not the best-found solutions, at least for a large clustering parameter +// System.out.println("Terminated, subswarm measures: " + BeanInspector.toString(pop.getPopulationMeasures())); +// if (m_Optimizer instanceof ParticleSwarmOptimization) { +// double swarmSp = ParticleSwarmOptimization.getPopulationVelSpeed(pop, 2, ParticleSwarmOptimization.partVelKey, null, null)[0]; +// System.out.println("Swarm speed: " + swarmSp); +// if (swarmSp > 0.05) { +// System.err.println("Too high speed"); +// term = false; +// } +// } +// } + return term; + } -// if (historicHWAgo.getFitness(0) > ((AbstractEAIndividual)pop.m_History.get(length-haltingWindow+i)).getFitness(0)) { -// System.out.println("( " + historic.getFitness(0) + "/" + ((AbstractEAIndividual)pop.m_History.get(length-haltingWindow+i)).getFitness(0)); -// return false; -// } - } - } - if (false) { // plot the historic fitness values - double[] a = new double[2]; - a[0] = 0; a[1] = 0; - Plot plot = new Plot("HaltingWindow", "History", "Fitness", a, a); - plot.setUnconnectedPoint(0, -1, 0); - for (int i = haltingWindow; i > 0; i--) { - a = speciesHistory.get(histLen-i).getFitness(); - plot.setUnconnectedPoint(haltingWindow-i+1, a[0], 0); - } - } - return true; } /** @@ -377,7 +454,10 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis boolean ret = (secIndy.isDominatingDebConstraints(firstIndy)); ret = ret && (fitDiff > epsilonBound); // there is improvement if the second is dominant and the fitness difference is larger than epsilon return ret; - } else return (secIndy.isDominatingDebConstraints(firstIndy)); + } else { + return (histComparator.compare(firstIndy, secIndy)>0); +// return (secIndy.isDominatingDebConstraints(firstIndy)); + } } private Population optimizeSpecies(Population species, boolean minorPlot) { @@ -401,7 +481,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis if (BeanInspector.hasMethod(m_Optimizer, "getLastModelPopulation", null)!=null) { Object pc = BeanInspector.callIfAvailable(m_Optimizer, "getLastTrainingPatterns", null); - System.out.println("MAPSO train set bef optSpec: " + BeanInspector.callIfAvailable(pc, "getStringRepresentation", null)); +// System.out.println("MAPSO train set bef optSpec: " + BeanInspector.callIfAvailable(pc, "getStringRepresentation", null)); } this.m_Optimizer.optimize(); @@ -442,7 +522,8 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis if (m_doomedPop.size()>0) { reinitPop = this.initializeIndividuals(m_doomedPop.size()); // do not add these to undifferentiated yet, that would mess up the evaluation count m_doomedPop.clear(); - if (TRACE) System.out.println("Reinited " + reinitPop.size() + " indies... "); +// if (TRACE) +// System.out.println("At " + m_Undifferentiated.getFunctionCalls() + " reinited " + reinitPop.size() + " indies... "); } int countIndies = (reinitPop != null ? reinitPop.size() : 0) + m_Undifferentiated.size(); for (int i=0; i 0) { if (m_Undifferentiated.getGeneration()<=1) plot(m_Undifferentiated.getGeneration()); } + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + // species evolution phase - - // optimize D_0 this.m_Undifferentiated.synchSize(); if (m_Undifferentiated.size()>0) { -// this.capMutationRate(this.m_Undifferentiated, 0); // MK this sets mutation rate to 0! why? +// this.capMutationRate(this.m_Undifferentiated, 0); // MK this sets mutation rate to 0! why? possibly to guarantee contraction of the species? m_Undifferentiated.putData(InterfaceSpeciesAware.populationTagKey, InterfaceSpeciesAware.explorerPopTag); m_Undifferentiated = optimizeSpecies(m_Undifferentiated, false); } else m_Undifferentiated.incrGeneration(); @@ -476,12 +563,17 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis // System.out.println("Diff.Size : " + ((Population)this.m_Species.get(i)).size() +"/"+((Population)this.m_Species.get(i)).getPopulationSize()); // } convergedCnt++; -// if (TRACE) - if (TRACE) System.out.print("--Converged: "+convergedCnt + " - " + testSpeciesForConvergence(curSpecies)); + if (TRACE_EVTS) System.out.println("!!!! Converged Spec!"); + if (TRACE) { + testSpeciesForConvergence(curSpecies); + System.out.print("--Converged: "+convergedCnt + " - " + testSpeciesForConvergence(curSpecies)); + } if (TRACE) System.out.println(curSpecies.getBestEAIndividual()); // memorize the best one.... - AbstractEAIndividual best = (AbstractEAIndividual)curSpecies.getBestEAIndividual().getClone(); +// AbstractEAIndividual best = (AbstractEAIndividual)curSpecies.getBestEAIndividual().getClone(); + AbstractEAIndividual best = curSpecies.getBestHistoric(); // usually we want the best alltogether + if (best == null) best = (AbstractEAIndividual)curSpecies.getBestEAIndividual().getClone(); // if (useDistraction) { // Add distractor! // if (distraction == null) distraction = new Distraction(distrDefaultStrength, Distraction.METH_BEST); // distraction.addDistractorFrom(curSpecies); @@ -490,6 +582,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis int toReinit=0; if (true) { //if (m_UseArchive) { m_Archive.add(best); +// System.out.println((""+ m_Population.getFunctionCalls() + " " + (BeanInspector.toString(best.getDoublePosition())).replaceAll(";|\\[|\\]", ""))); m_Species.remove(i); // remove the converged Species toReinit=curSpecies.size(); } @@ -521,7 +614,18 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis m_Undifferentiated.incrFunctionCallsBy(curSpecies.getFunctionCalls()); if (TRACE) System.out.println("### funcalls: "+m_Undifferentiated.getFunctionCalls()); } - + + ////////////////////// + synchronized (m_Population) { // fill the m_Population instance with the current individuals from undiff, spec, etc. + this.m_Population = (Population)this.m_Undifferentiated.clone(); + m_Population.setUseHistory(true); + for (int i = 0; i < this.m_Species.size(); i++) { + this.m_Population.addPopulation((Population)this.m_Species.get(i)); + } + if (m_doomedPop.size()>0) m_Population.addPopulation(reinitPop); // this is just so that the numbers match up... + m_Population.synchSize(); + } + ////////////////////// if ((this.m_Undifferentiated.getFunctionCalls()+(reinitPop==null ? 0 : (reinitPop.size()))) % this.m_PopulationSize != 0) { if (TRACE) System.out.println("### mismatching number of funcalls, inactive species?");// Correcting by " + (m_PopulationSize - (m_Undifferentiated.getFunctionCalls() % m_PopulationSize))); @@ -532,7 +636,8 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis // possible species differentiation and convergence if (this.m_Undifferentiated.getGeneration()%this.m_SpeciesCycle == 0) { if (TRACE) System.out.println("Species cycle:"); - this.m_CAForSpeciesDifferentation.initClustering(m_Population); + initClustering(); + if (this.m_UseSpeciesDifferentiation) { ///////////////////////////// species differentiation phase if (TRACE) printState("---Species Differentation", reinitPop); @@ -540,12 +645,13 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis ArrayList newSpecies = new ArrayList(); //cluster the undifferentiated population clusters = this.m_CAForSpeciesDifferentation.cluster(this.m_Undifferentiated, m_Population); - if (TRACE) System.out.println("clustered undiff to " + clusters.length); - replaceUndifferentiated(clusters[0]); + if (TRACE) + System.out.println("clustered undiff to " + clusters.length); for (int j = 1; j < clusters.length; j++) { // loop new clusters splitFromFirst(m_Undifferentiated, clusters[j], false); newSpecies.add(clusters[j]); } + replaceUndifferentiated(clusters[0]); for (int i = 0; i < this.m_Species.size(); i++) { // loop old species curSpecies = this.m_Species.get(i); // if (curSpecies.size()>m_minGroupSize) { // only active populations are clustered @@ -574,10 +680,8 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis // plot the populations if (this.m_ShowCycle > 0) { - if ((this.m_Undifferentiated.getGeneration() <= 2)) { + if ((this.m_Undifferentiated.getGeneration() <= 2) || (this.m_Undifferentiated.getGeneration()%this.m_ShowCycle == 0)) { this.plot(this.m_Undifferentiated.getGeneration()); - } else { - if (this.m_Undifferentiated.getGeneration()%this.m_ShowCycle == 0) this.plot(this.m_Undifferentiated.getGeneration()); } } @@ -590,6 +694,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis int[] assocSpec = m_CAForSpeciesMerging.associateLoners(m_Undifferentiated, m_Species.toArray(new Population[m_Species.size()]), m_Population); for (int i=m_Undifferentiated.size()-1; i>=0; i--) { // backwards or die! if (assocSpec[i]>=0) { + if (TRACE_EVTS) System.out.println("!!! Loner merge to " + i ); // loner i should be merged to species assocSpec[i] AbstractEAIndividual tmpIndy = (AbstractEAIndividual)this.m_Undifferentiated.get(i); if (m_Topology!=null) plotLine(m_Topology, tmpIndy, m_Species.get(assocSpec[i]).getBestEAIndividual()); @@ -610,11 +715,13 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis if (!specToRemove.contains(assocSpec[i])) { // the species has not yet been deactivated specToRemove.add(assocSpec[i]); + collisions ++; if (TRACE) System.out.println("Inactive merge - resetting " + spec1.size() + " surplus indies"); if (spec1.getBestEAIndividual().isDominating(aIndy)) { // update the archived one with the better one? No rather not - it may happen that a large species is assoctiated which is quite large and spans over several optima - in that case an earlier found may get lost // m_Archive.set(i, spec1.getBestEAIndividual()); } + if (TRACE_EVTS) System.out.println("!!! Reinit Spec " + assocSpec[i] + ", fit " + spec1.getBestEAIndividual()); m_doomedPop.addPopulation(spec1); } } @@ -634,7 +741,8 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis for (int i2 = i1+1; i2 < this.m_Species.size(); i2++) { spec2 = (Population)this.m_Species.get(i2); if (this.m_CAForSpeciesMerging.mergingSpecies(spec1, spec2, m_Population)) { - if (TRACE) System.out.println("----Merging species (" + i1 +", " +i2 +") ["+spec1.size()+"/"+spec2.size()+"]"); + + if (TRACE_EVTS || TRACE) System.out.println("!!! -Merging species (" + i1 +", " +i2 +") ["+spec1.size()+"/"+spec2.size()+"]"); mergeToFirst(spec1, spec2, true); this.m_Species.remove(i2); i2--; @@ -649,6 +757,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis for (int i=0; im_maxSpeciesSize) { + if (TRACE_EVTS) System.out.println("!!! Reinit indies " + (m_maxSpeciesSize - curSpec.size())); ArrayList sorted = curSpec.getSorted(reduceSizeComparator); for (int k=m_maxSpeciesSize; k0) { // assume that it should be set + if (this.m_CAForSpeciesDifferentation instanceof InterfaceClusteringDistanceParam) { + ((InterfaceClusteringDistanceParam)m_CAForSpeciesDifferentation).setClustDistParam(getClusterDiffDist()); + if (TRACE) System.out.println("### Clustering distance parameter set to "+ getClusterDiffDist()); + } else EVAERROR.errorMsgOnce("Warning: cluster distance is defined in CBN but the clustering method " + m_CAForSpeciesDifferentation.getClass() + " cant interpret it!"); + } + this.m_CAForSpeciesDifferentation.initClustering(m_Population); + } + // // /** // * Unite all current species and the undiff. pop and return @@ -727,7 +851,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis * @param pop */ private void replaceUndifferentiated(Population pop) { - // this.m_Undifferentiated = ClusterResult[0]; +// System.out.println("Adding " + pop.size() + " as undiff."); m_Undifferentiated.clear(); m_Undifferentiated.addPopulation(pop); } @@ -760,6 +884,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis * @param pop2 */ protected void mergeToFirst(Population spec1, Population spec2, boolean plot) { +// System.out.println("Merging " + spec2.size() + " to " + spec1.size()); if (plot && (m_Topology!=null)) plotLine(m_Topology, spec1.getBestEAIndividual(), spec2.getBestEAIndividual()); spec1.addPopulation(spec2); // keep longer history @@ -910,6 +1035,17 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis public void setPopulation(Population pop){ this.m_Undifferentiated = pop; + if (m_Archive==null) m_Archive = new Population(); + m_Archive.setPopMetric(pop.getPopMetric()); + m_Population.setPopMetric(pop.getPopMetric()); + m_doomedPop.setPopMetric(pop.getPopMetric()); + + if (m_CAForSpeciesDifferentation instanceof InterfaceClusteringMetricBased) { + ((InterfaceClusteringMetricBased)m_CAForSpeciesDifferentation).setMetric(pop.getPopMetric()); + } + if (m_CAForSpeciesMerging instanceof InterfaceClusteringMetricBased) { + ((InterfaceClusteringMetricBased)m_CAForSpeciesMerging).setMetric(pop.getPopMetric()); + } pop.setUseHistory(true); } @@ -917,10 +1053,21 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis return "Edit the properties of the population used."; } + public Population getArchivedSolutions() { + return (Population)m_Archive.clone(); + } + public SolutionSet getAllSolutions() { // return inactive species - Population sols = (Population)m_Archive.clone(); - sols.addPopulation(getPopulation()); + Population sols = getArchivedSolutions(); +// sols.addPopulation(getPopulation()); + for (Population sp : m_Species) { + sols.add(sp.getBestIndividual()); + } + if (m_Undifferentiated.size()>0) sols.add(m_Undifferentiated.getBestIndividual()); +// if (!sols.checkNoNullIndy()) { +// System.err.println("error in CBN..."); +// } sols.synchSize(); return new SolutionSet(getPopulation(), sols); } @@ -1027,6 +1174,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis } public void setShowCycle(int b){ this.m_ShowCycle = b; + if (b<=0) m_Topology=null; } public String showCycleTipText() { return "Determines how often show is performed (generations); set to zero to deactivate."; @@ -1144,9 +1292,7 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis public String[] getAdditionalDataHeader() { return new String[]{"numUndiff","numActSpec","avgSpecMeas","numArchived", - "archivedMedCorr", "archivedMeanDist" -// , "numCollisions", "clustSig" - }; + "archivedMedCorr", "archivedMeanDist", "numCollisions", "clustSig"}; } public String[] getAdditionalDataInfo() { @@ -1157,8 +1303,8 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis "The number of stored potential local optima", "The median correlation of archived solutions", "The mean distance of archived solutions", -// "The number of collisions events that happened so far", -// "The clustering distance" + "The number of collisions events that happened so far", + "The clustering distance" }; } @@ -1171,12 +1317,11 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis m_Archive.size(), m_Archive.getCorrelations()[3], m_Archive.getPopulationMeasures()[0], -// collisions, -// getClusterDiffDist() - }; + collisions, + getClusterDiffDist()}; // return m_Undifferentiated.size() + " \t " + m_Species.size() + " \t " + BeanInspector.toString(getAvgSpeciesMeasures()[0]) + " \t " + (m_Archive.size()); } - + /** * Calculate average of Population measures (mean, minimal and maximal distance within a species) * @return average population measures @@ -1218,5 +1363,65 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis public String[] customPropertyOrder() { return new String[]{"mergingCA", "differentiationCA"}; } - + +// public void setHistComparator(AbstractEAIndividualComparator histComparator) { +// this.histComparator = histComparator; +// } + public AbstractEAIndividualComparator getHistComparator() { + return histComparator; + } +// public String histComparatorTipText() { +// return "The comparator to keep track of old optima. Should correspond to the clustering metric."; +// } + + public void setClusterDiffDist(double clusterDiffDist) { + this.clusterDiffDist = clusterDiffDist; + if (clusterDiffDist<0) { + if ((m_Problem instanceof InterfaceProblemDouble) && (m_CAForSpeciesDifferentation instanceof ClusteringDensityBased)) { +// int numExpectedOptima = (int)((((double)getPopulationSize())*0.9)/((ClusteringDensityBased)m_CAForSpeciesDifferentation).getMinimumGroupSize()); +// this.clusterDiffDist = EsDpiNiching.calcEstimatedNicheRadius(((AbstractProblemDouble)m_Problem).makeRange(), numExpectedOptima, new EuclideanMetric()); + setUpperBoundClustDiff((InterfaceProblemDouble) m_Problem); + } else System.err.println("Warning, unable to calculate standard niche radius in CBN-EA"); + } + } + + /** + * Calculate the clustering parameter in such a way that about one q-th part + * of the range of the given problem is within one hyper sphere of the clustering parameter. + * + * For certain types of parameter adaption schemes, this automatically sets the upper limit + * if the clustering parameter is controlled. + * + * @param prob + * @param q + */ + public void setUpperBoundClustDiff(InterfaceProblemDouble prob) { + if (m_CAForSpeciesDifferentation instanceof ClusteringDensityBased) { + double meanSubSwarmSize=0.5*(((ClusteringDensityBased)m_CAForSpeciesDifferentation).getMinimumGroupSize()+getMaxSpeciesSize()); + int numExpectedOptima = (int)((((double)getPopulationSize()))/meanSubSwarmSize); + double[][] range = ((InterfaceProblemDouble)m_Problem).makeRange(); + int dim = range.length; + double nRad = EsDpiNiching.calcEstimatedNicheRadius(range, numExpectedOptima, ((ClusteringDensityBased) m_CAForSpeciesDifferentation).getMetric()); + nRad = nRad * Math.pow(0.5, 1/dim); +// System.out.println("Alternative clust diff from niche radius... " + nRad); + this.clusterDiffDist = nRad; +// System.out.println("Setting the clusterDiffDist to "+ clusterDiffDist); + ParamAdaption[] adaptors = getParameterControl(); + if (adaptors.length>0) { + for (ParamAdaption adpt : adaptors) { + if (adpt.getControlledParam().equals("clusterDiffDist")) { + if (adpt instanceof InterfaceHasUpperDoubleBound) { + ((InterfaceHasUpperDoubleBound)adpt).SetUpperBnd(clusterDiffDist); + } else System.err.println("Warning, unknown parameter adaption type for automatic setting of upper bound of the clustering sigma (CBN-EA)"); + } + } + } +// double estRad = EsDpiNiching.calcEstimatedNicheRadius(prob.makeRange(), expectedPeaks, metric); +// setClusterDiffDist(estRad); + } else System.err.println("Warning, unable to calculate standard niche radius in CBN-EA"); + } + + public double getClusterDiffDist() { + return clusterDiffDist; + } } diff --git a/src/eva2/server/go/strategies/EsDpiNiching.java b/src/eva2/server/go/strategies/EsDpiNiching.java new file mode 100644 index 00000000..a7baa4e4 --- /dev/null +++ b/src/eva2/server/go/strategies/EsDpiNiching.java @@ -0,0 +1,1137 @@ +package eva2.server.go.strategies; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Formatter; + +import eva2.gui.GenericObjectEditor; +import eva2.gui.TopoPlot; +import eva2.server.go.InterfacePopulationChangedEventListener; +import eva2.server.go.PopulationInterface; +import eva2.server.go.individuals.AbstractEAIndividual; +import eva2.server.go.individuals.InterfaceDataTypeDouble; +import eva2.server.go.operators.cluster.ClusteringDynPeakIdent; +import eva2.server.go.operators.distancemetric.EuclideanMetric; +import eva2.server.go.operators.distancemetric.IndividualDataMetric; +import eva2.server.go.operators.distancemetric.InterfaceDistanceMetric; +import eva2.server.go.operators.distancemetric.PhenotypeMetric; +import eva2.server.go.operators.mutation.InterfaceAdaptOperatorGenerational; +import eva2.server.go.operators.paramcontrol.ParamAdaption; +import eva2.server.go.operators.paramcontrol.ParameterControlManager; +import eva2.server.go.operators.selection.InterfaceSelection; +import eva2.server.go.operators.selection.SelectBestIndividuals; +import eva2.server.go.operators.selection.SelectBestSingle; +import eva2.server.go.operators.selection.SelectRandom; +import eva2.server.go.operators.selection.SelectTournament; +import eva2.server.go.operators.terminators.HistoryConvergenceTerminator; +import eva2.server.go.populations.InterfaceSolutionSet; +import eva2.server.go.populations.Population; +import eva2.server.go.populations.SolutionSet; +import eva2.server.go.problems.Interface2DBorderProblem; +import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; +import eva2.server.go.problems.InterfaceOptimizationProblem; +import eva2.tools.math.Mathematics; +import eva2.tools.math.RNG; + +/** + * The ES niching with dynamic peak identification. Basically, for a set of "peaks" a set of (mu,lambda)-ESes are + * performed in parallel, with interaction based on a niche radius (fixed so far). + * The number of peaks must be predefined, and lambda new samples are drawn for every peak in every iteration. + * Thus, in relation to the standard ES, some new parameters are due. On the other hand, the selection schemes + * are predefined. This is done according to Shir&Bäck, Niching in Evolution Strategies, Tec.Report 2005. + * + * Unfortunately the algorithm was not described in every detail. It remained unclear how exactly the + * peak population where filled and esp. what happens if DPI returns less than q peaks. + * In this implementation, this is solved by setting a maximum size to each peak cluster (muPerPeak). For + * a peak population, only the best muPerPeak remain and the rest is assigned to the unclustered set. From these, + * pseudo-peaks are then formed until the expected number of peak populations is produced. + * + * Also, they gave a general rule for setting the niche radius depending on the problem domain, however in their + * experiments, they were able to identify optima which were much closer (i.e., on Ackley's), so it is unclear + * which niche radius was actually used there + * + * Due to the "non-standard" environmental selection mechanism (a fixed proportion is chosen from the children, the + * rest from the parents), a preselection mechanism was implemented here, slightly breaking the ES framework + * which is still used to optimize the single peak populations. + * + * This class should also cover "Dynamic niching in evolution strategies with covariance matrix adaptation" + * by Shir & Bäck, CEC 2005, when employing SelectBest as parent selection and muPerPeak=1. + * + * Some notes: + * + * If strictNiching is deactivated, niches are disappearing because they are sometimes + * assigned the global opt. and in that niche are very bad so they get thrown away due to the maximum niche count + * if strictNiching is activated, niches are very small - or even empty. Why is that? This can hardly be sensible + * esp. with a very small niche radius for F8. This would mean that in the beginning, nearly all offspring + * are created from very few pairs of peaks. + * + * The mu/lambda information was lost on the way to the MutateESRankMuCMA class... I added it now as additional pop.data + * which is only loaded if the mu/lambda ratio deduced from the orig/selected population sizes does not make sense. + * + * With the explorer peaks reinited every 100 generations or so, the course of the MPR for a *single* run + * will not be a clear logistic function, because "finding" additional peaks produces small steps in the MPR. + * + * The Niching ES is now using the Population to catch the generational events, not producing it itself, similar + * to Tribes, ScatterSearch or IPOP-ES. Thus it could be used with a dynamic population size without too much + * hassle. + * + * TODO + * Add adaptive niche radius. Add parameter to every indy which is adapted after all new peaks have been found. + * + * + * @author mkron + * + */ +public class EsDpiNiching implements InterfaceOptimizer, Serializable, InterfaceAdditionalPopulationInformer, InterfacePopulationChangedEventListener { + private static final boolean TRACE=false, TRACE_DEMES=false; + private double nicheRadius = 0.3; + private int expectedPeaks = 5; + private int explorerPeaks = 0; + private int muPerPeak = 50; + private int lambdaPerPeak = 60; + private int eta = 30; + private boolean doEtaPreselection = true; + private int numRndImmigrants = 100; + private boolean useNicheRadiusEstimation = true; + private boolean reinitAlreadyFound = true; // check if current species have converged to already archived solutions - if so, deactivate them + private transient Population archive = new Population(); // collect deactivated optima + protected ParameterControlManager paramControl = new ParameterControlManager(); + + private transient EvolutionStrategies[] peakOpts = null; + + Population population = new Population(); + private InterfaceOptimizationProblem problem; + private transient InterfacePopulationChangedEventListener m_Listener; + private String identifier = "Niching-ES"; + private transient TopoPlot plot = null; + private transient Population randomNewIndies = null; + private int plotInterval = 0; + private InterfaceDistanceMetric metric = new PhenotypeMetric(); + private boolean addLonersToPeaks = false; + private InterfaceSelection parentSel = new SelectTournament(); + private boolean allowSingularPeakPops = false; + private int resetExplorerInterval = 1; // how often are the exploring peaks reset? + private int convCount = 0; + private int haltingWindowLen = 15; // the window length along which species convergence is measured + private double deactConvThresh = 1e-5; // the threshold for species convergence and deactivation (std. dev of historical fitnesses) + private int fitCriterion = 0; // fitness criterion to check for species convergence + private int collisions = 0; + private boolean doNumPeakAdaption = false; + private double collisionDetNicheRadius = 0.001; // distance below which collision between active species and archived solutios is assumed + private static final String origPeakIndyKey = "originalPeakIndividualKey"; + + public static final String originalPeakPop = "originalPeakPopulationID"; + + public EsDpiNiching() { + } + + /** + * Use the given parameters, the maximal estimation for the niche radius, no preselection and no explorers. + * + * @param muPerPeak + * @param lambdaPerPeak + * @param expectedPeaks + * @param rndImmigrants + */ + public EsDpiNiching(int muPerPeak, int lambdaPerPeak, int expectedPeaks, int rndImmigrants) { + this(-1., muPerPeak, lambdaPerPeak, expectedPeaks, rndImmigrants, 0, 0, 0); + } + + /** + * Use the given parameters, no preselection and no explorers. + * + * @param nicheRadius + * @param muPerPeak + * @param lambdaPerPeak + * @param expectedPeaks + * @param rndImmigrants + */ + public EsDpiNiching(double nicheRadius, int muPerPeak, int lambdaPerPeak, int expectedPeaks, int rndImmigrants) { + this(nicheRadius, muPerPeak, lambdaPerPeak, expectedPeaks, rndImmigrants, 0, 0, 0); + } + + /** + * If the niche radius is zero or negative, the automatic estimation is used. Otherwise the given radius is used. + * If etaPresel is positive, eta-preselection is activated, otherwise it is inactive. + * + * @param nicheRadius + * @param muPerPeak + * @param lambdaPerPeak + * @param expectedPeaks + * @param rndImmigrants + * @param explorerPeaks + * @param resetExplInterval + * @param etaPresel + */ + public EsDpiNiching(double nicheRadius, int muPerPeak, int lambdaPerPeak, int expectedPeaks, int rndImmigrants, int explorerPeaks, int resetExplInterval, int etaPresel) { + doEtaPreselection=(etaPresel > 0); + if (nicheRadius>0) { + setNicheRadius(nicheRadius); + setUseNicheRadiusEstimation(false); + } else setUseNicheRadiusEstimation(true); + this.eta=etaPresel; + this.muPerPeak = muPerPeak; + this.lambdaPerPeak = lambdaPerPeak; + this.numRndImmigrants = rndImmigrants; + this.expectedPeaks = expectedPeaks; + this.resetExplorerInterval = resetExplInterval; + } + + public EsDpiNiching(EsDpiNiching o) { + // TODO update! + this.nicheRadius = o.nicheRadius; + this.resetExplorerInterval = o.resetExplorerInterval; + this.expectedPeaks = o.expectedPeaks; + this.explorerPeaks = o.explorerPeaks; + this.muPerPeak = o.muPerPeak; + this.lambdaPerPeak = o.lambdaPerPeak; + this.eta = o.eta; + this.doEtaPreselection = o.doEtaPreselection; + this.numRndImmigrants = o.numRndImmigrants; + this.useNicheRadiusEstimation = o.useNicheRadiusEstimation; + this.setAllowSingularPeakPops(o.isAllowSingularPeakPops()); + + if (o.population!=null) this.population = (Population)o.population.clone(); + if (o.problem!=null) this.problem = (InterfaceOptimizationProblem)o.problem.clone(); + this.identifier = o.identifier; + this.plotInterval = o.plotInterval; + } + + public void hideHideable() { + setDoEtaPreselection(isDoEtaPreselection()); + setUseNicheRadiusEstimation(isUseNicheRadiusEstimation()); + GenericObjectEditor.setHideProperty(this.getClass(), "population", true); + } + + public Object clone() { + return new EsDpiNiching(this); + } + + public void init() { + convCount=0; // reset number of converged species (found peaks) + collisions=0; // reset the number of collisions + population = new Population((getExpectedPeaks()+getExplorerPeaks())*lambdaPerPeak); + population.setMaxHistoryLength(haltingWindowLen); + + archive = new Population(); + peakOpts = new EvolutionStrategies[getExpectedPeaks()+getExplorerPeaks()]; + for (int i=0; i0) { + generateEvalImmigrants(getNumRndImmigrants()); + } + if (isDoEtaPreselection() && (getEta()>getMuPerPeak())) { + System.err.println("Warning, eta should be less-equal mu... setting eta="+getMuPerPeak()); + setEta(getMuPerPeak()); + } + collectPopulationIncGen(population, peakOpts, randomNewIndies); + population.addPopulationChangedEventListener(this); + population.setNotifyEvalInterval(50); + + if (isUseNicheRadiusEstimation()) updateNicheRadius(); + } + + private void updateNicheRadius() { + AbstractEAIndividual indy=population.getEAIndividual(0); + if (indy instanceof InterfaceDataTypeDouble) { + setEstimatedNicheRadius(((InterfaceDataTypeDouble)indy).getDoubleRange()); + } else System.err.println("Error, default niche radius can only be estimated for individuals of type InterfaceDataTypeDouble!"); + } + + /** + * Use the deactivation of converged species, storing them to an archive, + * with the given parameters. If windowLen <= 0, the deactivation + * mechanism is disabled. + * This provides for semi-sequential niching with DPI-ES + * + * @param threshold + * @param windowLen + */ + public void setSpeciesDeactivation(double threshold, int windowLen) { + setHaltingWindow(windowLen); + setEpsilonBound(threshold); + } + + private void setEstimatedNicheRadius(double[][] range) { + double estRad = EsDpiNiching.calcEstimatedNicheRadius(range, expectedPeaks, metric); + setNicheRadius(estRad); + } + + /** + * Calculate the estimated maximal niche radius for a given problem range. This + * is an estimate on the q-th part of the volume transfered to the radius of a hypersphere, + * where q is the number of expected peaks. + * + * @param range + * @param numExpectedOptima + * @param metric + * @return + */ + public static double calcEstimatedNicheRadius(double[][] range, int numExpectedOptima, InterfaceDistanceMetric metric) { + int dim = range.length; + double r; + if (metric instanceof EuclideanMetric) { + r = Mathematics.getAvgRangeL2(range); + } else { //if (metric instanceof PhenotypeMetric) { // r can be assumed to be 0.5*sqrt(dim) + if (!(metric instanceof PhenotypeMetric)) { + if ((metric instanceof IndividualDataMetric) && (((IndividualDataMetric)metric).getBaseMetric() instanceof EuclideanMetric)) {} // this is ok as well + else System.err.println("Warning, unexpected metric in EsDpiNiching! Estimated niche radius may fail..."); + } + r = 0.5*Math.sqrt(dim); + } +// setNicheRadius(r*Math.pow(numExpectedPeaks, -1./(double)dim)); + return r*Math.pow(numExpectedOptima, -1./(double)dim); + } + + public void addExpectedPeaks(int k) { + // TODO what to do if new peaks should be introduced? + // inc. expected peaks, allocate new optimizers, possibly adapt clustering distance + + // this must happen to a specific time, namely before or after one iteration. + if (k<=0) System.err.println("Invalid k in addExpectedPeaks (" + k + ")."); + setExpectedPeaks(getExpectedPeaks()+k); + EvolutionStrategies[] newPeakOpts = new EvolutionStrategies[getExpectedPeaks()+getExplorerPeaks()]; + for (int i=0; i=getExpectedPeaks())) { + return (int)Math.max(((double)getExpectedPeaks())*1.2, 2.); + } else return 0; + } + + public boolean isDoNumPeakAdaption() { + return doNumPeakAdaption ; + } + public void setDoNumPeakAdaption(boolean doApt) { + doNumPeakAdaption = doApt; + } + public String doNumPeakAdaptionTipText() { + return "Activate online adaption of the number of expected peaks"; + } + + public void optimize() { + Population peakPopSet[]; + if (increaseExpectedPeaksCriterion()>0) { + addExpectedPeaks(increaseExpectedPeaksCriterion()); + } + if (TRACE) System.out.println("--- FULL POP SIZE: " + population.size() + " , funcalls: " + population.getFunctionCalls() + ", gen " + population.getGeneration()); + ClusteringDynPeakIdent dpiClustering = new ClusteringDynPeakIdent(getExpectedPeaks(), getLambdaPerPeak(), nicheRadius, true, metric ); + // perform dynamic peak identification resulting in the dynamic peak set dps + dpiClustering.initClustering(population); + peakPopSet = dpiClustering.cluster(population, population); + // remove this instance as listener because we only want to "hear" events of the main population + for (int i=0; i for each peak chose the original last population + // and transfer the data to the new cluster +// if (copyHashFromParents) { + copyDataFromParents(peakPopSet); +// } else { +// copyDataFromClosest(clusteredPeakPops); +// } + + int reqNewPeaks = 0; + if (curNumPeaks0) { + if ((getPopulation().getGeneration() % resetExplorerInterval == 0)) { + // reset the explorers + reqNewPeaks += getExplorerPeaks(); + } else { + // take over the last explorers + // TODO only if they have not made it to the desired peak populations? + Population[] clustersWithExplorers = new Population[peakPopSet.length + getExplorerPeaks()]; + for (int i=0; i0) && isReinitOnCollision()) { + double origNicheRad=dpiClustering.getNicheRadius(); + dpiClustering.setNicheRadius(collisionDetNicheRadius); + int[] assoc = dpiClustering.associateLoners(archive, peakPopSet, population); + for (int i=0; i=0) { + collisions++; + // if an association was found, there is a peak close to an archived solution, so reset it. + if (!archive.getEAIndividual(i).isDominating(peakPopSet[assoc[i]].getBestEAIndividual())) { + // the new found species is actually better than the archived solution + archive.set(i, peakPopSet[assoc[i]].getBestEAIndividual().clone()); // replace archived indy + } + if (TRACE) System.out.println(" Converged on archived solution.. resetting peak pop " + assoc[i]); + peakPopSet[assoc[i]] = initRandomPeakPop(getMuPerPeak()); + } + } + dpiClustering.setNicheRadius(origNicheRad); + if (TRACE_DEMES) printDemes("After archivie-merge: ", peakPopSet); + } + + if (TRACE) for (int k=0; k0) { + System.err.println("duplicate inherited in EsDpiNiching! OK if explorer peaks exist that are not always reinited and " + j + ">"+getExpectedPeaks() + " and " + k + "==0."); + } + } + } + plot=null; + // now generate the lambda offsprings +// nextGeneration = this.generateEvalChildren(dps); // create lambda new ones from mu parents + for (int clustIndex=1; clustIndex0) { + // possibly deactivate a converged species and add its representative to the archive + HistoryConvergenceTerminator hConv = new HistoryConvergenceTerminator(haltingWindowLen, deactConvThresh , fitCriterion , true); + if (hConv.isTerminated(curSpecies)) { // species is terminated + peakPopSet[clustIndex]=deactivateSpecies(clustIndex, true); // store best, reinitialize randomly + } + } + + Population optimizedSpecies = peakOpts[clustIndex-1].getPopulation(); + if (doDraw(population.getGeneration())) drawPeakPop(""+clustIndex, curSpecies); + if (TRACE) System.out.println("Optimizing cluster index " + (clustIndex) + ", size " + curSpecies.size()); + peakOpts[clustIndex-1].optimize(); // !!!!!!!! Actual optimization step + optimizedSpecies = peakOpts[clustIndex-1].getPopulation(); + optimizedSpecies.putData(origPeakIndyKey , curPeak); + population.incrFunctionCallsBy(optimizedSpecies.size()); +// optimizedSpecies.incrGeneration(); // is already done in the .optimize() call above +// optimizedSpecies.incrFunctionCallsBy(optimizedSpecies.size()); + if (TRACE) System.out.println(" ..." + optimizedSpecies.size() + " more funcalls... "); + + } + + // we may have a problem if, by chance, all species have been deactivated simultaneously AND there are no unclustered ! + if (dynamicPopSize()==0) { + // if this is the case, we just reinit a single in analogy to a missing peak + peakOpts[0].getPopulation().addPopulation(initRandomPeakPop(getMuPerPeak())); + } + + if (TRACE) for (int k=0; k1) { + // one may happen after a cluster had a size of one, since then another leader is added + if (cut.size()==2 && (peakPopSet[k].size()==2) && (peakPopSet[j].size()==2)) { + // two may happen since they can be added reciprocally to each other + } else { + System.err.println("duplicate indy in EsDpiNiching. OK if explorer peaks exist that are not always reinited and " + j + ">"+getExpectedPeaks() + " and " + k + "==0."); + } + } + } + } + if (doEtaPreselection) { // this basically replaces ES-environment selection + // select the eta best from the offspring per peak population + // fill up to muPerPeak by adding from the old peak population + SelectBestIndividuals selBest = new SelectBestIndividuals(); + Population loners = peakPopSet[0]; + plot=null; + for (int i=0; i=peakPopSet.length) { + System.err.println("Warning: fewer clusters than expected peaks in EsDpiNiching!"); + offspring.clear(); // empty set to avoid duplicates! + } else { + if (TRACE) System.out.println("EtaPresel: from " + offspring.size() + " offspring selecting "+ eta); + Population selected = selBest.selectFrom(offspring, eta ); + if (!selected.isSubSet(offspring)) { + System.err.println("fatal problem in EsDpiNiching!!!"); + } + if (offspring.setCut(peakPopSet[i+1]).size()>0) { + System.err.println("problem in EsDpiNiching!!!"); + } + int delta = muPerPeak - eta; + if (delta>0) { + Population filterPeakPop = peakPopSet[i+1].filter(selected); + if (TRACE) System.out.println("Adding " + Math.min(delta, filterPeakPop.size()) + " from peak population."); + // (few) duplicates may happen because parents are copied to another peak population + // if a cluster had a size of 1 AND parents may survive due to elitism. + selected.addPopulation(selBest.selectFrom(filterPeakPop, Math.min(delta, filterPeakPop.size())), false); + if (selected.size()2) { //one may happen after a cluster had size one (see above) + System.err.println("Warning, nonempty set cut between " + k + " and " + i + " !"); + } + peakOpts[i].m_Population.clear(); + peakOpts[i].m_Population.addAll(selected); + if (doDraw(population.getGeneration())) drawPeakPop(""+i, selected); + } + } + } +// System.out.println("Best of second peak: " + clusteredPeakPops[2].getBestEAIndividual()); + if (doDraw(population.getGeneration()) && archive!=null) { + for (int i = 0; i < this.archive.size(); i++) { + ClusterBasedNichingEA.plotIndy(plot, 'x',(InterfaceDataTypeDouble)archive.get(i)); + } + } + if (getNumRndImmigrants()>0) generateEvalImmigrants(getNumRndImmigrants()); + collectPopulationIncGen(population, peakOpts, randomNewIndies); + //this.firePropertyChangedEvent(Population.nextGenerationPerformed); // moved this to registerPopulationStateChanged which is called from the population + } + + private Population deactivateSpecies(int clustIndex, boolean resetRandomly) { + Population optimizedSpecies = peakOpts[clustIndex-1].getPopulation(); + if (TRACE) System.out.println("Terminated species: " + clustIndex + ", optimizer " + (clustIndex-1)); + if (TRACE) System.out.println(optimizedSpecies.getHistory()); + convCount++; + // get best indy, add it to archive, clear population of the optimizer for a reset + archive.add(optimizedSpecies.getBestIndividual()); + if (resetRandomly) { + optimizedSpecies.clear(); + optimizedSpecies.clearHistory(); + optimizedSpecies = initRandomPeakPop(getMuPerPeak()); + peakOpts[clustIndex-1].setPop(optimizedSpecies); + } + return optimizedSpecies; + } + + /** + * Copy the additional data from the parent populations to the new clusters. + * + * @param clusteredPeakPops + */ + private void copyDataFromParents(Population[] clusteredPeakPops) { + for (int i=1; i=0)) { + Population origPop = peakOpts[origEsPop].getPopulation(); + clusteredPeakPops[i].copyHashData(origPop); + clusteredPeakPops[i].SetHistory(origPop.getHistory()); // copy the history for deactivation! +// System.out.println("Copied hash for peak " + clusteredPeakPops[i].getBestEAIndividual() + ", cluster " + i + " from " + origEsPop); + } else { // ok in the first iteration of if the indy was a random immigrant + if (population.getGeneration()>1 && (getNumRndImmigrants()==0)) System.err.println("Error, empty original es pop ID!"); + } + } + } + +// /** +// * For each new cluster copy the additional data from the closest parent population. +// * +// * @param clusteredPeakPops +// */ +// private void copyDataFromClosest(Population[] clusteredPeakPops) { +// Population centers = new Population(clusteredPeakPops.length); +// AbstractEAIndividual indy; +// // collect leaders +// for (int i=0; i closestIdDist = Population.getClosestFarthestIndy(clusteredPeakPops[i].getBestEAIndividual(), centers, metric, true); +// Population closestPop = peakOpts[closestIdDist.head()].getPopulation(); +// System.out.println("Closest to new peak " + indy.toString() + " is old pop " + closestPop.getData(origPeakIndyKey)); +// System.out.println(" meanX was " + (MutateESRankMuCMA.getMeanXOfPop(closestPop))); +// clusteredPeakPops[i].copyHashData(closestPop); +// } +// } + + private void printDemes(String prefix, Population[] peakPops) { + System.out.print(prefix + " demes: "); + for (int i=0; i0) System.out.print(format(peakPops[i].getBestEAIndividual().getFitness(0), 3, 3)); + } catch(Exception e) { + System.err.println("NARG!"); + } + + } + System.out.println(); + } + + public static String format(double d, int len, int prec) { + StringBuffer sb = new StringBuffer(); + Formatter fm = new Formatter(sb); + if (Math.abs(d)>1e6) fm.format("%"+prec+"."+len+"e ", d); + else fm.format("%"+prec+"."+len+"f ", d); + return sb.toString(); + } + + private void setGeneration(int gen, Population[] pops) { + for (int i=0; i 0) { + randomNewIndies = new Population(cnt); + problem.initPopulation(randomNewIndies); + problem.evaluate(randomNewIndies); + population.incrFunctionCallsBy(cnt); + if (TRACE) System.out.println("evaluated immigrants: " + randomNewIndies.size()); + } else randomNewIndies = null; + } + + /** Something has changed + */ + protected void firePropertyChangedEvent(String name) { + if (this.m_Listener != null) this.m_Listener.registerPopulationStateChanged(this, name); + } + + private AbstractEAIndividual selectBestFromOtherSpecies(int i, Population[] clusteredSpecies) { + // the index must be >0 and != i: + int rndIndex = RNG.randomInt(clusteredSpecies.length-2); + if (rndIndex==0) rndIndex++; + if (rndIndex==i) rndIndex++; + if (TRACE) System.out.println("selected spec index " + rndIndex); + return clusteredSpecies[rndIndex].getBestEAIndividual(); + } + + private Population[] getOptPops() { + Population pops[] = new Population[peakOpts.length]; + for (int i=0; i0)) try { + if (plot == null) { + plot = new TopoPlot("Niching-ES " + npop.getGeneration(), "x", "y"); + plot.setParams(50, 50); + if (problem instanceof Interface2DBorderProblem) plot.setTopology((Interface2DBorderProblem)problem); + } + ClusterBasedNichingEA.plotPopConnected(plot, npop); + plot.drawIndividual(1, 0, "", npop.getBestEAIndividual()); +// plot.drawPopulation(prefix, npop); + } catch(Exception e) { + plot=null; + } + } + + private boolean doDraw(int gen) { + return (plotInterval>0) && ((gen%plotInterval)==0) ; + } + + /** + * Within pop, replace all individuals which are better than indy by indy. + * This makes sure that SelectBest will select indy on the population. + * + * @param pop + * @param indy + */ + private static void replaceWorstAndAllBetterIndiesBy(Population pop, + AbstractEAIndividual indy) { + int lastIndex = -1; + if (!pop.contains(indy)) { // replace best + int bestIndex = pop.getIndexOfBestIndividualPrefFeasible(); + pop.set(bestIndex, indy); + lastIndex=bestIndex; + } + int bestIndex = pop.getIndexOfBestIndividualPrefFeasible(); + while ((lastIndex!=bestIndex) && (pop.getEAIndividual(bestIndex).isDominatingDebConstraints(indy))) {// remove best, add indy + pop.set(bestIndex, indy); + lastIndex = bestIndex; + bestIndex = pop.getIndexOfBestIndividualPrefFeasible(); + } + } + + /** + * Perform mutation adaption for instances of InterfaceMutationGenerational. + * Specifically, the original population of each dps-member is cloned and the method + * adaptAfterSelection is called which may modify the population. The vector of these + * new populations is returned. + * + * @param collectedPop + * @param dps + * @param opts + * @return the vector of Population instances + */ + private static Population[] performDPSGenerationalAdaption(Population collectedPop, + Population dps, EvolutionStrategies[] opts) { + // loop over all dps individuals and + // clone the new mutator instance from the original population + // call the generational adaption for the new instance + Population[] resPops = new Population[dps.size()]; + for (int i=0; i0) expectedPeaks = ep; + else System.err.println("Error, expecting positive number of peaks!"); + } + public String expectedPeaksTipText() { + return "The number of expected peaks on the problem."; + } + + public int getExplorerPeaks() { + return explorerPeaks; + } + public void setExplorerPeaks(int ep) { + if (ep>=0) explorerPeaks = ep; + else System.err.println("Error, expecting nonzero number of explorer peaks!"); + } + public String explorerPeaksTipText() { + return "The number of additional explorer peaks."; + } + + public String getName() { + return identifier + "_" + getExpectedPeaks() + "_" + getNicheRadius(); + } + + public static String globalInfo() { + return "A niching ES with dynamic peak identification, after Shir and Bäck: Niching in Evolution Strategies, " + + "GECCO 2005. Basically, there are several variants of a (mu,lambda)-ES performed " + + "in parallel, which are reclustered in each iteration based on the dynamic peak set."; + } + + public void addPopulationChangedEventListener( + InterfacePopulationChangedEventListener ea) { + m_Listener = ea; + } + public boolean removePopulationChangedEventListener( + InterfacePopulationChangedEventListener ea) { + if (ea.equals(m_Listener)) { + m_Listener=null; + return true; + } else return false; + } + + public void freeWilly() {} + + public InterfaceSolutionSet getAllSolutions() { + Population peaks = new Population(peakOpts.length); + for (int i=0; i 0, show debug visualization at indicated iterations."; + } + + public int getEta() { + return eta; + } + public void setEta(int eta) { + this.eta = eta; + } + public String etaTipText() { + return "The number of offspring individuals per peak which will be preselected."; + } + + public boolean isDoEtaPreselection() { + return doEtaPreselection; + } + public void setDoEtaPreselection(boolean doEtaPreselection) { + this.doEtaPreselection = doEtaPreselection; + GenericObjectEditor.setShowProperty(this.getClass(), "eta", doEtaPreselection); + } + public String doEtaPreselectionTipText() { + return "Replace ES environmental selection by choosing some individuals from the offspring within a niche and some from the former niche population."; + } + + public void setNumRndImmigrants(int numRndImmigrants) { + this.numRndImmigrants = numRndImmigrants; + } + public int getNumRndImmigrants() { + return numRndImmigrants; + } + public String numRndImmigrantsTipText() { + return "A number of individuals will be randomly created in every iteration."; + } + + public int getMuPerPeak() { + return muPerPeak; + } + public void setMuPerPeak(int muPerPeak) { + this.muPerPeak = muPerPeak; + } + public String muPerPeakTipText() { + return "Number of parent individuals per niche."; + } + + public void setUseNicheRadiusEstimation(boolean useNicheRadiusEstimation) { + this.useNicheRadiusEstimation = useNicheRadiusEstimation; + GenericObjectEditor.setHideProperty(this.getClass(), "nicheRadius", useNicheRadiusEstimation); + } + public boolean isUseNicheRadiusEstimation() { + return useNicheRadiusEstimation; + } + public String useNicheRadiusEstimationTipText() { + return "Activate to use a niche radius corresponding to the q-th part of the search space (q number of peaks expected) - often niche radii should be smaller since this is close to the upper bound."; + } + + public InterfaceSelection getParentSelection() { + return parentSel; + } + public void setParentSelection(InterfaceSelection parentSel) { + this.parentSel = parentSel; + } + public String parentSelectionTipText() { + return "Set the parent selection method for the underlying ES."; + } + + public void setAllowSingularPeakPops(boolean allowSingularPeakPops) { + this.allowSingularPeakPops = allowSingularPeakPops; + } + public boolean isAllowSingularPeakPops() { + return allowSingularPeakPops; + } + public String allowSingularPeakPopsTipText() { + return "Allow peak populations of size 1 or force a randomly selected other peak as second indy."; + } + + public int getResetExplorerInterval() { + return resetExplorerInterval; + } + public void setResetExplorerInterval(int resInt) { + if (resInt>0) this.resetExplorerInterval = resInt; + else System.err.println("The explorer reset interval should be positive!"); + } + public String resetExplorerIntervalTipText() { + return "The explorer peaks are reset in intervals of iterations (generations)."; + } + + public int getHaltingWindow() { + return haltingWindowLen; + } + public void setHaltingWindow(int hw) { + haltingWindowLen = hw; + } + public String haltingWindowTipText() { + return "Number of generations after which a species without improvement is seen as converged and deactivated; set to zero to disable."; + } + + public double getEpsilonBound() { + return deactConvThresh; + } + public void setEpsilonBound(double epsilonBound) { + this.deactConvThresh = epsilonBound; + } + public String epsilonBoundTipText() { + return "If fitness std. dev. changes less than this value within the halting window, convergence is assumed."; + } + + public String[] getAdditionalDataHeader() { + return new String[]{"nicheRadius", "numExpectedPeaks", "numArchived", "archivedMeanDist", "numCollisions"}; + } + + public String[] getAdditionalDataInfo() { + return new String[]{"The niche radius employed for Dynamic Peak Identificatio", "The number of expected peaks", + "The number of stored potential local optima", "Mean distance of archived solutions", + "The number of collisions detected so far"}; + } + + public Object[] getAdditionalDataValue(PopulationInterface pop) { + return new Object[]{getNicheRadius(), getExpectedPeaks(), archive.size(), archive.getPopulationMeasures()[0], collisions}; + } + + /** + * This method is necessary to allow access from the Processor. + * @return + */ + public ParameterControlManager getParamControl() { + return paramControl; + } + + public ParamAdaption[] getParameterControl() { + return paramControl.getSingleAdapters(); + } + public void setParameterControl(ParamAdaption[] paramControl) { + this.paramControl.setSingleAdapters(paramControl); + } + public String parameterControlTipText() { + return "You may define dynamic paramter control strategies using the parameter name."; + } + + public void setReinitOnCollision(boolean reinitAlreadyFound) { + this.reinitAlreadyFound = reinitAlreadyFound; + } + public boolean isReinitOnCollision() { + return reinitAlreadyFound; + } + public String reinitOnCollisionTipText() { + return "Indicate whether already known (archived) peaks should trigger a reset of close-by species (corresp. to niche radius)."; + } + + public void registerPopulationStateChanged(Object source, String name) { + if (getPopulation() != source) { + System.err.println("Warning, mismatching population in " + this.getClass().getName()); + } + if (name.equals(Population.funCallIntervalReached)) { +// getPopulation().SetFunctionCalls(((Population)source).getFunctionCalls()); // this is ugly and I dont know what its for.. possibly if the population instance changes along the GUi? + this.firePropertyChangedEvent(Population.nextGenerationPerformed); + } else { + // this may come from cloned instances with the same listener - should not happen since they are removed. + // it may still come from "incGeneration" calls - we can ignore those +// System.err.println("Not forwarding event " + name); + } + } +} diff --git a/src/eva2/server/go/strategies/EsDpiNichingCma.java b/src/eva2/server/go/strategies/EsDpiNichingCma.java new file mode 100644 index 00000000..aa8a09dc --- /dev/null +++ b/src/eva2/server/go/strategies/EsDpiNichingCma.java @@ -0,0 +1,47 @@ +package eva2.server.go.strategies; + +import eva2.server.go.operators.selection.SelectBestSingle; + +public class EsDpiNichingCma extends EsDpiNiching { + + /** + * Preset all values according to the (1,lambda)-ES. This activates the automatic niche radius estimation + * (which frequently overestimates) and expects 10 peaks. + */ + public EsDpiNichingCma() { + this(10, 10, 0, 0); + } + + /** + * Preset all values according to the (1,lambda)-ES + */ + public EsDpiNichingCma(double nicheRadius, int lambda, int expectedPeaks) { + this(nicheRadius, lambda, expectedPeaks, 0, 0); + } + + /** + * Preset all values according to the (1,lambda)-ES + */ + public EsDpiNichingCma(int lambda, int expectedPeaks, int explorerPeaks, int resetExplInterval) { + this(-1, lambda, expectedPeaks, explorerPeaks, resetExplInterval); + } + + /** + * Preset all values according to the (1,lambda)-ES + */ + public EsDpiNichingCma(double nicheRadius, int lambda, int expectedPeaks, int explorerPeaks, int resetExplInterval) { + super(nicheRadius, 1, lambda, expectedPeaks, 0, explorerPeaks, resetExplInterval, 0); + setParentSelection(new SelectBestSingle()); + setAllowSingularPeakPops(true); + } + + @Override + public String getName() { + return "CMA-"+super.getName(); + } + + public static String globalInfo() { + return "A variant of the DPI Niching ES to be usable with CMA-Mutation (Shir&Bäck, CEC'05). " + + "Remember to turn off crossover for lambda=1, and to set CMA as mutation in the individual template."; + } +} diff --git a/src/eva2/server/go/strategies/EvolutionStrategies.java b/src/eva2/server/go/strategies/EvolutionStrategies.java index 20bc4cab..a8b7603f 100644 --- a/src/eva2/server/go/strategies/EvolutionStrategies.java +++ b/src/eva2/server/go/strategies/EvolutionStrategies.java @@ -1,11 +1,8 @@ package eva2.server.go.strategies; -import eva2.gui.BeanInspector; -import eva2.gui.GenericObjectEditor; import eva2.server.go.InterfacePopulationChangedEventListener; import eva2.server.go.individuals.AbstractEAIndividual; import eva2.server.go.operators.mutation.InterfaceAdaptOperatorGenerational; -import eva2.server.go.operators.mutation.MutateESSuccessRule; import eva2.server.go.operators.selection.InterfaceSelection; import eva2.server.go.operators.selection.SelectBestIndividuals; import eva2.server.go.operators.selection.SelectRandom; @@ -43,13 +40,15 @@ public class EvolutionStrategies implements InterfaceOptimizer, java.io.Serializ private InterfaceSelection m_PartnerSelection = new SelectRandom(); private InterfaceSelection m_EnvironmentSelection = new SelectBestIndividuals(); private int m_NumberOfPartners = 1; - private int origPopSize = -1; // especially for CBN + protected int origPopSize = -1; // especially for CBN // private double[] m_FitnessOfParents = null; private boolean forceOrigPopSize = true;// especially for CBN transient private String m_Identifier = ""; transient private InterfacePopulationChangedEventListener m_Listener; + private static final boolean TRACE = false; public static final String esMuParam = "EvolutionStrategyMuParameter"; + public static final String esLambdaParam = "EvolutionStrategyLambdaParameter"; public EvolutionStrategies() { this.m_Population.setTargetSize(this.m_Lambda); @@ -63,15 +62,18 @@ public class EvolutionStrategies implements InterfaceOptimizer, java.io.Serializ } public EvolutionStrategies(EvolutionStrategies a) { - this.m_Population = (Population)a.m_Population.clone(); - this.m_Problem = (InterfaceOptimizationProblem)a.m_Problem.clone(); this.m_Mu = a.m_Mu; this.m_Lambda = a.m_Lambda; this.m_UsePlusStrategy = a.m_UsePlusStrategy; + this.m_Population = (Population)a.m_Population.clone(); + this.m_Problem = (InterfaceOptimizationProblem)a.m_Problem.clone(); this.m_NumberOfPartners = a.m_NumberOfPartners; this.m_ParentSelection = (InterfaceSelection)a.m_ParentSelection.clone(); this.m_PartnerSelection = (InterfaceSelection)a.m_PartnerSelection.clone(); this.m_EnvironmentSelection = (InterfaceSelection)a.m_EnvironmentSelection.clone(); + this.m_NumberOfPartners = a.m_NumberOfPartners; + this.origPopSize = a.origPopSize; + this.forceOrigPopSize = a.forceOrigPopSize; } /** @@ -83,7 +85,7 @@ public class EvolutionStrategies implements InterfaceOptimizer, java.io.Serializ } public void hideHideable() { - GenericObjectEditor.setHideProperty(this.getClass(), "population", true); +// GenericObjectEditor.setHideProperty(this.getClass(), "population", true); } public Object clone() { @@ -97,8 +99,10 @@ public class EvolutionStrategies implements InterfaceOptimizer, java.io.Serializ // this.m_Population.setPopulationSize(this.m_InitialPopulationSize); // } //System.out.println("init"); + checkPopulationConstraints(); m_Population.putData(esMuParam, getMu()); + m_Population.putData(esLambdaParam, getLambda()); this.m_Problem.initPopulation(this.m_Population); this.evaluatePopulation(this.m_Population); // this.m_Population.setPopulationSize(orgPopSize); @@ -128,6 +132,7 @@ public class EvolutionStrategies implements InterfaceOptimizer, java.io.Serializ protected void evaluatePopulation(Population population) { this.m_Problem.evaluate(population); population.incrGeneration(); + if (TRACE) System.out.println("ES Evaluated " + population.size()); } // /** This method allows you to set myu and lambda @@ -183,7 +188,7 @@ public class EvolutionStrategies implements InterfaceOptimizer, java.io.Serializ AbstractEAIndividual tmpIndy; AbstractEAIndividual[] offSprings; Population parents; - + if (TRACE ) System.out.println("ES From pop size " + fromPopulation.size() + " selecting parents/creating children: " + lambda); this.m_ParentSelection.prepareSelection(fromPopulation); this.m_PartnerSelection.prepareSelection(fromPopulation); parents = this.m_ParentSelection.selectFrom(fromPopulation, lambda); @@ -198,6 +203,7 @@ public class EvolutionStrategies implements InterfaceOptimizer, java.io.Serializ } protected Population selectParents(Population fromPop, int mu) { + if (TRACE) System.out.println("ES env selecting parents: " + mu + " of " + fromPop.size()); this.m_EnvironmentSelection.prepareSelection(fromPop); return this.m_EnvironmentSelection.selectFrom(fromPop, mu); } @@ -222,7 +228,7 @@ public class EvolutionStrategies implements InterfaceOptimizer, java.io.Serializ if (parents.getEAIndividual(0).getCrossoverOperator() instanceof InterfaceAdaptOperatorGenerational) { ((InterfaceAdaptOperatorGenerational)parents.getEAIndividual(0).getCrossoverOperator()).adaptAfterSelection(getPopulation(), parents); } - + // now generate the lambda offsprings nextGeneration = this.generateEvalChildren(parents); // create lambda new ones from mu parents @@ -428,10 +434,12 @@ public class EvolutionStrategies implements InterfaceOptimizer, java.io.Serializ // for internal usage protected void setPop(Population pop) { + if (TRACE) System.out.println("ES Setting pop of " + pop.size()); m_Population = pop; } public void setPopulation(Population pop){ + if (TRACE) System.out.println("ES Setting pop of " + pop.size()); origPopSize = pop.size(); // System.err.println("In ES: orig popsize is " + origPopSize); this.m_Population = pop; diff --git a/src/eva2/server/go/strategies/SqPSO.java b/src/eva2/server/go/strategies/SqPSO.java new file mode 100644 index 00000000..15e9240b --- /dev/null +++ b/src/eva2/server/go/strategies/SqPSO.java @@ -0,0 +1,45 @@ +package eva2.server.go.strategies; + +import eva2.server.go.enums.PSOTopologyEnum; +import eva2.server.go.operators.cluster.ClusteringDensityBased; +import eva2.server.go.operators.distancemetric.IndividualDataMetric; +import eva2.server.go.operators.paramcontrol.ParamAdaption; + +/** + * A thunk class preconfiguring CBN-EA to function as a sequential niching method. This + * is to be comparable to parallel and semi-sequential niching (esp. using the same convergence + * criterion). + * + * @author mkron + * + */ +public class SqPSO extends ClusterBasedNichingEA { + public SqPSO() { + this(1e-10, 15, 15); // default values + } + + public SqPSO(double epsilonConv, int haltingWindow, int popSize) { + super(); + setClusterDiffDist(Double.MAX_VALUE); + setMaxSpeciesSize(-1); + // dummy: cluster all always + setDifferentiationCA(new ClusteringDensityBased(Double.MAX_VALUE, 1, + new IndividualDataMetric(ParticleSwarmOptimization.partBestPosKey))); + // just a dummy + setMergingCA(new ClusteringDensityBased(0., 0, new IndividualDataMetric(ParticleSwarmOptimization.partBestPosKey))); + setEpsilonBound(epsilonConv); + setHaltingWindow(haltingWindow); + setMaxSpeciesSize(popSize); + setOptimizer(new ParticleSwarmOptimization(popSize, 2.05, 2.05, PSOTopologyEnum.grid, 2)); + ParamAdaption[] defAdpt = new ParamAdaption[]{}; + setParameterControl(defAdpt); +// if (threshAdaption) addParameterControl(getDefaultThreshAdaption()); + setPopulationSize(popSize); + } + +// public void hideHideable() + + public String getName() { + return "SqPSO"; + } +}