eva2/src/eva2/server/go/strategies/ClusterBasedNichingEA.java

1196 lines
52 KiB
Java

package eva2.server.go.strategies;
import java.util.ArrayList;
import java.util.Collections;
import java.util.PriorityQueue;
import eva2.gui.BeanInspector;
import eva2.gui.GenericObjectEditor;
import eva2.gui.GraphPointSet;
import eva2.gui.Plot;
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.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.distancemetric.ObjectiveSpaceMetric;
//import eva2.server.go.populations.Distraction;
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.TF1Problem;
import eva2.tools.chart2d.Chart2DDPointIconCircle;
import eva2.tools.chart2d.Chart2DDPointIconText;
import eva2.tools.chart2d.DPoint;
import eva2.tools.chart2d.DPointIcon;
import eva2.tools.chart2d.DPointSet;
import eva2.tools.math.Mathematics;
/** The infamous clustering based niching EA, still under construction.
* It should be able to identify and track multiple global/local optima
* at the same time.
*
* Copyright: Copyright (c) 2003
* 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 $
*/
public class ClusterBasedNichingEA implements InterfacePopulationChangedEventListener, InterfaceAdditionalPopulationInformer, InterfaceOptimizer, java.io.Serializable {
private static final long serialVersionUID = -3143069327594708609L;
private Population m_Population = new Population();
private transient Population m_Archive = new Population();
private ArrayList<Population> m_Species = new ArrayList<Population>();
private Population m_Undifferentiated = new Population();
private transient Population m_doomedPop = new Population();
private InterfaceOptimizationProblem m_Problem = new B1Problem();
private InterfaceOptimizer m_Optimizer = new GeneticAlgorithm();
private InterfaceClustering m_CAForSpeciesDifferentation = new ClusteringDensityBased();
private InterfaceClustering m_CAForSpeciesMerging = new ClusteringDensityBased();
// private Distraction distraction = null;
private boolean useDistraction = false;
// private double distrDefaultStrength = .7;
private double epsilonBound = 1e-10;
transient private String m_Identifier = "";
transient private InterfacePopulationChangedEventListener m_Listener;
private int m_SpeciesCycle = 1;
// from which size on is a species considered active
// private int m_actSpecSize = 2;
private int m_minGroupSize = 3;
// private boolean m_UseClearing = false;
// private boolean m_UseArchive = true;
private boolean m_UseSpeciesDifferentiation = true;
private boolean m_mergeSpecies = true;
private int m_PopulationSize = 50;
private int convergedCnt = 0;
private static boolean TRACE = false, TRACE_STATE=false;
private int m_ShowCycle = 0;
transient private TopoPlot m_Topology;
private int haltingWindow = 15;
private double muLambdaRatio = 0.5;
private int sleepTime = 0;
private int m_maxSpeciesSize = 15;
private AbstractEAIndividualComparator reduceSizeComparator = new AbstractEAIndividualComparator();
public ClusterBasedNichingEA() {
this.m_CAForSpeciesMerging = new ClusteringDensityBased();
((ClusteringDensityBased)this.m_CAForSpeciesMerging).setMinimumGroupSize(m_minGroupSize);
// if (useDistraction) distraction = new Distraction(distrDefaultStrength, Distraction.METH_BEST);
}
public ClusterBasedNichingEA(ClusterBasedNichingEA a) {
this.epsilonBound = a.epsilonBound;
this.m_Population = (Population)a.m_Population.clone();
this.m_Archive = (Population)a.m_Archive.clone();
this.m_doomedPop = (Population)a.m_doomedPop.clone();
this.m_Problem = (InterfaceOptimizationProblem)a.m_Problem.clone();
this.m_Optimizer = (InterfaceOptimizer)a.m_Optimizer.clone();
this.m_Species = (ArrayList<Population>)(a.m_Species.clone());
this.m_Undifferentiated = (Population)a.m_Undifferentiated.clone();
this.m_CAForSpeciesMerging = (InterfaceClustering)this.m_CAForSpeciesMerging.clone();
this.m_CAForSpeciesDifferentation = (InterfaceClustering)this.m_CAForSpeciesDifferentation.clone();
this.m_SpeciesCycle = a.m_SpeciesCycle;
this.m_minGroupSize = a.m_minGroupSize;
this.m_UseSpeciesDifferentiation = a.m_UseSpeciesDifferentiation;
this.m_mergeSpecies = a.m_mergeSpecies;
this.m_PopulationSize = a.m_PopulationSize;
this.haltingWindow = a.haltingWindow;
this.m_maxSpeciesSize = a.m_maxSpeciesSize;
this.muLambdaRatio = a.muLambdaRatio;
this.sleepTime = a.sleepTime;
this.convergedCnt = a.convergedCnt;
}
public Object clone() {
return (Object) new ClusterBasedNichingEA(this);
}
public void init() {
this.m_Undifferentiated = new Population(m_PopulationSize);
this.m_Undifferentiated.setUseHistory(true);
this.m_Problem.initPopulation(this.m_Undifferentiated);
this.m_Optimizer.setPopulation(m_Undifferentiated);
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();
if (m_Undifferentiated.getFunctionCalls()!=m_PopulationSize) {
System.err.println("Whats that in CBN!?");
}
initDefaults(false);
}
public void hideHideable() {
GenericObjectEditor.setHideProperty(this.getClass(), "population", true);
setMaxSpeciesSize(getMaxSpeciesSize());
setUseMerging(isUseMerging());
}
/**
* Do not reinitialize the main population!
*
* @param evalPop
*/
private void initDefaults(boolean evalPop) {
this.m_Optimizer.addPopulationChangedEventListener(this);
this.m_Undifferentiated.setTargetSize(this.m_PopulationSize);
this.m_Species = new ArrayList<Population>();
this.m_Archive = new Population();
// if (useDistraction) distraction = new Distraction(distrDefaultStrength, Distraction.METH_BEST);
convergedCnt = 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
this.firePropertyChangedEvent("FirstGenerationPerformed");
}
/** This method will init the optimizer with a given population
* @param pop The initial population
* @param reset If true the population is reset.
*/
public void initByPopulation(Population pop, boolean reset) {
this.m_Undifferentiated = (Population)pop.clone();
if (reset) this.m_Undifferentiated.init();
initDefaults(reset);
}
/** This method will evaluate the current population using the
* given problem.
* @param population The population that is to be evaluated
*/
private void evaluatePopulation(Population population) {
this.m_Problem.evaluate(population);
population.incrGeneration();
}
private void plot(int gen) {
if (!(this.m_Problem instanceof TF1Problem) && !(this.m_Problem instanceof Interface2DBorderProblem)) return;
double[] a = new double[2];
a[0] = 0.0;
a[1] = 0.0;
if (this.m_Problem instanceof TF1Problem) {
// now i need to plot the pareto fronts
Plot plot = new Plot("TF3Problem at gen. "+gen, "y1", "y2", a, a);
plot.setUnconnectedPoint(0,0,0);
plot.setUnconnectedPoint(1,5,0);
GraphPointSet mySet = new GraphPointSet(10, plot.getFunctionArea());
DPoint point;
mySet.setConnectedMode(false);
for (int i = 0; i < this.m_Undifferentiated.size(); i++) {
AbstractEAIndividual indy = (AbstractEAIndividual)this.m_Undifferentiated.get(i);
double [] d = indy.getFitness();
point = new DPoint(d[0], d[1]);
point.setIcon(new Chart2DDPointIconCircle());
mySet.addDPoint(point);
}
for (int i = 0; i < this.m_Species.size(); i++) {
mySet = new GraphPointSet(10+i, plot.getFunctionArea());
mySet.setConnectedMode(false);
Population pop = (Population)this.m_Species.get(i);
// ArchivingAllDomiating arch = new ArchivingAllDomiating();
// arch.plotParetoFront(pop, plot);
for (int j = 0; j < pop.size(); j++) {
AbstractEAIndividual indy = (AbstractEAIndividual)pop.get(j);
double [] d = indy.getFitness();
point = new DPoint(d[0], d[1]);
point.setIcon(new Chart2DDPointIconText("P"+j));
mySet.addDPoint(point);
}
}
}
if (this.m_Problem instanceof Interface2DBorderProblem) {
DPointSet popRep = new DPointSet();
InterfaceDataTypeDouble tmpIndy1;
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.setTopology((Interface2DBorderProblem)this.m_Problem);
//draw the undifferentiated
for (int i = 0; i < this.m_Undifferentiated.size(); i++) {
tmpIndy1 = (InterfaceDataTypeDouble)this.m_Undifferentiated.get(i);
popRep.addDPoint(new DPoint(tmpIndy1.getDoubleData()[0], tmpIndy1.getDoubleData()[1]));
}
this.m_Topology.getFunctionArea().addDElement(popRep);
//draw the species
for (int i = 0; i < this.m_Species.size(); i++) {
pop = (Population)this.m_Species.get(i);
plotPopConnected(m_Topology, pop);
}
if (!useDistraction) {
for (int i = 0; i < this.m_Archive.size(); i++) {
plotIndy('x',(InterfaceDataTypeDouble)m_Archive.get(i));
}
} else {
// for (int i = 0; i < this.distraction.getDistractorSetSize(); i++) {
// plotPosFit('#',distraction.getDistractorCenter(i), distraction.getDistractorStrength(i));
// }
}
}
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void plotPopConnected(TopoPlot tp, Population pop) {
DPointSet popRep;
InterfaceDataTypeDouble tmpIndy1;
if (pop.size()>1) {
for (int j = 0; j < pop.size(); j++) {
popRep = new DPointSet();
tmpIndy1 = (InterfaceDataTypeDouble)pop.get(j);
popRep.addDPoint(new DPoint(tmpIndy1.getDoubleData()[0], tmpIndy1.getDoubleData()[1]));
tp.getFunctionArea().addDElement(popRep);
plotLine(tp, pop.getEAIndividual(j), pop.getBestEAIndividual());
}
} else {
// this is an inactive species
plotIndy('+',(InterfaceDataTypeDouble)pop.get(0));
}
}
private void plotLine(TopoPlot tp, AbstractEAIndividual indy1,
AbstractEAIndividual indy2) {
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);
}
private void plotIndy(char c, InterfaceDataTypeDouble tmpIndy) {
plotPosFit(c, tmpIndy.getDoubleData(), ((AbstractEAIndividual)tmpIndy).getFitness(0));
}
private void plotPosFit(char c, double[] position, double fitness) {
DPointSet popRep;
popRep = new DPointSet();
popRep.addDPoint(new DPoint(position[0], position[1]));
double d = Math.round(100*fitness)/(double)100;
DPointIcon icon = new Chart2DDPointIconText(c+""+d);
((Chart2DDPointIconText)icon).setIcon(new Chart2DDPointIconCircle());
popRep.setIcon(icon);
this.m_Topology.getFunctionArea().addDElement(popRep);
}
/** This method is called to generate n freshly initialized individuals
* @param n Number of new individuals
* @return A population of new individuals
*/
private Population initializeIndividuals(int n) {
Population result = new Population();
result.setUseHistory(true);
result.setTargetSize(n);
//@todo: crossover between species is to be implemented
m_Problem.initPopulation(result);
m_Problem.evaluate(result);
m_Optimizer.setPopulation(result); // for some initialization by the optimizer, such as PSO memory
// capMutationRate(result, RNG.randomDouble(0.001, 0.1));
return result;
}
/**
* This method checks whether a species is converged, i.e. the best fitness has not improved
* for a number of generations.
*
* @param pop The species to test
* @return True if converged.
*/
private boolean testSpeciesForConvergence(Population pop) {
ArrayList<AbstractEAIndividual> speciesHistory = pop.m_History;
int histLen = speciesHistory.size();
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;
// 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;
}
/**
* Define the criterion by which individual improvement is judged. The original version defined
* improvement strictly, but for some EA this should be done more laxly. E.g. DE will hardly ever
* stop improving slightly, so optionally use an epsilon-bound: improvement only counts if it is
* larger than epsilon in case useEpsilonBound is true.
*
* @param firstIndy
* @param secIndy
* @return true if the second individual has improved in relation to the first one
*/
private boolean testSecondForImprovement(AbstractEAIndividual firstIndy, AbstractEAIndividual secIndy) {
if (epsilonBound > 0) {
double fitDiff = (new ObjectiveSpaceMetric()).distance(firstIndy, secIndy);
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));
}
private Population optimizeSpecies(Population species, boolean minorPlot) {
m_Optimizer.setPopulation(species);
// m_Optimizer.initByPopulation(species, false);
if (m_Optimizer instanceof EvolutionStrategies) {
EvolutionStrategies es = (EvolutionStrategies)m_Optimizer;
int mu = Math.max(1,(int)(muLambdaRatio*species.size()));
if (mu >= species.size()) {
if (TRACE) System.err.println("warning, muLambdaRatio produced mu >= lambda.. reducing to mu=lambda-1");
mu = Math.max(1,species.size() - 1);
}
es.setMu(mu);
es.setLambda(species.size());
if (TRACE) System.out.println("mu: "+es.getMu() + " / lambda: " + es.getLambda());
}
if (TRACE) {
System.out.println("Bef: spec size: " + species.size() + ", target size " + species.getTargetSize());
System.out.println("Best bef: " + BeanInspector.toString(m_Optimizer.getPopulation().getBestFitness()));
}
if (BeanInspector.hasMethod(m_Optimizer, "getLastModelPopulation")!=null) {
Object pc = BeanInspector.callIfAvailable(m_Optimizer, "getLastTrainingPatterns", null);
System.out.println("MAPSO train set bef optSpec: " + BeanInspector.callIfAvailable(pc, "getStringRepresentation", null));
}
this.m_Optimizer.optimize();
Population retPop = m_Optimizer.getPopulation();
if (TRACE) {
System.out.println("Aft: spec size: " + retPop.size() + ", target size " + retPop.getTargetSize());
System.out.println("Best aft: " + BeanInspector.toString(retPop.getBestFitness()));
}
if (retPop.size() != retPop.getTargetSize()) {
if (TRACE) System.out.println("correcting popsize after opt: " + retPop.getTargetSize() + " to " + retPop.size());
retPop.synchSize();
}
// if (useDistraction) { // distraction step
// if ((distraction != null) && (!distraction.isEmpty())) {
// System.out.println("Distraction step!!!");
// boolean distrHappened = false;
// for (int i=0; i<retPop.size(); i++) {
// distrHappened |= distraction.applyDistractionTo(retPop.getEAIndividual(i));
// }
// if (distrHappened) {
//// Object distrList = species.getData("distraction");
//// if (distr)
//// species.addData("distr", value)
// }
// }
// }
return retPop;
}
public void optimize() {
Population reinitPop = null;
if (TRACE_STATE) {
printState("---- CBN Optimizing", m_doomedPop);
// System.out.println("-Funcalls: "+m_Undifferentiated.getFunctionCalls());
}
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... ");
}
int countIndies = (reinitPop != null ? reinitPop.size() : 0) + m_Undifferentiated.size();
for (int i=0; i<m_Species.size(); i++) countIndies+=m_Species.get(i).size();
if (TRACE) System.out.println("NumIndies: " + countIndies);;
if (this.m_ShowCycle > 0) {
if (m_Undifferentiated.getGeneration()<=1) plot(m_Undifferentiated.getGeneration());
}
// 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?
m_Undifferentiated.putData(InterfaceSpeciesAware.populationTagKey, InterfaceSpeciesAware.explorerPopTag);
m_Undifferentiated = optimizeSpecies(m_Undifferentiated, false);
} else m_Undifferentiated.incrGeneration();
Population curSpecies;
// optimize the clustered species
for (int i = this.m_Species.size() - 1; i >= 0; i--) {
if (TRACE) System.out.println("-Deme " + i + " size: " + ((Population)this.m_Species.get(i)).size());
curSpecies = ((Population)this.m_Species.get(i));
curSpecies.SetFunctionCalls(0);
curSpecies.synchSize();
// if (isActive(curSpecies)) { // Lets have only active species...
if ((haltingWindow > 0) && (this.testSpeciesForConvergence(curSpecies))) {
///////////////////////////////////////////// Halting Window /////////////////////////////////////////////////
// if (this.m_Debug) {
// System.out.println("Undiff.Size: " + this.m_Undifferentiated.size() +"/"+this.m_Undifferentiated.getPopulationSize());
// 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) System.out.println(curSpecies.getBestEAIndividual());
// memorize the best one....
AbstractEAIndividual best = (AbstractEAIndividual)curSpecies.getBestEAIndividual().getClone();
// if (useDistraction) { // Add distractor!
// if (distraction == null) distraction = new Distraction(distrDefaultStrength, Distraction.METH_BEST);
// distraction.addDistractorFrom(curSpecies);
// System.out.println("** Adding distractor! " + BeanInspector.toString(distraction.getDistractionCenter(curSpecies, distraction.getDistractionMethod().getSelectedTagID())));
// }
int toReinit=0;
if (true) { //if (m_UseArchive) {
m_Archive.add(best);
m_Species.remove(i); // remove the converged Species
toReinit=curSpecies.size();
}
// else {
// // reset the converged species to inactivity size = 1
// toReinit=curSpecies.size()-1;
// deactivateSpecies(curSpecies, best, null);
// }
// those will not be optimized anymore, so we dont need to doom them, but can directly add them to undiff!
m_Undifferentiated.addPopulation(initializeIndividuals(toReinit));
m_Undifferentiated.incrFunctionCallsBy(toReinit);
// if (this.m_Debug) {
// System.out.println("Undiff.Size: " + this.m_Undifferentiated.size() +"/"+this.m_Undifferentiated.getPopulationSize());
// System.out.println("Diff.Size : " + ((Population)this.m_Species.get(i)).size() +"/"+((Population)this.m_Species.get(i)).getPopulationSize());
// }
// if (this.m_Debug) System.out.println("--------------------------End converged");
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
} else {
// actually optimize D_i
// this.capMutationRate(curSpecies, 0.05);
curSpecies.putData(InterfaceSpeciesAware.populationTagKey, InterfaceSpeciesAware.localPopTag);
Population optimizedSpec = optimizeSpecies(curSpecies, true);
this.m_Species.set(i, optimizedSpec);
curSpecies = ((Population)this.m_Species.get(i)); // reset to expected population, just to be sure
}
// }
// This is necessary to keep track of the function calls needed
m_Undifferentiated.incrFunctionCallsBy(curSpecies.getFunctionCalls());
if (TRACE) System.out.println("### funcalls: "+m_Undifferentiated.getFunctionCalls());
}
//////////////////////
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)));
// if (TRACE) System.out.println("### undiff " + ((isActive(m_Undifferentiated)) ? "active!" : "inactive!"));
// m_Undifferentiated.incrFunctionCallsBy(m_PopulationSize - (m_Undifferentiated.getFunctionCalls() % m_PopulationSize));
} //else if (TRACE) System.out.println("### undiff active: " + isActive(m_Undifferentiated));
// 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);
if (this.m_UseSpeciesDifferentiation) {
///////////////////////////// species differentiation phase
if (TRACE) printState("---Species Differentation", reinitPop);
Population[] clusters;
ArrayList<Population> newSpecies = new ArrayList<Population>();
//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]);
for (int j = 1; j < clusters.length; j++) { // loop new clusters
splitFromFirst(m_Undifferentiated, clusters[j], false);
newSpecies.add(clusters[j]);
}
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
// check if a species has differentiated any further
clusters = this.m_CAForSpeciesDifferentation.cluster(curSpecies, m_Population);
if (TRACE) System.out.println("clustered " + i + " to " + clusters.length);
if (clusters[0].size()>0) mergeToFirst(m_Undifferentiated, clusters[0], false);
for (int j = 1; j < clusters.length; j++) { // set up new species
// this is treated as a split only if more than one cluster was found
// so if clustering results in a list of size 2: [undiff,spec], the curSpecies only is maintained.
if (clusters.length<=2) clusters[j].addDataFromPopulation(curSpecies); // copy earlier data to corresponding new cluster
else splitFromFirst(curSpecies, clusters[j], true);
newSpecies.add(clusters[j]);
}
// } else {
// // small populations are kept directly
// newSpecies.add(curSpecies);
// }
}
this.m_Species = newSpecies;
if (TRACE) printState("---After differentiation", reinitPop);
//if (this.m_Show) this.plot();
} // end of species differentiation
// plot the populations
if (this.m_ShowCycle > 0) {
if ((this.m_Undifferentiated.getGeneration() <= 2)) {
this.plot(this.m_Undifferentiated.getGeneration());
} else {
if (this.m_Undifferentiated.getGeneration()%this.m_ShowCycle == 0) this.plot(this.m_Undifferentiated.getGeneration());
}
}
if (this.m_mergeSpecies && (m_Species.size()>0)) {
///////////////////////////// species merging phase
if (TRACE) {
System.out.println("-Species merge:");
}
// first test if loners belong to any species
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) {
// 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());
this.m_Undifferentiated.remove(i);
m_Species.get(assocSpec[i]).add(tmpIndy); // TODO merge information from loners?
}
}
if (TRACE) printState("---After loner-merges", reinitPop);
Population spec1, spec2;
// test if species are close to already archived solutions - deactivate them if so
assocSpec = m_CAForSpeciesMerging.associateLoners(m_Archive, m_Species.toArray(new Population[m_Species.size()]), m_Population);
PriorityQueue<Integer> specToRemove = new PriorityQueue<Integer>(5,Collections.reverseOrder()); // backwards sorted or DIE!
for (int i=m_Archive.size()-1; i>=0; i--) {
if (assocSpec[i]>=0) {
AbstractEAIndividual aIndy = m_Archive.getEAIndividual(i);
spec1 = (Population)this.m_Species.get(assocSpec[i]);
// archived solution corresponds to an existing species
if (!specToRemove.contains(assocSpec[i])) {
// the species has not yet been deactivated
specToRemove.add(assocSpec[i]);
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());
}
m_doomedPop.addPopulation(spec1);
}
}
}
int lastRemoved = Integer.MAX_VALUE;
while (!specToRemove.isEmpty()) { // backwards sorted or DIE!
int specIndex = specToRemove.poll();
if (specIndex > lastRemoved) System.err.println("Stupid queue!!!");
if (TRACE) System.out.println("Removing species at index " + specIndex);
m_Species.remove(specIndex); // warning, dont try to remove Integer object but index i!
lastRemoved = specIndex;
}
if (TRACE) printState("---After archive-merges", reinitPop);
// Now test if species should be merged among each other
for (int i1 = 0; i1 < this.m_Species.size(); i1++) {
spec1 = (Population)this.m_Species.get(i1);
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()+"]");
mergeToFirst(spec1, spec2, true);
this.m_Species.remove(i2);
i2--;
}
}
}
if (TRACE) printState("---After merging", reinitPop);
} /// end of species merging
if (m_maxSpeciesSize >= m_minGroupSize) {
// reinit worst n individuals from all species which are too large
for (int i=0; i<m_Species.size(); i++) {
Population curSpec = m_Species.get(i);
if (curSpec.size()>m_maxSpeciesSize) {
ArrayList<AbstractEAIndividual> sorted = curSpec.getSorted(reduceSizeComparator);
for (int k=m_maxSpeciesSize; k<sorted.size();k++) {
if (curSpec.remove(sorted.get(k))) {
m_doomedPop.add(sorted.get(k));
}
}
// reinitCount = sorted.size()-m_maxSpeciesSize;
// curSpec.setPopulationSize(m_maxSpeciesSize);
// if (TRACE) System.out.println("Reduced spec " + i + " to size " + curSpec.size() + ", reinit of " + reinitCount + " indies immanent...");
// this.m_Undifferentiated.addPopulation(this.initializeIndividuals(reinitCount));
// this.m_Undifferentiated.setPopulationSize(this.m_Undifferentiated.getPopulationSize()+reinitCount);
}
}
}
} // end of species cycle
// add new individuals from last step to undifferentiated set
if ((reinitPop!=null) && (reinitPop.size()>0)) {
m_Undifferentiated.addPopulation(reinitPop);
m_Undifferentiated.incrFunctionCallsBy(reinitPop.size());
}
m_Undifferentiated.setTargetSize(m_Undifferentiated.size());
// output the result
if (TRACE) System.out.println("-Funcalls: " + this.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(m_doomedPop); // this is just so that the numbers match up...
m_Population.synchSize();
if (TRACE) {
System.out.println("Doomed size: " + m_doomedPop.size());
System.out.println("Population size: " + this.m_Population.size());
}
if (m_Population.size()!=m_PopulationSize) {
System.err.println("Warning: Invalid population size in CBNEA! " + m_Population.size());
}
if (TRACE_STATE) {
printState("---- EoCBN", m_doomedPop);
System.out.println("Archive: " + m_Archive.getStringRepresentation());
}
}
// if (TRACE) {
// // this is just a test adding all species centers as distractors with high strength
// Distraction distr = new Distraction(5., 0, m_Species);
// if (!distr.isEmpty()) {
// double[] distVect = distr.calcDistractionFor(m_Undifferentiated.getBestEAIndividual());
// System.out.println("species distract best towards " + BeanInspector.toString(distVect));
// }
// }
this.firePropertyChangedEvent(Population.nextGenerationPerformed);
}
//
// /**
// * Unite all current species and the undiff. pop and return
// * the merged set. Note that data such as generation, evaluations etc
// * are not copied!
// *
// * @return
// */
// private Population getCurrentPop() {
// Population pop = new Population(getPopulationSize());
// pop.addPopulation(m_Undifferentiated);
// for (int i=0; i<m_Species.size(); i++) pop.addPopulation(m_Species.get(i));
// pop.synchSize();
// return pop;
// }
/**
* Replace the undifferentiated population with the given one.
*
* @param pop
*/
private void replaceUndifferentiated(Population pop) {
// this.m_Undifferentiated = ClusterResult[0];
m_Undifferentiated.clear();
m_Undifferentiated.addPopulation(pop);
}
private void printState(String headline, Population reinit) {
System.out.print(headline + ", Gen. " + this.m_Undifferentiated.getGeneration());
System.out.print(" - Undiff.: " + specTag(m_Undifferentiated));
System.out.print(", Demes: ");
int sum=m_Undifferentiated.size() + (reinit==null ? 0 : reinit.size());
if (m_Species.size()>0) {
sum+=m_Species.get(0).size();
System.out.print(specTag(m_Species.get(0)));
for (int i=1; i<m_Species.size(); i++) {
System.out.print(", " + specTag(m_Species.get(i))) ;
sum+=m_Species.get(i).size();
}
}
System.out.println(", reinit: " + (reinit==null ? 0 : reinit.size()) + ", sum: " + sum);
}
private String specTag(Population spec) {
return spec.size() + "("+spec.getGeneration()+((spec.hasData("MAPSOModelInformation")) ? "/"+(BeanInspector.callIfAvailable(spec.getData("MAPSOModelInformation"), "getStringRepresentation",null)) : "")+ ")" ;
}
/**
* Merge two species by adding the second to the first. Keep the longer history. The second
* species should be deactivated after merging.
*
* @param pop1
* @param pop2
*/
protected void mergeToFirst(Population spec1, Population spec2, boolean plot) {
if (plot && (m_Topology!=null)) plotLine(m_Topology, spec1.getBestEAIndividual(), spec2.getBestEAIndividual());
spec1.addPopulation(spec2);
// keep longer history
if (spec2.m_History.size() > spec1.m_History.size()) spec1.m_History = spec2.m_History;
if (spec2.getGeneration() > spec1.getGeneration()) spec1.setGenerationTo(spec2.getGeneration());
// possibly notify the optimizer of the merging event to merge population based information
if (m_Optimizer instanceof InterfaceSpeciesAware) ((InterfaceSpeciesAware)m_Optimizer).mergeToFirstPopulationEvent(spec1, spec2);
}
/**
* A split event will reset the new species model so as to have a fresh start.
*
* @param parentSp
* @param newSp
* @param startAtP1Gen
*/
protected void splitFromFirst(Population parentSp, Population newSp, boolean startAtP1Gen) {
newSp.setTargetSize(newSp.size());
newSp.setUseHistory(true);
if (startAtP1Gen) { // start explicitely as a child population of p1
newSp.setGenerationTo(parentSp.getGeneration());
newSp.m_History = (ArrayList<AbstractEAIndividual>) parentSp.m_History.clone();
} else { // start anew (from undiff)
newSp.setGenerationTo(0);
newSp.m_History = new ArrayList<AbstractEAIndividual>();
}
if (m_Optimizer instanceof InterfaceSpeciesAware) ((InterfaceSpeciesAware)m_Optimizer).splitFromFirst(parentSp, newSp);
}
// /**
// * Return true if the given population is considered active.
// *
// * @param pop a population
// * @return true, if pop is considered an active population, else false
// */
// protected boolean isActive(Population pop) {
// return (pop.size() >= m_actSpecSize);
// }
// /**
// * Deactivate a given species by removing all individuals and inserting
// * only the given survivor, sets the population size to one.
// *
// * @param spec
// */
// protected void deactivateSpecies(Population spec, AbstractEAIndividual survivor, Population collectDoomed) {
// if (!spec.remove(survivor)) System.err.println("WARNING: removing survivor failed in CBN.deactivateSpecies!");;
// if (collectDoomed!=null) collectDoomed.addPopulation(spec);
// spec.clear();
// spec.add(survivor);
// spec.setPopulationSize(1);
// }
// public int countActiveSpec() {
// int k = 0;
// for (int i=0; i<m_Species.size(); i++) {
// if (isActive(m_Species.get(i))) k++;
// }
// return k;
// }
/** This method allows an optimizer to register a change in the optimizer.
* @param source The source of the event.
* @param name Could be used to indicate the nature of the event.
*/
public void registerPopulationStateChanged(Object source, String name) {
//Population population = ((InterfaceOptimizer)source).getPopulation();
}
public void addPopulationChangedEventListener(InterfacePopulationChangedEventListener ea) {
this.m_Listener = ea;
}
public boolean removePopulationChangedEventListener(
InterfacePopulationChangedEventListener ea) {
if (m_Listener==ea) {
m_Listener=null;
return true;
} else return false;
}
protected void firePropertyChangedEvent (String name) {
if (this.m_Listener != null) this.m_Listener.registerPopulationStateChanged(this, name);
}
/** This method will set the problem that is to be optimized
* @param problem
*/
public void SetProblem (InterfaceOptimizationProblem problem) {
this.m_Problem = problem;
this.m_Optimizer.SetProblem(this.m_Problem);
}
public InterfaceOptimizationProblem getProblem () {
return this.m_Problem;
}
/** This method will return a string describing all properties of the optimizer
* and the applied methods.
* @return A descriptive string
*/
public String getStringRepresentation() {
String result = "";
result += "Genetic Algorithm:\n";
result += "Optimization Problem: ";
result += this.m_Problem.getStringRepresentationForProblem(this) +"\n";
result += this.m_Population.getStringRepresentation();
return result;
}
/** This method allows you to set an identifier for the algorithm
* @param name The indenifier
*/
public void SetIdentifier(String name) {
this.m_Identifier = name;
}
public String getIdentifier() {
return this.m_Identifier;
}
/** This method is required to free the memory on a RMIServer,
* but there is nothing to implement.
*/
public void freeWilly() {
}
/**********************************************************************************************************************
* These are for GUI
*/
/** This method returns a global info string
* @return description
*/
public String globalInfo() {
return "This is a versatile species based niching EA method.";
}
/** This method will return a naming String
* @return The name of the algorithm
*/
public String getName() {
return "CBN-EA";
}
public Population getPopulation() {
// this.m_Population = (Population)m_Undifferentiated.clone();
// for (int i = 0; i < this.m_Species.size(); i++) this.m_Population.addPopulation((Population)this.m_Species.get(i));
// m_Population.setPopulationSize(m_Population.size()); // set it to true value
return this.m_Population;
}
public void setPopulation(Population pop){
this.m_Undifferentiated = pop;
pop.setUseHistory(true);
}
public String populationTipText() {
return "Edit the properties of the population used.";
}
public SolutionSet getAllSolutions() {
// return inactive species
Population sols = (Population)m_Archive.clone();
sols.addPopulation(getPopulation());
sols.synchSize();
return new SolutionSet(getPopulation(), sols);
}
// /** Clearing removes all but the best individuals from an identified species.
// * @return The current status of this flag
// */
// public boolean getApplyClearing() {
// return this.m_UseClearing;
// }
// public void setApplyClearing(boolean b){
// this.m_UseClearing = b;
// }
// public String applyClearingTipText() {
// return "Clearing removes all but the best individuals from an identified species.";
// }
/** This method allows you to set/get the switch that toggles the use
* of species convergence.
* @return The current status of this flag
*/
public boolean isUseMerging() {
return this.m_mergeSpecies;
}
public void setUseMerging(boolean b){
this.m_mergeSpecies = b;
GenericObjectEditor.setHideProperty(this.getClass(), "mergingCA", !m_mergeSpecies);
}
public String useMergingTipText() {
return "Toggle the use of species merging.";
}
/** Choose a population based optimizing technique to use
* @return The current optimizing method
*/
public InterfaceOptimizer getOptimizer() {
return this.m_Optimizer;
}
public void setOptimizer(InterfaceOptimizer b){
this.m_Optimizer = b;
if (b instanceof EvolutionStrategies) {
EvolutionStrategies es = (EvolutionStrategies)b;
setMuLambdaRatio(es.getMu()/(double)es.getLambda());
}
}
public String optimizerTipText() {
return "Choose a population based optimizing technique to use.";
}
/** The cluster algorithm on which the species differentiation is based
* @return The current clustering method
*/
public InterfaceClustering getDifferentiationCA() {
return this.m_CAForSpeciesDifferentation;
}
public void setDifferentiationCA(InterfaceClustering b){
this.m_CAForSpeciesDifferentation = b;
}
public String differentiationCATipText() {
return "The cluster algorithm on which the species differentation is based.";
}
/** The Cluster Algorithm on which the species convergence is based.
* @return The current clustering method
*/
public InterfaceClustering getMergingCA() {
return this.m_CAForSpeciesMerging;
}
public void setMergingCA(InterfaceClustering b){
this.m_CAForSpeciesMerging = b;
}
public String mergingCATipText() {
return "The cluster algorithm on which the species merging is based.";
}
// public void setUseArchive(boolean v) {
// m_UseArchive = v;
// }
// public boolean isUseArchive() {
// return m_UseArchive;
// }
// public String useArchiveTipText() {
// return "Toggle usage of an archive where converged species are saved and the individuals reinitialized.";
// }
/** Determines how often species differentation/convergence is performed.
* @return This number gives the generations when specification is performed.
*/
public int getSpeciesCycle() {
return this.m_SpeciesCycle;
}
public void setSpeciesCycle(int b){
this.m_SpeciesCycle = b;
}
public String speciesCycleTipText() {
return "Determines how often species differentation/convergence is performed.";
}
/** TDetermines how often show is performed.
* @return This number gives the generations when specification is performed.
*/
public int getShowCycle() {
return this.m_ShowCycle;
}
public void setShowCycle(int b){
this.m_ShowCycle = b;
}
public String showCycleTipText() {
return "Determines how often show is performed (generations); set to zero to deactivate.";
}
/** Determines the size of the initial population.
* @return This number gives initial population size.
*/
public int getPopulationSize() {
return this.m_PopulationSize;
}
public void setPopulationSize(int b){
this.m_PopulationSize = b;
}
public String populationSizeTipText() {
return "Determines the size of the initial population.";
}
public String[] getGOEPropertyUpdateLinks() {
return new String[] {"population", "populationSize", "populationSize", "population"};
}
// /**
// * @return the muLambdaRatio
// */
// public double getMuLambdaRatio() {
// return muLambdaRatio;
// }
/**
* This is now set if an ES is set as optimizer.
* @param muLambdaRatio the muLambdaRatio to set
*/
public void setMuLambdaRatio(double muLambdaRatio) {
this.muLambdaRatio = muLambdaRatio;
}
/**
* @return the haltingWindow
*/
public int getHaltingWindow() {
return haltingWindow;
}
/**
* @param haltingWindow the haltingWindow to set
*/
public void setHaltingWindow(int haltingWindow) {
this.haltingWindow = haltingWindow;
}
public String haltingWindowTipText() {
return "Number of generations after which a cluster without improvement is seen as converged and deactivated; set to zero to disable.";
}
// /**
// * @return the useDistraction
// */
// public boolean isDistractionActive() {
// return useDistraction;
// }
//
// /**
// * @param useDistraction the useDistraction to set
// */
// public void setDistractionActive(boolean useDistraction) {
// this.useDistraction = useDistraction;
// }
// /**
// * @return the distrDefaultStrength
// */
// public double getDistrStrength() {
// return distrDefaultStrength;
// }
//
// /**
// * @param distrDefaultStrength the distrDefaultStrength to set
// */
// public void setDistrStrength(double distrDefaultStrength) {
// this.distrDefaultStrength = distrDefaultStrength;
// distraction.setDefaultStrength(distrDefaultStrength);
// }
/**
* @return the sleepTime
*/
public int getSleepTime() {
return sleepTime;
}
/**
* @param sleepTime the sleepTime to set
*/
public void setSleepTime(int sleepTime) {
this.sleepTime = sleepTime;
}
public String sleepTimeTipText() {
return "Let the thread sleep between iterations (nice when visualizing)";
}
/**
* @return the epsilonBound
*/
public double getEpsilonBound() {
return epsilonBound;
}
/**
* @param epsilonBound the epsilonBound to set
*/
public void setEpsilonBound(double epsilonBound) {
this.epsilonBound = epsilonBound;
}
public String epsilonBoundTipText() {
return "If fitness improves less than this value within the halting window, convergence is assumed. May be set to zero.";
}
public String getAdditionalFileStringHeader(PopulationInterface pop) {
return " Undiff. \t #Act.spec. \tAvg.Spec.Meas. \t #Archived.";
}
public String getAdditionalFileStringValue(PopulationInterface pop) {
// int actives = countActiveSpec();
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
*/
protected double[] getAvgSpeciesMeasures() {
if (m_Species==null || (m_Species.size()==0)) return new double[]{0};
else {
double[] measures = m_Species.get(0).getPopulationMeasures();
for (int i=1; i<m_Species.size(); i++) {
Mathematics.vvAdd(measures, m_Species.get(i).getPopulationMeasures(), measures);
}
if (m_Species.size()>1) Mathematics.svDiv((double)m_Species.size(), measures, measures);
return measures;
}
}
public int getMaxSpeciesSize() {
return m_maxSpeciesSize;
}
public void setMaxSpeciesSize(int mMaxSpeciesSize) {
m_maxSpeciesSize = mMaxSpeciesSize;
GenericObjectEditor.setShowProperty(this.getClass(), "reduceSizeComparator", (m_maxSpeciesSize >= m_minGroupSize));
}
public String maxSpeciesSizeTipText() {
return "If >= " + m_minGroupSize + ", larger species are reduced to the given size by reinitializing the worst individuals.";
}
public String reduceSizeComparatorTipText() {
return "Set the comparator used to define the 'worst' individuals when reducing species size.";
}
public AbstractEAIndividualComparator getReduceSizeComparator() {
return reduceSizeComparator;
}
public void setReduceSizeComparator(
AbstractEAIndividualComparator reduceSizeComparator) {
this.reduceSizeComparator = reduceSizeComparator;
}
public String[] customPropertyOrder() {
return new String[]{"mergingCA", "differentiationCA"};
}
}